isolation: worktree subagents silently write outside the worktree via absolute file_path (Edit/Write target primary clone, not the isolated worktree)

Resolved 💬 1 comment Opened May 26, 2026 by claude-for-evoskamp Closed Jun 27, 2026

Summary

A subagent spawned with isolation: "worktree" has its cwd correctly set to the isolated worktree, but its Edit/Write/Read calls routinely target an absolute file_path rooted at the primary clone rather than the worktree. Because the harness nests worktrees under the primary clone (<repo>/.claude/worktrees/agent-<id>/), that absolute path resolves to a real, writable file on the primary clone's checked-out branch (e.g. main). The edit succeeds silently — no error — and the subagent's work lands on the wrong branch entirely, outside its isolation.

The isolation boundary is therefore advisory, not enforced: nothing stops a subagent from editing the parent checkout, and the most natural failure mode (absolute path = the canonical repo path the agent carries in context) points directly at the parent.

Why this is serious

  • Silent loss of isolation: the entire point of isolation: worktree is that a subagent can't touch the parent. It can, trivially, via any absolute path.
  • Silent wrong-branch writes: edits land uncommitted on the primary clone's branch (often main). The subagent's own verification (relative-path git diff/md5) inspects the worktree, sees no change, and misreports the write as a "flush failure" — so the agent doesn't even know it leaked.
  • Cross-session accumulation: leaked edits sit uncommitted on the primary branch indefinitely and get attributed to later sessions.

Evidence (observed in a real session)

Two subagents in one run, cwd correctly the worktree for 100% of records:

  • doc-updater: all 4 Edit/Write calls used the primary-clone absolute path -> all 4 leaked onto main. Agent concluded (wrongly) that the "write-path silently failed to flush," because its relative-path verification saw the unmodified worktree copy.
  • issue-developer: first 5 Edit calls used the primary-clone path (leaked), then it empirically detected the dirty primary clone, hand-reconstructed the worktree-rooted absolute path, and the next 5 calls were correct. So the correct absolute worktree path is constructible — the agent just defaults to the wrong root.

Across every subagent transcript in this project, 51/51 Edit/Write/MultiEdit calls used absolute paths; the agents never use relative. The absolute path they assemble defaults to the canonical repo root from context (the primary clone), not git rev-parse --show-toplevel of the worktree.

Root cause

Edit/Write/Read require absolute file_path. The agent assembles it from the canonical repo path in its context (= primary clone), not from its actual worktree root. The harness does not fence Edit/Write to the worktree, and worktree-under-primary-clone nesting means the wrong path hits a real file. Result: mandated-absolute-path + salient-path-is-primary-clone + nested-worktree = silent escape.

Suggested fix (harness-side)

Guard Edit/Write (and Read/MultiEdit) in an isolation: worktree session: if the resolved absolute file_path falls outside the worktree root (git rev-parse --show-toplevel), reject the call with an error naming the worktree-relative path the agent should use. Deterministic; doesn't depend on prompt compliance. (Optionally: don't nest worktrees under the primary clone, so an out-of-worktree path can't resolve to a sibling file — but the guard is the robust fix.)

Related but distinct

  • #52958 — cwd leaks into the parent. Here cwd is correct; the file_path escapes.
  • #47548 — setup-time isolation failure (parent branch switched). Here isolation sets up fine and is breached post-setup via path.

Environment

  • Claude Code: 2.1.150
  • Model: claude-opus-4-7 (Opus 4.7) — both the orchestrator and the two affected subagents (issue-developer, doc-updater) ran on this model
  • Platform: darwin 25.5.0
  • Repo shape: standard git repo, worktrees at <repo>/.claude/worktrees/agent-<id>/
  • Reproduced with both doc-updater and issue-developer subagent types in a single /issue-address orchestration run.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗