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.
- ReadSnapshot work and manifest.
- DecideChoose actions from the snapshot.
- ActCheck, dispatch, or advance.
- 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.
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) callsnode_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 throughbdand 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, nobd.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) andact_cleanup_fanout(line 1023), with the helperstake_claim,drop_claim,record_exitandrecord_refusal(lines 782–852). Every effect goes throughpipeline-transition.sh, the single writer ofpipeline.*state, or throughpipeline-executor.shfor a dispatch. - Exit — the tail of
run_pass()(from line 1044, status chosen around line 1115) computes the typed statusdone,suspended-human,suspended-running,ready,budget,escalatedoridle, records it aspipeline.driver_laston the scope root through therecord-runverb, and prints it as the JSON envelope that the hosts,pipeline-run.shand the/drivecockpit, 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.
| 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
| 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.
| 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.
| 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.
proposerecords the artifact awaiting acceptance.completeruns the declared checks and advances a non-terminal step.integraterecords a container’s integrated acceptance.deliverproves 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.
| 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
| 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.
| 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.
| 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
| 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 |
| 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.