"Separate enough not to conflict." That was my read on the seven Claude Code sessions I had running against this site's repository one afternoon in May. Different blog posts, different validators, different docs. No overlap I could see. A few hours later I caught three of those sessions in a death spiral, each one more confused than the last as adjacent files changed underneath it mid-run. I threw away all three sessions' work and restarted them from clean state.

The embarrassing part is that nothing exotic broke. Anthropic's own best practices recommend running multiple Claude sessions in parallel, and the tooling makes it a one-flag affair. What the recommendation quietly assumes is isolation. Isolation is exactly what I had skipped, because the work felt separate, and feelings are not a partitioning strategy. The fix took about an hour and all of it was structural: git worktrees as the session default, a write-path guard that denies out-of-tree writes, and one serialized path back to main. Since those guards landed in late May, I have run four to eight concurrent sessions on most working days without a repeat.

This post covers the architecture that hour bought, what it does not buy (worktrees isolate less than you think), and the constraint that survives every layer of tooling: how you partition the work in the first place.

What happens when Claude Code sessions share one working tree

A Claude Code session is a long-running argument with state. It reads your files, builds an in-context model of how the code fits together, and plans edits against that model. The model is a snapshot. When a parallel session edits an adjacent file, the snapshot silently goes stale: the imports it planned against moved, the helper it was mid-refactor on changed shape, the test it just wrote now exercises code that no longer exists. The session re-reads, reconciles, plans again. Then it happens again. Re-reading buys less than it looks like it should: the session patches one file's picture while the rest of its plan still assumes the old shape, and every reconciliation pass burns context window on archaeology instead of progress. That was the death spiral I watched in May. Not one dramatic crash. A slow compounding of confusion until killing the session was cheaper than untangling what it believed about the codebase. I mapped this failure class more formally in the four-gate decision map for orchestrating subagents: parallel writers to one surface make conflicting implicit choices that no amount of summarizing reconciles.

And the public record says my afternoon was not an outlier. One bug report documents ~/.claude.json corruption when eleven concurrent sessions raced read-modify-write cycles on the shared global config; every tool call updates usage stats in that file, and there is no file lock. Another documents git lock contention across thirteen parallel worktree agents: five committed, eight failed, because worktrees still share the parent repository's .git internals for ref packing and object creation. Even Anthropic's agent teams documentation is explicit that teammates are not isolated from each other by default, and tells you to "Break the work so each teammate owns a different set of files." The vendor knows where the bodies are buried.

Before

One shared checkout, N sessions

  • Every session sees every other session's half-finished edits
  • In-context plans go stale as adjacent files move
  • One branch, one index, racing writes
  • Failure mode: compounding confusion, discarded work
After

One worktree per session

  • Each session owns its files, branch, and index
  • A session's model of the code stays valid for its whole run
  • Shared git history, zero shared working files
  • Failure mode: merge conflicts you can see and serialize

Keep the shape of that contrast in mind. The corruption class is not "two sessions on one machine." It is unmanaged sharing: working state that two writers can mutate with no structure deciding who owns what.

The hour of hardening: worktrees, write guards, one merge path

In late May I spent roughly an hour converting "be careful" into structure. Three layers, cheapest first. One scope note before the list: this hour covers the git and write layer. Runtime isolation, ports and databases and dependency installs, is a separate problem, and the next section is honest about it.

Layer one: worktrees as the session default. As of June 2026, worktree support is built into Claude Code. Launch with --worktree and the session starts on its own branch with its own files and index, sharing only git history with the main checkout; the desktop app creates one per session automatically. The tooling around Claude Code has converged on the same primitive from every direction. claude-squad wraps tmux plus worktrees to manage multiple Claude Code, Codex, and Gemini instances. The Superpowers plugin ships a using-git-worktrees skill, one I run in this repo, that steers feature work into an isolated worktree before implementation starts. Conductor does the same as a macOS app, one worktree per agent. I compared these manager layers when I wrote about when /batch beats claude-squad and agent teams; the managers disagree about everything except the isolation primitive underneath.

Layer two: write-path guards that fire structurally. A worktree isolates by convention, not by force. Any session can still write an absolute path that resolves into the main checkout, and subagents are worse: a subagent dispatched from a worktree does not reliably inherit the worktree working directory (the dispatch can spawn anchored to the main project root, not to the worktree its parent sits in), so its relative paths can land in the wrong tree entirely. Claude Code's hook system closes both holes. A PreToolUse hook sees every Write and Edit before it lands and can return a deny decision. Mine blocks any absolute-path write that resolves inside the main repository but outside the active worktree; a sibling hook denies subagent dispatch unless the prompt pins the worktree root on its first line.

pre-write-worktree-path-guard.mjs (the logic, condensed)
const t = resolve(toolInput.file_path);
const inMain = t.startsWith(MAIN_ROOT);
const inTree = t.startsWith(WT_ROOT);
if (inWorktree && inMain && !inTree) {
deny(`write escapes worktree: ${t}`);
}

Why a hook and not a line in CLAUDE.md? Because prose rules are advisory and this rule has to fire every time. That routing logic, advisory guidance in CLAUDE.md versus structural enforcement in hooks, is the whole subject of my decision tree for CLAUDE.md, settings, skills, and hooks. A deny from a PreToolUse hook holds even when a session runs in permissive modes. A paragraph of guidance does not.

Layer three: one serialized path back to main. Parallel work, serial integration. Every worktree branch becomes a pull request, and integration happens one merge at a time through a wrapper script that merges, verifies worktree cleanup, and deletes the remote branch before the next merge starts. The merge queue is boring on purpose. Continuous integration checks stay the authority on what lands; that is the same containment argument I made for CI agents and the deterministic gate on the path to main, applied to local parallelism. Setting up this kind of layer for a team is most of what my Claude Code infrastructure work looks like in practice.

The three-layer isolation architecture for parallel sessionsA vertical flow: N parallel Claude Code sessions pass every write through a PreToolUse write-path guard, which only lets writes land inside each session's own worktree. Each worktree branch becomes a pull request feeding a serialized merge gate, which integrates to main one merge at a time.Sessions 1..NWrite-path guard hookWorktree per sessionMerge gate, then main every writein-tree onlyone merge at a time
Sessions parallelize. Writes are guarded. Merges serialize.

Worktrees are not the only wall that works, just the cheapest. When Anthropic's own team built a C compiler with sixteen parallel Claude agents, they reached for Docker containers per agent plus file-based task locks, roughly two thousand sessions over two weeks. Heavier walls, same principle. Every parallel-Claude setup that survives contact with real work picks an isolation boundary and enforces it structurally instead of behaviorally.

What worktrees don't isolate

Here is the part the how-to guides skip. A worktree gives each session its own files, branch, and index. Everything else on the machine stays shared.

LayerA worktree isolates it?What still collides
Working files, branch, indexYesNothing; this is the isolation you bought
Git internalsNoRefs, object store, and locks in the shared .git
Claude Code global stateNo~/.claude.json, one file across every session
RuntimeNoPorts, databases, Docker services, per-tree installs

Guillaume Moigneu's writeup on worktrees for parallel AI agents documents the runtime row well: every dev server wants port 3000, worktrees share the same Docker daemon and database state, each fresh worktree needs its own dependency install, and one developer watched a 2GB codebase swell to 9.82GB of disk after automatic worktree creation. If your project runs services, the remediation direction is heavier walls: a container per agent with its own ports and database, the same move Anthropic's compiler team made. The git-internals row is the lock contention issue from earlier: thirteen committers, eight losers, inside fully worktree-isolated agents. And the tooling itself can be the hazard. One report documents the worktree cleanup path deleting a main checkout's .git directory along with its source files. Isolation automation that can run rm deserves the same suspicion as any other agent with a destructive tool.

There is also an honest counterexample to the worktree default. GitButler runs multiple Claude Code sessions against one shared working directory, using lifecycle hooks to route each session's file changes onto separate virtual branches at commit time. Trigger.dev ditched worktrees entirely in favor of that model, for exactly the environment-overhead reasons in the table: their take is that you end up building worktree management scripts instead of shipping features. The academic data points the same direction at the margin. A 2026 study of asynchronous coding agents measured "soft isolation," instruction-level separation with no worktrees, within three points of full worktree isolation on bounded tasks (56.1 versus 59.1 percent on one benchmark), while finding that instruction-level separation "is not sufficient to fully eliminate interference over longer trajectories." Note what even that result holds constant: the soft-isolation agents still ran under explicit instruction-level separation. Nobody in the study ran unmanaged.

So the precise version of my claim survives, and the sloppy version does not. Sharing a working tree is not what corrupts sessions. Unmanaged sharing is. GitButler manages the sharing with virtual branches and hooks; containers manage it with walls; worktrees manage it with copies. My seven-session May failed for the simplest version of this: nothing at all was deciding which writes belonged to whom. Worktrees were just the cheapest structure to add.

Partitioning, not session count, is the binding constraint

The guards stop corruption. They do not stop two sessions from doing overlapping work, and overlap is where the throughput math quietly dies. My working rule, learned the hard way in May and re-earned on every multi-session day since: Claude Code resolves slight merge conflicts without ceremony, but a session should never have to rework around a parallel session's changes. The moment that starts, you are paying an agent to fight your own org chart. If two units of work keep touching the same module, they were one unit of work and I mis-partitioned it.

The published data backs the lived rule. The AgenticFlict dataset, built from over 142,000 AI-agent pull requests across 59,000 GitHub repositories, found 27.67 percent of parallel agentic PRs hit merge conflicts, and conflict probability rises with the functional similarity of the concurrent tasks. Similarity predicts collision. Session count does not even make the model. Zoom out one level and the premise itself is conditional: Google Research's analysis of scaling agent systems found multi-agent variants degraded performance by 39 to 70 percent on sequential planning tasks, with the coordination tax growing as agents carry more tools. Sequential, stateful work does not get faster because you opened more terminals. It gets slower, with extra steps.

0 .67% of 142k+ parallel agentic PRs hit merge conflicts (AgenticFlict, AIware 2026)
up to 0 % multi-agent performance drop on sequential tasks (Google Research)

How do you know a unit of work is parallel-safe? If you can name the files it owns and no other live session needs them, it is. If you cannot name them, you are gambling on "separate enough." I ran that experiment in May so you don't have to.

The 4-8 session playbook

What this looks like on a normal working day for me: four to eight live sessions against this repository. A blog post moving through its pipeline in one worktree. A validator change in another. A long-form guide in a third, a review lane in a fourth. At my day job the spread is wider: a handful of the engineers I work with run several concurrent sessions, and the rest stay at one at a time. What separates them is preparation, not nerve: the structure below either exists when they sit down, or it doesn't. Every step in it does one of two jobs: keep similarity low, or keep isolation structural.

1

Partition before you parallelize

Name the file-disjoint work units first. If two tasks need the same module, they are one task; give them one session.

2

One worktree per session

claude --worktree, or a manager like claude-squad or the Superpowers worktree skill. Never two sessions in one checkout.

3

Turn the guards on

A PreToolUse hook denying writes that escape the active worktree, and a dispatch guard pinning the worktree root for every subagent.

4

Keep sessions boring

A session that starts reworking around a parallel session's diff is a partitioning failure. Stop it, repartition, restart.

5

Serialize the merge

One integration at a time through a wrapper that merges, verifies cleanup, and deletes the branch. CI required checks stay the authority.

6

Clean up structurally

Remove worktrees through tooling that verifies state first. The cleanup path has destroyed working trees before; treat it like any destructive agent action.

Two resource realities to plan around. Parallel sessions draw from one shared rate-limit budget, the same pool as everything else you run; I hit this wall first when unattended PR auto-fix sessions started competing with my interactive work. And every worktree wants its own dependency install, the same disk math that turned a 2GB codebase into 9.82GB above; on this Node project that means a multi-minute npm ci and a few gigabytes for every session I spin up. Start at two or three sessions. Grow only while your merges stay boring, and drop back to one when the work in front of you is a single deep sequential problem, because parallelism buys breadth, never depth.

The hour of hardening was the best infrastructure trade I made in May: three discarded sessions of work against sixty minutes of hooks and scripts, and the failure class has not recurred since. When the session count grows past what one person reviews comfortably, the next bottleneck is verification, not isolation, and that is a design problem I covered in the verification contract for fleet-scale Claude Code workflows. If you want a second pair of eyes on your own parallel-session setup, book 15 minutes and bring your merge history.

FAQ

Can two Claude Code sessions safely share one working tree?

Not without a coordination layer. Each session plans against the files it has read, and when a parallel session changes those files mid-run, the first session's plan goes stale and it compounds confusion trying to reconcile. GitButler's virtual-branch model is the managed exception: it routes each session's changes to separate branches from one directory. The default answer stays one git worktree per session.

What is the --worktree flag in Claude Code?

Launching Claude Code with --worktree (or -w) starts the session in a fresh git worktree: its own branch, files, and index, with shared git history. As of June 2026, worktree support is built in, and the desktop app creates a worktree per session automatically. It is the cheapest structural isolation available for parallel sessions.

How many parallel Claude Code sessions should you run?

As many as your work splits into file-disjoint units, and no more. I run four to eight a day on this repo since adding structural guards, but that number is an output of partitioning quality, not a target. Start at two or three and only grow while your merges stay boring.

Glossary terms used