Worktree Isolation

Description

Worktree Isolation is a filesystem-level isolation mechanism built on top of git worktree. When activated -- either via the --worktree CLI flag at session startup, the EnterWorktree tool mid-session, or the isolation: "worktree" parameter on the Agent tool -- Claude Code creates a new git worktree inside .claude/worktrees/<slug> with a dedicated branch based on the default remote branch (typically origin/main). Each worktree gets its own working directory, staging area, and HEAD pointer while sharing the .git/objects/ store with the main repository, so history is stored once regardless of how many worktrees exist. This means multiple agents can write to the same codebase concurrently without stepping on each other's files.

The system supports three distinct usage patterns. First, session worktrees created at startup with --worktree (optionally combined with --tmux for a detached terminal session) set the worktree as the session's project root for the entire session. Second, mid-session worktrees created via the EnterWorktree tool let a user switch into an isolated workspace during an ongoing conversation and return via ExitWorktree. Third, agent worktrees are created automatically when the Agent tool is invoked with isolation: "worktree" -- each spawned subagent gets its own ephemeral worktree (slug pattern agent-a<7hex>), works in isolation, and the worktree is automatically cleaned up if no changes were made. Agents that produce commits leave their worktree and branch intact so the work can be reviewed and merged through standard pull requests.

Cleanup follows a layered strategy. On explicit exit, the ExitWorktree tool offers keep (preserve worktree and branch on disk) or remove (delete both, with a safety check for uncommitted files and unmerged commits that requires discard_changes: true to override). If a session ends while inside a worktree, the WorktreeExitDialog component prompts the user to keep or remove. For ephemeral agent worktrees that leak due to process termination (Ctrl+C, crashes), a periodic background sweep (cleanupStaleAgentWorktrees) scans .claude/worktrees/ for directories matching ephemeral slug patterns (agents, workflows, bridges), checks that they are older than 30 days, have no uncommitted changes, and have no commits unreachable from remotes, then removes them with git worktree remove --force followed by git worktree prune.

Key claims

  1. Branch naming convention is worktree-<slug> -- the function worktreeBranchName() prepends worktree- to the flattened slug (forward slashes replaced with + to avoid git D/F conflicts). The branch is created with git worktree add -B worktree-<slug> <path> <base>, where -B resets any orphaned branch from a previously removed worktree without requiring a separate git branch -D.
  2. Evidence: worktree.ts lines 221-223 (worktreeBranchName), line 328 (-B flag in addArgs).

  3. Worktrees are stored under .claude/worktrees/<flat-slug> -- the worktreePathFor() function joins the repo root with .claude/worktrees/ and the flattened slug. Nested slugs like user/feature are flattened to user+feature for both the directory name and branch name to prevent directory nesting conflicts.

  4. Evidence: worktree.ts lines 204-227 (worktreesDir, flattenSlug, worktreePathFor).

  5. Agent worktrees auto-clean when unchanged -- after an agent completes, AgentTool.tsx calls hasWorktreeChanges() which checks both git status --porcelain (uncommitted changes) and git rev-list --count <original>..HEAD (new commits). If both are clean, removeAgentWorktree() runs git worktree remove --force and git branch -D to delete the worktree directory and branch. If changes exist, the worktree is kept and its path and branch are returned in the agent's result.

  6. Evidence: AgentTool.tsx lines 666-684 (cleanupWorktreeIfNeeded), worktree.ts lines 1144-1173 (hasWorktreeChanges), lines 961-1020 (removeAgentWorktree).

  7. Post-creation setup propagates settings, hooks, symlinks, and gitignored files -- the performPostCreationSetup() function copies settings.local.json into the worktree, configures core.hooksPath to point at the main repo's .husky or .git/hooks directory, symlinks directories listed in settings.worktree.symlinkDirectories (e.g., node_modules) to avoid disk bloat, and copies gitignored files matched by .worktreeinclude patterns. It also supports sparse checkout via settings.worktree.sparsePaths for large monorepos.

  8. Evidence: worktree.ts lines 506-624 (performPostCreationSetup), lines 321-366 (sparse checkout in getOrCreateWorktree), settings/types.ts lines 438-457 (settings schema).

  9. Hook-based worktrees extend isolation to non-git VCS -- when WorktreeCreate/WorktreeRemove hooks are configured in settings.json, the system delegates worktree creation and cleanup to those hooks instead of using git worktree. This allows worktree isolation with Mercurial, Perforce, or other version control systems. The hook-based path skips git-specific operations (branch management, change detection) and always keeps the worktree on exit since it cannot detect VCS-level changes.

  10. Evidence: worktree.ts lines 715-728 (hook-based branch in createWorktreeForSession), lines 825-835 (hook-based cleanup in cleanupWorktree), EnterWorktreeTool.ts prompt lines 20-21.

Relations

Sources

src-20260409-a14e9e98c3cd