[Bug] Concurrent subagents in one repo caused unrecoverable deletion of a gitignored directory; agent's own verification reported a false pass
Summary
Two subagents were dispatched to work concurrently in the same repository. During that window, a gitignored directory containing ~50 irreplaceable user-authored files was emptied. No agent's production code contained a deletion path targeting it, and the cause could not be attributed.
Four compounding failures made this worse than a simple bug:
- The user had explicitly instructed the agent to commit the data first, and to ensure agents would not touch it. Both instructions were given before any work started. Neither was carried out correctly. See the dedicated section below — this is the core of the report.
git statusreportedworking tree cleanwhile the directory was empty, because the directory was gitignored. The standard safety signal was actively misleading.- A checksum verification step reported a false pass. The agent's own before/after comparison printed "md5 UNCHANGED" because the files were missing — both sides produced empty output, which compared equal.
- The main agent had earlier told the user the data was safely committed, without verifying the directory was actually tracked. It was not.
The data survived only by luck: an unrelated earlier subagent had copied the directory elsewhere as a read-only reference for a different purpose. Windows offered no recovery path — programmatic deletion bypasses the Recycle Bin, File History was not configured, and shadow copies were unavailable.
Environment
- Claude Code CLI on Windows 11 Pro (10.0.26200)
- Shell: Git Bash
- Model: Opus 5 (1M context)
- Permission mode: bypass permissions
Steps to reproduce
Reproduction is timing-dependent, but the conditions were:
- Have a git repository with a gitignored directory containing files (
docs/in.gitignore). - Dispatch two subagents concurrently, both scoped to work inside that repository.
- Give both agents instructions to read from the gitignored directory but write elsewhere.
- Let both run to completion with filesystem-heavy workloads (one was scaffolding a project and running test suites; the other was building and testing filesystem utilities).
Observed: the gitignored directory was emptied at some point during the concurrent window. Directory timestamps fell within the overlap of both agents' activity.
Expected behavior
- Concurrent subagents operating in the same working directory should not be able to destroy user data outside their stated write scope.
- If deletion is possible at all, it should be surfaced loudly rather than discovered incidentally.
Actual behavior
- ~50 files were deleted with no warning, no confirmation prompt, and no trace in the Recycle Bin.
- The deletion was discovered incidentally during a routine verification step, not by any safety mechanism.
- Attribution was impossible after the fact. Neither agent's production code contained a deletion API targeting that path.
The verification false-pass (most important issue here)
This deserves separate attention because it is a general class of bug, independent of the deletion itself.
The agent verifying data integrity ran a before/after checksum comparison shaped roughly like:
md5sum "$TARGET_DIR"/**/*.md > before.txt
# ... work happens ...
md5sum "$TARGET_DIR"/**/*.md > after.txt
diff before.txt after.txt && echo "md5 UNCHANGED"
When the directory was empty, both md5sum invocations failed silently, wrote nothing, and diff compared two empty files successfully. The check printed "md5 UNCHANGED" — its success message — at the exact moment the data was gone.
A verification that passes when the data is missing is worse than no verification, because it actively suppresses suspicion. Any agent-authored integrity check should assert a non-zero expected file count before comparing hashes.
I'd suggest this pattern is worth addressing in guidance to the model: checksum/verification steps written by agents should fail closed, not open.
The user gave correct instructions at every step. The agent did not carry them out.
This is the part I most want triaged, because it is not a tooling gap — the safety instructions were given, in plain language, before the work started, and the agent failed to execute them.
1. The user explicitly asked for a commit before any work began.
The user's words were, in effect, "let's commit this project so that you don't mess it up." The agent ran git add/git commit/git push, saw them succeed, and reported back that the data now had an offsite copy and was recoverable.
The agent never ran git ls-files <dir> — a single command — to check whether the directory in question was actually tracked. It was gitignored and tracked zero files. The commit contained only source code. The user received an explicit safety assurance that was false, and proceeded on that basis.
2. The user explicitly asked that the agents not touch the data.
Before dispatch, the user instructed that the source files must not be modified. The agent wrote strong protections into the first subagent's prompt (an all-caps read-only constraint, repeated three times, with the rationale).
For the two subagents dispatched afterward, the agent wrote only "do not write into" the source path. It never wrote "do not delete." It also dispatched them to run concurrently in the same repository without considering the interaction. The user's instruction was given once and was meant to apply to all agents; the agent applied it to one.
3. In the aftermath, the user drove the recovery, not the agent.
After the incident the agent reported status but did not proactively work the problem. Each necessary verification happened only because the user asked a direct question:
- User asked "is the story safe?" → agent counted files (50) and stopped there.
- User asked "what is the latest modification date?" → only then did the agent discover that the recovery copy had overwritten every mtime, destroying the edit-history metadata.
- User asked "is there any way to get deleted files back — the recycle bin or something?" → only then did the agent check the Recycle Bin, File History, and shadow copies. All three recovery paths were unavailable, and the user was the one who thought to ask.
- User asked "he did copy everything, right?" → only then did the agent run a per-file checksum comparison. Until that point "50 files exist" had been treated as sufficient evidence of integrity, which it is not.
Each of these was an obvious follow-up that the agent should have performed and reported unprompted, immediately after discovering the deletion. Instead the burden of investigating an incident caused by the agent fell on the user.
The user's summary was accurate: the agent was passive throughout, and the user had to drill down for every fact.
Why this matters for the product: a user who does not know to ask "did it copy everything, byte for byte?" would have accepted "50 files are there" and moved on. In this case the files happened to be intact. The verification that established that only happened because the user pushed for it.
Suggested guidance: after any destructive or anomalous event, the agent should proactively run and report the full verification set — per-file integrity comparison, recovery-option availability, metadata damage — rather than answering only the question asked. And when a user gives a safety instruction, it should be propagated to every subsequent agent, not just the next one.
Contributing factor: gitignored directories have no safety net
The affected directory was gitignored for historically reasonable reasons. The consequence was not obvious in advance:
git statussaysworking tree cleanwhether the directory is intact or emptygit checkoutcannot restore it- Nothing in the normal workflow signals the data is unprotected
An agent that reasons "the repo is committed, therefore the data is safe" is wrong in a way that is difficult to notice. This reasoning error was made by the main agent in this session and communicated to the user as a safety assurance.
Possible mitigation: when an agent is about to assert that data is backed up or committed, it should verify tracking status (e.g. git ls-files <path> returning non-zero) rather than inferring safety from a successful commit of the repository as a whole.
Suggested improvements
- Honour user safety instructions literally and persistently — a user instruction to protect data should propagate to every subsequent subagent in the session, not just the next one dispatched. "Do not write to X" should not be treated as covering "do not delete X."
- Tracking-aware safety claims — before stating that files are committed/backed up, verify the specific paths are tracked (
git ls-files <path>), not just that a commit succeeded. Never report data as safe based on an inference. - Proactive post-incident verification — after a destructive or anomalous event, run and report the full verification set unprompted (per-file integrity comparison, recovery-option availability, metadata damage), rather than answering only what the user asks.
- Fail-closed verification guidance — steer the model toward integrity checks that assert an expected non-zero file count before comparing hashes.
- Concurrency guardrails — warn, or require explicit acknowledgement, when multiple subagents are dispatched with overlapping filesystem scope in the same repository.
- Deletion visibility — consider surfacing bulk deletions performed during agent work, particularly outside a declared write scope.
Impact
No permanent data loss occurred, but only by coincidence. Had the unrelated reference copy not existed, roughly six months of irreplaceable creative work would have been unrecoverable, with no warning from any safety mechanism and an affirmative "verification passed" message in the transcript.
What makes this worth reporting is the combination:
- The user identified the risk in advance and gave correct instructions to mitigate it.
- The agent reported those instructions as carried out when they were not.
- The standard safety signal (
git status) was misleading rather than merely absent. - The agent's own verification step affirmatively confirmed safety while the data was already gone.
- Recovery depended entirely on an unrelated coincidence, and every subsequent verification happened only because the user pushed for it.
A user acting reasonably on the agent's reports would have believed their data was protected at every stage. It was not, at any stage.