| name | build |
| description | Execute a planned multi-phase task — resume from /plan's committed artifacts and run each phase red→green→refactor inline in the main session under hook enforcement and script gates. Generation stays in one hot context; agents are spawned only to verify (spikes already done, review lanes, refuters, seam, provenance, drive). One final user gate over the whole task diff — for large tasks (15+ files), after /plan. |
| argument-hint | Path to a planned task (e.g., docs/tasks/043-harness-redesign) |
Build
Execute a task whose intent is data (tests.manifest.json ratified at /spec) and whose plan is committed (plan/ landed by /plan). You — the main session — generate: tests, code, fixes, commits, all in one context. Agents are spawned only where a fresh, independent context is the point, never to re-do work you already hold: review lanes, refuter votes, the seam lane, provenance, the behavioral drive. Coordination is scripts and files, never agent prose.
Bootstrap — fail loudly
Verify before anything; a missing item means an upstream slice has not landed. STOP and report — never emulate a script by hand.
- Plan artifacts in
<task-path>: plan/checklist.json and plan/phase-<id>.md per phase, committed. Absent → /plan has not run; stop.
- Spec artifacts:
requirements.md (R<n> ids), tests.manifest.json (schema 1, rows status: ratified), mechanics.md when written.
- Scripts in
~/.claude/skills/build/bin/: scaffold, plan-lint, red-gate, green-gate, close-gate, collect, seams, join, selftest. --help on each (no task path) is the contract.
- Hooks in settings.json: prod-write blocker (PreToolUse on Edit|Write|MultiEdit) and format-on-write (PostToolUse). Verify with
grep -rl red-first ~/.claude/hooks/ — absent hooks mean red-first is unenforced; stop.
- Not on
master/main. On a default branch, stop and ask for a working branch.
- Background pre-authorization:
permissions.allow covers the project's test/build commands (backgrounded deep set stalls on an unallowed command).
Data plane — three stores, one precedence rule
<task-path>/tests.manifest.json — INTENT. User-owned, hook-locked. Rows: {id, phase, tier, mode, fqn, drives, given, when, then, requirement, status}. Decision rows: {id, text, enforcement}. Gates reject any test not traceable to a ratified row (amendments below).
<state-dir>/receipts.jsonl — EVIDENCE. Append-only, written by gate scripts only. <state-dir> = ${XDG_STATE_HOME:-$HOME/.local/state}/claude-build/<repo-basename>/<task-id>. One line per gate event: {item, gate, rows, testDigest, reportDigest, head, modes, verdict, at}; task-tier attest receipts are {item: "task", gate: "attest", rows, evidence, at}.
<task-path>/plan/checklist.json — STATE. The projection you and scripts read. Per phase: {id, slug, kind, deps, status, rows, touches, stubAllow, testExceptions, validation, checks, timeboxMin, pct, startedAt, receipts, runs, findings}. Statuses: pending → red → green → closed, plus blocked (refactor runs while status stays green). base is the plan-commit anchor.
Precedence: receipts outrank the checklist, which outranks memory of this conversation. Resume is one rule: the first non-closed phase, first non-done item. Reconcile checklist claims against git log <base>..HEAD and receipts before continuing a resumed task.
Load on demand — do not front-load
Context is a cache, not an inventory. The plan already settled the what and why; the codebase answers how. So each phase's mandatory reads are only three: its checklist entry, its plan file (plan/phase-<id>.md), and its rows in the manifest. Everything else — mechanics sections, source under drives/touches, sibling tests — loads when a row you are writing needs it, by grep/read at that moment, not before. A question that cannot be answered without re-opening a decision is a spec defect: file an amendment, never re-deliberate in-session. This is the whole reason plan and build are separate sessions — do not re-import the plan's deliberation.
Step 0: Resume
- Parse
<task-path>. Read base from checklist.json (do not recompute). Slice requirements.md for the Deferred ledger and narrate it once before the first phase (grep -A, not a full read).
bin/selftest <task-path> — runs the gate bite suite, writes a harness receipt {buildlibDigest, suite, verdict}. You are about to trust the gates; this proves they bite at the current digest. Cite the receipt in any verifier prompt that references the gates.
- Read the check commands (fast gate, deep set, benchmark) from
CLAUDE.md/the manifest when a step first needs them.
Step 1: Phases — inline red→green→refactor
Repeat until every phase is closed: take a pending phase whose deps are all closed with no open blocker. Serial. (Parallel disjoint phases are not spawned as isolated authors here — the whole point is one generating context; genuinely independent tracks are separate /build runs.)
Load the three mandatory reads. Confirm the phase's validation command runs — a broken command is a blocked return, not something to improvise around.
Arm the phase FIRST. Set startedAt on the checklist entry and save, while status is still pending. This activates the write-blocker for the phase's touch scope — without it the hook's startedAt guard short-circuits and red-first is silently off. Do this before writing anything.
- Red (per row
mode): write the phase's tests as direct instantiations of its rows — the row's fqn names the test, its then values are the assertions, its drives seam is the signature you code against. assertion-red fails on an assertion; compile-red is legal only when the row declares it; characterization passes green-first against current behavior; infra/docs kinds get the generalized probe demonstrating the gap. stubAllow globs are your only production exception (outside-in interface stubs). Then run bin/red-gate <task-path> <phase> — it computes digests, appends the receipt, flips pending→red; your claims change nothing. If it rejects a test as un-ratified, that test needs an amendment (bin/join --propose) — never sneak it in, never silently drop it. When a test or its comment names the invariant it verifies, write the invariant's content ("a taken-down planner can't republish"), never the bare INV<n> id — the id is spec-internal and provenance-lint blocks it in committed code.
- Green: the minimum production code to flip the receipt's FQNs. The write-blocker lifts once the red receipt exists. Iterate on the scoped validation command (seconds); never widen scope to "check everything" — the close gate owns breadth. Test files are frozen:
green-gate verifies byte-identical digests, so editing a test to fit code is a failed gate, not a shortcut. Run bin/green-gate <task-path> <phase>.
- Refactor: improve structure with the green tests as the net — tests green AND unchanged, diff inside
touches. Formatting is hook-owned; never hand-format.
The manifest's styleGate (lint/checkstyle) runs at red-gate, green-gate, and close-gate.
Close the phase (the independence backstop):
bin/close-gate <task-path> <phase> — receipts join, test-digest recheck against the last gate receipt (a post-green tamper dies here), the unit gate (fastGate: whole unit suite, integration tier excluded), the phase's row test classes (derived from the rows' FQNs, not a hand-written command), the checks analyzers (ArchUnit, checkstyle, semgrep; a full-suite test check is skipped), diff-scope. Fail → fix in-session and re-run (bounded: 2 rounds, then escalate).
- Commit the phase: explicit paths — the code, this phase's
plan/phase-<id>.md, plan/checklist.json with its status flip. Self-contained why-focused message; no session narrative.
- Start the next ready phase. Per phase runs only its own gates; the full suite, slow analyzers, and review lanes run once at the task tier (Step 2), after every phase closes.
Escalation — surface, don't improvise. Inline there is no author to respawn, so a stuck phase surfaces to the user with its checkpoint (status, percent, what blocked it): a design pivot (the planned mechanism is dead — say what killed it), a guard test outside scope breaking by design conflict, or close-gate failing past 2 rounds. Before conceding a pivot, spawn ONE adversarial agent to attack the approach (a fresh context is worth it here) — a pivot that survives becomes an amendment proposal. Never push a mechanism pivot through silently; it becomes precedent for later phases, and that is above the phase's authority by design.
Amendments — the only legal drift: file {status: proposed} via bin/join --propose; surface with AskUserQuestion (ratify / reject / defer); record via bin/join --resolve. The join blocks while any amendment sits proposed. Ratified structural decisions get their enforcement rule written in the same phase.
Step 2: Task tier — verification, after all phases close
Once, over the finished task diff:
- Deep set:
bin/collect --kick <task-path> <HEAD> runs the full suite (integration tier + slow analyzers) in the current worktree; then bin/collect <task-path> harvests — retry failed classes once, diff analyzers against the baked baseline, file a confirmed regression as a blocker finding. Pass-on-retry files a flaky row; coverage is trend only.
- Review lanes: one Workflow over the whole diff, routed by globs (SQL/migrations → performance + security; concurrency primitives → reliability; module seams → architecture), each spawned with
agentType naming its reviewer. Findings {file, line, severity, scenario}, schema-validated. Blockers refuter-verified (2 of 3 must fail to refute; refuters model: 'sonnet'), fixed green-to-green; sub-blockers render to deferred.md.
bin/join <task-path>: every ratified row × receipts × phase closures, spike verdicts (dead requires a ratified amendment), amendment states, taskLevel attest receipts, open blocker findings (block until --close-finding), and deep-set completeness (a declared deepSet must have run green at HEAD; missing, stale, or red is an exception). Task-level requirements are attested via bin/join --attest '{"row": "R…", "evidence": "…"}'. Writes verification.md + verification.json + deferred.md; exit 1 on any exception.
- Benchmark vs the baked baseline; behavioral drive over the union of touch-map flows (project verify skill if present, else a
general-purpose spawn model: sonnet per /run); config-drift probe when deploy paths changed; provenance sweep via provenance-verifier over git diff <base> HEAD + commit messages.
bin/seams <task-path> → pairwise phase-diff intersections + cross-phase decision rows; ONE judgment lane (model: 'opus', effort: 'high') reviews those entries.
- Failures here: traces into
verification.md, fixes green-to-green.
Step 3: Final gate + capture
- Background, non-blocking: task-level meme pass —
~/.claude/skills/meme/bin/meme draft --from-phase <state-dir> then meme sweep. Never stage or commit memory.
- Commit the verification record: stage
verification.md, verification.json, deferred.md, seams.json, the final plan/checklist.json, and review-findings.md (when present) by explicit path and commit. Self-contained message; no session narrative.
- Present:
git diff --stat <base> HEAD, phase list with SHAs, join verdict, deep set + benchmark + drive + drift + provenance verdicts, open follow-up rows, the Deferred ledger re-disclosed verbatim, and the amendment log (ratified and rejected both). Then END YOUR TURN — the user reviews the whole task diff on their own clock; their sign-off is the completion truth.
Check placement (fixed; per-phase batteries select from it)
| Check | When | Where | Blocking |
|---|
| Formatter | every write | PostToolUse hook | invisible |
| Scoped tests | inner loop | main session | its own loop |
| Checkstyle (touched) | leg boundary | leg script | seconds |
| Unit tier + ArchUnit + checkstyle + semgrep + ErrorProne | phase close | bin/close-gate, main session | ~2 min |
| Full suite, integration tier, slow analyzers, review lanes, benchmark, drive, drift | task tier | main session | once |
Script contracts (bin/)
scaffold <task-path> — manifest → checklist + phase skeletons. Idempotent (preserves base, statuses, findings across re-runs). Run by /plan.
plan-lint <task-path> — the plan lint battery (bijection, DAG, touches, row-report coverage, FQN test-name compliance with the project's ArchUnit naming rule, exception globs, validation dry-run, drives seams, parallel disjointness, decision enforcement); exit 0/1 + findings on stdout.
red-gate <task-path> <phase> — run the rows' own test classes (derived from their FQNs); prove each row red per mode; styleGate; append receipt; flip status. Exit nonzero with the parsed reason.
green-gate <task-path> <phase> — receipt exists; test digests unchanged; scope clean; same FQNs pass.
close-gate <task-path> <phase> — receipts join + digest recheck + declared battery + scope. The authority run.
collect <task-path> [--kick <sha>] — kick or harvest deep-set runs; flaky retry; analyzer-baseline diff; coverage-trend recording; blocker filing.
seams <task-path> — pairwise phase-diff ∩ + cross-phase decisions → seams.json.
join <task-path> [--propose|--resolve|--spike|--attest <row-json>] — aggregate verification join → verification.md + verification.json, exit 1 on any exception. Flags append evidence and exit: --propose an amendment, --resolve its decision, --spike a probe verdict, --attest a task-level receipt.
selftest <task-path> — run the gate bite suite, write a harness receipt {buildlibDigest, suite, verdict}.
All gate scripts log wall-clock per invocation to <state-dir>/timings.log.