Skip to content

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

  1. Given the shared tree on main and clean, when I scaffold slug X, then a worktree .claude/worktrees/NNN-X exists on branch NNN-X, the session's working directory is that worktree, and the shared tree is still on main and clean.
  2. 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.
  3. Given scaffolding has just completed, when the turn ends, then spec.md has 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

  1. Given spec.md is written and agreed, when I move to discovery (step 1.5), then the branch has already been pushed to origin with spec.md committed.
  2. 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

  1. Given a merged PR for NNN-slug, when step-6 cleanup runs, then the worktree directory is removed and branch NNN-slug no longer exists locally.

Requirements ​

  • R1 — Step 0 creates the branch and an isolated worktree via the native worktree tool (EnterWorktree), not git checkout -b in the shared tree.
  • R2 — scripts/new-spec.ts performs no branch switch and no git checkout; branch creation belongs to the native tool. (regression)
  • R3 — new-spec.ts exposes an allocate mode that prints NNN-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 on origin/main unioned with existing NNN-* branches, so the branch number cannot disagree with the worktree's contents and a second parallel spec never reuses the first's number. (research.md V2; SC1.)
  • R4 — new-spec.ts exposes a scaffold mode that writes the template files into specs/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 by EnterWorktree (research V1), not by the script: allocateSpec already unions in every NNN-* 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.md is committed and pushed to origin before 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-worktrees moves 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, since ExitWorktree and finishing-a-development-branch do not cover the .claude/worktrees/ layout (research.md V3). 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-spec command doc orchestrates the sequence: allocate → EnterWorktree name=NNN-slug → scaffold → report directory, branch, and worktree path, then stop without starting spec.md.

Discovery verdicts (research.md, 2026-08-03):

  • V1 — confirmed. EnterWorktree / git worktree add -b fails loudly on a pre-existing branch and leaves the shared tree on main; R5's guard is belt-and-braces, not load-bearing.
  • V2 — confirmed; R3 tightened to derive numbering from origin/main.
  • V4 — resolved. scaffold.test.ts P3 #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-branch skill but documented commands in CLAUDE.md's Cleanup step (human decision, 2026-08-03; research.md V3).

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 the docs/superpowers/ count is 0.
  • SC5 — The spec is committed and pushed (the origin branch exists) before discovery.
  • SC6 — On merge, git worktree list no longer lists the worktree and the branch is gone.
  • SC7 — new-spec.ts runs no git 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 origin remote exists and local main tracks it (PRs #4–#8 merged through it).
  • Single developer: allocate mode and scaffold mode reading specs/ moments apart yield the same NNN, so there is no allocation race.
  • The EnterWorktree native tool is available in the session (it is — using-git-worktrees relies 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.

CriterionTest
P1 #1R2 / 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 #2SC1: allocate skips a number taken only by an in-flight branch (distinct number); worktree coexistence human-verified
P1 #3human-verified — the /new-spec command stops before spec.md
P2 #1R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; the push itself human-verified
P2 #2R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; per-artifact push human-verified
P3 #1SC6 (proxy): CLAUDE.md documents the merge-time cleanup commands; the removal human-verified
SC1SC1: allocate skips a number taken only by an in-flight branch + R3: basenames are taken and non-spec entries dropped; coexistence human-verified
SC2R2 / SC7: writing the scaffold performs no git operation + R3 / SC7: allocate numbers from origin/main and mutates nothing
SC3human-verified — after EnterWorktree the session cwd is the worktree on NNN-<slug>
SC4SC4 / R4: writeScaffold writes the four artifacts with placeholders substituted + SC3: no workflow artifact is written outside specs/NNN-<slug>/ (docs/superpowers stays 0)
SC5R7 (proxy): CLAUDE.md states the spec is committed and pushed before discovery; the push human-verified
SC6SC6 (proxy): CLAUDE.md documents the merge-time cleanup commands; git worktree list emptiness human-verified
SC7R2 / SC7: writing the scaffold performs no git operation + R3 / SC7: allocate numbers from origin/main and mutates nothing