isolation: "worktree" does not actually isolate subagent file writes
Upstream issue draft (Claude Code, option A)
This is the body for the issue filed against anthropics/claude-code per
task 42's acceptance criteria. Kept in the repo so the wording is
versioned and the team can see what we asked for.
---
Title: isolation: "worktree" does not actually isolate subagent file writes
Repo: anthropics/claude-code
Labels (suggested): bug, subagents, worktrees
Summary
When the Agent tool is called with isolation: "worktree", the subagent's
initial working directory is set to the new worktree, but Edit/Write tool
calls still accept absolute paths. In practice subagents repeatedly hand
back absolute paths that point to the parent checkout instead of the
worktree, then commit those changes via Bash. Result: changes intended
for the worktree branch land on whatever branch the parent has checked
out (typically main), defeating the entire point of the isolation flag.
Why this matters
We dispatch parallel subagents for audit/fix sweeps over a large repo.
The expected workflow is "each subagent works in its own worktree, the
parent merges the worktree branches when they're done." What actually
happens, repeatedly, is:
- Subagent edits files at absolute paths under the parent checkout.
- Subagent runs
git add && git commitfrom inside the worktree (cwd
is correct), but because the staged blobs were never written to the
worktree's working tree, only its index changes; the bulk of the work
stays in the parent's working tree, dirtied.
- Or the subagent
cds back to the worktree and re-edits, leaving
duplicate commits in two trees.
- Or the subagent commits directly to the parent's
mainand never
touches the worktree at all.
Two consecutive parallel rounds in our project hit this. Concrete cases:
- Round 2 Group F: all three fixes (F1, F2, F3) committed to
main. - Round 2 Group D: D2/D3/D4 committed to
main; one accidentally
bundled Group E's in-flight WIP from main into the same commit.
- Round 2 Group C: C1 committed once to
main, then again to its
worktree branch, leaving an orphaned duplicate commit in history.
- Round 1: care-review fix, admin/tenant fix, Estate CTAs fix,
permissions matrix fix all committed to main.
Each round cost 30+ minutes of merge-conflict resolution, manual stash
juggling, and post-hoc commit attribution. Conflicts arose inseed.ts and load-history.ts because subagents' WIP in main
collided with the same subagents' final commits in the worktree
branch.
Repro
- Dispatch a subagent with
isolation: "worktree"and a non-trivial
task that requires editing several existing files.
- Observe (in the parent session, or in
git statusafterwards) that
the subagent's Edit/Write file_path arguments are absolute paths
under the parent checkout, not the worktree.
- Observe that the parent's working tree is dirty after the subagent
completes, with the same files the subagent claimed to edit.
The failure mode does not require any deliberate action by the
subagent. It is the natural consequence of subagents using absolute
paths that they remember from earlier in the conversation (or that the
parent prompt named) without re-anchoring to the worktree.
Proposed fix (option A in our local task)
Have the runtime enforce the isolation invariant for subagents whoseisolation is "worktree":
- Path rewriting. For every Edit/Write/MultiEdit/NotebookEdit
call, if file_path is absolute and starts with the parent
checkout's prefix, rewrite it to the corresponding path inside the
worktree before executing.
- Bash anchoring. For every Bash invocation, force
cd <worktree>
as the first command (or run the bash with cwd set, ignoring any
in-prompt cd to outside the worktree).
- Optional, more aggressive. Refuse, with a clear error, any
Edit/Write whose path resolves outside the worktree even after
rewriting (e.g. .. traversal). Do the same for Bash commands that
try to cd or git -C to a parent path.
(1) and (2) alone fix the common case. (3) catches deliberate or
accidental escape attempts.
Workaround (option B in our local task)
For anyone hitting this today: a PreToolUse hook onBash|Write|Edit|MultiEdit|NotebookEdit that compares the cwd to a
worktree-marker prefix and refuses tool calls whose targets resolve
outside the worktree works as a stopgap. We've shipped one in our
project at .claude/hooks/worktree-guard.sh, plus a small preamble
helper scripts/agent-preamble.ts that prepends a "verify pwd before
every git command" rule to the subagent prompt. The hook makes the
failure mode loud (the subagent sees a structured deny and re-issues
the call with the right path) instead of silent. We'd rather not own
this code; the runtime is the right place for the fix.
Environment
- Claude Code subagent dispatch via the Agent tool,
isolation: "worktree". - Linux, bash, git 2.4x.
- Project: TypeScript / Next.js multi-tenant app, ~50 source files
per parallel sweep.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗