Spec 009 — Worktree at scaffold
Status: implemented Branch: 009-worktree-at-scaffold
Status is set by the human, never by the agent. It moves draft → approved → implemented.
Problem
Step 0 scaffolds a new spec by running git checkout -b in the shared working tree. Isolation does not arrive until step 3.5. The consequence is that a second spec cannot be started while a first is in flight: scaffolding it switches the shared tree off the first spec's branch, and any uncommitted spec work blocks the switch outright. The moment at which a spec first reaches the remote is also unstated, so a branch can live only on one machine through spec, discovery, and planning.
User stories
Ordered by priority. Each story must be independently testable and shippable.
P1 — Scaffold isolates immediately
As a developer, I want step 0 to create the worktree and move me into it, so that I can begin a second spec without disturbing the first.
Independent test: scaffold spec A, then spec B; A's worktree still exists on its own branch and the shared tree never left main.
Acceptance scenarios
- Given the shared tree on
mainand clean, when I scaffold slugX, then a worktree.claude/worktrees/NNN-Xexists on branchNNN-X, the session's working directory is that worktree, and the shared tree is still onmainand clean. - Given spec A already scaffolded (its worktree present), when I scaffold spec B, then B gets its own worktree and branch and A's worktree and branch are untouched.
- Given scaffolding has just completed, when the turn ends, then
spec.mdhas not been started — only the draft template is present.
P2 — The spec reaches the remote before discovery
As a developer, I want spec.md committed and pushed before discovery begins, so the branch exists on the remote early and parallel work is safe.
Independent test: after spec.md is agreed and before research.md is touched, origin carries branch NNN-slug with the spec commit.
Acceptance scenarios
- Given
spec.mdis written and agreed, when I move to discovery (step 1.5), then the branch has already been pushed tooriginwithspec.mdcommitted. - Given any later artifact (
research.md,plan.md,tasks.md), when it is completed, then it too is committed and pushed.
P3 — Cleanup on merge
As a developer, I want the worktree and branch removed when the PR merges, so worktrees do not accumulate.
Independent test: after the PR merges and step-6 cleanup runs, git worktree list no longer shows the worktree and the branch is gone locally.
Acceptance scenarios
- Given a merged PR for
NNN-slug, when step-6 cleanup runs, then the worktree directory is removed and branchNNN-slugno longer exists locally.
Requirements
- R1 — Step 0 creates the branch and an isolated worktree via the native worktree tool (
EnterWorktree), notgit checkout -bin the shared tree. - R2 —
scripts/new-spec.tsperforms no branch switch and nogit checkout; branch creation belongs to the native tool. (regression) - R3 —
new-spec.tsexposes an allocate mode that printsNNN-slug: it validates the slug, writes nothing, and makes no git mutation. Numbering agrees with the worktree's base ref and stays unique across in-flight specs: it is derived from merged specs onorigin/mainunioned with existingNNN-*branches, so the branch number cannot disagree with the worktree's contents and a second parallel spec never reuses the first's number. (research.mdV2; SC1.) - R4 —
new-spec.tsexposes a scaffold mode that writes the template files intospecs/NNN-slug/with no git operation. - R5 — The existence guards remain: scaffolding refuses when the spec directory already exists (
writeScaffold), before anything is written. The branch-exists clause is enforced byEnterWorktree(research V1), not by the script:allocateSpecalready unions in everyNNN-*branch, so the number it hands back is always fresh and a same-number branch can never pre-exist — a script-side branch guard would be dead code. (Implementation note, 2026-08-03.) - R6 — After step 0 the session's working directory is the feature worktree, on branch
NNN-slug. - R7 —
spec.mdis committed and pushed tooriginbefore discovery (step 1.5) begins; each later artifact (research.md,plan.md,tasks.md) is likewise committed and pushed. - R8 — Step 3.5 (Isolate) is removed from
CLAUDE.md;superpowers:using-git-worktreesmoves to the step-0 row of the technique table. - R9 — The worktree and branch are removed once the PR merges, by documented commands in
CLAUDE.md's Cleanup step, run from the main tree:git worktree remove .claude/worktrees/NNN-slug,git branch -D NNN-slug,git worktree prune. Cross-session by nature, sinceExitWorktreeandfinishing-a-development-branchdo not cover the.claude/worktrees/layout (research.mdV3). No new command or script — packaging chosen by the human on 2026-08-03. - R10 — The
docs/superpowers/file count stays 0. - R11 — The
/new-speccommand doc orchestrates the sequence: allocate →EnterWorktree name=NNN-slug→ scaffold → report directory, branch, and worktree path, then stop without startingspec.md.
Discovery verdicts (research.md, 2026-08-03):
- V1 — confirmed.
EnterWorktree/git worktree add -bfails loudly on a pre-existing branch and leaves the shared tree onmain; R5's guard is belt-and-braces, not load-bearing. - V2 — confirmed; R3 tightened to derive numbering from
origin/main. - V4 — resolved.
scaffold.test.tsP3 #2 / SC4 do assert the old checkout; they change with R2. - V5 — confirmed. SC1/SC3/SC5/SC6 are covered by
(proxy)+ human-verified rows, the rest by unit tests — see Traceability. - R9 — resolved. Cleanup is refuted-then-redecided: not the
finishing-a-development-branchskill but documented commands inCLAUDE.md's Cleanup step (human decision, 2026-08-03;research.mdV3).
Success criteria
Measurable and technology-agnostic.
- SC1 — Scaffolding a second spec leaves the first spec's worktree and branch intact.
- SC2 — Scaffolding never changes the shared working tree's current branch; it stays on
main, clean. - SC3 — After step 0, the session's working directory is the worktree and the branch is
NNN-slug. - SC4 — The template files exist in the worktree's
specs/NNN-slug/, and thedocs/superpowers/count is 0. - SC5 — The spec is committed and pushed (the
originbranch exists) before discovery. - SC6 — On merge,
git worktree listno longer lists the worktree and the branch is gone. - SC7 —
new-spec.tsruns nogit checkout. (regression)
HTTP surface
None. This is CLI / workflow tooling — no HTTP endpoints and no OpenAPI document. Recorded explicitly rather than omitted, per the project rule that the HTTP surface is first-class spec content.
Out of scope
- Performing the GitHub merge itself: cleanup is triggered by the human merging the PR, not by the tool.
- Changing the worktree base ref away from
fresh(origin/main). - Migrating specs already in flight to worktrees — the new flow applies from the next spec onward.
- Changing the numbering scheme.
Assumptions
- An
originremote exists and localmaintracks it (PRs #4–#8 merged through it). - Single developer: allocate mode and scaffold mode reading
specs/moments apart yield the sameNNN, so there is no allocation race. - The
EnterWorktreenative tool is available in the session (it is —using-git-worktreesrelies on it). - Spec 009 itself is scaffolded with the current, pre-009 flow; the new flow applies from spec 010.
Traceability
Each acceptance scenario and success criterion maps to a named test. Fill in during implementation. The split between automatable and workflow-level criteria is itself a [NEEDS VERIFICATION] above; the table records the resolution.
| Criterion | Test |
|---|---|
| P1 #1 | R2 / SC7: writing the scaffold performs no git operation (shared tree unchanged) + SC4 / R4: writeScaffold writes the four artifacts with placeholders substituted; worktree/session state human-verified (EnterWorktree) |
| P1 #2 | SC1: allocate skips a number taken only by an in-flight branch (distinct number); worktree coexistence human-verified |
| P1 #3 | human-verified — the /new-spec command stops before spec.md |
| P2 #1 | R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; the push itself human-verified |
| P2 #2 | R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; per-artifact push human-verified |
| P3 #1 | SC6 (proxy): CLAUDE.md documents the merge-time cleanup commands; the removal human-verified |
| SC1 | SC1: allocate skips a number taken only by an in-flight branch + R3: basenames are taken and non-spec entries dropped; coexistence human-verified |
| SC2 | R2 / SC7: writing the scaffold performs no git operation + R3 / SC7: allocate numbers from origin/main and mutates nothing |
| SC3 | human-verified — after EnterWorktree the session cwd is the worktree on NNN-<slug> |
| SC4 | SC4 / R4: writeScaffold writes the four artifacts with placeholders substituted + SC3: no workflow artifact is written outside specs/NNN-<slug>/ (docs/superpowers stays 0) |
| SC5 | R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; the push human-verified |
| SC6 | SC6 (proxy): CLAUDE.md documents the merge-time cleanup commands; git worktree list emptiness human-verified |
| SC7 | R2 / SC7: writing the scaffold performs no git operation + R3 / SC7: allocate numbers from origin/main and mutates nothing |