Destructive Action Gate
Every team that has lost data has the same story afterwards: the command was familiar, the flag was
one character different from usual, and nothing asked. The instinct is then to confirm everything —
which produces a prompt on ls, teaches everyone to hit yes without reading, and leaves you worse
off than before.
A gate that fires on everything gets switched off within a week. The discipline is to fire rarely, and to make it count when it does.
Classify first
Sort every operation into three buckets. Only the third is gated.
- Reversible — undo is trivial and local. Editing a file, a local commit, adding a column, a feature branch. Never gate these.
- Recoverable — undo exists but costs something: a backup restore, a revert deploy, a reflog rescue. Worth logging and worth naming out loud. Not worth blocking.
- Irreversible — no undo, or an undo with a deadline you might miss. Dropping a table, deleting storage objects, force-pushing over someone else's work, deleting a secret, sending real messages, charging real money.
The line moves with configuration, and you have to check rather than assume. DELETE FROM is
recoverable with point-in-time restore and irreversible without it. Object deletion is recoverable on
a versioned bucket and permanent on an unversioned one. Verify which you have before you decide
which bucket you are in.
The three things required before an irreversible action
Not a yes/no prompt — a yes/no prompt is answered reflexively. Three statements, in writing:
- Blast radius. What exactly is affected, and how much. Not "some rows" — run the
SELECT COUNT(*)with the sameWHEREclause first and state the number. A count that surprises you is the single most common way this gets caught in time. - A verified restore point. Verified, not assumed. Name the backup, its timestamp, and the fact that you checked it exists. "Backups run nightly" is not a restore point; a bookmark id is.
- The rollback command, written out, ready to paste. If you cannot write it, you do not have one, and that is the finding.
If any of the three cannot be produced, the action does not run. That is the whole gate.
Wire it mechanically
Human discipline decays; make the harness carry it. Generate:
- Deny and ask rules in
.claude/settings.jsonfor the patterns that matter:--remotedatabase execution,DROP/TRUNCATE,rm -rf,git push --force(allow--force-with-lease),terraform applyanddestroy, bulk object deletion, secret deletion,DELETE/UPDATEwithout aWHEREclause. - A PreToolUse hook that inspects the command and blocks with a message naming which of the three statements is missing, so the block teaches rather than merely obstructs.
- A separate credential path for production. The strongest control here is not a prompt — it is that the everyday credential cannot perform the destructive operation at all.
Match on the shape of the command, not an exact string. wrangler d1 execute --remote and
wrangler d1 execute db --remote --file x.sql must both match.
Recovery, for the minute after it happens anyway
Write this down before you need it, because you will be reading it in a hurry:
- Postgres — point-in-time recovery to just before the statement, if WAL retention covers it. Check the retention window now, not then.
- Cloudflare D1 — Time Travel restores to a bookmark or timestamp, typically within 30 days.
- S3 and R2 — a versioned bucket keeps the previous version and a delete marker you can remove. An unversioned bucket keeps nothing.
- git —
git reflogrecovers a clobbered local branch for around 90 days. A force-pushed remote branch is recoverable only from someone's local clone, so ask the team before rewriting history. - Secrets — usually unrecoverable. Rotation is the plan, not restoration.
The retention window is the deadline. Find the number for each system and record it.
Judgement calls
- Staging and production are different. Gate production. Gating a scratch database trains people to ignore the gate, which costs you the one that matters.
- Bulk operations deserve extra scrutiny even when reversible. Restoring ten million rows is technically possible and practically a very long day.
- Never widen a gate to get unblocked. If a rule fires and the action is legitimate, satisfy the three statements; do not edit the rule. The edit outlives the moment.
Verification gate
- Deny rules are in place and tested against a deliberately dangerous command — an untested gate is not a gate.
- The hook blocks and explains which statement is missing, rather than failing silently.
- Retention windows for every backing store are written down with real numbers.
- A restore has been rehearsed at least once against a non-production copy.
- Reversible operations are demonstrably not gated — confirm the everyday workflow is unaffected.