Parallel worktrees
Run multiple branches in parallel via `git worktree add`. What env files to mirror, the cookie-jar caveat, and the future per-worktree hostname plan.
The repo expects you to use git worktrees for parallel branches. Each
worktree is a complete checkout pointing at a different branch, with shared
.git/ storage. Use .agents/worktrees/ for local agent worktrees; it is
gitignored alongside the legacy .worktrees/ path.
Creating a worktree
git fetch origin dev
git worktree add -b pr-x/topic .agents/worktrees/pr-x-topic origin/dev
cd .agents/worktrees/pr-x-topicYou now have a complete checkout at .agents/worktrees/pr-x-topic/ on a new
branch pr-x/topic tracking origin/dev. Run pnpm install once and the
worktree is ready (pnpm shares the global store, so it's fast).
Env files to mirror
Fresh worktree checkouts have an empty working tree for every gitignored
file. The next-app per-package .env*.local files plus the root .env.cloud
are gitignored but required for the dev server and cloud-credential scripts
to work. Mirror them from your main checkout the first time:
SRC="/path/to/your/main/checkout"
for f in \
.env.cloud \
apps/web/.env.development.local \
design-system/docs/.env.local; do
[ -f "$SRC/$f" ] && cp "$SRC/$f" "./$f" && echo "copied $f"
doneWhat each one is:
| File | Purpose |
|---|---|
.env.cloud | Claude Code cloud-session env vars (VERCEL_TOKEN, AWS, GH PAT, E2E secrets, Slack tokens, bypass token). Template: .env.cloud.example |
apps/web/.env.development.local | OIDC client_id + secret for web's dev profile. Without it, auth init fails |
design-system/docs/.env.local | Docs-site overrides |
pnpm cloud:bootstrap regenerates apps/web/.env.development.local via
vercel pull if VERCEL_TOKEN is in .env.cloud — useful when the upstream
values change.
Do not run pnpm dev:remote in a fresh worktree before
mirroring — they delete or overwrite .env.development.local and you'll
lose your OIDC creds.
Per-worktree dev ports — only apps/web is automatic today
apps/web/scripts/dev-server.mts scans 4060-4069 for the first free port. So if
worktree A is running apps/web's pnpm dev on :4060, worktree B's pnpm dev
lands on :4061 without configuration.
Other apps do not use the scanner. They are hardcoded:
apps/tangible→next dev --port 4070apps/dev-docs→next dev --turbopack -p 4070apps/webview-bridge-docs→next dev --turbopack(Next default port)
So two worktrees can run their apps/web side by side, but port conflicts are
still possible for the other apps. Generalizing the scanner beyond web is on
the follow-up list.
Cookie jar caveat — host-scoped, not port-scoped
Two worktrees on localhost:<different ports> share one cookie jar within
a single browser profile (RFC 6265 — port is not part of cookie scope).
Logging into one replaces the other's session in that profile.
Workarounds:
- Automation: Playwright
browser.newContext()andagent-browser --session <name>each isolate their own cookie jar. Verified by the two-worktree simultaneous-login E2E in PR #656. - Humans: use a separate Chrome profile per worktree when you need two worktrees logged in at the same time. Clunky but it's the only workaround at the port layer today.
Future direction — per-worktree hostnames
There's a planned (not yet built) scheme to give each worktree its own hostname instead of port. Schema:
<project>-<feature>.localhost:<port>Examples: today-web-localhost-bff.localhost:4060,
today-web-pr-ca-dev-docs.localhost:4061. *.localhost resolves to
loopback in Chrome and Firefox without /etc/hosts edits, so a distinct
hostname = distinct cookie jar = true same-profile parallel login.
The constraint to know: keep the name single-label (one dot before
.localhost). A multi-level form like today.dev-docs.localhost is
unreliable behind DNS-intercepting proxies (Surge, Proxyman) which can route
depth-greater-than-1 .localhost names to the real internet. The project
name is therefore folded into the single label with a dash.
Tracked in the team-lead memory feedback_dev_preview_url_scheme.
Cleanup
# From the main checkout
git worktree list # see all live worktrees
git worktree remove .agents/worktrees/pr-x-topic # delete checkout + branch metadata
git worktree prune # clean up stale worktree recordsDon't delete .agents/worktrees/<name>/ manually with rm -rf — git keeps a
record of every worktree under .git/worktrees/ that gets stale.
Local state noise to ignore
git status --ignored in the main checkout may show:
D .claude/scheduled_tasks.lock— Claude Code session state, ephemeral!! .agents/worktrees/— ignored local checkouts for parallel agent work
Both are local agent state and are ignored. Don't stage them.