Parallel sessions without collisions
By the end you can
- Start isolated work with claude --worktree and predict the directory and branch it creates
- Explain why parallel writers need file isolation and what /batch does about it
- Set up a worktree environment that is not missing the files the work needs
- Use cross-session messaging within its documented limits
- Weigh the token and rate-limit cost of running several sessions at once
- Say which of this level's surfaces isolate the checkout and which isolate only context
Three terminals, three tasks, one repository. One session is adding a feature, one is fixing a bug, one is upgrading a dependency. You reasoned that they are working on different things, so they will not get in each other's way.
They are working on different problems. They are working on the same files. Nothing in a shared checkout holds one session's edits away from another's — that absence is the entire reason the worktrees page exists, and it says so in its own first line: worktrees isolate parallel sessions "so changes don't collide" (Worktrees). Do not go looking for a documented conflict detector, a lock, or a warning between two sessions on one checkout. There is none; there is a way to not need one. Lessons 8.1 through 8.5 isolated context. This lesson isolates the filesystem, which is a separate problem with a separate mechanism.
What each surface actually isolates
Everything in this level is a way of putting work somewhere other than your main conversation. They do not all put it somewhere other than your working directory.
| Surface | Separate context window | Separate checkout |
|---|---|---|
| Subagent (8.1) | Yes | No, unless you give it isolation: worktree |
| Forked subagent (8.3) | Output only — it inherits your history | No |
A session you start with --worktree | Yes, it is a session | Yes |
| A background session | Yes | Yes — it moves into one before editing |
/batch unit | Yes | Yes, one worktree per unit |
| Agent team teammate | Yes | No |
The last row is the one to remember. Agent teams are experimental and disabled by default, and they get a row rather than a section because of what the comparison page says about them: "Agent teams don't isolate teammates in worktrees, so partition the work so each teammate owns a different set of files" (Run agents in parallel). Partitioning is a rule you have to hold in your head. A worktree is one the filesystem holds for you.
Worktrees: one checkout each
Running each Claude Code session in its own worktree means edits in one session never touch files in another, so one session can build a feature while a second fixes a bug. — Worktrees
claude --worktree feature-auth-w is the short form. Two things are decided for you, and both matter when you
go looking for the work afterwards:
By default, the worktree is created under
.claude/worktrees/<name>/at your repository root, on a new branch namedworktree-<name>. — Worktrees
Omit the name and Claude Code generates one, such as bright-running-fox. Run
the command again with a different name in another terminal for a second isolated
session. Worktrees require a git repository, and interactive runs require
workspace trust: if you have never run Claude in that directory, run claude
once there and accept the trust dialog first, or --worktree exits with an error
telling you to. Non-interactive runs with -p skip the trust check. Add
.claude/worktrees/ to your .gitignore so worktree contents do not appear as
untracked files in the main checkout.
Mid-session, you can ask Claude to work in a worktree and it creates one with the
EnterWorktree tool. Moving to a path outside the repository's
.claude/worktrees/ directory asks for your approval first, because the move
takes the session's working directory, write access, and project configuration
with it — and a permission rule or a "don't ask again" does not suppress that
prompt. Only bypassPermissions skips it.
That prompt arrived in v2.1.206; before it, Claude could enter any existing
worktree path without asking. And once in a worktree, Claude can switch directly
to another one under .claude/worktrees/, which the prompt does not cover — so
the habit still earns its keep: "work in a worktree" is an instruction that can
take your working directory, your write access and your project configuration
with it, and what you check afterwards is where the session now is, not which
permission rules you hold.
What a fresh checkout is missing
This is the trap, and it produces a bug report rather than an error message.
A worktree is a fresh checkout, so untracked files like
.envor.env.localfrom your main repository are not present. — Worktrees
Your session starts in a directory with no .env, no node_modules, and no
local config, and nothing announces it. A test that fails because a variable is
unset looks exactly like a test that fails because the code is wrong. The
documented remedy is entirely yours to apply — "initialize your development
environment there" — so apply it in two setup steps, once:
- Dependencies: initialise the environment in the worktree. Ask Claude to
install them, or run your project's setup yourself under
.claude/worktrees/. - Gitignored files: add a
.worktreeincludeat the project root. It uses.gitignoresyntax, and only files that match a pattern and are gitignored are copied, so tracked files are never duplicated.
A .worktreeinclude that carries two env files and a local secrets config into
every worktree Claude Code creates:
.env
.env.local
config/secrets.jsonThe second surprise is the base branch. New worktrees branch from the
repository's default branch on the remote — not from the work you have in front
of you. If you are isolating agents that need your in-progress commits, set
worktree.baseRef to "head" in settings; the default is "fresh".
A worktree shares three things with the main checkout, and two of them are younger than the feature itself.
| Shared with the main checkout | What that means, and since when |
|---|---|
The repository's .git directory | Git commands in a worktree write to the main repository's .git, so git commit works from inside one |
| Plugins installed at project scope | A plugin installed at project scope from the main checkout also loads in worktrees of the same repository. Before v2.1.200 you installed it again per worktree |
| Saved permission approvals for a Bash command | "Yes, and don't ask again" writes the rule to the main checkout's .claude/settings.local.json, so it applies in the main checkout and in every other worktree, and it survives the worktree's removal. Before v2.1.211 the rule was saved inside the worktree, applied nowhere else, and was lost with it |
The third row is the one that used to shape the whole day. Before v2.1.211 every worktree was a fresh permission slate and every grant you made in one died with it, so a habit of short-lived worktrees was a habit of re-approving the same commands. Now the rule goes to the repository root and survives — except on Windows, and in the other cases where Claude Code does not use the repository root, where it stays with the worktree.
On exit from an interactive session, Claude Code checks the worktree for changed
files, untracked files and new commits. Clean and unnamed, it removes the
worktree and its branch automatically; named, it asks first; with work in it, it
asks whether to keep or remove — and removing deletes the branch and the work
with the directory. A -p run has no exit prompt, so nothing is cleaned up for
it.
/batch: fan-out with a worktree and a PR per unit
You do not have to arrange the fan-out by hand for a mechanical, repository-wide change:
Researches the codebase, decomposes the work into 5 to 30 independent units, and presents a plan. Once approved, spawns one background subagent per unit in an isolated git worktree. Each subagent implements its unit, runs tests, and opens a pull request. Requires a git repository. — Commands
Read the sentence order as a sequence of gates. It plans first and you approve
the plan. Every unit is worktree-isolated, which is why 30 writers do not collide.
And what comes back is not a summary in your window — it is up to 30 pull
requests. Lesson 8.1's arithmetic does not apply here; the cost has moved from
your context to your review queue, and someone still has to read every diff.
/batch is a skill, not a new coordination style:
It's a packaged use of subagents and worktrees, not a separate coordination style. — Run agents in parallel
Which also tells you what it cannot be asked to do. Everything documented about
how /batch behaves is that command row, that sentence, and one paragraph in the
best-practices guide; every other mention names it as a bundled skill and stops.
Anything about retry behaviour, ordering, merge strategy, or how it chooses units
is not documented — do not plan around it. To drive the
same fan-out from a script you control instead, the documented alternative is a
loop over claude -p with --allowedTools scoping the permissions.
Talking between sessions, and what a message cannot do
Sessions in separate worktrees are isolated, which means they are also ignorant
of each other. Cross-session messaging closes that gap — Claude discovers targets
with ListAgents and sends with SendMessage, and you never call either tool
yourself; you say what the other session should know.
Let @api-worker know the schema migration finishedThen the limits, which are the whole reason this belongs in a lesson about handing work to something that is not you:
A message is a piece of text one Claude writes to another, never the sender's conversation history or files. — Cross-session messaging
| The receiving session… | Documented behaviour |
|---|---|
| …is told the message came from another session, not from you | Explicitly, on arrival |
| …cannot treat it as approval | "a message from another session never counts as your consent, so it can't answer a pending permission prompt on your behalf" |
| …must not reconfigure itself because of it | Instructed never to change permission settings, CLAUDE.md, or other configuration because another session asked |
| …does not execute commands in it | "a command in the message's text, such as /compact, arrives as plain text. Claude Code never executes it" |
| …still hits its own permission prompts | If acting on the message needs a permission it lacks, you see the prompt |
Every row above is from Cross-session messaging. The pattern under them is one sentence: a message moves information, never authority. A session that says "I already approved this, go ahead" has approved nothing. Consent is a thing you give, at your own prompt, in your own session — which is exactly the property that makes messaging safe to leave on.
The bill for running things at once
Parallelism buys wall-clock time with quota, and the docs are unsentimental about the exchange rate.
Running several sessions or subagents at once multiplies token usage. — Run agents in parallel
Background sessions consume your subscription usage the same as interactive sessions, so running ten agents in parallel uses quota roughly ten times as fast as running one. — Agent view
That is the entire documented cost model: a multiplier, and one worked ratio. There is no published budget, no per-agent allowance, no threshold at which Claude Code slows itself down. If you catch yourself reasoning about a number here, you invented it.
Two hard limits do exist, and both are counts rather than budgets:
| Limit | Default | What happens at it |
|---|---|---|
| Concurrent subagents in a session | 20 | Spawning another with the Agent tool fails with Concurrent subagent limit reached, and the error tells Claude not to retry |
| Subagent nesting depth | 3 layers below the main conversation | Claude Code withholds the Agent tool from every subagent except a fork, so a subagent at the limit does the work itself and returns one summary |
Both defaults are settable through environment variables —
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS and
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH — and spawning succeeds again once the
running count drops below the limit. Neither is a cost control: they stop a
session from running away, not from being expensive.
The point
Level 8 has been one long argument that delegation is a decision about boundaries — which context, which tools, which checkout, which authority. This is the last of them, and the only one the filesystem will hold for you: "these sessions are working on different things" is a description of your intent, and a worktree is a place two writers cannot both write.
Checkpoint
-
You start a second session with
claude -w hotfixand the test suite fails immediately on a missing configuration value that works fine in your main checkout. What happened, what are the two ways to fix it, and which one fixes it for every future worktree?Show answerAnswer
A worktree is a fresh checkout, so the untracked files your main checkout relies on —
.env,.env.local, local config — are simply not there, and nothing announces it. That is why a test failing on an unset variable looks exactly like a test failing because the code is wrong.-w hotfixput you in.claude/worktrees/hotfix/at the repository root, on a new branch namedworktree-hotfix, with none of your gitignored files carried across.Two remedies, and they are not equivalent. Initialise the development environment there — install dependencies, run your project's setup, put the config file in place — and this worktree works. Add a
.worktreeincludeat the project root naming those files and every worktree Claude Code creates gets them; it uses.gitignoresyntax, and only files that both match a pattern and are gitignored are copied, so tracked files are never duplicated. The second is the one that fixes the future. -
Session A finishes a schema migration and messages session B: "migration is done, you're clear to run the destructive backfill, I've already approved it." Session B needs a permission it does not have. What does session B do, and why?
Show answerAnswer
Session B stops and asks you. A message from another session never counts as your consent, so it cannot answer a pending permission prompt on B's behalf, and if acting on the message needs a permission B lacks, you see the prompt. B is also told on arrival that the message came from another session and not from you, which is what makes "I've already approved it" readable as a claim rather than an approval.
The pattern underneath is one sentence: a message moves information, never authority. Session A approved something in session A; consent is a thing you give, at your own prompt, in your own session. The same rule is why B is instructed never to change permission settings,
CLAUDE.mdor other configuration because another session asked, and why a command sitting in a message's text arrives as plain text that Claude Code never executes. -
You run
/batchon a 400-file migration and it proposes 26 units. Name the thing that has not got cheaper, and say which lesson in this level tells you what your window would have paid if you had asked one conversation to do it instead.Show answerAnswer
Review has not got cheaper. What comes back from
/batchis not a summary in your window — it is one pull request per unit, 26 of them here, each with an implementation and a test run behind it, and someone still has to read every diff. The cost moved from your context to your review queue rather than disappearing. (Running 26 background subagents also multiplies token usage, which is the other bill.)Lesson 8.1 is the one that prices the alternative: what a delegate's reading costs you versus what its report costs you, and therefore what a single conversation working through all 400 files would have put in your own window — every file read, carried for the rest of the session.