Worktree-isolated subagent ran git reset --hard in the main checkout; worktree guard did not fire

Status Open
Reported on v2.1.143
Maintainer reply None cached
Activity 1 comment · opened Jul 26, 2026

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 with isolation: "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.

View original on GitHub ↗

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