Control Tower’s dispatch seam had, from the moment it existed, exactly one caller: engine-gateway-lab’s dispatcher. Nothing in the code technically required that specific tool (the orchestrator just shelled out to whatever gateway_run pointed at), but nothing in the design had ever been asked to prove that. A seam with one real caller and a design doc that says “this is modular” is a claim, not a fact. The fact came from a different move entirely.

Turning a caller into one of many

The dispatch seam was rewritten as a formal, runner-agnostic contract: any executable invoked as <runner> --json <spec-path> that emits a specific, documented shape on exit. Nonzero means failed or refused; exit 0 requires specific stdout JSON fields (run_id, engine, status, skipped, and a few more) that the orchestrator reads and nothing else. engine-gateway-lab’s gateway-run.sh didn’t get rewritten to fit this contract; it already satisfied it, because the contract was written to describe the seam that already existed, not to invent a new one. What changed is that it stopped being the runner and became one conforming runner. The field that points at it in a DAG spec is still called gateway_run, kept for backward compatibility with already-registered DAGs; the contract note just makes explicit that the name should now be read as “runner,” a Hekton-specific label surviving on a Hekton-agnostic seam.

The stub that proves the claim instead of asserting it

The second half of the decision is the one that actually tests the first half: a new minimal reference implementation, stub-run.sh, satisfies the identical contract with zero AI, zero cloud calls, and zero Hekton machinery. It ships in a checked-in examples/demo-project/ that a new user (standalone, no estate access, no accounts) can clone and run to watch the entire init → dag load → dispatch → close → report loop execute for real, end to end, against fake work instead of a real engine. If the stub couldn’t satisfy the same contract the real gateway satisfies, the modularity claim would have been false and this would have surfaced it immediately, instead of staying a comfortable assumption nobody had tested.

What the contract actually pins down

Three details in the contract carry real weight, not just shape. The exit code alone means failed-or-refused; nothing gets recorded on nonzero, full stop. worktree_path, when present, tells the orchestrator the runner isolated its work in a throwaway git worktree rather than touching the real repo directly, which changes where verify_command has to run: in the worktree, never against the untouched real tree, and a missing worktree directory reports as a failed verify rather than raising an exception. And the status field is read against the narrow allowlist this arc’s previous post describes in detail: the contract and the fail-closed guard are two views of the same seam, documented once so a future change to either side has exactly one place to update.

What next

Having the contract written down and a stub that satisfies it changes what “done” means for the rest of the build: every remaining feature could now be validated against something consequence-free before it ever touched a real integration partner, which is exactly the move the next post in this arc makes explicit.

What Next

Build the Fake Project First

Every remaining V1 feature gets proven against a checked-in demo project before it ever touches anything real, including the refusal path.