In an orchestrator-driven run, the engine dispatches the agents, meters the spend, and moves the state file forward. The design's answer to "who is actually in charge?" has four parts: a prohibition built into the code, a shared-file discipline both writers obey, one authority per deployment, and an autonomy level that is earned and measured instead of declared.
A structural "never"
gateline has two operating modes. In v0 a human plays the Orchestrator
role, dispatching each agent by hand and moving state.yaml
through its phases. In v1 an engine executes the same role spec: the specs
are identical in both modes, and only who executes
orchestrator.md changes. The engine is a stateless
reconciler. It wakes on a trigger, reads
state.yaml at the run branch tip, derives the next action from
committed files alone, executes it, commits, and exits. No conversation state
survives between wakes, so waiting at a gate costs nothing: a gate wait is
simply a state from which no action derives.
The load-bearing safety property is structural. The engine has
no code path that writes gates.*.approved: "the
orchestrator never approves a gate" is not an instruction a model is asked to
follow but a property the code cannot violate. The one gate entry a
machine writes is the pull-request approval sync, which copies a human
reviewer's PR approval into G2 under that reviewer's name; it runs in
Gatehouse's server and gateline sync, never in the engine. A
run's closure
block sits on the same read-only list, so the engine can pause a run and
never end one. The role spec says the same in its own grammar: the
Orchestrator owns the gates in the sense that it presents them, and it must halt until a named human records approval. And a
deterministic loop that meets a state it has no rule for escalates, where a
model in the same position would improvise.
One file, two writers
state.yaml is the entire control plane in both directions:
there is no orchestrator API, config channel, or command queue. Humans speak
through Gatehouse, the framework's web UI, and the gateline CLI;
the engine speaks by advancing state that the human surfaces recognize as
needing a person. Two writers on one file need rules, and the co-writer
contract supplies seven. Every write is a compare-and-swap ref update. YAML
edits preserve comments and formatting, through a document API rather than
re-serialization. Timestamps are written in ISO-8601 form. Commit messages
follow a structured grammar under distinguishable identities. Review-round counts live
only in state.yaml. Phase, status, and pause values stay within
the contract's enums. And a gate decision is legal only for the gate on the
table. That last rule means the profile's first undecided gate, while the run
stands in a phase that gate is decided in. Gatehouse, the CLI, the
API, and the pull-request approval sync all refuse an out-of-order
approval.
Compare-and-swap (CAS) is what makes the two writers safe. Each writer reads the branch tip, makes its edit, commits, and then updates the ref only if it still points at the commit it read. If the ref moved (a human decided mid-tick, an agent committed), the update is refused; the loser discards its work and re-derives from the new tip. A refused write is the designed outcome of a race, not an error, and it is how a human decision always wins: the engine never forces its bookkeeping through.
Provenance is legible at a glance. Engine commits are authored by a
dedicated bot identity (gateline-orchestrator, one per install),
never a person's git config, and they use a reserved set of bookkeeping
verbs: dispatched, bounced, advanced,
escalated, paused, metered,
harvested. The decision grammar (G2 approved by
<name>, staged by <name>, armed by
<name>, closed by <name>, reopened to
<phase> by <name>) is reserved for humans, and the
metrics reader treats it as authoritative. A run's branch history therefore
reads like a transcript of the partnership:
c4b9d95 state(creation-seam): G2 approved by Nathan Carter [burden: confirmation]
fdef5f2 state(creation-seam): metered verifier $3.11
The creation-seam record shows both hands in the same file:
ledger entries written by the engine, each carrying the dispatch's cost and
the token counts the harness reported (the input count leaves out cached
context, so the cost column is the one to trust), and
gate entries written by the named human, each carrying a burden rating of how
much work the review took. Its engine commits carry the bot name of their
day, agentic-orchestrator. Finished runs keep the author that
wrote them, and the current identity is
gateline-orchestrator.
Caps, pauses, and the escalation path
Autonomy inside a phase has hard edges. Implementer ⇄ Reviewer cycles are
capped at three rounds per work item; a fourth round escalates automatically,
with both sides' artifacts. Resolving that escalation grants one more round,
and the engine asks again for each extra round. Spend is capped too: every
model invocation flows through the dispatch seam and is metered into the
run's budget ledger, and before any dispatch the engine projects the ledger
sum plus a static per-role estimate against the run's cost limit. A projected
exceedance pauses the run as budget-exhausted and escalates. The
engine never degrades quality to fit the budget. Resuming from that pause carries the budget decision: gateline resume
--cost-limit <usd> writes a higher limit in the same commit as
the phase restore, and a resume without one is refused, because the pause is
a condition the engine recomputes on every tick.
Metering is unconditional, but enforcement is a switch. Operators on
flat-rate-billed harnesses can start the engine with
--no-budget-enforcement; the ledger, derived totals, and token
counts record regardless. Pause-don't-degrade governs enforcement, not
measurement. The creation-seam run shows both halves in one
incident.
With the ledger at $44.02, the engine projected $68.02 against the run's $60 limit, paused, and escalated. The human resolved the escalation by restarting the engine with budget enforcement disabled and resuming; metering kept running, and the record closes at $104.54 spent against the original $60 limit: the machine's stop, the human's override, and the full cost all legible in the same file.
Approve-and-hold. Sometimes signing a
gate should not start the next phase: another human decision still stands in
between. gateline approve --hold <reason> signs the gate
and sets phase: paused in the same commit; the held run is an
ordinary rest state, and resume releases it into whatever phase the gate
ledger implies. Signing and advancing are separate acts so that a signature
can never accidentally launch work.
One authority per deployment
The control plane has one authority per deployment. Gatehouse and the
engine run as one supervised unit over one clone (the gateline
up topology) with one sync loop and one push path; the git host's
origin is the linearization point, so a dispatch whose intent commit origin
rejects is never launched. An origin that is merely unreachable does not
block the launch, because the clone is only ahead, and a later accepted push
carries the commit. A deployment can also run
local-only, with no push, no fetch, and no GitHub calls
(gateline up --local-only); it is a named, first-class
topology. The rule was learned from
incidents: the topology design records a day of split deployment in which an
engine kept deriving against a stale budget limit, and machine bookkeeping
and human decisions accumulated on divergent histories that needed manual
repair.
The blessed checkout stays on the default branch, and a code-tree monitor
enforces it. Only a clean fast-forward of the default branch counts as an
update; on one, the engine drains in-flight work and exits with code 75: a
deliberately ordinary "temporary failure" exit that a supervisor restarts
onto the new code (unsupervised, the operator restarts it by hand). A dirty
tree, a branch switch, or
any non-fast-forward movement pauses dispatch instead. The engine never
dispatches on mixed code, and it never pulls: updating is always an operator
act (git pull, or gateline self-update). Unmerged
changes are tried from their own worktree with gateline ui,
never up, so unreviewed code is never put in charge of live,
metered dispatch.
Autonomy is earned, and measured
The framework treats orchestration autonomy the way you would treat a new
hire's: v0 exists so the team builds a calibrated sense of where agents are
strong before granting routing autonomy. The promotion criterion is explicit
(gate reviews have become confirmations rather than corrections),
and it is measured: the gate frontend records a burden (confirmation,
light-correction, or heavy-correction) in the act
of deciding, and hand-recorded decisions are expected to set it, so the
criterion is checkable from the record where decisions flow through the
frontend, while older or hand-entered decisions may lack it.
Autonomy arrives across a milestone ladder, and the ladder has a gate of
its own. M0 lands the contracts and a hand-kept ledger. M1 runs the engine in
shadow mode (a dry-run tick against finished v0 runs,
comparing each derived action to what the human orchestrator actually did);
the bar was three full runs with every disagreement dispositioned as an
engine bug or a design finding. M2 is the autonomous loop on one vendor, and
it could not begin until the promotion criterion was credibly met: a
sustained majority of confirmation burden across v0 gate
decisions. M2 has since run: the engine has driven real runs against this
repository, creation-seam among them, with humans acting only
at gates and escalations. M3 adds cross-vendor dispatch with the vendor pins
enforced at dispatch time; M4 hardens crash recovery and isolation, and
per-task worktrees, session resume, and crash recovery are in place.
The shadow record shows the bar working as design review. Against the first v0 run the engine's derived action matched the human's on 8 of 19 steps, and the disagreements traced to bookkeeping gaps the design then closed; by the later runs agreement reached 18 of 22 and 16 of 23 steps, with the remaining disagreements specific, fixable defects. Autonomy here is not a switch that ships: it remains gated on the measured criterion, and no mode of the framework is "fully autonomous."
The third page covers the other half of the trust story, how gateline stays vendor-neutral, so no model or harness is baked into the pipeline. For the decision commands themselves, see the CLI reference; for every field the two writers share, the state.yaml reference.