Execute an approved implementation plan in small per-behavior batches. Reads the plan, implements each step one behavior at a time in the Code-First Small Batches cadence with a refactor on every green, runs inline review checkpoints, and produces verification evidence. Use when the user says "build this", "implement the plan", "start building", or after /plan has been approved.
Resources
1Install
npx skillscat add bdfinst/agentic-dev-team/build Install via the SkillsCat registry.
Build
Role: orchestrator. This command implements an approved plan — it does not create plans or specs.
You have been invoked with the /build command.
Orchestrator constraints
MUST — confirm agent-dispatch capability before any review-agent dispatch (issue #1461). This skill dispatches review agents at three points: Step 3 (spec-compliance-review acceptance-criteria gate), sub-step 4 (per-step review checkpoint for complex steps), and sub-step 6 (slice review checkpoint). Before any of those dispatches, you MUST confirm the Agent (or Task) tool is actually present and available in your current toolset. If it is not: STOP at that point — do not self-apply the reviewer's checklist inline as a substitute for independent dispatch, and do not mark the gate/checkpoint passed on that basis. Report to the user/operator plainly: this review checkpoint cannot run in this environment because no agent-dispatch capability (Agent/Task tool) is available; name exactly what's missing; and state that the build cannot proceed past this gate (nor reach the Step 6 /code-review backstop, which independently enforces the same rule) until it is run from a session with that capability. This is a hard requirement, not a preference.
- Follow the plan exactly. If the plan is wrong, stop and ask the user — do not deviate silently.
- Every step is small per-behavior batches, one agent. The cadence is Code-First Small Batches — each behavior follows IMPLEMENT → TEST → REFACTOR (the statistically separated winner of the workflow experiments;
docs/experiments/RECOMMENDATIONS.mdRec 3). There is no cadence to resolve and no per-plan opt-in (ADR 0017) — Code-First Small Batches is the only cycle a step follows. One agent writes the code, the test, and the refactor for a unit of work; the refactor runs on every green — never deferred to an end-of-build pass, never skipped, never made conditional on task size or complexity (Rec 4); tests are frozen during REFACTOR; and big-batch shapes are prohibited — never all the code then all the tests, never all the tests then all the code. - Incremental. Each step must leave the codebase in a working, committable state.
- Verification evidence required. Paste fresh test output before claiming a step is done.
- Review checkpoints (granularity scales with complexity). Run inline review (static self-heal pass first, then spec-compliance, then quality agents) per step for
complexsteps; batchstandard/trivialsteps into one review at the slice boundary. When~/.claude/telemetry.jsonconsent is enabled (andDEV_TEAM_REVIEW_VALUEis notoff), record each checkpoint's find/fix/no-op outcome to.claude/metrics/review-value.jsonl. The final/code-reviewis the backstop. - Be concise. Report step status and verification evidence, no narration.
- Diagnose before retry. When any bash command run during the build fails (a script, a test run, a build-tooling invocation), read the exit code and error text and state a one-line cause hypothesis before correcting and re-issuing it. Never re-run a failed command unmodified. If the shallow diagnosis reveals a real defect rather than a shell-level mistake (bad path, typo, missing arg), escalate to Systematic Debugging per the Escalation section below.
Parse Arguments
Arguments: $ARGUMENTS
--plan <path>: Path to the plan file. If omitted, searchplans/for the most recently modified plan with statusapproved.--yes: Auto-approve the build's approval gates (steps 2 and 3) without prompting (non-interactive opt-in).--backstop-review=skip: Suppress only Step 6's backstop/code-review(#1962). Legal only when the caller has its own review pass over a diff that strictly contains this build's — an enclosing orchestrator's end-of-phase review loop, not a human running/builddirectly. Default off: an unflagged/buildruns Step 6 exactly as before. Everything else is untouched — the inline checkpoints (sub-steps 4/6), the full-suite run (Step 5), runtime verification (4.9), invariants (4.10), and the Farley Score (Step 7) all still run, so this narrows one duplicated layer rather than trading review for speed. When set, print an audit line into the build output naming the enclosing reviewer:Step-6 backstop review skipped (--backstop-review=skip) — enclosing reviewer: <name>.Never set it on a/buildwhose diff nothing else reviews.
Interactivity. The run is non-interactive when any of these hold: --yes was passed, DEV_TEAM_AUTO_APPROVE=1 is set, or stdin is not a usable TTY (test -t 0 is false — the headless/CI/automation case). The approval gates in steps 2 and 3 use this: interactive runs prompt exactly as before; non-interactive runs auto-proceed and record the bypass in the build output rather than hanging.
Steps
0. Context loading
Invoke the Context Loading Protocol at the start of this task. It decides which agents and skills to load for the current phase, and sets the context budget before any implementation work begins. This is a lightweight read — it does not add agents to context; it decides the load order so context stays under the 40% ceiling throughout the build.
1. Find the plan
If --plan was provided, read that file. Otherwise, search plans/ for .md files and find the most recently modified one with **Status**: approved. If no approved plan is found, tell the user: "No approved plan found. Run /plan first, then approve it."
1.5. JS project bootstrap gate
Before any implementation step runs, make sure a JS-flavored plan has a project to build in. A fresh JS project with no package.json will fail the first test run (no test runner, no scripts), so bootstrap it first.
- Check for
package.jsonin the working directory. If it exists, skip this gate silently and continue to Step 2. - Check the plan file for JS/TS signals — any of
.js,.mjs,.ts,.jsx,.tsx,node,npm,vitest,jest,eslint. If none are present, the plan is non-JS: skip this gate silently and continue to Step 2. - Bootstrap (only when
package.jsonis absent and the plan is JS-flavored): print exactly one line —No package.json found for a JS plan — bootstrapping with project-init.— then invoke theproject-initskill. - Halt on failure. If
project-initfails, stop/buildand report the failure — do not proceed to Step 2 or any implementation step.
The user sees no more than one line of output before project-init runs, and nothing at all when the gate is skipped.
2. Verify plan status
Read the plan file. If the status is not approved:
- Interactive → ask the user: "This plan has status ''. Approve it before building, or continue anyway?"
- Non-interactive (see Parse Arguments) → do not block. Auto-approve and continue, and print an explicit audit line into the build output:
Auto-approved plan status '<status>' (non-interactive) — no human gate. Trigger: <--yes | DEV_TEAM_AUTO_APPROVE=1 | no TTY>.
Either path appends an approval entry to .claude/metrics/config-changelog.jsonl per the
human-oversight-protocol § Audit trail
schema — proposed states the plan status being approved, evidence_shown points at
the plan file, risks_surfaced is [] unless the plan's status itself signals a risk
(e.g. resuming an in-progress plan). The non-interactive path writes the same three
fields; description names the bypass trigger.
3. Verify acceptance criteria (gate)
Dispatch-capability gate (re-confirm here — issue #1461). Before dispatching spec-compliance-review below, re-verify the Agent/Task tool is present. If it is not, STOP per the Orchestrator constraints above — do not self-verify the criteria inline; report the missing capability and halt before implementation begins.
Before implementation begins, dispatch the spec-compliance-review agent (by subagent_type) in criteria verification mode: pass it the plan's acceptance criteria and per-step test expectations, and ask it to evaluate the criteria themselves (see below) rather than a diff.
The reviewer evaluates each criterion for:
- Specificity: Could two developers independently verify this criterion and agree on pass/fail?
- Testability: Can this criterion be validated with a test or observable output?
- Completeness: Are edge cases and error conditions addressed?
If any criteria are flagged:
- Present the findings to the user with the reviewer's suggested improvements
- Interactive → Ask: "Revise these criteria before building, or proceed anyway?"
- If the user overrides, log the override in the build output and continue
- If the user revises, update the plan file and re-verify
- Non-interactive (see Parse Arguments) → do not block. Proceed and record the bypass in the build output:
Acceptance-criteria gate auto-passed with N flagged criterion(s) (non-interactive) — no human gate. Trigger: <--yes | DEV_TEAM_AUTO_APPROVE=1 | no TTY>.Include the flagged findings in the record so the bypass is auditable.
Whichever path is taken (proceed, revise, or override), append an approval entry to.claude/metrics/config-changelog.jsonl per the human-oversight-protocol § Audit trail
schema — proposed is the acceptance-criteria set under review, evidence_shown
points at the plan file (and, for an interactive override, the reviewer's findings if
written to .claude/memory/), and risks_surfaced lists the flagged criteria ([] if none
were flagged). An interactive override (user overrides the reviewer's findings) is
logged as type: "override" instead, with proposed recording the reviewer's
flagged concern and description recording the user's decision to proceed anyway.
4. Implement each step
Work the plan wave by wave (the plan's ## Parallelization section, derived by scripts/plan_waves.py). Within a wave, independent slices may build concurrently; across waves a barrier holds the next wave until the current one reconciles green.
Base-ref check (top-level session, before any subagent dispatch). Worktree subagents (isolation: "worktree") must branch from the caller's local HEAD, not origin/<default>, so the docs/specs/<slug>.md (when the spec was file-persisted — see /specs' GitHub-issue persistence path for the alternative, which leaves no local file to miss) and plans/<slug>.md files /ship just produced are visible to them (issue #553). This is controlled by Claude Code's worktree.baseRef setting, which is honored only at project (.claude/settings.json) or user (~/.claude/settings.json) scope — not at plugin or project-local scope (docs/spikes/worktree-baseref-head-spike.md). /build cannot set this on the user's behalf, so it runs a read-only detect-and-warn in this top-level /build session, before any subagent dispatch, so the warning is visible in the human-facing transcript:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/build_worktree_baseref.py" detect # prints head|fresh|unset|unknownhead→ no warning. Proceed.fresh,unset, orunknown(detection failed — e.g.jqunavailable; treated fail-safe) → unlessDEV_TEAM_WORKTREE_BASE_FRESH=1is set, print a loud warning naming the exact file to edit and a paste-ready snippet, then continue:⚠ worktree.baseRef is not "head" (detected: <value>) — worktree subagents will branch from origin/<default>, not your current HEAD. Any uncommitted-to-origin spec/plan/WIP files will be invisible to them. Add this to .claude/settings.json (project) or ~/.claude/settings.json (user) — plugin and project-local settings.json are NOT honored for this key: { "worktree": { "baseRef": "head" } } To keep fresh-from-origin worktrees deliberately, set DEV_TEAM_WORKTREE_BASE_FRESH=1 to silence this warning.If detection returned
unknown, the warning additionally states thatworktree.baseRef could not be detected.
/build never mutates a settings file. The check is read-only end to end — it never writes .claude/settings.json, ~/.claude/settings.json, or any other settings file. There is nothing to restore and no crash-recovery surface.
Resolve the wave schedule and concurrency first:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/build_wave.py" <plan-file> # ordered waves + members
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/build_jobs.py" --wave-width <W> [--jobs N] # effective concurrencybuild_jobs.py resolves min(--jobs, DEV_TEAM_MAX_PARALLEL_BUILDS, wave width). Parallel fan-out is opt-in: when neither --jobs nor DEV_TEAM_MAX_PARALLEL_BUILDS is set, the default is sequential (effective 1) — fan-out never saves tokens, it trades them for wall-clock (#1515). Opt in with --jobs N (bounded only by wave width) or DEV_TEAM_MAX_PARALLEL_BUILDS (an explicit env value is honored verbatim, never re-capped); non-positive/non-integer values clamp to 1. Sequential fallback: when effective concurrency is 1 (the unset default, a fully-dependent plan, --jobs 1, or max 1), build slices one at a time in a single worktree in dependency order — no worktree fan-out, no reconcile step (today's behavior exactly).
Concurrent dispatch (effective concurrency > 1):
- Dispatch each independent slice in the wave to its own git worktree (
isolation: "worktree"), up to the effective concurrency. Each slice's changes stay isolated until reconcile, and each slice still runs its full per-behavior cycle and inline review gates. - Report the concrete level and cost — name the slice count and the resulting multiplier, e.g. "building wave 2 — 5 slices concurrently; ~5× wall-clock speedup, but burns your token budget faster — roughly 5× for 5 concurrent slices." The cost is reported, never auto-throttled: cap it yourself with
--jobs NorDEV_TEAM_MAX_PARALLEL_BUILDSif the burn rate is too high (#1170). - Barrier + reconcile once the wave's slices finish:
build_wave_reconcile.py --into <integration> --base <ref> --test-cmd "<full suite>" <slice-branch>...merges them order-independently and gates on the full suite before any next-wave slice starts. - Loud halt, never silent:
- A failing slice → exit non-zero naming it, list which same-wave slices succeeded and where their (preserved) worktrees are, print the resume command, and start no next-wave slice. Resume rebuilds only the incomplete slice.
- A reconcile conflict (two same-wave diffs touch one file) → exit non-zero naming the file, pick no side, start no next-wave slice.
Slice dispatch bookkeeping (issue #865). Before a slice's first step begins:
- Freeze scope (opt-in only). Check whether the plan opts into scope enforcement:
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py enabled <plan-file>(exit 0 = engaged). Declaring slice-level**Files:**alone never freezes anything — only a**Scope enforcement:** freezemetadata line does (Ambiguity Log Q1). When engaged and the dispatching slice declares**Files:**, runpython3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py engage <plan-file> --slice <id> --hooks-dir <worktree>/.claude/hooks— this writes.claude/hooks/freeze-state.jsonwithallowed_patternsset to the slice's declared paths plus the fixed bookkeeping allowlist (the plan file,.claude/memory/**,.claude/metrics/**, and the AC3-exemptmetrics/verify-log.jsonl), sohooks/pre_tool_guard.pyblocks any Write/Edit outside that scope without also blocking/build's own progress writes. The--hooks-dirvalue must point at<worktree>/.claude/hooks, not<worktree>/hooks—hooks/pre_tool_guard.pyreads the freeze state viahooks/lib/artifact_paths.py's per-repo.claude/hooks/freeze-state.jsonconvention (issue #1890), and a mismatched write location means the freeze is silently never enforced. Clear it at slice completion (sub-step 5 below):python3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py clear --hooks-dir <worktree>/.claude/hooks. - Rollback point. When the slice declares
**Rollback point:**, resolve the symbolic value to a concrete SHA and record it:python3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_rollback_point.py resolve --symbolic <value> --repo <worktree> --slice-start <HEAD-at-dispatch> --wave-start <wave-start-ref> --plan-start <plan-start-ref>, thenpython3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_rollback_point.py record --path .claude/memory/build-rollback.json --slice <id> --symbolic <value> --sha <resolved-sha>. This is the boundary a dead-end escalation (issue #864) names verbatim: "revert to<sha>(<symbolic>)" — retrieve it with the script'sgetsubcommand. A slice withoutRollback pointrecords nothing.
For each step within a slice, dispatch the software-engineer agent (by subagent_type) scoped to a single unit of work. Pass it its step and the slice's Gherkin scenario(s) — the scenarios are the behavioral contract the step's test must satisfy — plus an explicit constraint: the design is settled; do not design. The agent implements the step exactly as planned, one behavior at a time, per the per-behavior cycle below — it does not revisit or re-derive design decisions the plan already made. Require its step report to state assumptions explicitly: when the plan under-specifies a detail that doesn't rise to an escalation (exact error-message wording, an unspecified boundary condition, a choice between two equally plan-consistent shapes), the agent records the decision and its basis in the report rather than resolving it silently — /pr's "Decisions & assumptions" section (skills/pr/SKILL.md) collects these from each step's report.
Authoring digest (#2209). Append the step's write-time checklist to the dispatch prompt: python3 "${CLAUDE_PLUGIN_ROOT}/scripts/authoring_digest.py" --files <the step's planned files> — it resolves the same select_lenses.py lenses the review checkpoints use and prints only their ## Authoring checklist bullets (~500 tokens for a typical diff, empty when no lens has one). Paste the output verbatim under a ## Authoring checklist (write-time) heading; an empty digest adds nothing. Re-use the same call for review-fix correction prompts, scoped to the files being fixed. Planned files come from the step's **Files:** (or the slice's); when neither is declared, skip the digest rather than guessing.
Within the per-behavior mini-cycle below, repeated Write/Edit calls can race a PostToolUse hook that rewrites files (e.g., a formatter): an Edit failing on a stale old_string is expected to self-correct by re-Reading the file before the next Edit attempt, not to escalate immediately.
Phase-state bookkeeping (guard input). /build owns .claude/memory/build-phase.json as mechanical step bookkeeping: write {"phase": "<implement|test|refactor>", "step": "<N.M>", "written_at": "<ISO8601>", "test_files_staged": [], "plan_path": "<repo-relative path of the plan file>"} at each phase transition. At step completion do not delete the file: rewrite it with "phase": "between-steps", "step": "<next N.M>", a fresh written_at, "test_files_staged": [] and the same plan_path, so a compaction between steps still restores the plan path and next step (the guards ignore any phase other than refactor). Clear the file only when the plan's last step completes ("cleared" = absent, empty, or {}); the 4-hour staleness rule still bounds a crashed run. plan_path lets the post-compaction re-injection hook find the active plan without globbing; the one shared reader is hooks/lib/build_state.py, pinned to this schema by tests/hooks/test_build_state_contract.py. At the TEST → REFACTOR transition, additionally stage the step's test files — git add them, including new/untracked ones — and record their paths in test_files_staged: the index becomes the refactor baseline the refactor_test_freeze_guard / refactor_test_revert_guard hooks enforce the tests-frozen invariant against. Standalone-dispatch fallback: when software-engineer is dispatched directly in an isolated worktree and no /build session is writing this record for it (the record is absent, or its phase is not refactor, at REFACTOR entry), the dispatched agent writes .claude/memory/build-phase.json itself before entering REFACTOR — entering REFACTOR with no phase record present silently disables the tests-frozen guard (refactor_test_freeze_guard.py treats an absent/non-refactor record as "nothing to enforce").
Work each step one behavior at a time — never all the code then all the tests, never all the tests then all the code:
First phase — IMPLEMENT. Implement exactly one behavior from the step — no cleanup, no behavior beyond what the step requires. Apply the software engineer's Per-Edit Authoring Discipline checklist (Surgical Changes, Simplicity First, Think Before Coding) at this phase, not deferred to review.
Explore before editing. Before writing the behavior's code, prefer CodeGraph/Repowise over a raw Grep/Read sweep — one call returns the relevant source plus its callers and blast radius, which is what the Per-Edit Authoring Discipline checklist needs to size the change correctly. See `knowledge/codegraph-vs-graphify.md` for tool selection and the fallback contract.
Second phase — TEST. Write the test covering the behavior's slice scenario, immediately after the code. Before writing a test double for this behavior, check the collaborator against
${CLAUDE_PLUGIN_ROOT}/knowledge/internal-collaborator-doubling.md#the-three-blockers-exhaustive's blocker table. Run the full test suite. Hard gate: all tests must pass — paste the passing output. Do NOT proceed to REFACTOR without pasted passing output.Before each repair iteration (here and in the review-fix loop, sub-step 4), read
${CLAUDE_PLUGIN_ROOT}/knowledge/failure-routing.mdand classify the failing output/exit code by its regex table — deterministic pattern match only, no LLM call, no extra dispatch. Follow the matched route (inline fix / systematic-debugging / test-generation / security-engineer dispatch / human arbitration);unclassifiedfalls through to the generic loop below, unchanged. A route switch spends from the same iteration budget — it never resets or raises the cap.2a. Repair loop on failure — failure-signature dead-end detection (issue #864). Whichever route the classification above sends the failure down, repair it in place rather than handing back a bare failure:
- Compute a failure signature after every repair iteration (an edit followed by a re-run): the pair of (1) the sorted, deduplicated set of failing test identifiers, using the runner's native IDs (pytest node IDs, jest/vitest full test names,
go testnames, etc.), and (2) the error class per failing test (assertion failure vs. exception type vs. compile/collection error, e.g.AssertionError,TypeError,SyntaxError). - Normalize before comparing. Strip volatile output first — timestamps, durations, memory addresses, temp paths, PIDs/ports, random seeds — so two runs identical except for that noise produce the same signature. Never compare raw output.
- Track signatures as in-context iteration state — a small per-step table (iteration → signature) held for the duration of this repair loop. This is not a
.claude/memory/file; the durable record on dead-end is the checkpoint commit below (plus the existing.claude/memory/build-escalation-<plan-slug>.mdrecord on a non-interactive halt). - Two identical consecutive signatures is a dead-end. If iteration N+1's normalized signature equals iteration N's, stop — do not dispatch a third attempt against the unchanged signature.
- A changed signature continues repair normally. Fewer or different failing tests, or a different error class, is progress: keep repairing, and restart the dead-end comparison from the new signature. No new iteration cap — the review loop's 5-iteration cap (sub-step 4) is untouched; this is a no-progress cutoff, not a count cap, so a repair loop that keeps changing its signature may run as long as it keeps progressing. A route switch (per the classification above) spends from this same budget — it never resets or raises it.
- On dead-end, commit a checkpoint before escalating. Commit the current working tree as-is (no per-iteration snapshots in v1) on the working branch — never
main— with a conventional message explicitly marked as a dead-end checkpoint, e.g.chore(build): dead-end checkpoint — step <N>, <M> tests still failing. If an earlier iteration was strictly better than the current one, name that regression in the escalation rather than reverting to it. - Escalate with the best candidate, not a bare failure, stating all three: (a) improved — tests that were failing at repair start and now pass, (b) remaining — the current (unchanged) failing signature, (c) the checkpoint commit ref.
- Cite the architecture-questioning rule at 3+ failed attempts. Count every repair iteration that ended with a real edit and a re-run that failed to reach green (regardless of whether its signature changed) as one failed fix attempt. When 3 or more distinct fix attempts have failed by the time the dead-end fires, the escalation must explicitly cite Systematic Debugging's rule: "After 3+ failed fix attempts, question the architecture — stop patching."
- This is a hard stop, matching the Escalation section below: leave plan status unchanged and never proceed to
/prover the unresolved escalation. A red checkpoint commit is never presented as done. - Out of scope / unchanged:
hooks/verify_guard.pyis not modified and continues to own the separate, syntactic case — the same verify command re-run with zero intervening edits. This repair loop fires only when edits do happen but the failure signature doesn't change.
- Compute a failure signature after every repair iteration (an edit followed by a re-run): the pair of (1) the sorted, deduplicated set of failing test identifiers, using the runner's native IDs (pytest node IDs, jest/vitest full test names,
REFACTOR (every green, never skipped). Clean up structure, naming, duplication without changing behavior. Runs in every per-behavior cycle: never deferred to an end-of-build pass, never made conditional on task size or complexity (
docs/experiments/RECOMMENDATIONS.mdRec 4 — deleting just this step erased the cadence's changeability advantage entirely). Tests are frozen for the phase — a refactor must never change a test (enforced by the freeze/revert guards; recovery: return to the TEST phase, change the test there, re-verify green, re-enter REFACTOR). Run tests again — they must still pass. If tests break, undo and try a smaller change. A no-op refactor (nothing worth changing, stated in one line) satisfies the phase — the mandate is the check on every green, not a diff — and any refactor made stays within the code the step touched; adjacent-file cleanups are follow-ups, not refactors.Self-verification, mandatory (#2107). Before this step may be marked done (sub-step 5), also run the project's lint and type-check tools — whichever apply to its stack (
ruff/ESLint/tsc/pmd/dotnet build, etc.) — when the project has them, and confirm they pass; paste the output alongside the test run. This closes a real gap the TEST phase's test-only hard gate leaves open: a green suite proves behavior, not that the REFACTOR pass above left no unused import, type error, or style violation behind — those otherwise surface only downstream, in Step 6's backstop review, after the step already reads[x]. Same barquality-gate-pipeline's Phase 2 ("Required Evidence") already states for every completion claim; this is that bar made explicit and mandatory at the one moment in this cadence it was previously only implied. Skip only what the project genuinely has none of (state which, in one line) — never skip because it "should still be clean."Inline review checkpoint — granularity scales with complexity. Where the checkpoint runs depends on the step's Complexity classification (review depth still scales too):
- trivial: Skip inline review. The final
/code-review(step 6) covers all modified files. - standard: Defer review to the slice boundary (sub-step 6) — do not review now. Track the step's changed files so the slice checkpoint reviews them in one batch. Per-step review on standard steps is N near-identical passes where one at slice end largely does the same work, and the final
/code-review(step 6) remains the backstop. This is the batching win — fewer review dispatches per multi-step slice at bounded quality risk. - complex: Dispatch-capability gate (re-confirm here — issue #1461): before this review dispatches, re-verify the
Agent/Tasktool is present. If it is not, STOP per the Orchestrator constraints above — do not self-apply this checkpoint's checklist inline; report the missing capability and halt rather than marking the step done. Otherwise, review now, per step — smaller blast radius per fix. Run the static self-heal pass to completion first — pass, or cap-and-escalate, perreferences/static-self-heal.md— then/review-agent spec-compliance-review --internal, then the quality lenses the resolver selects for this step's changed files —python3 ${CLAUDE_PLUGIN_ROOT}/scripts/select_lenses.py --files <step's changed files>returns the applicable lenses (see the resolver note after sub-step 6) — dispatched cheap-first (non-opus lenses before the opus-tiersecurity-review/domain-review/arch-review), with the review-fix loop (up to 5 iterations per${CLAUDE_PLUGIN_ROOT}/knowledge/three-phase-workflow.md#review-loop) — including its deterministic-first re-verification triage (../code-review/SKILL.mdstep 6a, #1610): before re-dispatching an agent to confirm a fix, prefer a close via whichever language-appropriate lint/type-check tool(s) applies to this project's own stack (not just Python'sruff— ESLint/tscfor JS/TS,pmdfor Java,dotnet format/dotnet buildfor C#, etc.) plus the test suite/grep, when the fix is mechanical and the claim is deterministically checkable, and escalate to an agent only when semantic judgment is genuinely needed. Ledger-scoped dispatch (#2167). Before dispatching those lenses, narrow their file sets and append the dispatch-prompt scope marker — see the shared Ledger-scoped dispatch (#2167) rule stated once after sub-step 6 below (applies to both checkpoint fix loops). Abort check (#2168). Before dispatching the remaining opus-tier lenses, runcheckpoint_abort.py's abort check — see the shared Abort check (#2168) rule stated once after sub-step 6 below (applies to both checkpoint fix loops). Before each review-fix iteration, classify the finding/failure via${CLAUDE_PLUGIN_ROOT}/knowledge/failure-routing.mdand follow its route (see the TEST-phase note above) — a security-finding class dispatches security-engineer, a reviewer-conflict class routes to human arbitration,unclassifiedstays in the generic loop. Escalate to user if the loop doesn't converge. Then record the checkpoint outcome (sub-step 7). - If no complexity is specified, default to standard.
- UI changes (any complexity): After the relevant review passes (per-step for complex, at the slice checkpoint for standard), run browser verification via
/browsein automated smoke test mode. Skip with warning if the dev server is not running. See${CLAUDE_PLUGIN_ROOT}/knowledge/three-phase-workflow.md#phase-3-implementStage 3.
- trivial: Skip inline review. The final
Mark step done — Use the Edit tool to update the plan file's
## Build Progresssection on disk:- Do not flip a checkbox on a "should work"/"should be fixed" impression. The self-verification evidence required in sub-step 3 above (tests, and lint/type-check where the project has them) must be fresh from this session — not recalled from earlier in the conversation, not assumed — before this bullet may fire. This is the same completion-claim bar
quality-gate-pipeline's Phase 2 states generally, made mandatory at this specific step-completion moment (#2107). - Change
- [ ] Step N.M: <title>to- [x] Step N.M: <title>for the completed step. - When every step under a slice is
[x], that is not the same as the slice being done — check off the parent- [ ] Slice N: <title>only after sub-steps 4.9 (runtime verification) and 4.10 (invariants) both pass, if applicable; a slice with no runtime surface and no declared invariants has nothing further to wait on and may be checked off once its steps and review checkpoint(s) are done. - After all slices are
[x], change**Status**: approvedto**Status**: in-progress. - Phase-state handoff. Rewrite
.claude/memory/build-phase.jsonas thebetween-stepsrecord for the next unchecked step (see Phase-state bookkeeping above); delete it instead when this was the plan's last step. - This disk write is the durable commit. If a
/clearoccurs,/continuereads## Build Progressto determine the resume point without needing conversation history. - Clear freeze scope (issue #865). When every step under the slice is
[x]and freeze was engaged for it (dispatch bookkeeping above), runpython3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py clear --hooks-dir <worktree>/.claude/hooksbefore starting the next slice. A slice that never engaged freeze has nothing to clear.
- Do not flip a checkbox on a "should work"/"should be fixed" impression. The self-verification evidence required in sub-step 3 above (tests, and lint/type-check where the project has them) must be fresh from this session — not recalled from earlier in the conversation, not assumed — before this bullet may fire. This is the same completion-claim bar
Slice review checkpoint (batched). Dispatch-capability gate (re-confirm here — issue #1461): before this checkpoint dispatches, re-verify the
Agent/Tasktool is present. If it is not, STOP per the Orchestrator constraints above — do not self-apply the batched checkpoint's checklist inline; report the missing capability and halt rather than checking off the slice. Otherwise, when every step under the current slice is[x]and the slice had any deferredstandard(or unspecified) steps, run one review pass over the slice's accumulated changed files: the static self-heal pass first (references/static-self-heal.md), then/review-agent spec-compliance-review --internal, then the quality lenses the resolver selects —python3 ${CLAUDE_PLUGIN_ROOT}/scripts/select_lenses.py --files <slice's accumulated changed files>— dispatched cheap-first (non-opus before opus-tier), plusrefactor-opportunity-reviewdispatched by name (#1976). That lens declaresScope: on-demand, so the resolver no longer returns it; this once-per-slice dispatch is its post-GREEN home, and it is the ONLY place/buildruns it — do not also dispatch it per behavior at the REFACTOR phase (that would spend more, not less, than the per-diff panel slot it replaced) and do not treat the resolver's silence as "this lens was dropped". Its subject is a slice's accumulated shape — semantic vs. structural duplication across everything the slice touched — which is visible here and not in any single behavior's diff. Ledger-scoped dispatch (#2167). Before dispatching, narrow file sets and append the dispatch-prompt scope marker — see the shared rule below (applies to both checkpoint fix loops above). Abort check (#2168). Before dispatching the remaining opus-tier lenses, runcheckpoint_abort.py's abort check on the cheap-tier lenses' (from the cheap-first dispatch above) results — see the shared rule below (applies to both checkpoint fix loops above). Either way, apply the same review-fix loop (up to 5 iterations; escalate if it doesn't converge).trivial-only and all-complexslices have nothing to batch — skip this pass. Then record the checkpoint outcome (sub-step 7).Abort check (#2168) — applies to both checkpoint fix loops above (sub-steps 4 and 6). Once the cheap-tier lenses return, invoke
checkpoint_abort.py(python3 ${CLAUDE_PLUGIN_ROOT}/scripts/checkpoint_abort.py --cheap-results-from <cheap-tier results> --lenses <the resolver's full ordered lens list>) before dispatching the remaining opus-tier lenses. Onaborted: true: skip dispatching the deferred opus-tier lenses this round, run the review-fix loop against the cheap-tier findings only, and once it converges, dispatch the deferred lenses and merge findings into the round's finding set viapython3 ${CLAUDE_PLUGIN_ROOT}/scripts/checkpoint_abort.py --mode merge --from <JSON: {"existing": [...], "new": [...]}>(checkpoint_abort.merge_findings(existing, new)'s CLI form) — the same dedup-by-(agent, file, line, severity, message)function Step 1.1 already proved order-independent.existinghere is the cheap-tier findings STILL OUTSTANDING after the fix loop converged (whatever the fix loop's own re-classification left as unresolved — typically none, if the fix loop actually fixed the triggering finding), never the original pre-fix-loop cheap-tier result set. The triggering finding iserror/highby construction (checkpoint_abort.py'sQUALIFYING_SEVERITY/QUALIFYING_CONFIDENCE), so passing the unfixed original set asexistingwould keep it in the merged set regardless of the fix loop's outcome, and--mode outcomebelow would then reportblockedon every aborted round no matter what the deferred lenses found (backstop review, #2168) — the fix loop's own result is whatexistingmust reflect.newis the deferred lenses' findings, dispatched fresh at the fix loop's final content. This checkpoint's report must name every deferred lens and the triggering finding (checkpoint_abort.py'sdeferredLenses/triggeringFinding/triggeringAgent). The round's pass/blocked outcome always comes from callingpython3 ${CLAUDE_PLUGIN_ROOT}/scripts/checkpoint_abort.py --mode outcome --from <JSON: {"aborted": ..., "redispatched": ..., "findings": [the merged set above]}>(compute_round_outcome(aborted, redispatched, findings)'s CLI form) — never independently reimplemented — so a round that aborted and never re-dispatched its deferred lenses cannot report a pass. Onaborted: false, dispatch every opus-tier lens normally.Finding log (#2211). After each lens returns at a checkpoint, record every finding (fail-open; never blocks the phase):
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/review_findings_log.py" append --lens <lens> --category <finding category> --severity <severity> --iteration <fix-loop iteration> [--fixed](--fixedonce a later iteration clears it).review_findings_log.py reportranks(lens, category)by frequency and first-pass-fix rate — the input for promoting or pruning authoring-checklist bullets.Ledger-scoped dispatch (#2167) — applies to both checkpoint fix loops above (sub-steps 4 and 6). Two additions, both needed together — the first is what makes the second possible:
- Scope marker. Append the same structured, single-line marker
../code-review/SKILL.mdstep 4 documents to every checkpoint dispatch prompt here —Files in scope for this review: <path>, <path>, ...(the literalhooks/lib/review_verdicts.SCOPE_MARKER_PREFIX), listing exactly the files passed to that dispatch. Without this,hooks/review_verdict_recorder.py(theSubagentStophook that writes.claude/metrics/review-verdicts.jsonl) has no scope to parse for a checkpoint dispatch and records nothing for it — today's actual gap: only/code-review's own step 4 emits this marker, so a lens sub-step 4/6 clears never gets a ledger row at all, and Step 6's backstop has nothing to skip against no matter how many checkpoints already reviewed the same content. - Consult before dispatch. Before dispatching the resolver-selected lenses (the cheap-first list
select_lenses.pyreturned above), narrow them against the ledger:
Dispatch each lens only againstpython3 "${CLAUDE_PLUGIN_ROOT}/scripts/verdict_scope.py" --root <worktree> --lens-files '<JSON: {"<lens>": [<this checkpoint's changed files>], ...} for every lens the resolver returned>'toDispatch[<lens>]; a lens named infullySkippedLensesis dropped from this checkpoint's dispatch entirely (nothing left to review, so no fix-loop iteration for it either). Name every ledger-skipped(lens, file)pair — and its matched row — in this checkpoint's own report, the same "report loudly, never silently" rule../code-review/SKILL.mdstates for its own copy of this mechanism; that file's copy also states the exhaustive fail-closed list (verdict_scope.py's own module docstring), not repeated here to avoid two independently-drifting copies.
Why this closes the Step 6 backstop gap, with no Step-6-specific code. Step 6 runs
/code-review --internal, which performs this exact same consult (../code-review/SKILL.mdstep 4's own "Ledger-scoped dispatch" bullet) against whatever.claude/metrics/review-verdicts.jsonlrows already exist. Once sub-steps 4/6 above are writing rows too (via the scope marker addition), a file/lens pair a checkpoint already cleared already has apassrow by the time the backstop's own consult runs — so a slice whose steps were all reviewed at sub-step 4 has nothing left for the backstop to re-review, without the backstop needing to know anything about checkpoints at all.--backstop-review=skipis unaffected either way — it still suppresses the whole Step 6 dispatch, ledger-scoped or not.Verification-mode re-dispatch (#1628) — applies to both checkpoint fix loops above (sub-steps 4 and 6). When a checkpoint re-dispatches an agent to CONFIRM a fix (rather than to discover new problems), send the narrowed verification payload — the finding, the fix diff hunks ± ~20 lines, and the agent's lens definition, never the full file set — with the mandatory
insufficient-contextescape, and resolve the agent's verification tier withpython3 "$CLAUDE_PLUGIN_ROOT/scripts/verify_tier.py" --agent <name>. The payload contract, the escape's escalation path, and the declared-never-inferred tier-down rule are stated once in `../../knowledge/verification-mode.md` and are not restated here.Round-ledger termination rules (#1625) — applies to both checkpoint fix loops above (sub-steps 4 and 6). The 5-iteration cap is a backstop, not a churn control: it counts passes, not finding identity, so a loop that keeps re-finding residue from its own fixes looks identical to one making progress. Classify each checkpoint round with the shared helper and honor its verdict:
# RUN_ID scopes the ledger to THIS checkpoint. Each checkpoint is its own # run — without it, one step's signatures would suppress the next step's # genuinely-new findings as "carried". python3 "$CLAUDE_PLUGIN_ROOT/skills/code-review/scripts/finding_signature.py" \ --round <N> --findings <this-round's-findings.json> \ --run-id "<plan>-slice<S>-step<N.M>" \ --state .claude/memory/review-round-state.jsonThe script resets the ledger on round 1, on a
--run-idmismatch, and on
a state file older than 24h — see `../code-review/SKILL.md` step 6a's ledger-lifecycle table. Pass a--run-idthat is unique per checkpoint; the step/slice identity above is sufficient.Architectural-impact gate. The resolver's
lenseslist is narrowed
further for structural lenses: pipe the checkpoint's diff throughskills/code-review/scripts/change_impact.py --files <changed files>and
drop any agent in itsskipLenses. A step whose diff changes no import,
adds/moves/deletes no file, edits no manifest or infra file, and changes
no public symbol has moved no boundary forarch-reviewto evaluate. The
gate is fail-safe — anything it cannot classify keeps every lens. Contract
and rationale:../code-review/SKILL.mdstep 3.The three rules — hard round cap at 4, severity floor from round 2 (only
error/warningathigh/mediumconfidence justifies another round; suggestion-tier findings are logged, never chased), and loop-until-dry — are stated once in `../code-review/SKILL.md` step 6a and implemented once in that script. They are not restated here: this is the same contract, same implementation, reached from a different caller. Around-capverdict escalates to the user with the ledger attached, exactly like a non-converging loop.Resolver note (
select_lenses.py, #1516). The resolver reads each review agent'sScope:declaration and returns only the lenses whose domain matches the changed files, so a backend-only diff does not dispatch frontend/UI lenses (a11y, js-fp, component-architecture, svelte) that would only no-op — while theScope: alwayslenses (correctness, security, structure, spec-compliance, domain, arch, …) run on every checkpoint — with one exception (#1923):correctness-reviewadditionally drops out when every changed file is non-executable (docs/config/assets/lockfiles, never functional Claude-config markdown), since it self-skips that diff shape per its own## Skipclause anyway; the resolver removes it fromlensesbefore dispatch rather than paying for that self-reported skip, and records the drop as askipped-non-executable:correctness-reviewentry inwarnings. No otherScope: alwayslens is affected by this. It prints{"lenses":[...cheap-first...],"warnings":[...]}; dispatch thelensesin order and surfacewarningsin the checkpoint's own findings/telemetry the same way/code-reviewdoes (see../code-review/SKILL.md's resolver-eligibility section for the full warning-shape list). The final/code-review(step 6) remains the backstop and applies its own selection independently. Scope note (#2168):/code-reviewstep 4's parallel bounded-wave dispatch (dispatch_waves.py) computes and fires whole waves sized only bymaxParallel, with no cheap-tier/opus-tier ordering boundary within a wave, so thischeckpoint_abort.pyabort-on-cheap-blocker optimization is not ported there — it is scoped to/build's own checkpoints (sub-steps 4 and 6) only.- Scope marker. Append the same structured, single-line marker
Record review value (#348). For each checkpoint that runs (per-step
complexin sub-step 4, and per-slice in sub-step 6), check~/.claude/telemetry.jsonconsent first — if consent is not enabled, skip this step entirely (no file is written). When consent is enabled, append one JSON line to.claude/metrics/review-value.jsonlcapturing whether review actually changed anything — counts and outcomes only, never code or file content (consistent with the cost meter's privacy boundary). Schema inperformance-metrics:{"timestamp":"<ISO8601>","plan":"<plan-file>","slice":"<N>","step":"<N.M or all>","checkpoint":"step|slice|backstop","complexity":"standard|complex","source":"build-checkpoint|build-backstop","diff_shape":"test-only|mixed","agents_run":["spec-compliance-review","..."],"issues_found":0,"severity_breakdown":{"errors":0,"warnings":0,"suggestions":0},"issues_fixed":0,"fix_iterations":0,"outcome":"no-op|fixed|escalated|skipped"}Step 6's backstop row (#1962) uses this same schema with
source: "build-backstop"andcheckpoint: "backstop";outcome: "skipped"is reserved for a backstop suppressed by--backstop-review=skipand never appears on a checkpoint row.diff_shape(#1964) records the shape of the diff this checkpoint
reviewed, so per-lens outcomes can be split by it. Do not eyeball the file
list — read it from the same deterministic helper the gates use:python3 "$CLAUDE_PLUGIN_ROOT/skills/code-review/scripts/change_shape.py" --files <this checkpoint's changed files>Its
isTestOnlyfield decides the value:true→"test-only",false
→"mixed". The helper is include-biased (any file not provably a test
makes the answerfalse), sotest-onlyis never over-claimed. This is
the measurement that decides whether any lens may eventually be gated out
of a test-only diff — it gates nothing today.outcomeisno-opwhen the checkpoint passed clean (found nothing),fixedwhen it found and auto-fixed actionable issues,escalatedwhen the loop didn't converge.severity_breakdownsplitsissues_foundby severity (errors/warnings/suggestions, the same enum as/code-review), so/harness-auditStep 3 can flag a lens producing mostly minor findings — the three counts must sum toissues_found(#1256). This is the sensor that tells a build where review caught a real defect from one where every loop passed no-op — it turns the pipeline's "value untested" into "value measured" and feeds the plan/step tiering decisions. Disable withDEV_TEAM_REVIEW_VALUE=off.
4.9. Verify runtime behavior before the slice is done (issue #727)
A "done" step that only passed its own tests is not the same as a feature that works — a red suite catches structural regressions, not "it fails the first time someone actually runs it." Once a slice's steps are all [x] (sub-step 5) and its review checkpoint(s) have run (sub-steps 4/6), decide whether the slice has a runtime surface to exercise before the slice may be marked [x] complete:
Classify the slice's changed files, per
knowledge/test-file-indicators.md. If every changed file is a test file, or the rest are docs/config only (no source or runtime file changed), there is nothing to exercise at runtime — recordoutcome: "skipped"with areason(below) and continue.Otherwise, exercise the change end-to-end using the project's own test/verification tooling — its test runner, checker scripts, or a direct invocation of the changed entry point (CLI command, API call, script run) — scoped to the slice's changed runtime files, before the slice's checkbox is flipped to
[x]. This is a pattern, not a named command: there is no/verifyskill shipped by this plugin, so pick whatever the project already uses to run/exercise the affected surface for real (e.g. its integration test suite, a smoke-test script, or manually invoking the changed function/endpoint/command). This generalizes the UI-only/browsesmoke test (sub-step 4's UI bullet) into a universal completion criterion: APIs, CLIs, bots, and background jobs get the same "did this actually run" check UI changes already get.Not bypassable by
--yes,DEV_TEAM_AUTO_APPROVE=1, or no-TTY. Contrast with the approval gates in Steps 2–3: those bypass a human judgment call when no human is present. This gate needs no human judgment — the agent runs the verification itself — so non-interactive mode never skips it. There is no override flag for this step.A failed verification run is a failing test. Per Step 5's "Quality ownership" language: do not mark the slice
[x]or the planimplemented. Enter Systematic Debugging, find the root cause, fix it, and re-run the verification before proceeding — never silently override.Record the outcome. Append exactly one JSON line per slice with a runtime surface to
metrics/verify-log.jsonl, schema modeled on.claude/metrics/review-value.jsonl(sub-step 7):{"timestamp":"<ISO8601>","plan":"<plan-file>","slice":"<N>","branch":"<branch>","files":["<changed runtime file>","..."],"outcome":"ran|skipped|failed-then-fixed","reason":"<set when outcome is skipped>"}outcomeisran(the verification ran and passed),skipped(no runtime surface in the diff —reasonstates why, e.g."tests-only"or"docs-only"), orfailed-then-fixed(the verification failed at least once before the fix landed).python3 ${CLAUDE_PLUGIN_ROOT}/scripts/progress_guardian.py --pre-prreads this log: a branch with runtime-surface changes and no matching entry fails the pre-PR gate the same way an incomplete step or a missing commit does.
4.10. Run slice invariants (issue #865)
When the slice declares **Invariants:**, run them after the slice's own suite is green (sub-step 5) and its review checkpoint(s) have run (sub-steps 4/6) — invariants check what must stay green beyond the slice's new acceptance tests, so they gate on top of everything else, not instead of it:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_invariants.py" --plan <plan-file> --slice <id> --repo <worktree>A non-zero exit fails the slice gate exactly like a red test — fix it or escalate (Escalation section below), never step over it, and never flip the slice checkbox to [x] until it's green. A slice with no Invariants line runs its gate unchanged (the script itself no-ops with "No invariants declared" — nothing to enforce). Invariant commands run as-is from the repo root; the plan author owns their portability, same trust model as the plan's own test commands.
5. Run full test suite
After all steps are complete, delete .claude/memory/build-phase.json if a between-steps record remains, then run the full test suite. Paste the output as final verification evidence.
Quality ownership — the whole suite must be green, not just this branch's tests. A failing test is a failing test regardless of whether this change caused it: a red suite blocks /pr even when the failure pre-dates the branch. Do not wave a failure past as "pre-existing / unrelated." Either fix it (enter Systematic Debugging for the root cause), or — if it is genuinely out of scope — explicitly surface and triage it (/triage a record or quarantine it with a reason) and report the suite as not green. Never proceed to /pr on red by attributing the failure to someone else's change.
6. Run code review
Run /code-review --internal against all files modified during the build —
deliberately not --json, to keep the review-fix loop running per/code-review's own step-6 exception (b).
Record the backstop's value (#1962). This pass is the build's most
duplicated review layer — every file it reviews was already seen by an inline
checkpoint (sub-steps 4/6) — but until now nothing measured whether it earned
its cost, because review-value.jsonl recorded only checkpoint rows. Subject
to the same ~/.claude/telemetry.json consent check and DEV_TEAM_REVIEW_VALUE
kill switch as sub-step 7, append one row for this pass using the identical
schema, with source: "build-backstop", checkpoint: "backstop", andstep: "all". That row is what turns "is the backstop redundant?" from an
argument into a measurement.
--backstop-review=skip suppresses this step (#1962). When the flag is
set: do not dispatch the panel, print the audit line from Parse Arguments
naming the enclosing reviewer, and append a row with outcome: "skipped" so
the suppression is visible in the same stream as the runs it replaces — never
a silent absence. Proceed to Step 7. The flag reaches only this step; a build
that skips the backstop still cannot reach /pr on red (Step 5) or on an
unresolved escalation.
The flag is a caller's assertion, not a shortcut this skill may take on its
own initiative. /build never infers it — an orchestrator passes it, and
only when its own review pass covers a superset of this build's diff.
7. Final test quality score (branch)
Produce a Farley Score for the tests written on this branch — the last quality signal before /pr.
- Resolve the branch base:
git merge-base HEAD origin/HEAD(fall back toorigin/main, thenmain,master,develop).
Degenerate-base check (issue #916). The fallback chain assumes at least one candidate ref sits meaningfully behind HEAD. That's false in a single-branch/no-remote repo — there's nooriginat all (themerge-basecall fails outright) or every commit landed directly on the fallback branch itself (e.g.master), sogit merge-base HEAD masterresolves to HEAD. Treat base == HEAD, or every candidate ref unresolvable, as a resolution failure, not a valid base — silently continuing to sub-step 2 diffs HEAD against itself and reports a false "no tests written":- Fall back to the plan's recorded plan-start commit — the same anchor a
Rollback point: plan-startslice already resolved against (issue #865):python3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_rollback_point.py get-by-symbolic --path .claude/memory/build-rollback.json --symbolic plan-start --repo <worktree> --ancestor-of HEAD..claude/memory/build-rollback.jsonis a flat store never cleared between plans sharing a worktree, so--repo/--ancestor-ofmatter: they reject a staleplan-startentry left behind by an unrelated earlier build (its SHA won't be an ancestor of this branch's HEAD) rather than trusting the first match blindly. If a qualifying entry is found, use itsshaas<base>for sub-step 2 — this is the "found" case below. - If no qualifying plan-start rollback point was found (exit 1: no slice in this build declared
Rollback point: plan-start, or the only recorded entry failed the ancestry check as stale), do not proceed to sub-step 2 as if nothing were wrong. Print an explicit warning —Branch-base resolution degraded: origin/HEAD, origin/main, main, master, develop all resolved to HEAD or failed, and no plan-start rollback point is recorded — the Farley Score step below cannot distinguish "no tests written" from a resolution failure.— then continue to sub-step 2 with the degraded (== HEAD) base anyway, so sub-step 3 knows to treat an empty diff as inconclusive rather than clean. This is the "not found" case sub-step 3 branches on below — distinct from the "found" case immediately above, where the plan-start SHA is a trustworthy base and an empty diff against it is genuine evidence of no tests written.
- Fall back to the plan's recorded plan-start commit — the same anchor a
- List the branch's changed test files:
git diff --name-only <base>...HEAD, keeping only test files (indicators inknowledge/test-file-indicators.md—*.test.*/*.spec.*/__tests__/, xUnit/JUnit attributes,.featurefiles). - If no test files changed on the branch, print one line —
No tests written on this branch — skipping Farley Score.— and continue to Step 8. Exception: when sub-step 1's degenerate-base check hit the "not found" case (no qualifying plan-start rollback point, base left at HEAD), print that sub-step's degraded-resolution warning instead of this clean line — an empty diff off an unresolved HEAD-equals-base is a resolution failure, not evidence of a clean branch. The "found" case (a validated plan-start SHA used as base) is a real base, so a genuine empty diff against it prints the ordinary clean line above. Either way, continue to Step 8. - Otherwise invoke the
farley-scoreskill scoped to those files. Present the suite-level Farley Score, rating, and distribution as the final pre-PR signal. This is informational — a low score does not block/pr, but surface it so the user can decide whether to revise before opening the PR.
7.5. Assemble the evidence bundle
Before the completion report, assemble a structured evidence bundle per${CLAUDE_PLUGIN_ROOT}/knowledge/evidence-bundle.md — no new checks, no
re-execution; it renders data this run already produced:
- Checks run: the Step 5 full-suite command + result, the Step 6
/code-reviewstatus, the Step 7 Farley Score command/output (or its
skip line when no tests changed). - Scope notes: review agents dispatched vs. skipped across the build's
checkpoints (sub-steps 4/6), and any gate reported "not applicable." - Untested regions: read
baseline-coverage.json/coverage-history.json
if present (from/coverage-baseline//coverage-delta); otherwise state
"not measured — no coverage tool detected." - Residual risks: derived-first from this run's
.claude/metrics/review-value.jsonl
entries withoutcome: "escalated", any non-interactive gate-bypass audit
lines printed in Steps 2–3, and anyfailed-then-fixedruntime-verification
entries inmetrics/verify-log.jsonl. "None identified" only when all of
those are empty.
Follow the degradation rule: every one of the four section headers appears in
the completion report even when a section has nothing to show — it states why.
8. Update plan status
Use the Edit tool to change **Status**: in-progress to **Status**: implemented in the plan file. Briefly confirm completion, report the branch Farley Score, include the Step 7.5 evidence bundle in the completion report, and direct the user to /pr.
9. Learning loop
Invoke the Feedback & Learning skill at task completion to capture any correction turns from this session. If the user used correction language during the build (e.g. "no, actually", "revert", "that's wrong", "stop doing X"), record the pattern so it can become an instruction rule. If no corrections occurred, this step is a no-op — invoke and it will report nothing to capture.
Escalation
A failure is a debugging task first, not a hand-back. Before escalating any test, review, or bash/command failure, run a Systematic Debugging pass — reproduce, find the root cause, state it in one sentence — and escalate with that diagnosis, never just an attempt count.
Stop and ask the user when:
- A test still fails after systematic debugging has identified the root cause and the fix needs a decision you can't make (e.g. it requires changing the spec or the architecture)
- The plan requires architectural decisions not covered by the plan
- A review checkpoint fails after 2 correction iterations and the root cause is understood but unresolvable within scope
- You discover the plan is incomplete or contradictory
- A step's required behavior cannot be tested in isolation — the plan has an architectural gap (e.g. the step's contract depends on scaffolding no earlier step produced)
- A step's dependency was not produced by a prior step that was supposed to produce it (most likely in a same-wave or cross-wave parallel build,
isolation: "worktree") — stop and escalate rather than stubbing or guessing the missing interface inline, which would silently expand the step's scope beyond what it was dispatched to do - The
verify_guard.pyhook blocks a verify command ([BLOCK]on a test/lint/build re-run) — this is the deterministic signal that the same command has run repeatedly with no intervening code change, i.e. a stuck loop rather than a progressing per-behavior cycle. Run the Systematic Debugging pass above instead of retrying the command again, and escalate with the diagnosis if it's still unresolvable in scope. - The step-4 repair loop hits a failure-signature dead-end (issue #864): two consecutive repair iterations produce the same normalized failure signature (failing test IDs + error class, volatile output stripped). This is a hard stop, not another auto-approval: commit the current working tree as a checkpoint on the working branch — never
main— with a conventional message explicitly marked as a dead-end checkpoint (e.g.chore(build): dead-end checkpoint — step <N>, <M> tests still failing), then escalate stating (a) improved — tests that went failing → passing since repair start, (b) remaining — the current failing signature, (c) the checkpoint commit ref. If 3 or more distinct fix attempts have failed, the escalation must also cite Systematic Debugging's "3+ failed fix attempts → question the architecture" rule. Leave plan status unchanged and never proceed to/prover this escalation.
Non-interactive runs: an escalation is a hard stop, not another auto-approval.
The approval gates in Steps 2–3 auto-proceed because they bypass a judgment call the
human delegated by going non-interactive; an escalation exists because the agent hit
something it cannot safely decide — that authority was never delegated. When any
condition above fires and no user can answer (--yes, DEV_TEAM_AUTO_APPROVE=1, or
no TTY): write the escalation (trigger, one-sentence root-cause diagnosis, options
considered) to .claude/memory/build-escalation-<plan-slug>.md, leave the plan status
unchanged, report the halt as the build result, and stop. Never resolve an escalation
by picking an interpretation and continuing, and never proceed to /pr over an
unresolved escalation.
Integration
/specsproduces the intent, architecture, and acceptance-criteria artifacts that inform the plan/plandecomposes the feature into slices, authors each slice's Gherkin, and produces the plan this command executes- Sub-step 4.9 exercises each runtime-surface slice end-to-end, using the project's own test/verification tooling scoped to the diff, before it may be marked done (issue #727) — this is a pattern to follow, not a named
/verifycommand /code-reviewruns the full review suite after implementationfarley-scorescores the branch's tests (Farley Score) as the final pre-PR quality signal${CLAUDE_PLUGIN_ROOT}/knowledge/evidence-bundle.mddefines the structured evidence bundle assembled in Step 7.5 and surfaced in the Step 8 completion report/prcreates the pull request after a successful build, assembling its own evidence bundle independently (no handoff file)/continuecan resume a partially completed build across sessionspython3 ${CLAUDE_PLUGIN_ROOT}/scripts/progress_guardian.py --plan <plan-file>validates step completion and commit discipline at each step boundary;--pre-pralso fails closed when runtime-surface changes have no matchingmetrics/verify-log.jsonlentry (issue #727), and warns (never fails) on out-of-scope edits against declared sliceFiles(issue #865)${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py,${CLAUDE_PLUGIN_ROOT}/scripts/build_rollback_point.py, and${CLAUDE_PLUGIN_ROOT}/scripts/run_invariants.pyimplement the plan-as-contract fields (issue #865): opt-in freeze scope, rollback-point resolution/recording, and the slice invariants gate, respectively