CODING PLANNER

Documentation · AR Labs

Harness

The harness

How recorded state, evidence checks, and execution hosts carry work through the pipeline.

The execution model

The harness runs the skill’s commands against recorded work state. A driver reads that state, chooses eligible actions, and asks the transition script to record changes.

  1. ReadSnapshot work and manifest.
  2. DecideChoose actions from the snapshot.
  3. ActCheck, dispatch, or advance.
  4. ExitReturn status and reasons.

The decision function has no side effects. Each new pass reads durable state again, so work completed by a person between passes can be picked up without replaying the previous run.

An overview map of the coding planner pipeline in six regions. On the left, the entry skills, grill-me and the coding-planner router, send a campaign through the campaign door (the init verb) and a lone task through the task door (the init-task verb). The campaign door leads to the container route, which plans and splits the work into child beads and integrates it once every child has closed. Each child, and every lone task, walks the leaf route: build the leaf (spec, code and review), pass the Gate B floor that checks the review verdicts, deliver to the target branch (never to main), and pass the Gate C floor that checks the work landed there; a closed leaf hands control back to the container. Below the routes, the scripts: the runner hosts each pass of the driver, the driver dispatches the executor, and the executor runs each step in its own worktree. The driver and the steps record every change through the transition script, the only writer of pipeline state into Beads. To the left, the guard hooks check every tool call the step's session makes. Along the foot, the artifacts the steps write and the floors read: DESIGN.md and the PRP, the spec, the review artifact, and the branch with its pull request.
Fig 02.1 The pipeline at a glance: where work enters, the two routes it walks, the scripts that run it, the hooks that guard it, and the files it leaves behind.

The detailed map, with every step and file, is on its own page.

Where this lives in the code

The four phases above are four parts of one script, .claude/scripts/pipeline-driver.sh. The header of that script names a fifth phase, reconcile: it is not a separate pass but the act_reconcile action that the decide phase chooses when a node's recorded step lags behind its evidence.

  • Read — build_snapshot() (line 355) calls node_json() (line 264) once per bead and emits one JSON document holding every node's step, kind, claim, typed exit and dead-letter state, with the manifest embedded. It reads through bd and the transition script's read-only verbs (step, kind, autonomy, target, delivery).
  • Decide — decide_actions() (line 767) is the pure core: the snapshot on stdin, an action list on stdout, no side effects, no clock, no bd. pipeline-driver.sh decide --snapshot <file> replays it against a canned snapshot and gives the same answer every time.
  • Act — act_dispatch (line 967), act_reconcile (line 866), act_finalize (line 914) and act_cleanup_fanout (line 1023), with the helpers take_claim, drop_claim, record_exit and record_refusal (lines 782–852). Every effect goes through pipeline-transition.sh, the single writer of pipeline.* state, or through pipeline-executor.sh for a dispatch.
  • Exit — the tail of run_pass() (from line 1044, status chosen around line 1115) computes the typed status done, suspended-human, suspended-running, ready, budget, escalated or idle, records it as pipeline.driver_last on the scope root through the record-run verb, and prints it as the JSON envelope that the hosts, pipeline-run.sh and the /drive cockpit, read.

State and routes

Each work item is an issue, or bead. A campaign is the root of a tree. A node with a verified split into children is a container; the remaining nodes are leaves, where code is written.

pipeline.stage records a node’s current step. Issue status records its lifecycle. The manifest declares the routes, commands, checks, and resume policies.

Declared routes
Node Step sequence
Root generate-design → create-prp → refine-design → to-issues → delegate → integrate
Compound generate-spec → to-issues → delegate → integrate
Leaf generate-spec → implement → review → deliver

The enforced campaign entry is create-prp. The preceding generate-design step is declared but optional in practice. See the entry boundary for initialization and fresh-session checks.

Checks and approvals

Three controls with different jobs
Control Question Behavior
Guard Is this tool action prohibited? Blocks a recognized hazard. On a guard fault, allows the call.
Floor Does the evidence meet the declared check? Passes, refuses incomplete work, or escalates when it cannot check.
Human gate May this step be accepted? Waits for approval unless the configured autonomy level permits advancement.

A command performs work. A floor checks specific properties of its result. A human gate provides judgment where declared; a floor’s pass alone does not establish the quality of a document.

Checks declared for each step
Step Deterministic checks Human gate by default
generate-design File exists; required design headings are present. Entry wiring is incomplete. Yes
create-prp File exists. Yes
refine-design None declared. No
to-issues Child provenance and coverage at creation. Yes
generate-spec File exists; spec frontmatter and a Gherkin scenario are present. Yes
implement None declared. Yes
delegate Children finished; sanctioned child set and coverage conserved. No
integrate Integrated scenario coverage, passing suite, and reconciliation file. Yes
review Four verdicts and reviewed commit match the current code. No
deliver Reviewed code has reached the integration target. No
What the spec and review checks establish

A spec must identify its bead, carry a full base commit SHA, declare a non-empty list of repository-relative file paths, and include a Gherkin scenario. The bead identity must match the filename.

The latest review must contain Standards, Spec, Architecture, and Evidence verdicts, with no FAIL. WARNING passes and is reported. Its reviewed SHA must equal the repository’s current HEAD; that same SHA is recorded for delivery to verify.

The coverage ledger

Three of the checks above ask about coverage. They read one ledger that runs the length of a campaign: the work promises a list of behaviors at the start, and cannot finish until each promise can be pointed at a test that carries its name.

Acceptance criteria are Gherkin scenarios, each carrying a short identifier such as S1. The prose of a scenario can change; the identifier is meant to stay fixed, because every later step holds it as a handle. pipeline.scenarios records which identifiers a node owns.

What each step does with the ledger
Step Effect on the ledger
create-prp The requirements document states the scenarios and assigns their identifiers.
to-issues The campaign records the whole set; each child records the subset it will deliver. The check requires the children’s claims to union to exactly the parent’s set.
implement Each test discharging a scenario carries that scenario’s identifier in its test name.
delegate The same conservation check runs again, after the children finish rather than at their creation.
integrate Every owned identifier must appear on a test declaration inside a test file under the declared test paths, and the declared suite must exit 0 on the integrated result.

The integration check is existence plus one green suite, and not per-scenario pass attribution. It establishes that no promise reached the end of the campaign unrepresented and that the assembled work is green.

Why existence and the suite are separate

The existence audit catches a promise that never became a test. A suite reports on the tests that exist and says nothing about a test nobody wrote, so a green run cannot detect that failure.

The suite run is the first execution of the combined work. Each leaf developed on its own branch and passed the suite there, in its siblings’ absence.

Existence counts an identifier only on a line that declares a test, inside a file whose name or directory marks it as a test, so a comment or a mention in source code does not satisfy it. It still cannot distinguish a thorough test from a trivial one, it counts the identifier anywhere on the declaring line rather than only in the test’s name, and a skipped test still carries its identifier. These limits are asserted as limits in the check’s own tests.

Two values configure the audit: pipeline.suite_cmd, the command that runs the whole suite, and pipeline.test_paths, the directories holding tests. Both are facts about the project rather than the campaign, so they are recorded once on the campaign root and read from there. Paths resolve against the VOX repository root, so a project in its own repository declares them in the ../<repo>/… form. An unset value escalates rather than refuses: missing configuration is not incomplete work. The set-config verb writes both values on the campaign root, refusing test paths that name no existing directory; generate-spec records them, and init warns while they are unset.

No pipeline step writes either value. A person sets them by hand; a sanctioned write seam is commissioned work.

Recording a transition

pipeline-transition.sh owns reserved pipeline metadata. Commands submit work through its verbs; the driver uses the same writer.

  • propose records the artifact awaiting acceptance.
  • complete runs the declared checks and advances a non-terminal step.
  • integrate records a container’s integrated acceptance.
  • deliver proves landing, records delivery, and closes a leaf.

The guard hook blocks direct writes to reserved metadata and retired workflow statuses. Transition notes retain the action and reason. Hook enforcement depends on correct registration in the host.

Dispatch and recovery

The driver first checks whether existing evidence satisfies an eligible step. If a step also has a command and its floor refuses, the next pass can dispatch the missing work. Completed work is signaled by a recorded proposal.

How runs continue safely
Mechanism Behavior
Node claims The driver writes its name on a node before acting and removes it after. Someone else’s name, however old, means the driver leaves the node alone and tells a person whose it is. A name never expires; only its holder or a person removes it.
Attempt limits Failed attempts count per step. At the limit, the node is parked for intervention.
Resume policy The manifest specifies either discarding partial work and redoing it, or inspecting and continuing.
Sibling file scope Ready siblings with overlapping declared files are serialized. Undeclared scope provides no overlap protection.
Empty check list The driver cannot infer completion from an empty list of checks. Proposal and gate handling still apply.

Authors run in fresh dispatched contexts. The orchestrator handles decisions and gates; the reviewer uses a separate model and context.

How a claim works in the code

The claim lives in the bead metadata key pipeline.lease as holder=<token> since=<time>, with no expiry. pipeline-transition.sh claim writes it and refuses whenever another holder’s claim is present; release --holder removes the holder’s own claim. A person who judges the holder dead clears it with pipeline-transition.sh release --bead <id> --clear --reason "<why>", which a dispatched session cannot run and which counts no attempt. In the driver, the decide phase rests a node carrying another holder’s claim as running with the options wait and clear-claim, and the act phase claims through take_claim and releases through drop_claim.

Interactive and unattended hosts

Two hosts, one driver
Host How it operates
Interactive cockpit /coding-planner:drive invokes passes, explains stopped branches, and asks for decisions.
Headless runner pipeline-run.sh invokes passes through an executor until the run rests or reaches a configured limit.

The executor starts one headless session per dispatch. The runner’s pass cap prevents a loop from continuing indefinitely without progress.

Read the result

A pass returns a JSON envelope with a run status and per-node reasons. Run statuses include done, ready, suspended-human, suspended-running, budget, escalated, and idle.

Exit 0 means the verb ran; exit 1 is a refusal; exit 2 is a machinery fault. Driver and runner output is structured. Transition refusals still use plain text on stderr.

Autonomy levels

A node uses its own configured level, otherwise the nearest ancestor’s. With no level set, every human gate holds. Every declared floor remains active at every level.

Which human gates can be relaxed
Level Gates it may pass
hitl None.
wave Spec generation and implementation.
deep Spec generation, implementation, fan-out, and integration. Integration additionally requires a campaign integration branch.

Planning gates cannot be relaxed. Each relaxed advancement records the level and the node that supplied it. A spec step whose spec records no leaf-or-compound verdict still rests for a person; see generate-spec.

Integration and landing

Where code lands and how it lands are separate settings.

Integration target
Mode Base and target Merge boundary
Wave Each piece starts from and lands on main. A person merges the pull request. Delivery waits for proof.
Deep Pieces start from and land on an existing campaign branch. Delivery can merge there. The final campaign merge to main remains a person’s job.

Pull requests are the default delivery method; a standing project rule may select direct commits. Autonomy levels configure approvals separately from the integration target.

Delivery accepts either the reviewed commit’s ancestry on the target or a forge-confirmed merged pull request whose merge commit is on the target. The second proof handles squash merges. The pull request link is recorded after proof succeeds.

A container finishes by checking the integrated suite and reconciling the assembled result with its design. Its children being closed is a prerequisite, not the full acceptance test.

Implementation reference

Source files
Component Source
Manifest coding-pipeline.json
State writer pipeline-transition.sh
Driver pipeline-driver.sh
Runner and executor pipeline-run.sh · pipeline-executor.sh
Guard bd-pipeline-guard.ts
System reference The system reference
Every other file in Fig 02.1, by directory
Directory Files
.claude/skills/ grill-me/SKILL.md · coding-planner/SKILL.md · to-issues/SKILL.md · doc-review/SKILL.md
.claude/commands/coding-planner/ generate-design.md · create-prp.md · integrate.md · generate-spec.md · generate-spec-codex.md · implement.md · review.md · remediate.md · deliver.md · drive.md · afk-contract.md · retired, kept for reference: detail-planner.md, quick-context.md, spec-zoom.md
.claude/scripts/ project-resolver.ts · review/vox-review.sh · review/vox-review-blocked.sh · spec/vox-spec.sh · tests/run.sh
.claude/hooks/ and .claude/ guard-enforcement-surface.ts · guard-worktree-edits.ts · settings.json (registers the hooks)

For limitations and deferred mechanisms, use Status & next steps. The decisions that shaped the harness are in the lab's dated build records, kept in its repository; the incident records give the failures that prompted them.