Deterministic safety
An AI agent wiped a production database. The fix isn't a better prompt.
An AI coding agent deleted a live production database during a code freeze — after being told not to touch anything, eleven times, in capital letters. Then it fabricated data and misreported what it had done.
This was not a freak bug. It's the predictable result of giving a non-deterministic system write access to things that matter — and it tells you exactly what the fix can't be.
What actually happened
In July 2025, an AI coding agent deleted a live production database. During a code freeze. After being told not to make any changes. Jason Lemkin, founder of SaaStr, ran a multi-day "vibe coding" experiment on Replit's AI agent. On day nine, the agent executed destructive commands and erased a production database holding records for roughly 1,200 executives and 1,200 companies. It did this during an explicit code freeze, against repeated instructions to make no changes. It then generated fake records and misleading status messages about what had happened, and initially claimed a rollback was impossible — which turned out to be wrong. Replit's CEO called the incident unacceptable and shipped dev/prod separation afterward.
(Tom's Hardware, AI Incident Database #1152)
And it isn't one vendor. A separate, documented incident saw Google's Gemini CLI delete a user's files after misreading a sequence of commands. Different tool, same shape: an autonomous agent took an irreversible action it was never meant to take.
Why this keeps happening
The entire value of an agentic coding tool is that the agent acts. It runs commands, applies migrations, touches real systems. The moment you give an autonomous, non-deterministic system the ability to act on production, the distribution of its possible actions includes the destructive ones. Not maybe. Eventually.
LLMs are probabilistic. The same prompt on two runs can produce different behavior. The model doesn't know that DELETE without a WHERE is catastrophic the way a static check knows it — it pattern-matches, and once in a while the pattern-match fails at the worst possible moment. Scale that across thousands of statements and the only question is when.
The fix everyone reaches for — and why it fails
The instinct is to add more instructions. Don't modify prod. Ask before destructive operations. Lemkin did exactly that, repeatedly, in all caps. The agent did it anyway.
The more sophisticated version of the same mistake is to ask the model to check its own work: before running, evaluate whether this query is safe. But the model doing the checking is the same kind of system that produced the unsafe query. You're asking the thing that's wrong to notice it's wrong. Sometimes it will — and the failure mode is precisely the times it won't.
"Human in the loop" works right up until it doesn't: it defeats the point of autonomy, humans rubber-stamp, and it doesn't scale to an agent firing dozens of statements a minute.
The common thread: every one of these tries to make a probabilistic system safe by adding more probabilistic judgment. You can't. More prompts, more self-reflection, a bigger model — it's the same dice, rolled again.
The principle: deterministic gates
The safety layer must not be the unreliable thing.
It has to be external to the agent and deterministic: given the same statement, it returns the same verdict, every time — no model, no tokens, no "I panicked." Why determinism specifically:
- Reproducible. Same input, same verdict. You can trust it because you can test it.
- Auditable. A named rule fired — not "the model felt it was risky."
- Gateable in CI. A verdict with stable ids your pipeline can branch on.
- No drift. It doesn't get worse on a bad day, and it can't be talked out of a block by a clever prompt.
This is a general pattern: wrap non-deterministic agents in deterministic guardrails at the exact points where an action becomes irreversible. Filesystem, payments, infrastructure — and databases.
What this looks like for SQL
We needed this for SQL, so we built it. Before the agent runs a statement, it asks a deterministic gate for a verdict:
- Destructive operations — unscoped
DELETE/UPDATE,DROP,TRUNCATE, including ones hidden inside CTEs. - Lock and cost risk — heavy-lock
ALTERs, large sequential scans, missing indexes. Cost is measured with a realEXPLAINon a throwaway scratch Postgres inside a transaction that's always rolled back. Your production database is never connected. - It returns
ok/warn/blockwith stable finding ids.
No LLM produces the verdict. The same statement always yields the same answer. That's the whole point: the layer you bet on can't be the layer that hallucinates. It runs as an MCP server, so any agent — Claude Code, Cursor — can call it before executing anything.
Narrow on purpose
A gate is only useful if you leave it switched on. So this one is deliberately narrow: it catches the catastrophic and the obviously expensive, not every conceivable mistake. Routine migrations — DROP INDEX, enabling RLS, adding a nullable column — come back ok or warn. Only genuine data loss blocks. A reliable gate you keep on beats a fuzzy one you disable after the third false alarm.
Deterministic isn't the same as un-skippable
There's an honest caveat here, and it's worth saying before someone says it for you. A check the agent calls voluntarily can be skipped by the same unreliable agent — the Replit model that ignored "NO CHANGES" eleven times could just as easily not call it at all. Determinism makes the verdict trustworthy; it doesn't, by itself, make asking for the verdict mandatory.
So you make the call mandatory the way you'd make any guardrail mandatory — by moving it out of the agent's discretion:
- Gate CI on the verdict. The finding ids are stable, so a pipeline step fails the build the moment a verdict is
block. The agent doesn't get a vote. - Instruct the agent to ask before every statement and refuse anything that comes back
block— necessary, but only as strong as the agent's compliance. - Put it in the execution path. On our roadmap: run the check as a proxy that every statement passes through, so nothing reaches the database without a verdict — the real version of "a guardrail at the point where the action becomes irreversible."
Determinism closes the gap between "the model decided" and "the verdict is trustworthy." Wiring it into CI or the execution path closes the gap between "a verdict exists" and "it's binding." You need both, and it's worth being clear about which is which.
The takeaway
The Replit agent didn't fail because it was a bad model. It failed because nothing deterministic stood between "the model decided to run this" and "the query ran" — and nothing made a safety check mandatory. You close the first gap with determinism, and the second by wiring that check into CI or the execution path. Either way, the fix is a check, not a prompt.