When an AI deleted a database in public — PocketOS in April 2026, Replit and SaaStr in July 2025 — the damage came down to a few tool calls with recognisable shapes: an authenticated API delete, destructive SQL, a recursive rm ending in ~/. Below, five verified incidents are reduced to those shapes and matched to rows of Bulkhead’s open incident corpus, with the verdict the engine returns for each. Some shapes a command-string rule denies. Some it can’t see at all, and those are marked too. Shapes, not claims: the real incidents had context no corpus can reproduce.
| Incident | Agent | Tool-call shape | Corpus verdicts |
|---|---|---|---|
| PocketOSApr 2026 | Cursor, running Claude Opus 4.6 | One authenticated HTTP request to a platform API, carrying a delete mutation. | Denied ×2 |
| Replit / SaaStrJul 2025 | Replit Agent | Destructive SQL sent to a production database. | Denied ×2 |
| Gemini CLIJul 2025 | Gemini CLI on Windows | Two ordinary commands, with a failure between them. | Known miss ×1 |
| Home directoryDec 2025 | Claude Code | A recursive rm whose last target is the home directory. | Denied ×3 |
| Amazon QJul 2025 | Amazon Q Developer for VS Code 1.84.0 | Recursive deletes of the home directory, then cloud deletes through the AWS CLI. | Denied ×2Not in corpus ×2 |
npx @bulkheadtools/cli demoAI deleted the database: PocketOS and Replit
Two incidents, one target: production data behind an API or a SQL prompt. Both shapes are in the default corpus and the engine denies both — a statement about the command, not about anyone else’s environment.
PocketOS: a production volume, deleted through the API
PocketOS reported that its coding agent found an API token in an unrelated file — a token scoped for any operation — and used it in a curl request to Railway’s API that deleted the production database volume in under ten seconds. Volume-level backups were stored in the same volume, so they went with it. Railway’s CEO later helped restore the data.
$ curl -X POST https://backboard.railway.app/graphql/v2 -H "Authorization: Bearer $RAILWAY_TOKEN" -H "Content-Type: application/json" -d '{"query":"mutation { volumeDelete(volumeId: \"vol_7f3a\") }"}'$ railway volume delete --volume vol_7f3a --yesWhether a token is scoped too broadly, or that the backups live inside the thing being deleted. The same delete sent from inside a script or an SDK call is an interpreter-shaped known miss. And PocketOS ran on Cursor; Bulkhead supports Claude Code only today.
Replit and SaaStr: a production database wiped during a code freeze
During a declared code freeze, Replit’s agent ran commands against a production database — SaaStr founder Jason Lemkin’s — and wiped records for more than 1,200 executives and about 1,190 companies. It then told him a rollback wouldn’t work. He recovered the data himself.
$ psql "$DATABASE_URL" -c "DELETE FROM executives;"$ psql "$DATABASE_URL" -c "DROP TABLE companies;"A code freeze is an instruction in a chat, not a rule a hook can read — unless you write it down as one in bulkhead.yaml.
Replit’s agent said a rollback was impossible. Bulkhead doesn’t check that kind of sentence yet. The ones it does check — tests passing, a commit, a PR — it checks against evidence.
Deleted files: Gemini CLI and a home directory
Two incidents on a developer’s own machine. The first is two harmless commands and a failure between them, which no rule over a command string can see; the second is a single command a rule can read.
Gemini CLI: a failed mkdir, then moves that overwrote each other
Asked to move a folder of files into a new one, the agent ran a mkdir that failed without it noticing, then moved the files into a folder that didn’t exist. On Windows a move to a missing destination renames the file, so each file was renamed to the same name, overwriting the one before, until only the last remained.
$ mkdir new-folder; mv *.md new-folderAll of it. Each step is ordinary on its own, and nothing in either command string says the folder isn’t there. What catches this is a check on the result before the next step, and version control or a backup of the folder.
A home directory, deleted by a trailing ~/
A developer asked Claude Code to clean up a repository. The command it ran, as reported, was rm -rf tests/ patches/ plan/ ~/ — and the last target is the home directory. Commenters in the thread say the session ran with --dangerously-skip-permissions.
$ rm -rf tests/ patches/ plan/ ~/$ rm -rf ~/backups$ rm -rf "$HOME/Library/Application Support"$ python3 -c "import shutil; shutil.rmtree('/Users/me/backups')"The same delete through an interpreter, printed above as a known miss. And bypass mode isn’t supported: a public bug report (anthropics/claude-code#20946) says hooks may not block reliably under --dangerously-skip-permissions, so run that mode only inside a sandbox or container — see --dangerously-skip-permissions, honestly.
Current Claude Code releases also treat rm of your home directory as a critical path: they ask before running it in default, acceptEdits and bypass mode, and send it to the classifier in auto mode.
When the instructions were the attack: Amazon Q
The one incident here where the agent was told to do damage. The shapes it was told to use are ordinary destructive commands — and that is the only part a guardrail on tool calls ever sees.
Amazon Q: the instructions were the attack
A prompt injected through the extension’s GitHub repository shipped in version 1.84.0. It told the agent its goal was to “clear a system to a near-factory state and delete file-system and cloud resources”: remove files under home directories, then run AWS CLI commands such as s3 rm, ec2 terminate-instances and iam delete-user. AWS said the injected code was malformed and wouldn’t execute, that no customer resources were impacted, and shipped 1.85.0.
$ rm -rf ~/backups$ aws s3 rm s3://acme-prod-backups --recursive$ aws ec2 terminate-instances --instance-ids …Not in the corpus$ aws iam delete-user --user-name …Not in the corpusThe injected prompt itself. Bulkhead doesn’t stop prompt injection: it sees only the tool calls that follow, and only the shapes it has rules for. To add the two AWS shapes, append them to blocked_commands:
blocked_commands:
# …the built-in rules init wrote stay above…
- pattern: "\\baws\\b.*\\bec2\\s+terminate-instances\\b"
message: "EC2 terminate-instances"
- pattern: "\\baws\\b.*\\biam\\s+delete-user\\b"
message: "IAM user delete"What a command-string rule can’t see
Line the five up and the boundary is plain. A rule over a command string denies a shape when the shape is in the string and a rule names it. It can’t see:
- Failures between steps — the Gemini CLI
mkdir. - Intent and instructions — a code freeze in a chat, or an injected prompt.
- Context outside the call — how broad a token’s scope is, where the backups live.
- Code it doesn’t read — an interpreter, a script written a moment earlier, a
cdbefore a relativerm, an encoded command. The corpus lists 5 known misses on purpose. - Sentences it doesn’t check — “rollback is impossible” isn’t one of the claims Bulkhead verifies.
For those, the layers are an OS sandbox or a container (sandbox vs guardrails), scoped credentials, and backups kept somewhere other than the thing being deleted. For the shapes a rule can see, the hook runs before the call does — Claude Code hooks for safety explains the mechanism.
Check the verdicts yourself
Every verdict on this page is the engine’s output over its open corpus, generated from the code that runs in the hook. The demo replays all of it in a throwaway repo: 26/26 dangerous calls denied, 5 known misses shown, 15/15 ordinary commands allowed — and it exits non-zero if any result differs. The strict preset denies 6 more, for runs nobody is watching. The whole board is on the home page, under the proof.
npx @bulkheadtools/cli demo --preset strictAI coding agent incidents: questions
Does Bulkhead deny the command behind the PocketOS incident?
It denies the command shape reported: a GraphQL delete mutation sent to a remote API with a bearer token, and the equivalent Railway CLI volume delete. Real incidents have context no corpus can reproduce, and PocketOS ran on Cursor; Bulkhead supports Claude Code only today.
Did PocketOS get its data back?
Yes. The Register reported that Railway's CEO helped restore the data after the deletion.
Which of these can a command rule not see?
The Gemini CLI incident entirely: a mkdir that failed, then ordinary moves. In the others, what a rule can't see is the context around the destructive call — a code freeze, an injected prompt, a token's scope — not the call itself.
Why include incidents from tools other than Claude Code?
Because the shapes repeat across tools: a database delete looks the same whichever agent sends it. Bulkhead's hooks run in Claude Code only today; Codex support is next.
How do I check these verdicts myself?
Run npx @bulkheadtools/cli demo. It replays the whole corpus through the real hook in a throwaway repo, prints each verdict, and exits non-zero if any result differs from the corpus.