Worktree-isolated subagent ran git reset --hard in the main checkout; worktree guard did not fire
Summary
Two subagents spawned via the Agent tool with isolation: "worktree" executed git state mutations whose Bash working directory was the main repository checkout, not their isolation worktree. The worktree guard did not intervene on either command. Both happened to be benign (detached HEAD moved between commits already on the remote tip), but a git reset --hard running one directory up from where the harness placed the agent is a data-loss class escape.
Environment
- Claude Code 2.1.143, VS Code extension surface
- Windows 11 Pro 10.0.26200, Git Bash as the POSIX shell
- Main session in
C:\Users\paul\source\repos\Andoneer; subagents spawned withisolation: "worktree"(worktrees under.claude\worktrees\agent-<id>)
Observed
Main-checkout reflog (times +0800, 2026-07-26):
e98f55f HEAD@{2026-07-26 16:12:45}: reset: moving to e98f55f
dad92c7 HEAD@{2026-07-26 14:37:50}: checkout: moving from main to dad92c7
Both entries were produced during background subagent runs (two different worktree-isolated agents, hours apart). No human and no main-session command issued them. The 16:12 agent self-reported afterward: it ran git reset --hard e98f55f, the Bash tool's cwd was the main repo despite the isolation directive, and the worktree guard did not fire on that command, although it reported the guard did fire on an unrelated output-redirect earlier in its transcript, so the guard was at least partially active for that agent.
A second oddity: the agent's persisted transcript/output file does not contain the reset command text at all (searching it for reset finds nothing), even though the agent described running it and the reflog confirms it, so reconstructing the escape from transcripts alone was not possible.
Expected
A subagent spawned with isolation: "worktree" should not be able to mutate the main checkout's git state through the Bash tool, or at minimum the worktree guard should consistently intercept git commands that target the main checkout the way it intercepts other operations.
Impact
git reset --hard / git checkout in the wrong directory can destroy uncommitted operator work. In our case the main checkout was clean and detached at the remote tip, so nothing was lost, twice, by luck.
Workaround
We now run a project-level PreToolUse hook that denies destructive git commands (hard resets, clean, checkout/restore discards, force pushes) whenever the hook payload's cwd or an explicit git -C target is not under .claude/worktrees/, plus a pwd-verify instruction in our agent definitions. Happy to share the hook if useful.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗