Merge is ignition
You write a feature plan, open it as a pull request, and merge it. That merge is the ignition. Nothing else arms, schedules, or approves the work.
Why it is built this way
Every automation I have run eventually needs an answer to what authorised
this? The usual answers are a flag, a label, a queue entry, or a comment — all of
which some piece of software can write. So I made the authorisation something only a
human's reviewed merge produces: reachability on main.
A plan file that is valid but sitting on an unmerged branch is invisible to the dispatcher by design. Linting checks validity, not authority.
The corollary is uncomfortable and worth stating plainly. A repository where an agent
account can push, approve, or merge main does not qualify, and the watcher
refuses it outright rather than degrading. That includes the convenient case: if a merge
queue merges under its own account, then a queue that can merge is a queue that can
ignite.
The flow
What follows is the design. What is actually built is a shorter list, and it is at the end.
① An interactive session drafts the work: an umbrella
plan.md plus one plans/NNNN-slug.md per feature. Each feature
plan carries front matter — status, zone, risk tier, the file paths it may touch — and a
Boundaries section stating what not to do and why. It opens as a plan pull
request.
② Merging plan.md does nothing. It is documentation.
That distinction is the whole of the design: the umbrella is for humans, the per-feature
file is the ignitable unit.
③ Merging a feature plan with status: ready starts
exactly one orchestrator for that feature. One merge per feature, one go/no-go per
feature. The alternative — merge the umbrella, fan out every feature it lists — is a
single approval spending several sessions' worth of tokens with no per-feature stop.
The watcher polls allowlisted, branch-protected repositories, finds
ready plans on main with no work pull request against them, assigns a lease
on the branch the work will use, and spawns one process. It is deliberately mechanical:
it never edits a repository, never talks to a model, and never acts on staleness. When
an orchestrator dies it surfaces that once and stops. Restarting is a human comment, and
that authorisation is consumed rather than standing, so one old instruction cannot
restart every future failure.
④ The orchestrator is a deterministic wrapper around a judgment core, and the rule that shapes it is:
Every guarantee lives in the wrapper, as code. Only judgment lives in the prompt.
A guarantee written as prompt text is a hope. So the wrapper holds the lease, heartbeats, builds a worktree, and constructs the implementer's prompt by concatenating a fixed template with the merged plan verbatim. Copying is deterministic; a model asked to "include the plan" is not.
⑤ The implementation session runs in that worktree. Which harness it uses is a routing decision rather than a constant: a local model on the GPU box costs no subscription, a hosted model costs quota or dollars. Same plan, different runner, is how you run a bakeoff.
⑥ Review is a separate session with a separate mandate, and the reviewer's prompt is fixed text parameterised only by structured facts — repository, pull request number, head commit, plan path. The orchestrator supplies those and nothing else, because it is graded on approved: letting it author the review mandate would let it steer the reviewer away from its own weak spots without ever touching the reviewer's credentials. Separating coder from reviewer has to hold at the mandate level, not just the credential level.
Blocking findings go back for a fix pass. The loop is bounded by a per-repository
setting, and exhausting it is a hard stop that notifies a person — not an escalation to
a stronger model, and not an open-ended loop. Findings worth doing but not blocking come
back as drafted plan files in one follow-up pull request at the end of the run, at
status: draft. Nothing arms until a human merges it. That is the only
answer consistent with the rest: a system whose findings auto-queue their own work has
quietly become a system that authorises itself.
⑦ You merge, and that closes the feature. ⑧–⑪ are the same pipeline for the next feature, running at the same time. A separate branch lease means a separate worktree, so two plans in one repository do not collide.
The invariant
Every human gate in the diagram is a human. The orchestrator has no approval code path — not a permission it lacks, an absence the test suite asserts. Credential separation alone would not be the control, because the thing being graded is whether this got approved.
What is actually built
The watcher ships. The orchestrator ships in part: lease, heartbeat, worktree, plan read, prompt construction. The implementation session, review loop, comment protocol and post-approval freeze are specified and not yet wired, and the wrapper refuses to arm while that is true.
That last refusal took a while to get right, and the reasoning is worth keeping. Once a file exists at the path the watcher spawns, the spawn succeeds — so a half-built wrapper that merely exited would convert a loud failure into a plan that looks armed and is not. Ignition must never look armed when it isn't. The boundary between built and unbuilt is therefore an explicit stop that names the phase it has not reached yet.
Still open: spawning onto a second host; whether a collaborator's runs use a shared team subscription or their own credentials; and per-run cost attribution, which is a report rather than plumbing, since session history is already collected.
This page and its diagram are hand-mirrored from a single written source, kept beside them, so that a change to one is a change to all three rather than a page that drifts from its own figure.