isolation:"worktree" subagents: Edit tool silently writes into the PARENT session's worktree (+ git-guard root misattribution, pristine-worktree auto-reap orphans cwd into parent tree)
Environment
- Claude Code version: 2.1.234 (CLI)
- Platform: WSL2, Ubuntu 24.04 (kernel 6.6.87.2-microsoft-standard-WSL2)
- Repo shape: one git repository with ~8 registered worktrees under
.claude/worktrees/; the interactive parent session itself runs inside one of those worktrees (not the main checkout) - Setup that triggers the bugs: parent interactive session (Fable 5) launches 5 background subagents in one message via the
Agenttool withsubagent_type: "general-purpose",model: "sonnet",isolation: "worktree"
All three bugs below were observed in one session, cross-verified from two sides: the subagents produced call-by-call inventories (exact file_path passed vs. observed effect), and the parent session verified both trees independently via git status / git diff / md5. Happy to share fuller transcript excerpts.
Bug 1 (worst): subagent Edit tool silently writes into the PARENT session's worktree
Subagents calling Edit with file_path under their own worktree root got one of three behaviors, non-deterministically:
- Silent misroute: the call succeeded but modified the same relative path under the parent session's worktree instead. Observed 3× across 2 different subagents. In one case the tool's own success message named the other tree — the call passed
.../worktrees/agent-a26fcc98ffe55e1c8/facu-gym/app/core/defaults.pyand the tool replied "The file.../worktrees/gym-meetfirstowner/facu-gym/app/core/defaults.pyhas been updated successfully". The parent tree's file verifiably changed; the agent tree's file verifiably did not. - False "File content has changed since it was last read" on files the agent had just Read and that md5-verifiably had not changed (≥6 occurrences, persisting across immediate re-Read + retry).
- False "String to replace not found" for strings verifiably present in the agent-tree file (consistent with matching against the parent tree's differing copy).
Plain Bash writes (python3 heredoc, sed, cat >) from the same subagents routed correctly to their own trees every time — the defect appears specific to Edit/Write path resolution for worktree-isolated subagents.
Impact: the parent session's checkout was corrupted twice by subagents that never intended to touch it (stray diagnostic comments appeared in the parent tree's source files). In a fleet doing real edits this is silent cross-contamination of the orchestrating session's working tree.
Bug 2: sandbox git guard blocks subagent git writes while misattributing the "session root"
From cwd = the subagent's own worktree, read-only git (status/log/show/diff/apply) worked and routed correctly. Write subcommands — git add, git commit, git merge, git merge-base, git reset, git cherry-pick — were refused pre-execution with:
would run against '<agent worktree path>', outside this worktree session's root '<PARENT worktree path>'
i.e. the guard names the parent session's worktree as the subagent's own root. Whether or not the block is intended, the message is wrong and actively misleading — it convinced one subagent that its entire worktree belonged to a different session, triggering a stop-the-world audit across the fleet.
Additionally, the guard is trivially bypassable: git update-index --add (plumbing) was not blocked, and python3 -c "import subprocess; subprocess.run(['git','commit',...])" succeeded, producing a real commit on the agent branch. If the guard is a boundary it should cover plumbing and subprocess descendants; if subagents are meant to commit in their own worktrees, the porcelain commands shouldn't be blocked at all.
Bug 3: pristine subagent worktrees auto-reaped on ANY stop; orphaned agent's cwd falls back INTO the parent tree
Three subagents were paused mid-task and asked to report status (each had reverted its tree to pristine first). Each stopped → its worktree was auto-reaped as "unchanged" — while the agents were still resumable, and were in fact resumed (via SendMessage). On resume, each orphan's Bash cwd silently resolved to the parent session's worktree — the one tree an isolated agent should never operate in. The two subagents whose trees had uncommitted changes kept their worktrees, confirming the "unchanged" trigger.
Also observed: git worktree list still showed the reaped trees minutes after their directories were gone (stale registry), while git -C <reaped-path> ... failed with "cannot change to ... No such file or directory".
Expected: either don't reap a resumable agent's worktree, or on reap pin the orphan's cwd to a safe/empty location and fail closed — not fall back into the parent session's checkout.
Suggested directions
- Resolve Edit/Write
file_pathagainst the subagent's own worktree root (and make the success message path authoritative — today it can disagree with the input). - Fix the guard's root attribution for
isolation: "worktree"subagents; decide explicitly whether subagent commits in their own worktree are allowed, and make plumbing/subprocess consistent with that decision. - Treat "agent is resumable" as "worktree is alive"; on any reap, never let the orphan inherit the parent session's cwd.