Among the orchestration formats
Where this format sits against Ansible, CI pipelines, Airflow, Temporal and the rest, and the design choices argued as positions rather than listed as features.
An argument for Gantry Patterns relative to comparable orchestration systems. Written 2026-08-16; claims about external systems rest on a July 2026 web survey and carry that date.
1. The problem: workers that can report success they did not achieve
Every orchestration format in production use — Ansible playbooks, CI pipeline configs, Airflow DAGs, Temporal workflows, Amazon States Language — orchestrates deterministic steps, and a deterministic step that fails says so: an apt module returns an error code, a compile step exits non-zero, an activity throws. Failure handling can therefore be an afterthought in a vocabulary that provides its constructs elsewhere — for inventory, for triggers, for scheduling, for retry backoff.
A coding agent is stochastic and able to report success it did not achieve: it can end its session declaring a task complete while the tests are red, write a summary describing work it did not do, or produce a file whose contents are wrong and plausible. An orchestrator that trusts the worker's own account compounds the error, because the next step builds on the unverified claim.
When workers are unreliable, verification and recovery are core vocabulary. For every step the orchestrator must answer what runs next, how the step is known to have worked, what happens when it did not, and when the run is done.
Gantry Patterns is that format. A pattern is a TOML file answering all four. Gantry executes it with a deterministic interpreter containing no LLM: every intelligent act is a fresh, clean-context agent subprocess, and every truth claim is checked against deterministic evidence — a test command's exit code, a declared file's presence, the state of the git tree — before the run proceeds. The workspace is a git repository, and project checks report their result by exit code. That covers more than code — a data-curation pipeline's gate is its validation scripts and its roster is task files derived from database entries — but a workflow with no repository and no checkable truth is out of scope.
2. The format in one page
The format has nine primary words: pattern (a named sequence of steps and parameters); the producing step kinds agent, gate, and command; the composition words for, if, stop, and call; and the value word output. It has no expression language, variables, counters, or author-defined control operators; unknown fields are load errors.
The division of labor is an axiom: agents supply intelligence, deterministic checks supply truth, files hold state. The pattern only sequences. An agent step runs one fresh untrusted agent session. A gate step runs Gantry's authoritative project checks. A command step runs a deterministic command and records declared outputs. An output declares a file or directory path produced by one step, optionally with a closed values vocabulary. if routes on one fact: an output value, a step outcome, or absence.
Three further commitments:
- Directory task lists. Every list is a directory of task files.
forruns a body pattern once per file, sequentially and in sorted order. The durable ledger omits completed tasks. - Recursive
call. Unbounded repetition is a recursivecall. The loader requires tail position for every call in a call-graph cycle, so a recursive retry cannot return to stale work. - Run identity and frozen closure. At run start, the engine copies the resolved pattern closure, reachable prompts, and inline script sources into the run directory. The frozen closure and bound inputs form the run identity, so an edited pattern cannot change a resumed run.
3. The survey
Ansible playbooks
Ansible is the original inspiration: process definitions as files to read, diff, fork, and hand to someone. Gantry Patterns keeps that property and almost nothing else. Ansible's vocabulary provides constructs for inventory, ordering, and idempotence, because its workers — modules applying state to hosts — are deterministic and convergent: running a task twice is safe, and a failed task says it failed. Neither property holds for an agent, so a pattern provides its constructs for declared outputs, gates, and routing instead. Ansible also grew what its problem demanded and Gantry's refuses: Jinja2 templating, when: expressions, registered variables, loops with computed lists. Those are the right tools for parameterizing configuration across a fleet, and they are the computation the no-computation axiom excludes.
CI pipeline configs (the GitHub Actions family)
CI configs orchestrate commands that fail honestly, so their vocabulary is about triggers, caching, matrices, and artifact storage; verification is the pipeline's body, not a per-step doctrine. A CI run is stateless and event-shaped; a Gantry run is stateful, with frozen closure, resume, and recursion. GitHub Actions expression syntax (${{ }}) illustrates how a conditional feature can become an expression system.
Airflow DAGs
Airflow's vocabulary provides constructs for scheduling: DAGs of operators, cron-shaped triggers, backfills, sensor waits, executor pools. It assumes tasks whose success signal is trustworthy and whose interesting failure mode is infrastructural — a worker died, a dependency was late. An Airflow DAG is a Python program: the definition is code that must be evaluated to be inspected, which is the data-versus-program choice the Starlark entry below treats directly. A pattern is data — diffable, pinnable, hashable into a run identity, and exhaustively validated at load, none of which a definition that is a program admits.
Temporal workflows
Temporal is the precedent for one specific commitment: durable execution with deterministic replay, where a workflow's history is a first-class recorded artifact and the code that resumes it must be deterministic. Gantry's run identity and freeze semantics apply that precedent to agent orchestration. A Temporal workflow is general-purpose code in a host language, orchestrating activities that are trusted to report their own outcomes; determinism is a discipline imposed on the author. A pattern is closed data, orchestrating agents that are trusted with nothing; determinism is a property of the interpreter, not an obligation on the author. Temporal also has what Gantry lacks and section 5 concedes: replay testing, a cheap way to exercise a definition's paths without running the workers.
Amazon States Language / Step Functions
ASL is the closest format precedent: JSON, closed state types (Task, Choice, Map, Parallel), and declarative Retry/Catch policies. Gantry Patterns uses git for state and a test suite for truth. if accepts only a declared closed vocabulary, recursion is its only loop, and a command step supplies a computed predicate. Workflow Patterns literature uses "patterns" for control-flow vocabularies; here a pattern is one authored orchestration file.
Starlark / Bazel — the data-versus-program choice
Bazel faced the same tradeoff this format faced — a build definition must be deterministic and analyzable, and users keep needing more expressiveness — and answered it differently: a hermetic language rather than no language. Starlark is deliberately restricted Python — deterministic, terminating, sandboxed — and it is the strongest version of the question "why not a small safe language?" A Starlark file must be evaluated to be inspected: what a BUILD file declares depends on what its .bzl macros compute, and the analyzable artifact is the evaluation's output, not the file. A pattern is the artifact — the file the author wrote is the thing the loader validates, the diff reviewers read, the closure the run hashes — which is what admits the load-time checks and the frozen identity of section 2. A hermetic language would trade those for expressiveness the format's two standing answers (section 4) already cover.
Erlang/OTP supervision trees and Make — precedents, not competitors
Erlang/OTP supplies the division of labor. Gantry Patterns similarly puts task iteration, resume, frozen closure, and records in the engine; patterns declare evidence routes, and worker failure is a failed outcome.
Make supplies the state model. A pattern has no variables because steps communicate through named files. A declared file contract makes absence routable.
The agent-orchestration field (as surveyed July 2026)
Microsoft Conductor (announced May 2026) is the closest in intent: an MIT-licensed CLI running declarative YAML multi-agent workflows, motivated by "deterministic routing instead of LLM-based routing" — the same thesis as Gantry's no-LLM interpreter. Conductor carries Jinja2 templating and expression evaluation (the computation Gantry excludes); it targets general agent workflows such as research synthesis and design review rather than repository-grounded work; it has no gate or ground-truth concept; and it documents no freeze, resume, or run identity. The failure routing of section 4 — three routable facts, engine-forced stop for facts the pattern does not route — has no counterpart there.
GitHub Agentic Workflows (gh-aw, GitHub Next) is the closest in ecosystem position: Markdown files with YAML front matter defining agent jobs, compiled by a trusted compiler into hardened, SHA-pinned Actions workflows, harness-pluggable across Copilot, Claude, Codex, and Gemini. It shares Gantry's harness-neutrality and its compile-time validation. But it is event-triggered CI automation: one agent per trigger, no loop-until-green, no repair escalation, no fan-out with a systemic stop, no resume.
The Ralph loop lineage is a direct precedent for the problem framing: fresh context, iteration, and external verification. Gantry expresses that loop as the shipped until.toml pattern rather than an engine feature.
In-process agent frameworks — LangGraph, CrewAI, AutoGen / Microsoft Agent Framework, Mastra, and the 2025–26 academic declarative-agent-DSL papers — orchestrate LLM calls inside one application process: state is objects, verification is optional, orchestration is code. Gantry orchestrates whole harness subprocesses against a filesystem. This is a different layer, and the comparison mostly serves to keep the work from being filed under "yet another agent framework."
4. The design choices, argued
The failure doctrine
The format keeps produced values, absent outputs, and failed steps distinct. if = "output_name" routes an output value through an exhaustive branch table. Its else arm routes absence. if = { step_name = "failed" } routes a failed step. Malformed authored data is failure; a missing optional output is absence. An unrouted fact reaches an engine stop and the header's on_stop hook.
An output declaring values = ["complete", "retry", "blocked"] fails when it contains probably-done. The troubleshoot agent step's troubleshoot_revert output may be absent and its route declares that absence. A step whose failure does not matter routes it to the engine-provided identity pattern nothing with if = { <step> = "failed" } then = "nothing".
Exception-style handling (Temporal, most code-first frameworks) collapses the three facts into one thrown object and lets an unhandled case propagate to a generic handler. Retry/Catch policies (ASL) are declarative but attribute-shaped: the failure handling is metadata on the state, not a visible route in the flow, and absence is not a concept at all. CI configs mostly have continue-on-error. None of these forces exhaustiveness, and none distinguishes "the worker said no" from "the worker said nothing" from "the worker's answer was not one of the answers".
The no-computation axiom and its standing answers
A pattern performs no computation. The objection is that every "no expressions" configuration format eventually acquires them — GitHub Actions, Amazon States Language, and Helm each began with a small conditional feature and widened it into an expression system. The answer is that the format removed the conditional feature rather than resisting the widening: the retired format carried one textual-equality condition (flag), and the redesign deleted it instead of porting it. if tests exactly one fact, and there is no numeric predicate, no parameter comparison, no list-emptiness check, and no compound condition anywhere for such a feature to grow from.
Every computation has one of two answers. A judgment is an agent step with a declared closed output vocabulary. A deterministic derivation is a command step that writes a declared output. Its run shell text is outside the loader's analysis, but its decision output remains closed and load-checked.
Fork-by-copy, and its honest costs
Variance in Gantry Patterns is a copied file with an edited line and a new name. map.toml's check_streak command carries the streak threshold that stops the run after two consecutive failed tasks; change that line in a copy and the copy stops after one instead.
The cost is real: forks drift. A local fork does not receive a shipped improvement. include cannot override one definition because duplicate pattern names in a composed closure are load errors. Parameters and include reduce the need to copy a pattern, but do not update a copied pattern.
TOML as host, and its honest costs
A pattern is TOML because a pattern is data, and TOML is a diffable, universally parseable host for data with comment support.
The first cost is fragmentation. Every if arm names a pattern, so control flow becomes many named patterns. gantry pattern show renders a static diagram because the reader must reconstruct the graph across the file.
The second cost is the tail-position rule. A reader must trace a call-graph cycle to identify a loop, and an author must keep every recursive call in tail position. The loader rejects a violation.
These costs accompany the load-time checks and frozen run identity described above.
What is deliberately absent
- No human-approval step. Spec § 12 does not model operator action. Resume applies the frozen pattern without recording work done while stopped.
- No concurrency in
for. Aforis sequential. Tasks share a git worktree and run record. - No recursion ceiling in the grammar.
sprintanduntilinstead declareattempt_ceilingandmax_cyclesparameters, both defaulting tounbounded. - No variables, expressions, user-defined step kinds, or LLM in the interpreter. Lifecycle controls are runtime concerns; a prompt id does not select a role, stage, branch, or recovery policy.
The gate and completion contract are engine-owned. No pattern declares a commit, DONE row, ledger, or task-completion operation; interpreting for records task completion.
5. Open weaknesses
These are criticisms the July 2026 review left open.
- Fork-by-copy drift is unmitigated at ecosystem scale. A fork does not receive an upstream improvement.
- There is no dry-run. The interpreter's
Worldtrait has default effect methods and its tests useScriptedWorld, but no CLI dry-run exists. - Under
forwithfail = "continue", an unrouted body failure writes no pattern-authored verdict file. A dead session is recorded by the engine but does not reach themappattern's streak check. - The only loop has no bound in the grammar. A recursive retry remains unbounded unless lifecycle controls or an authored parameter stop it.
- The analyzability boundary sits at the
commandstep. The loader checks a command's declared decision output, not its shell text.
docs/patterns/positioning.md in the Gantry repository, rendered as it stands