Parallel Session Grid (isolated worktrees)¶
Run multiple Claude sessions in parallel without them tangling. Each session
gets its own git worktree (isolated index + files), so two sessions can never
race on one working tree. Backed by scripts/wt-grid.ps1; driven by four
PowerShell aliases.
Why not the old
8w/8sgrid? Those open every pane in the same main tree, which causes the 2026-06-05 collision class (a sibling's pre-commit stashes your unstaged edits, two sessions conflict mid-edit, pushes get blocked). The grid here makes that impossible — any real overlap becomes a safe merge conflict at merge time, never a live race that loses work. Policy:.claude/rules/concurrent-sessions.md.
Aliases (defined in your PowerShell profile)¶
| Alias | Action | Common flags |
|---|---|---|
8wt |
Launch grid: pane 1 = main tree (servers + coordinator), panes 2..N = isolated worktrees | -Tasks <file>, -Panes <2..8> |
8wts |
Status: which slots exist, clean/dirty, commits ahead | |
8wtm |
Merge every wt/slot* branch into your current branch |
-Into <branch> |
8wtd |
Tear down all slots (safe) + delete merged branches | -Force (discard unmerged) |
Every pane runs Opus: the launcher exports ANTHROPIC_MODEL=opus, which outranks
the repo's checked-in .claude/settings.json "model": "opusplan" (that setting
would otherwise plan on Opus and execute on Sonnet). There is no Sonnet grid —
-Model sonnet is rejected.
If the aliases aren't found, reload the profile: . $PROFILE
First-time shakeout (do this once)¶
. $PROFILE # load the aliases
8wt -Panes 3 # small grid, no tasks -> new wt window, panes in slot2/slot3
8wts # from your original terminal: confirm slot2/slot3 show 'clean'
8wtd # confirm clean teardown; 8wts should then show nothing
If panes open in the right directories and teardown is clean, it's ready.
Daily use¶
1. Write a task manifest (one task per line)¶
Blank lines and lines starting with # are ignored. Line 1 -> slot 2, line 2 ->
slot 3, and so on. Save anywhere, e.g. tasks.txt:
# line 1 -> slot 2, line 2 -> slot 3, ...
Fix all TS errors under frontend/src/services/stackup
Add a golden recording for the run_pdn_analysis tool
Update docs/guides/AI_NATIVE_SHELL.md for the new export formats
2. Launch the seeded grid¶
Each seeded pane writes its line to _TASK.md in its worktree and auto-starts
Claude already told to read it and do the work — no typing or pasting. Panes
beyond the task count open a plain session. (Seeded slots run in acceptEdits
without plan-mode so they execute; an unseeded 8wt keeps opus + plan-mode.)
3. Merge the work back¶
From your main-tree / coordinator terminal:
Clean slots merge into your current branch; conflicts are aborted-and-reported for manual resolution; dirty/incomplete slots are skipped.
Then it tests the combined result. Each slot only ever tested its own change,
alone. When at least one slot merged, 8wtm runs
scripts/grid-postmerge-check.ps1 on everything the round added:
| The round changed… | What runs |
|---|---|
anything under frontend/ (a deletion counts) |
tsc --noEmit on the whole project + vitest run --changed <before> (only the tests affected) |
a backend .py file |
pytest on the test files the round added or edited |
| backend code but no backend test file | nothing — it prints a NOTE saying so |
It is targeted on purpose: the main tree is shared with other sessions, and the
full suites stay the nightly's job. A failure prints POST-MERGE CHECK: FAIL
and makes 8wtm exit 1 — the merges are kept, never undone, so fix forward
before pushing. If HEAD moves during the run (another session committed into
the tree), the result is reported void. Measured 2026-09-23 on real merges:
4–5 s when only two backend test files changed; 79 s when a frontend change
pulled in 50 affected test files (tsc 21 s + vitest 53 s + pytest 4 s).
- Skip it:
8wtm -NoCheck. - Re-run it by hand (for example after resolving a conflict yourself):
powershell -NoProfile -File scripts\grid-postmerge-check.ps1 -Since <sha before the round>—8wtmprints the exact command when a check fails.
4. Clean up¶
Or: steps 3 and 4 in one command¶
Merges and tests as in step 3, then archives the finished task text
(8wta), commits only tasks.txt + tasks.archive.txt, and tears the grid
down (8wtd). Before this it was four hand steps with the # DONE marks left
uncommitted in between.
It stops before archiving — keeping every slot and exiting 1 — and says why, when anything needs you:
| Stops when… | Why it must stop |
|---|---|
| a slot was skipped as dirty | teardown force-removes the checkout, so its unsaved edits would be deleted |
| a slot conflicted | its branch still needs a hand merge |
| the post-merge check failed or was void | fix it while the slots still exist |
Two more rules it follows:
- It never commits task lines you were still writing. If
tasks.txtortasks.archive.txtalready had unsaved edits before the merge, it archives and tears down but leaves both files uncommitted and says so. - ⚠ A re-run after a stop merges nothing new, so the check does not run
again. Confirm your fix with the printed
grid-postmerge-check.ps1 -Since …command first, then run8wtm -Finishagain.
Tested 2026-09-23 on throwaway slots: a clean round (merged, archived, committed, torn down, exit 0), a dirty slot, a failing type check (both stopped with exit 1 and kept the slot) and pre-edited task files (not committed).
Two rules that matter¶
- Commit or
8wtmbefore8wtd. Teardown force-removes the worktree checkout, so uncommitted edits in a slot are lost. Committed work survives on itswt/slot*branch even after teardown. (The seeded prompt tells Claude to commit, so the automated flow is safe.) - Write non-overlapping tasks. Isolation removes the live race but not scope
overlap — two slots editing the same file collide at
8wtmas an ordinary merge conflict. Partitioning the work is the one piece only you can decide.
Troubleshooting¶
| Symptom | Cause / fix |
|---|---|
8wt errors Slot worktree exists / Branch wt/slotN exists |
A previous grid wasn't torn down. Run 8wtd (add -Force to discard unmerged branches). |
| Aliases not recognized | . $PROFILE, or open a new terminal. |
| A pane opened in the wrong directory | Likely the wt argument string; check scripts/wt-grid.ps1 Do-Launch. |
Slot has commits but 8wtm says "nothing to merge" |
It compares against your current branch — make sure you're on the same base branch the grid was launched from. |
8wtm ends with POST-MERGE CHECK: FAIL |
Two slots that each passed alone break together (or one slot's check was skipped). The merges are kept; read the failing step's output above the summary, fix, then re-run the printed grid-postmerge-check.ps1 -Since … command. |
8wtm -Finish says finish STOPPED … |
It names the reason (dirty slot, conflicted slot, or failed check). Every slot is kept; fix that one thing, then run 8wtm -Finish again. |
Summary says the verdict is VOID |
HEAD moved while the check ran — another session committed into the main tree. Re-run the printed command once it is quiet. |
How it works (internals)¶
scripts/wt-grid.ps1 delegates all worktree create/teardown to
scripts/worktree-session.ps1 (the proven, footgun-protected script) and never
runs git worktree remove itself. Worktrees are named pcb-wt-slotN on branch
wt/slotN, with backend/venv and frontend/node_modules junctioned to the
main tree (usable in seconds, no installs) and the root .env copied. The dev
servers (ports 8000/5180) always run from the main tree only.