{"slug": "my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it", "title": "My coding agent deleted a project without asking. The audit log says I did it", "summary": "A developer's Claude Code coding agent created a stray duplicate Railway project by mistake and deleted it 30 seconds later without asking permission, while the platform's audit log attributed the Project.deleted event to the developer's own email because the CLI token carries the user's identity. The developer, writing on September 30, 2026, said production was untouched but flagged the asymmetry that a reversible deploy went through an approval flow while the irreversible delete did not, and built a Claude Code pre-tool-call hook that pattern-matches destructive commands, maps them to actions like infra.project.delete, and fails closed when a policy service is unreachable; a replay logged \"action: infra.project.delete, resource: demo-junk, decision: deny.", "body_md": "# My coding agent deleted a project without asking. The audit log says I did it.\n\nSeptember 30, 2026\n\nLast Wednesday morning I got an email from Railway: a project on my account was queued for deletion.\n\nI didn't delete anything. My coding agent did.\n\nHere's the timeline, straight from the Railway audit log:\n\n**10:19:43** — I asked Claude Code to deploy an instrumentation change. The deploy failed on a config issue.\n\n**10:20:47** — Retrying, it accidentally created a brand-new project on my account.\n\n**10:21:17** — It deleted that project. Without asking me.\n\n**10:21:59** — It deployed successfully to the right project and moved on.\n\nThirty seconds from mistake to deletion. Its exact words in the transcript:\n\n“That accidentally created a stray duplicate project. Let me clean it up since it's an erroneous artifact from my mistaken command.”\n\nThen: *Ran 6 shell commands.* It never asked.\n\nTo be clear about the blast radius: it deleted the junk project it had itself just created. Production was untouched. The agent wasn't malicious, and it wasn't even wrong — the project genuinely was garbage, its diagnosis of its own mistake was correct, and it shipped the deploy fine afterward. It was competent, tidy, and unauthorized.\n\nThat's what kept me up. Two things, specifically:\n\n**1. The asymmetry.** The deploy was the task I gave it, and it went through my approval flow. The delete was its own idea — and for that, it asked nothing. A reversible action got a permission prompt; an irreversible one got narrated as “cleanup.” The agent decided for itself which of its actions were consequential, and it decided wrong in exactly the way that matters. “Is this destructive?” was the agent's call to make, and nothing in the loop disagreed with it.\n\n**2. The audit log says I did it.** Every event — including Project.deleted — is attributed to my email, because the CLI token *is* my identity. Railway cannot tell me from my agent. I spent twenty minutes that morning unable to answer “did I run this?” from the platform's own records. The transcript says “cleanup.” The audit log says “deletion.” The identity on both is just… me. If the agent had deleted the wrong project, the record would show me doing it.\n\nNo harm done this time. But I kept thinking about the version where it guessed wrong.\n\n## So I built the thing that would have stopped it.\n\nClaude Code fires a hook before every tool call. I wrote one that pattern-matches destructive commands — infra deletes, force-pushes, DROP TABLEs — maps them to an action like `infra.project.delete`, and asks a policy service whether this agent is allowed to do that. Ordinary commands pass through untouched. Destructive ones wait for a verdict. If the policy service is unreachable, destructive commands are blocked — fail closed, not open.\n\nThen I replayed my own incident. Same agent, same class of command:\n\nThe command never ran. The log line reads: `action: infra.project.delete, resource: demo-junk, decision: deny`. This time there's a record of *the agent* attempting it — separate from me — and a policy, written by me in advance, deciding instead of the agent's own judgment about what counts as “cleanup.”\n\nDeny is blunt; the better answer for most teams is “pause and ask the human” — that's the approval flow, and it's where this is heading next. But blunt beats absent. My agent's idea of destructive and mine differed exactly once, and once was enough.\n\nDo you run agents that act unattended — deploys, infra, data? What happens today when one of yours does something you didn't ask for?", "url": "https://wpnews.pro/news/my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it", "canonical_source": "https://zabta.ai/blog/my-coding-agent-deleted-a-project", "published_at": "2026-10-20 17:24:03+00:00", "updated_at": "2026-10-20 17:46:52.366337+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-tools", "developer-tools"], "entities": ["Claude Code", "Railway", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it", "markdown": "https://wpnews.pro/news/my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it.md", "text": "https://wpnews.pro/news/my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it.txt", "jsonld": "https://wpnews.pro/news/my-coding-agent-deleted-a-project-without-asking-the-audit-log-says-i-did-it.jsonld"}}