Worktree-isolated subagents leak Edit/Write into parent checkout (Windows)
Summary
When Agent({ isolation: "worktree", ... }) dispatches a subagent on Windows, the subagent's Edit / Write operations sometimes write to the parent checkout's files instead of the isolated worktree's files. The same pattern affects subagent-issued git operations — commits land on the parent's branch ref instead of the worktree's branch.
The PR on remote ends up correct because the subagent runs git push -u origin <branch>, which pushes the branch regardless of where it lives locally. But the parent's local state is silently corrupted: branch ref jumps forward, working tree gains modified/untracked files. Recovery for the user requires git reset --hard origin/<branch>.
Environment
- Platform: Windows 11 Home 10.0.26200
- Shell: PowerShell (also Bash via the Bash tool)
- Claude Code: Opus 4.7 (1M context),
claude-opus-4-7[1m] - Repo layout: monorepo with multiple isolated worktrees under
.claude/worktrees/agent-* - Project env:
CLAUDE_PROJECT_DIRset to repo root
Repro pattern (one session, today)
3 of 7 worktree-isolated subagent dispatches in one session leaked. All briefs used repo-relative paths (e.g., .claude/settings.json, .claude/hooks/foo.js). All used isolation: "worktree". All received explicit "do not push to master directly. PR only." instructions in the brief.
| Subagent | Symptom | Self-detected? |
|---|---|---|
| A | First Write hit parent's .claude/settings.json instead of worktree's. Caught via grep showing the worktree file unchanged. | Yes — recovered with git checkout -- then re-wrote to worktree path. |
| B | 8 commits landed on parent's master branch directly. Subagent also pushed the branch as origin/feat/<name> (PR is correct). Local master ref silently advanced. | No — caught later by controller diffing parent state. |
| C | Modified files + untracked files in parent's working tree (.claude/workflow/scripts/...) that the subagent created. | No — caught later. |
The other 4 dispatches (using the same controller pattern, same brief style) didn't leak. No obvious differentiator in brief content or task type.
Suspected root cause
Edit / Write tools may resolve relative or CLAUDE_PROJECT_DIR-rooted paths against the parent's project dir env var rather than the subagent's worktree cwd. A subagent's brief that says "edit .claude/settings.json" gets resolved to <parent CLAUDE_PROJECT_DIR>/.claude/settings.json instead of <worktree>/.claude/settings.json.
Subagent A's report (verbatim, after self-detection):
Initial Write inadvertently hit the parent repo's.claude/settings.json(not the worktree's). Caught via grep showing 10 gsd-* entries still present in the worktree file. Restored the parent's working copy viagit checkout --and re-wrote to the correct worktree path.
git -C <worktree-path> invocations from the subagent's Bash tool work correctly when the path is explicit. The leak appears to be specific to Edit / Write (and to git invocations without -C that inherit a cwd that's actually the parent).
Impact
- Silent local corruption: parent branch advances without controller intent. Next push to master would push subagent work straight to master without going through the PR.
- Destructive recovery:
git reset --hardis the only practical recovery. If operator had uncommitted work in parent at the time, it's at risk. - Cascading confusion: a second subagent wave dispatched off a contaminated parent inherits the leak.
What might help
If the root cause is path resolution against the wrong CLAUDE_PROJECT_DIR:
Edit/Writeshould resolve paths against the subagent's own cwd / worktree dir, not the parent'sCLAUDE_PROJECT_DIR.- When a subagent runs in
isolation: "worktree", the tool runtime should overrideCLAUDE_PROJECT_DIRfor that subagent to the worktree path before any tool invocations. - Subagent's first invocation should fail-loud if cwd doesn't match the worktree the controller created.
If you'd like a more targeted repro, I can produce a single-session script with mock briefs and surface which dispatches leak. Happy to share full controller + brief content from today's session if helpful.
Workaround in the field
I shipped a controller-side guard at JBudgeME/BOOKREADER-REPO:
check-subagent-isolation.mjs snapshot/verify— snapshots parent git state before dispatch and detects drift after, exiting non-zero on leaksubagent-brief-preamble.md— verbatim block pasted into every Agent brief, has the subagent run cwd + git-dir + status checks before any write
Reduces blast radius. Doesn't fix the underlying path resolution.
Side note (separate, lower-priority)
While filing this issue, my repo's PreToolUse commit-format-conventional hook false-positived on gh issue create because the body content contained the literal string "git commit" inside a heredoc. The hook's \bgit\s+commit\b regex matches anywhere in the command, including inside string arguments. Not a Claude Code bug — my hook needs tightening — but worth flagging in case you've seen similar false-positive interactions with PreToolUse hooks parsing Bash commands. I worked around with --body-file.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗