- name
- build
- description
- 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.
- argument-hint
- [--plan <path>] [--yes] [--backstop-review=skip]
- user-invocable
- true
- allowed-tools
- Read, Write, Edit, Glob, Grep, Bash, Agent, AskUserQuestion
# 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.
1. **Follow the plan exactly.** If the plan is wrong, stop and ask the user — do not deviate silently.
2. **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.md` Rec 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.
3. **Incremental.** Each step must leave the codebase in a working, committable state.
4. **Verification evidence required.** Paste fresh test output before claiming a step is done.
5. **Review checkpoints (granularity scales with complexity).** Run inline review (static self-heal pass first, then spec-compliance, then quality agents) per step for `complex` steps; batch `standard`/`trivial` steps into one review at the slice boundary. When `~/.claude/telemetry.json` consent is enabled (and `DEV_TEAM_REVIEW_VALUE` is not `off`), record each checkpoint's find/fix/no-op outcome to `.claude/metrics/review-value.jsonl`. The final `/code-review` is the backstop.
6. **Be concise.** Report step status and verification evidence, no narration.
7. **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, search `plans/` for the most recently modified plan with status `approved`.
- `--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 `/build` directly. Default **off**: an unflagged `/build` runs 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 `/build` whose 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](../context-loading-protocol/SKILL.md) 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.
1. **Check for `package.json`** in the working directory. If it exists, **skip this gate silently** and continue to Step 2.
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.
3. **Bootstrap** (only when `package.json` is 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 the `project-init` skill.
4. **Halt on failure.** If `project-init` fails, **stop `/build`** and 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 '<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](../human-oversight-protocol/SKILL.md#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:
1. Present the findings to the user with the reviewer's suggested improvements
2. **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
3. **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](../human-oversight-protocol/SKILL.md#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:
```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/build_worktree_baseref.py" detect # prints head|fresh|unset|unknown
```
- **`head`** → no warning. Proceed.
- **`fresh`**, **`unset`**, or **`unknown`** (detection failed — e.g. `jq` unavailable; treated fail-safe) → unless `DEV_TEAM_WORKTREE_BASE_FRESH=1` is 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 that `worktree.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:**
```bash
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 concurrency
```
`build_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):**
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.
2. **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 N` or `DEV_TEAM_MAX_PARALLEL_BUILDS` if the burn rate is too high (#1170).
3. **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.
4. **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:
1. **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:** freeze` metadata line does (Ambiguity Log Q1). When engaged **and** the dispatching slice declares `**Files:**`, run `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/build_slice_scope.py engage <plan-file> --slice <id> --hooks-dir <worktree>/.claude/hooks` — this writes `.claude/hooks/freeze-state.json` with `allowed_patterns` set to the slice's declared paths plus the fixed bookkeeping allowlist (the plan file, `.claude/memory/**`, `.claude/metrics/**`, and the AC3-exempt `metrics/verify-log.jsonl`), so `hooks/pre_tool_guard.py` blocks any Write/Edit outside that scope without also blocking `/build`'s own progress writes. The `--hooks-dir` value must point at `<worktree>/.claude/hooks`, not `<worktree>/hooks` — `hooks/pre_tool_guard.py` reads the freeze state via `hooks/lib/artifact_paths.py`'s per-repo `.claude/hooks/freeze-state.json` convention (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`.
2. **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>`, then `python3 ${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's `get` subcommand. A slice without `Rollback point` records nothing.
View on GitHub