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.

Flow diagram: an interactive session writes feature plans; merging a per-feature plan file ignites a watcher, which spawns one orchestrator per feature; each runs an implementation session and a bounded review loop before a human merges. Two features run in parallel.
Scroll sideways for the second, concurrent feature track. Green is shipped, amber is partly built, blue is specified and not yet wired, purple is a human.

① 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.