V025 Multi-Worktree Workflow¶
Problem¶
A V025 checkout is not a single working directory. It is several git worktrees sharing one repository, each playing a distinct role — but those roles are nowhere documented. Every new contributor and every Claude session re-derives them from git error messages and history:
worktree-manager.sh createfails becausemainis checked out in some other worktree.- A merge into
mainrequires a "dance" — youcdinto a different worktree to run the merge, because you can't checkmainout where you are. - The active workspace carries a long-lived branch whose name has drifted from its content, and the fix (rename in place) is an unwritten convention.
- Spec numbers assigned on unmerged branches collide with numbers the current worktree can't see.
Each symptom has its own surface explanation, so the shared root cause stays invisible. The
knowledge lives in lived practice and in per-user MEMORY.md, but memory is the wrong home: it
truncates, drifts as state changes, and is per-user not per-repo. These are repo-level toolkit
conventions and belong in the toolkit's documentation surface. This doc is that home.
The three roles¶
V025 uses three worktree roles, each with a distinct lifecycle. A worktree's role is defined by the branch it holds, not by its path.
| Role | Branch it holds | Lifecycle |
|---|---|---|
| Main-Holder | main |
The worktree that currently holds main — often none. main is frequently checked out nowhere; it only needs a holder at merge time, and that holder is usually thrown away afterward. Not a standing role — a transient state. See merge into main and EC-1 below. |
| Active-Workspace | a long-lived chore/* or docs/* branch |
The repo-root checkout where day-to-day tooling commands run and commit (intake-qualify routings, spec births, doc-sync updates). Accumulates commits between merges to main. When its branch name no longer reflects its content, it is renamed in place, not deleted — it is the only checkout this worktree has. See rename the active-workspace branch. |
| Feature | feature/spec-NNN-* |
One per active spec, created from main via worktree-manager.sh create, living under .worktrees/feature/. Built through the spec workflow (/vt-d-2-plan → /vt-d-3-build → …), then merged back into main after /vt-d-complete. Removed once merged. |
Why "Main-Holder" is a transient state, not a standing role. Because main is usually held
nowhere, there is normally no Main-Holder to find. Merges are performed by temporarily giving
some worktree main (or spinning up a throwaway holder), merging, then releasing it. If you go
looking for a permanent main-holder worktree, you often won't find one — and that is correct, not
a broken state.
Edge cases referenced in this doc¶
Four recurring situations are labeled EC-1 / EC-2 / EC-3 / EC-4 throughout the procedures below:
- EC-1 —
mainis held nowhere. The normal resting state: no worktree hasmainchecked out, so a merge intomainneeds a throwaway holder. See merge into main. - EC-2 — the Active-Workspace branch name has drifted from what the branch now contains and needs an in-place rename (not a delete). See rename the active-workspace branch.
- EC-3 — a feature branch forked from
mainbefore a recent merge and is now behindmain. See sync a fresh feature branch after a main-merge. - EC-4 — a forked phase must never prompt.
4-reviewand5-finalizedeclarecontext: fork; anAskUserQuestioninside one is swallowed and the sub-agent dies silently. See running several worktrees in parallel.
Standard procedures¶
Create a feature worktree¶
From the Active-Workspace, always use the manager script (never raw git worktree add — the
script copies .env* files, manages .gitignore, and keeps the directory layout consistent):
bash ${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh create feature/spec-NNN-slug
The script creates the branch from main and handles the held-main case gracefully (fixed at
commit b53fed62). /vt-d-activate Step 6.7 offers to do this for you at activation time.
Side effect to undo when
mainis held nowhere (EC-1). The script's currency step runsgit checkout mainin the Active-Workspace before adding the worktree. Whenmainis held elsewhere that checkout fails harmlessly and is skipped; but whenmainis held nowhere it succeeds and leaves the Active-Workspace sitting onmain— HEAD is never restored. After the command returns, confirm withgit -C "<active-workspace-path>" branch --show-currentand, if it now readsmain, restore it:git -C "<active-workspace-path>" checkout <active-workspace-branch>.
Merge into main¶
Never check out main in the Active-Workspace to merge. If main is held in another worktree
you'll get a already used by worktree error (see anti-patterns);
if it's held nowhere you could check it out, but the convention is to keep the Active-Workspace on
its own long-lived branch. Instead:
- Find who holds
main, if anyone: - If a holder path is printed (an absolute path from step 1), merge the source branch in that worktree without changing your own cwd:
- If nothing is printed (
mainheld nowhere — the common case, EC-1), create a throwaway holder worktree onmain, merge in it, then remove it:# Anchor every path on the MAIN worktree root (the first entry git lists), so these # steps work unchanged from any cwd — repo root OR a feature worktree. root="$(git worktree list --porcelain | awk 'NR==1{sub(/^worktree /,"",$0);print;exit}')" # Attach the EXISTING main branch with raw `git worktree add` — the one documented # exception to "always use the manager script". The script can't do this: it always # passes `-b` (→ `fatal: a branch named 'main' already exists`) and its currency step # would `git checkout main` in the Active-Workspace, stranding it on main. git worktree add "$root/.worktrees/main-holder" main git -C "$root/.worktrees/main-holder" merge --no-ff <source-branch> git worktree remove "$root/.worktrees/main-holder"
The pre-commit hook has a MERGE_HEAD exception, so --no-ff merges into main need no
--no-verify. This is a local ephemeral-branch workflow — main is not pushed by default.
Sync a fresh feature branch after a main-merge¶
A feature worktree created from main before the day's Active-Workspace commits were merged is
now behind. Bring it current from inside the feature worktree (EC-3):
Do this right after creation if main moved between when the feature branch forked and when you
started work.
Rename the active-workspace branch¶
When the Active-Workspace branch name no longer fits what's on it (EC-2), rename in place rather than deleting — the branch is this worktree's only checkout:
(Real example: docs/doc-sync-2026-05-18 → chore/scratch once the branch had accumulated more
than doc-sync work.)
Anti-patterns & error signatures¶
| You see / you do | What it means | What to do instead |
|---|---|---|
fatal: '<branch>' is already used by worktree '<path>' |
The branch (often main) is checked out in the worktree at <path>. Git allows a branch in only one worktree at a time. |
Don't check it out here. cd "<path>" and work there, or use a throwaway holder. See merge into main. |
Checking out main in the Active-Workspace to merge |
Breaks the role separation; fails outright if main is held elsewhere. |
Merge from the Main-Holder (or a throwaway holder), never from the Active-Workspace. |
| Deleting the Active-Workspace branch because its name is stale | Loses the worktree's only checkout and its accumulated pre-merge commits. | git branch -m to rename in place. |
New feature worktree's first build sees stale main state |
The branch forked from main before a recent merge. |
git merge main inside the feature worktree (EC-3). |
spec-from-requirements proposes a SPEC number that already exists elsewhere |
Pre-SPEC-165 behaviour: the tool saw only the current worktree's registry. | Should no longer happen — the allocator owns this. See spec-numbering across branches. |
Parallel-work hazards and guardrails¶
The roles and procedures above describe how to run several worktrees. This section describes what running several worktrees has actually cost this repo, and the guardrail that follows from each. Every hazard here is one we have already paid for at least once, with the commit that paid it cited. They are documented as hazards to avoid re-paying — not as arguments against working in parallel.
Hazard (a) — the registry keeps saying specified after teardown¶
The gap. The Feature role's lifecycle ends "Removed once merged" (see the three roles). Nothing in that step reconciles the two places a spec's status lives:
| Where | Written by | Authoritative for |
|---|---|---|
specs/[N]-feature/state.yaml |
the workflow phases (/vt-d-2-plan, /vt-d-complete, …) |
the spec's real status |
.design-state.yaml → specs_status.SPEC-NNN.status |
hand edits and some skills | the dashboard, wave computation, /vt-d-activate |
A spec can therefore be built, finalized, merged and its worktree removed while the registry still
advertises it as specified — and the worktree that knew better no longer exists.
What it cost. SPEC-158 was built, finalized (GO, v3.51.0) and merged at edea6444, while
.design-state.yaml still marked it specified; its own state.yaml already read completed.
Repairing one field took a dedicated branch, commit 3dae31ad, and merge commit 611dd10f — a
one-line diff that cost a full branch-and-merge cycle because it was found long after the fact.
Sibling failure, same family. /vt-d-complete never clears .active-spec; the pointer is only
overwritten by the next activation, so a completed spec stays falsely "active" until something
else happens to replace it (bugs/017-complete-active-spec-stale/report.md, which cites the earlier
incident the report cites as 931ad01 — work on SPEC-29 ran while .active-spec still named the
completed SPEC-23. ⚠ That SHA does not resolve in this repository; it is quoted from the bug
report rather than independently verifiable, so treat the incident as reported, not as a lookup).
Completion propagating to one record but not the others is the shape to watch for.
Guardrail — reconcile before you remove.
git worktree removeis the point of no return: it deletes the copy ofstate.yamlthat would have told you the truth. Before removing a feature worktree, confirm the registry agrees with the spec's ownstate.yaml, and fix it if not.
Run from the repo root; it compares every registry entry against its per-spec state.yaml and prints
only the mismatches:
python3 - <<'PY'
import os, yaml
reg = yaml.safe_load(open('.design-state.yaml')).get('specs_status') or {}
drift = compared = 0
for sid, meta in sorted(reg.items()):
meta = meta or {}
# BOTH key names are live: the registry carries `specs_dir` on most entries and `dir` on the
# rest. Reading only one silently skips the majority and prints a confident all-clear.
d = meta.get('dir') or meta.get('specs_dir')
if not d:
continue
p = os.path.join(d.rstrip('/'), 'state.yaml') # specs_dir values carry a trailing slash
if not os.path.exists(p):
continue
live, shown = (yaml.safe_load(open(p)) or {}).get('status'), meta.get('status')
if shown is None or live is None:
continue
compared += 1
if shown != live:
print('%s registry=%s state.yaml=%s' % (sid, shown, live)); drift += 1
print('compared: %d drift: %d' % (compared, drift))
PY
The compared: count is deliberate — read it before you trust drift: 0. A check of this shape
once shipped here reading only dir, which compared 43 of 174 entries and reported a clean
tree while a real mismatch sat in the other 131.
drift: 0 is the clean state. Any line above it names a spec whose registry entry lies; the per-spec
state.yaml is the one to believe, so fix .design-state.yaml to match it.
Three things the check cannot tell you. Entries with no status: key at all are skipped rather
than flagged — the newest spec births do not write one, so absence is normal and is not drift.
Entries with neither dir nor specs_dir are skipped too. And the check compares only what the
current worktree can see: a stale branch shows you its own old registry, not main's. Merge main
first (EC-3) if you want the comparison to mean anything repo-wide.
Hazard (b) — two branches claim the same version, and git says nothing¶
The gap. Version numbers and the count mirrors beside them are edited on parallel branches, but git merges them as text. It has no notion of "these two branches both claimed 3.49.0". Whether you find out depends entirely on which of three shapes the edit took:
| What the branches did | What git does | How you find out |
|---|---|---|
| Both wrote the same new value | Merges clean — identical text on both sides is not a conflict | You don't. Two changes ship under one version number |
| Wrote different values on the same line | Conflicts — you resolve it by hand | Immediately. This is the safe shape |
| Branch never touched the version files | Merges clean; main's value is retained | You don't. The bump the branch intended silently never happens |
Only the middle row is loud. The other two are the hazard: one loses a version number, the other loses a bump.
What it cost. Four reconciliations, all in one week:
- SPEC-160 and SPEC-162 both finalized
3.49.0. SPEC-162 reachedmainfirst (1bd6fa78), so SPEC-160 was rebased on merge to3.49.0→3.50.0, taking main's skill count in every mirror. - BUG-003 and BUG-004 both claimed
3.50.1. BUG-003 was re-based to3.50.2— its ledger entry still records it, "reconciled onto main after BUG-004 independently claimed 3.50.1". - SPEC-146 and SPEC-159 never touched the version files at all. Main's value was retained on merge and the intended bump had to be applied afterwards, on the merged tree. SPEC-159's finalize gate flagged a collision hazard that the merge itself then resolved invisibly.
Note what BUG-004's entry says about the code: "Main never touched persona-select.sh, so the
code fix merged conflict-free." The code was never the problem. The metadata beside it was.
Guardrail — ask what
mainalready claimed, before you finalize a bump. A version number is not yours until it is merged. Check before you write it, not after:
# Substitute the plugin you are bumping.
p=vt-product-dev
# Paths go in as ARGUMENTS, never interpolated into the python source — a value containing a quote
# would otherwise break out of the string literal.
printf 'branch=%s main=%s\n' \
"$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['version'])" \
"plugins/$p/.claude-plugin/plugin.json")" \
"$(git show "main:plugins/$p/.claude-plugin/plugin.json" | python3 -c "import json,sys;print(json.load(sys.stdin)['version'])")"
If main has moved past where your branch forked, merge main first (EC-3) and re-derive the bump
on the merged tree — that is what SPEC-146 and SPEC-159 ended up doing the slow way.
Second detection signal — the count mirrors. A skill added on a parallel branch leaves
components.skills and the manifest disagreeing. Derive both; never quote either:
for p in plugins/*/.claude-plugin/plugin.json; do
d=$(dirname "$p"); m="$d/skill-symlinks.manifest"; [ -f "$m" ] || continue
live=$(grep -v '^#' "$m" | grep -c ' -> ')
shown=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1])).get('components',{}).get('skills','-'))" "$p")
[ "$live" = "$shown" ] || echo "DRIFT $d: plugin.json=$shown manifest=$live"
done
Silence means every mirror agrees. Two traps in that one-liner, both already paid for: the
grep -v '^#' is required because each manifest's header comment contains a literal -> and a
naive count overcounts by one per file; and the count must be derived per plugin, because since
the plugin split each manifest carries its own prefix.
The general form and the mirror list live elsewhere — do not duplicate them here.
docs/solutions/patterns/count-reconciliation-single-source.md is the general rule (reconcile to ONE
source, pin ALL N mirrors); docs/solutions/patterns/critical-patterns.md P-003 carries the
per-bump checklist of which fields move together. That checklist's detection signal used to hardcode
grep -c "^vt-c-", which returned 0 for every plugin but vt-base and read as drift-free; it was
corrected on 2026-08-13 (BUG-067) and now carries the same derivation used above, pinned by
scripts/tests/setup/57-plugin-component-counts-agree.sh. Either place is safe to follow.
Hazard (c) — running setup.sh from a worktree (fixed; guard in place)¶
setup.sh derives TOOLKIT_ROOT from BASH_SOURCE and writes it as the absolute symlink target
for every skill, command, agent and hook. Deploying from a feature worktree therefore points your
live ~/.claude at a directory that disappears when that worktree is removed.
This is fixed. assert_deployable_root() (scripts/setup.sh:966-1043) refuses to deploy from a
linked worktree; --allow-worktree (parsed at :3316, declared at :60-61, honoured by the guard's
early return at :967) is the deliberate opt-out. The incident record — two occurrences, and the
finding that a worktree deploy does not merely defer the damage but silently downgrades live content
— is in bugs/009-setup-worktree-path-baking/report.md. Read it there; it is not repeated here.
Hazard (d) — resolving the merge, once you are in it¶
Merge into main above covers how to run the merge — finding the holder, the
throwaway-holder recipe, the MERGE_HEAD pre-commit exception. This is the other half: which files
to reconcile, and how, once git has stopped and handed you conflicts. Do not look for these rules
in the procedure section; they are not there.
The distinction that decides every case: is this file a ledger, or a mirror?
| File | Kind | Rule |
|---|---|---|
.design-state.yaml (specs_status, bugchen_status) |
ledger — each branch registers its own spec/bug | Keep BOTH sides. The entries are additive, not competing. Dropping one un-registers a spec that exists |
docs/change-ledger.md |
ledger — each branch appends its own entry | Keep BOTH sides, in date order |
plugins/*/CHANGELOG.md |
ledger | Keep BOTH sides |
plugin.json version, components.*, README/docs skill counts |
mirror — one correct value | Take main's, then re-derive on the merged tree. See hazard (b) |
Keeping both sides of a ledger and taking one side of a mirror is the whole rule. The failure is applying a mirror's instinct ("pick the right one") to a ledger, which silently deletes a real record.
Worked example, from the SPEC-160 reconciliation (docs/change-ledger.md, SPEC-160 entry): main's
skill count was taken in every mirror, while both entries were kept in the CHANGELOG and the
ledger. Same merge, two opposite resolutions, chosen by file kind.
Historical note — the
--theirsgate-file rule no longer applies. That same entry records--theirson the ephemeral.review-gate.md/.test-gate.md. Both files are now gitignored (.gitignore:44-47) and untracked —de032495(BUG-014) deleted the tracked copies, andscripts/tests/setup/48-ephemeral-state-is-not-tracked.shkeeps them that way. Untracked files cannot produce a merge conflict, so the rule is recorded here only so that reading the older ledger entries does not mislead you. Do not go looking for a gate-file conflict to resolve.
How many at once — and what it costs you¶
The working ceiling is 4–6 concurrent feature worktrees. Treat that as a target to plan against, not a measurement: it comes from SPEC-167's user story, which is where the number was proposed. No throughput study has been run in this repo, and none is planned — see the honest status below.
What bounds it is human attention, not machine capacity. Git will happily give you twenty
worktrees. Each one in flight needs a person to read its review findings, attest its DoR/DoD items,
resolve its merge, and decide what to do about it — and git shortlog -sne --all shows that review
attention concentrated in effectively one reviewer: two identities carry essentially all of it
(rolfsterhh-visitrans and the pm-team address), against single- and low-double-digit counts for
everyone else. Run it yourself before relying on the number. The async phases (context: fork review and
finalize) genuinely run without you; everything on either side of them does not. The ceiling is
therefore a statement about one person's review bandwidth, and it moves if that changes.
The costs are the four hazards above. They are not incidental — they are the tax:
| Cost | Where it lands |
|---|---|
| Registry and pointer drift | hazard (a) — every extra worktree is another record that can go stale after teardown |
| Version and count collisions | hazard (b) — collisions scale with the number of branches finalizing near each other, and two of the three shapes are silent |
| Merge reconciliation | hazard (d) — every parallel branch eventually pays it |
| Staleness | EC-3 — a branch forked early drifts behind main while you work elsewhere, and the drift is invisible until you merge |
Beyond those, one cost stated as reasoning, not measurement — the same standard this section applies to the benefit: context-switching raises overhead and lowers deep flow. Steering several tasks means holding several problem-states, and re-entering one you left hours ago costs real time. That is a trade, not a free lunch, and it is the reason this section states a ceiling at all.
Honest status of the benefit. The claim that parallel work is faster — that the async phases
amortise the ceremony across idle gaps — is an unvalidated hypothesis, not a finding.
docs/evaluations/vtd-vs-v025-comparison.md labels it verbatim "The hypothesis (to validate, not
assert)", and the trial that would have tested it was deliberately cut from SPEC-167 as an
organizational experiment rather than a toolkit concern (specs/167-*/decisions.md, Decision 1).
So: this doc tells you how to run several worktrees without re-paying the hazards above. It does
not tell you that doing so is faster, because nobody here has measured that.
Serial work remains fully supported. Nothing in this doc deprecates working one spec at a time, and the ceiling is an upper bound, not a quota.
Running several worktrees in parallel¶
The loop below composes parts that already exist — it introduces no new machinery. Each step links to the procedure that owns it rather than repeating it.
Per task, once:
- Create the worktree — create a feature worktree. Use the manager
script, and remember its EC-1 side effect: when
mainis held nowhere the script can leave the Active-Workspace sitting onmain, so check and restore HEAD afterwards. - Sync it if
mainhas moved —git merge maininside the new worktree (EC-3). Do this at creation, not at merge time; a branch that forks early and drifts is the cheapest hazard to avoid and the most annoying to discover late. - Activate the spec there —
/vt-d-activate SPEC-NNNfrom inside that worktree, so the spec'sstate.yamland the branch agree.
Then, steering several at once:
- Work the synchronous phases yourself —
/vt-d-2-plan,/vt-d-3-build. These need you. - Hand off the asynchronous ones —
/vt-d-4-reviewand/vt-d-5-finalizeboth declarecontext: forkin their SKILL.md frontmatter and run without you. This is the only place parallelism actually buys idle time, and it is where you switch to another worktree. - Merge — merge into main for the mechanics, then hazard (d) for which files to reconcile.
- Reconcile before teardown — run the drift check from
hazard (a) before
git worktree remove. After removal the evidence is gone.
EC-4 — a forked phase must never prompt. Because
4-reviewand5-finalizerun forked, they MUST NOT callAskUserQuestion. The skills state the consequence plainly: "The question will be swallowed, the sub-agent will die, and the parent conversation receives no output at all" — see the## Error Handlingsection of bothworkflow-5-finalize/SKILL.mdandworkflow-4-review/SKILL.md(cited by section, not line: these files run past 1400 lines and the numbers drift). A forked phase reports; it does not ask. If you are extending either skill, that constraint is load-bearing — a prompt there does not degrade, it silently produces nothing.
What does not parallelise. Steps 1–4 and 6–7 are yours. Only step 5 runs while you are elsewhere, which is why the ceiling in the previous section is set by your review bandwidth rather than by how many worktrees git will create.
Spec-numbering across branches¶
This procedure is retired (SPEC-165, vt-base 3.53.0). It existed because
/vt-d-spec-from-requirementsread only the current worktree's.design-state.yaml, so in a fragmented multi-worktree checkout the number it proposed could collide — and it carried its own retirement clause: "ifspec-from-requirementsis ever fixed to scan all branches, this procedure can be retired." That fix shipped. Do not hand-compute SPEC IDs.
Both skills that mint IDs (/vt-d-spec-from-requirements, /vt-d-specs-from-prd) now call
allocate_spec_id.py, which claims the number under a lock on a store shared by every worktree of
the clone and takes the maximum of the working tree, every local and remote ref, and the stored
counter. It always exits 0; on any failure it warns on stderr and falls back to a local scan that
still includes the all-refs floor.
Both failure modes this section used to enumerate by hand are covered:
| Old mode | What it was | Covered by |
|---|---|---|
| A — fragmented-ahead | IDs on unmerged feature branches sit higher than main's registry, including spec directories that are committed but not yet registered |
The all-refs scan: registry blobs and ls-tree spec-directory basenames, across refs/heads + refs/remotes |
| B — stale-behind-main | The current branch forked from main long ago, so its registry misses IDs main has since assigned |
Same scan — main is just another ref; no divergence check needed |
| (neither, and the real defect) | An ID allocated but not yet committed in another worktree is invisible to any ref-based scan | The shared counter, which reserves the number the moment it is handed out |
To see what it decided, ask it rather than reconstructing the answer:
--show prints the resolved store path, each scanner's maximum, the chosen ID, the claim cost and
any named diagnostic. Note it allocates: every call reserves its number, --show included, so
use it when you actually want an ID, not as a read-only probe.
Illustrative layout example¶
This is an illustrative example of the role pattern at one point in time — your checkout will differ. It shows the shape, it is not a maintained inventory. Run
git worktree listyourself for the current picture.
A git worktree list might look like this:
V025-claude-toolkit [chore/scratch] ← Active-Workspace (repo root)
.worktrees/feature/spec-149-... [feature/spec-149-...] ← Feature
.worktrees/feature/spec-151-... [feature/spec-151-...] ← Feature
V025-wt-ims-workflow-gates [docs/doc-sync-...] ← a sibling worktree (not currently holding main)
Note what's absent: no line shows [main]. In this snapshot main is held nowhere — the normal
resting state (EC-1). A Main-Holder appears only transiently, at merge time.
Where this came from¶
- SPEC-143 activation session (2026-05-28) surfaced the three symptoms — a
worktree-manager.shfailure under heldmain, the cross-worktree merge dance, and the implicit branch-rename — and recognized them as one missing convention doc. Source proposal:intake/processed/from-projects/2026-05-28-v025-multi-worktree-workflow-conventions.md. - The 2026-07-04 spec-numbering incident is the worked example for Mode B: specs were created
on
chore/scratch, which had forked frommainand was ~60 commits behind, so the tool mis-numbered them; the fix was a surgical re-apply of only the genuinely-new specs onto currentmain. This very SPEC-151 session then hit the fragmented-ahead hazard it documents — a scan of all branches surfaced a droppedSPEC-152still living onchore/scratch, higher thanmain's registry. - The surface bug behind symptom 1 was fixed separately at commit
b53fed62(intake/processed/bugs/2026-05-28-resolved-worktree-manager-fails-when-from-branch-checked-out-elsewhere.md). - SPEC-167 (2026-08) added everything from parallel-work hazards and guardrails onward — the four hazards, the 4–6 ceiling, and the parallel-run procedure. Those sections came from incidents this repo had already paid for but had recorded only in commit messages and per-entry ledger notes; SPEC-167 generalised them into rules. The sections above that heading are SPEC-151's original doc and were not modified.
Related¶
plugins/vt-base/skills/using-git-worktrees/SKILL.md— how to create an isolated worktreeplugins/vt-base/skills/git-worktree/SKILL.md— theworktree-manager.shwrapperplugins/vt-product-dev/skills/activate/SKILL.md— Step 6.7 detects the held-maincase at activationplugins/vt-product-dev/skills/statusline-spec-phase/SKILL.md— opt-in statusline showing the active spec and phase for the session you are in (single-session by design; it cannot see other worktrees, so it complements this doc rather than replacinggit worktree list)docs/solutions/patterns/cross-repo-spec-tracking.md— the sibling "one repo, one spec namespace" ruleintake/projects.yaml— registry of projects for cross-project awareness