parallel-dev worktrees — second-developer bootstrap (CVN-N014-EC-S16)¶
scripts/new_worktree.sh spins up a second Claude Code "developer": a fully
equipped parallel workspace on its own branch, identical to the primary checkout.
Two parallel devs = two git worktrees + two sessions (one window each) — not
a second git clone.
Why a worktree (and not a clone)¶
A second clone duplicates the .git object store, drifts on fetches, and forces
you to re-wire every local-only secret. A git worktree shares one .git
(objects / refs / remotes), so a commit or push on one side is instantly visible
on the other — and the script symlinks the gitignored local environment so the
second session is a faithful clone of the first.
What it sets up¶
new_worktree.sh <branch> [dir] [base]
├─ git worktree on a dedicated branch (new from <base>, or attach existing)
├─ .venv_airflow → symlink to the primary venv (no duplication)
├─ .env → symlink to the primary env (single secrets source)
├─ .claude/settings.json + settings.local.json → symlinks (same perms / hooks / MCP)
└─ shared project memory: ~/.claude/projects/<worktree>/memory → primary's memory
Tracked artefacts (skills, hooks, documentation/ADR.md, CLAUDE.md) follow the
branch for free at checkout — no extra wiring. ADRs and deployments need nothing
special: they are repo edits + gh / kubectl / helm, all present in the
worktree.
Usage¶
# new branch off main (default dir ../champollion-<branch-slug>)
scripts/new_worktree.sh feat/CVN-N001-EI-S06-foo
# explicit dir, and/or attach an EXISTING branch (e.g. a PR branch)
scripts/new_worktree.sh chore/airflow-xcom-reader ../champollion-dev2
| positional | default | meaning |
|---|---|---|
<branch> |
— (required) | branch to check out — created from <base> if it doesn't exist, else attached |
[dir] |
../champollion-<branch-slug> |
worktree path |
[base] |
main |
base ref for a new branch |
Then open the new dir in a fresh VSCode window (the Claude extension starts its
own session) or cd "$DIR" && source .venv_airflow/bin/activate && claude.
Iron rules (worktree gotchas)¶
- One window per worktree. A session is bound to the folder it was launched in; it cannot "become" the other folder. Two windows on the same folder = same worktree = HEAD/index collision.
- Different branch per worktree. Git refuses to check out the same branch in two worktrees — each dev is on a different branch. That is the point.
- Branch new stories from
origin/mainaftergit fetch— the localmainmay be stale relative to the remote. - Do not
git stashin a worktree —git stashuses one shared stack (refs/stashin the common git dir) across all worktrees. A stash pushed in one worktree and popped in another applies the wrong diff to the wrong tree and silently corrupts it. See Parking WIP without stash below.new_worktree.shprints this warning in its completion banner. - Per-worktree identity is auto-pinned.
.git/configis shared, so identity set with plaingit configleaks between worktrees.new_worktree.shenablesextensions.worktreeConfigand writes the primary'suser.name/user.emailwithgit config --worktree, so each worktree's authorship is isolated and explicit — no manual step. - Serialise cluster runs. Both workspaces share one prod cluster (Airflow,
MLflow, Postgres
ftf_config, Loki). Code and plan work parallelises; DAG / FTF runs must be serialised (max_active_runs=1, shared compute pool +ftf_config).
Parking WIP without git stash¶
Because the stash stack is shared, use one of these worktree-safe alternatives instead — each keeps WIP bound to the worktree it belongs to:
Instead of git stash |
Do this |
|---|---|
| Quick "set it aside" | git commit -am wip (amend or git reset --soft HEAD~1 to resume) |
| Switch context, keep WIP visible | git worktree add ../champollion-scratch <branch> — a third throwaway worktree |
| Move uncommitted work to another branch | git switch -c <branch> (the working changes follow you, no stash) |
If you absolutely must shelve without committing, scope it to one worktree and pop it before touching any other worktree — never leave a stash live while two devs are active.
Memory sharing¶
The Claude auto-memory dir is keyed by the sanitised cwd, so a worktree at a
different path would otherwise get a different, empty memory. new_worktree.sh
symlinks ~/.claude/projects/<worktree>/memory → the primary's memory, so
MEMORY.md + handoffs are shared — the durable cross-session channel between
the two devs. The live conversation context is not shared: only what is
written to memory crosses between them.
Reference: memory reference_parallel_dev_worktrees · plan dossier
../reviews/2026-06-05-cvn-n014-ec-s16-dev-productivity-tooling-plan.md.