Isolation & Worktree
When multiple agents work on the same codebase simultaneously, they need isolation to avoid stepping on each other’s changes. Claude Code uses git worktrees as its primary isolation mechanism — each agent gets its own working copy of the repository, sharing the same git history but with independent file systems.
Why Git Worktrees?
Section titled “Why Git Worktrees?”The alternatives and why they were rejected:
| Approach | Problem |
|---|---|
| Same directory | File conflicts between concurrent agents |
| Full clone | Disk space, slow, separate git history |
| Docker/container | Heavy, complex setup, not universally available |
| Git worktree | ✅ Lightweight, shared history, independent files |
Git worktrees are a native git feature (git worktree add) that creates a new working directory linked to the same repository. Agents can independently modify files, create branches, and commit without affecting each other.
Isolation Modes
Section titled “Isolation Modes”Agent definitions support two isolation modes, declared in BaseAgentDefinition:
isolation?: 'worktree' | 'remote'| Mode | Availability | Description |
|---|---|---|
worktree | All users | Local git worktree on disk |
remote | Anthropic internal only | Remote execution in Claude Code Remote (CCR) |
The remote mode is gated at parse time:
// src/tools/AgentTool/loadAgentsDir.ts — AgentJsonSchemaisolation: (process.env.USER_TYPE === 'ant' ? z.enum(['worktree', 'remote']) : z.enum(['worktree'])).optional(),External users can only use worktree. Attempting to set remote in an agent definition will fail validation.
Worktree Lifecycle
Section titled “Worktree Lifecycle”sequenceDiagram participant L as Leader Agent participant W as Worktree System participant G as Git participant A as Agent (in worktree)
L->>W: Spawn agent with isolation: 'worktree' W->>G: git worktree add <path> <branch> G-->>W: Worktree created at <path> W->>A: Start agent with cwd = worktree path Note over A: Agent works independently<br/>in its own working copy A->>A: Read, Edit, Bash, Commit... A-->>L: Report results L->>W: Cleanup / Kill agent W->>G: git worktree remove --force <path> G-->>W: Worktree removedWorktree in Team Members
Section titled “Worktree in Team Members”Each team member can have its own worktree, stored in the team configuration:
// TeamMember in teamHelpers.tstype TeamMember = { agentId: string worktreePath?: string // Path to this member's worktree // ...}When a team is disbanded or a member is killed, the worktree must be cleaned up. The destroyWorktree() function handles this carefully:
// src/utils/swarm/teamHelpers.ts — destroyWorktree (conceptual)async function destroyWorktree(worktreePath: string): Promise<void> { // 1. Read .git file to find the main repository // (worktree .git is a file, not a directory: "gitdir: /path/to/main/.git/worktrees/...") const gitContent = await readFile(join(worktreePath, '.git'), 'utf-8')
// 2. Extract main repo path from gitdir reference const mainRepoPath = resolveMainRepo(gitContent)
// 3. Use git worktree remove for clean removal try { await exec(`git worktree remove --force ${worktreePath}`, { cwd: mainRepoPath }) } catch { // 4. Fallback: force remove the directory await exec(`rm -rf ${worktreePath}`) }}Fork + Worktree Integration
Section titled “Fork + Worktree Integration”When the fork mechanism creates a child in an isolated worktree, a special notice is injected into the child’s context:
export function buildWorktreeNotice(parentCwd: string, worktreeCwd: string): string { return `You've inherited the conversation context above from a parent agentworking in ${parentCwd}. You are operating in an isolated git worktree at${worktreeCwd} — same repository, same relative file structure, separateworking copy.
Paths in the inherited context refer to the parent's working directory;translate them to your worktree root.
Re-read files before editing if the parent may have modified them sincethey appear in the context.
Your changes stay in this worktree and will not affect the parent's files.`}This notice addresses three critical issues:
- Path translation — inherited paths point to the parent’s directory, not the worktree
- Stale context — files read in the parent’s context may have changed
- Isolation guarantee — changes won’t affect the parent’s working copy
Team Directory Cleanup
Section titled “Team Directory Cleanup”When teams are cleaned up, all worktrees must be destroyed before directories are removed:
flowchart TD A[cleanupTeamDirectories<br/>teamName] --> B[Read team config.json] B --> C[For each member<br/>with worktreePath] C --> D[destroyWorktree<br/>member.worktreePath] D --> E{Success?} E -->|yes| F[Continue] E -->|no| G[Log error, continue] F --> H[Remove task directories<br/>for each member] H --> I[Delete team config file]Session-level cleanup (cleanupSessionTeams) runs when a session ends, ensuring no orphaned worktrees remain:
// Cleanup finds all team files for the session and destroys their worktreesasync function cleanupSessionTeams(sessionId: string): Promise<void> { const teamFiles = await findTeamFilesForSession(sessionId) for (const teamFile of teamFiles) { await cleanupTeamDirectories(teamFile.name) }}Worktree Session Management
Section titled “Worktree Session Management”The worktree system also supports standalone sessions (not just team members). getCurrentWorktreeSession() in src/utils/worktree.ts tracks worktree metadata for the current session:
// Used in prompts.ts for the statusLine agent inputtype WorktreeSession = { name: string // Worktree name/slug path: string // Full path to worktree branch?: string // Git branch original_cwd: string // Directory before entering worktree original_branch?: string}This metadata flows into the status line configuration, letting users see which worktree they’re in and quickly navigate back.
Security Considerations
Section titled “Security Considerations”Worktree paths are validated to prevent path traversal:
- Paths must be absolute
- Paths cannot escape the project root
- The
.gitfile in worktrees is read to validate it points to a legitimate repository
The validateMemoryPath() function in src/memdir/paths.ts shows the same pattern of path validation used throughout the system — rejecting relative paths, root paths, null bytes, and UNC paths.