CODING PLANNER

Documentation · AR Labs

Reference

Agent reference: coding-planner(7)

One screen of load-bearing facts for an agent operating the coding pipeline: routes, steps, floors, transition verbs, labels, reserved metadata and hosts.

One screen of load-bearing facts for any agent operating the coding pipeline. This page is an index: it cites the authorities, it never overrides them. Authorities: the manifest (.claude/pipelines/coding-pipeline.json), the single state writer (.claude/scripts/pipeline-transition.sh, whose header documents every verb), and the step command files. Human-facing explanation: the system reference and the guides. Unattended runs are governed by .claude/commands/coding-planner/afk-contract.md.

Model in four sentences

Work is tracked as beads; the pipeline's whole memory is reserved pipeline.* metadata on those beads, written ONLY by pipeline-transition.sh (a deterministic guard denies direct writes; VOX_PIPELINE_OVERRIDE=1 is the human-only repair hatch). A campaign root fans out into containers and leaves; a node's kind is DERIVED (step + verified fan-out), never stored. Each node walks a route of steps; leaving a step goes through complete, which runs that step's deterministic floor; human gates rest until a human answers (in a cockpit session, asking in conversation IS the gate). The driver (pipeline-driver.sh pass|decide|snapshot|digest) re-reads the world each pass and dispatches owed work; done-work is signalled by a recorded proposal (propose), never by an agent's say-so.

Routes

route steps (in order; last is terminal)
root generate-design → create-prp → refine-design → to-issues → delegate → integrate
compound generate-spec → to-issues → delegate → integrate
leaf generate-spec → implement → review → deliver

Entry: grill-me (pre-pipeline, HITL) produces a draft brief → init binds the campaign and enters at create-prp (entry_stage; generate-design is declared but unenforced). The --next flag on complete is required only at the branch point: leaving generate-spec where the compound and leaf routes disagree.

Steps

step command file gate deterministic floor resume
generate-design generate-design.md human (waivable design:waived) file_exists + section grep inspect-continue
create-prp create-prp.md human (waivable prp:waived) file_exists inspect-continue
refine-design generate-design.md (same skill, PRP in hand) human — inspect-continue
to-issues skills/to-issues/SKILL.md human fanout_birth_facts discard-redo
generate-spec generate-spec.md human file_exists + spec_frontmatter inspect-continue
implement implement.md human — discard-redo
review review.md none (floor is the gate) review_verdicts discard-redo
deliver deliver.md none — TERMINAL, own verb landed_on_target inspect-continue
delegate none (container's middle slot) none children_terminal —
integrate integrate.md human — TERMINAL, own verb integrated_acceptance + file_exists inspect-continue

Floor meanings, one line each: fanout_birth_facts — ≥1 child, acyclic, goal fields + one execution: label each, scenario coverage conserved. spec_frontmatter — bead matches filename, full base_sha, repo-relative file_scope, Gherkin present. review_verdicts — latest build/reviews/<bead>-*.md has all four axis verdicts, none FAIL, reviewed_sha == repo HEAD, and every id in the previous review's findings: dispositioned fixed|withdrawn (else owed=human) (waiver: review:skip). children_terminal — every child closed, coverage conserved, and the child set unchanged since fan-out (that last clause is skipped when no pipeline.fanout_children record exists). landed_on_target — the reviewed sha is on origin/<target> (ancestry, or forge lookup for squash merges); on pass the verb first fast-forwards the primary checkout ($VOX_ROOT) after a surface landing on main, or refuses owed=human when that is unsafe (lib/primary-sync.sh), then stamps external_ref, records delivered, and closes the leaf. integrated_acceptance — every owned scenario id must appear on a test-declaration line (an it/test/describe/context/bench call, or a def/func/fun/function or shell function definition) inside a test file (*.test.*, *.spec.*, *_test.*, test_*.*, or under a test/tests/tests/spec/specs directory) under pipeline.test_paths, and a refusal names the files where an id was found out of that position — AND pipeline.suite_cmd exits 0; absent config, or declared paths holding no test file, escalates. Known limit: a declaration inside a JavaScript-style /* */ block can still count in some layouts.

Verbs (pipeline-transition.sh; full contracts in its header)

handoff grill→bead; init bind campaign + approve brief (stamps approved_by); entry-context read-only preview; propose record gate artifact, non-advancing; complete advance through the floor (per-node; --next at the branch point only; under VOX_AFK=1 it refuses a human-gated step unless the node's own autonomy level relaxes it, and the note then names that level; a held gate on a relaxable, non-terminal step whose proposal is older than the level's silence window passes, attended with --on-silence or dispatched, with an advanced on silence note, and complete recomputes that from recorded state); waive root-scoped step waiver with ruling; set-step ungated seed/repair (audited); step/kind/target/delivery/coverage read-only; set-scenarios coverage ledger; birth-facts verify + record fan-out; set-config the integrate floor's config (--suite-cmd/--test-paths, campaign root only, ungated but audited; refuses test paths that name no existing directory) — generate-spec records it from the Test Infrastructure sweep, and init warns while it is unset; integrate/deliver terminal-step verdict verbs (under VOX_AFK=1 integrate refuses its human gate, as complete does, unless the node's dial is deep with an integration branch set; it never advances on silence); claim/release driver claims, which never expire (release --clear --reason is a person's clear of another holder's claim); record-exit/record-run typed exits (3 failed attempts → dead-letter); spec-author --bead <id> read-only, who authors this bead's spec; discover --from <finder> --parent <epic> --title <t> --description <d> files found work and doors it at the finder's autonomy in one act (refuses a duplicate title among the parent's open children and a parent whose children are the pipeline's; past discovery.max_entries_per_tree it files without dooring), printing the new id; set-queue --bead <epic> --order <ids> records the epic's order (pipeline.queue_order, audited), queue --bead <epic> [--ids <trees>] prints the run queue read-only (discovered, then that order, then ready execution:afk descendants, each with a door verdict), and queue-enter --bead <id> --epic <epic> doors an un-doored queued bead through init-task at the epic's autonomy.

Labels (outside the guard's remit — set and removed by hand)

Two exceptions write labels by machine: waive and init-task --waive-prp add the waiver label they record, and the to-issues fan-out sets one execution: label on each child.

Waivers: prp:waived · design:waived · spec:skip · review:skip. Routing: execution:hitl / execution:afk · project:<name>.

Spec author: spec:codex selects Codex authoring at generate-spec (the harness .claude/scripts/spec/vox-spec.sh writes the document; generate-spec-codex.md is the route that runs it); spec:claude is its explicit opposite; neither means the default, Claude. spec-author --bead <id> resolves it nearest-ancestor-wins and prints author=<claude|codex> from=<node|default>; a node carrying both labels refuses rather than guessing. A human sets it, and only a human: no pipeline verb writes the label. --add-label is outside the guard's remit, so that is a convention these docs state, not a constraint the machinery enforces.

Reserved metadata (read freely, write only via verbs)

pipeline.name (root marker) · pipeline.stage (the program counter) · pipeline.artifact (pending proposal) · pipeline.proposed (step=<s> date=<iso>, when it was proposed; written by propose --artifact) · pipeline.entry_artifact · pipeline.scenarios (coverage ledger) · pipeline.reviewed / pipeline.delivered / pipeline.integrated (gate records) · pipeline.suite_cmd / pipeline.test_paths (integrate config, campaign root, via set-config) · pipeline.integration_branch (presence = deep mode; unset = wave mode, human merges) · pipeline.delivery (pr | direct-commit-main) · pipeline.dead_letter · pipeline.driver_last · pipeline.autonomy (the dial: hold | hitl | wave | deep — the manifest's level table names which human gates each level relaxes; a relaxed advance still runs every floor; set via set-mode or init-task, nearest-ancestor-wins; autonomy.silence gives each level the seconds a held gate's proposal may go unanswered before the driver advances it: hitl 4h, wave and deep 2h, hold never) · pipeline.discovered_from / pipeline.discovery_root (found work's finder and the tree its entry counts against, via discover) · pipeline.queue_order (an epic's run order, via set-queue) · grill.* / grill_brief (handoff provenance).

Configuration: every setting the planner reads — manifest keys, every pipeline.* key with its writing verb and default, the project overlay, ~/.vox/config.json, environment variables, behaviour labels and agent model pins — is tabled once, with home, scope, writer, default and reader, in the system reference's Configuration page.

Hosts

Default dial: wave (a campaign with an integration branch: deep); hitl only when the owner asks to watch, hold only when the owner says the work must wait. Every host keeps going: a held gate is asked once and recorded as a question: note while other work runs, and unless its level is hold the driver advances it on silence once the level's window passes; discovered work is filed as beads, doored, and worked next; a host whose scope is done starts the next task (SKILL.md Operating Posture, drive.md step 6).

Attended: /coding-planner:drive (cockpit; held gates = asking in conversation). Unattended: pipeline-run.sh --root <bead> hosting the same driver, dispatching via pipeline-executor.sh (one headless session per step, AFK contract §4 pre-answers only the gates its table names). Either way, session death loses nothing: position lives on the beads. Reports come from pipeline-driver.sh digest --root <epic> [--since <iso>] [--json], which writes nothing and prints, one line per bead with its title, what landed (PR or commit), what waits for a person and why, what is running, and what was discovered. pipeline-run.sh --root <bead> --queue <epic> keeps going after the root rests: it drives each bead queue offers, entering un-doored ones with queue-enter, and ends when nothing untaken is left, with an envelope listing every bead that waits on a person and why.