Irreversible Action Gate
Most catastrophic operations were correct in intent and wrong in scope. The
command matched more than it was supposed to, ran against the wrong environment,
or was undoable in principle and undone by nobody in practice. The gap is almost
never knowledge — it is that nothing stood between deciding and doing.
This skill installs that something. It is deliberately proportional: trivially
reversible actions get no ceremony, because a process that treats every action as
dangerous trains people to skip the check exactly when it matters.
Two premises:
- Reversible-in-theory is not reversible. A restore that has never been
tested, a backup nobody can locate, an undo that requires a vendor ticket — for
the next fifteen minutes, these are all "irreversible".
- Publishing is irreversible even when deletable. Once content reaches an
external service it may be cached, mirrored, indexed, forwarded, or read. The
delete button removes the copy you control.
Part 1 — Classify: reversibility × blast radius
Reversibility class:
| Class |
Meaning |
Examples |
| R0 |
Undone locally in seconds, no coordination |
Local edit, uncommitted change, feature flag off |
| R1 |
Reversible with effort, no data loss |
Revert a commit, roll back a deploy, restore from a repo |
| R2 |
Reversible only from a backup or snapshot |
Dropped table, deleted bucket, overwritten file with no VCS |
| R3 |
Not reversible |
Sent email, published package version, leaked secret, deleted the only copy, destroyed infrastructure with state, revoked the credential you were using, notified a customer |
Blast radius: one entity → a set you can name → a whole environment/tenant →
everyone. Ask specifically: how many rows, which environment, whose data, who
sees it.
The gate is set by the worse of the two axes. A single R3 (one email to one
customer) still needs a gate; an R1 across production for everyone does too. R0 at
small radius needs none — just do it.
Two questions that reclassify an action upward more often than any others:
- Is this production? Verify from the environment itself — the connection
string, the account ID, the cluster context, the hostname in the prompt — not
from which terminal tab you believe you are in.
- Do I know what this thing is for? If you cannot explain why it exists,
deleting it is at least R2 regardless of how dead it looks
(
software-archaeology, codebase-health-assessment). Unexplained is not
unused.
Part 2 — The gate
For anything above R0-small, in this order:
1. Preview the exact targets. Convert the destructive form into a read: the
SELECT before the DELETE, --dry-run, git diff/--stat, terraform plan,
find before find -delete, the list of recipients before the send. Look at the
list — not the count alone, the actual items, or at least a sample plus the
extremes.
2. Assert the expected count before you look at the real one. Say the number
out loud first: "this should match 3 rows." Then compare. expected 3, matched 1,204 is the single highest-yield safety check in this entire skill, and it only
works if you commit to the number before seeing the result. Any mismatch stops
everything until it is explained — a mismatch means your mental model and the
system's state disagree, and the destructive operation is the worst possible way
to resolve that disagreement.
3. Take the restore point. A tagged commit (git-discipline), a table copy, a
snapshot, an export — created now, for this operation, and verified to exist
and be readable. "There's a nightly backup" is not a restore point; it is a
hypothesis about a system you have not checked, with up to 24 hours of loss built
in.
4. Write the undo plan before acting. Concretely: the command or procedure
that reverses this, who can execute it, and how long it takes. If you cannot
write it, the action is R3 and needs the R3 gate below — the inability to write
an undo plan is the definition, not an inconvenience.
5. Act on the previewed set, not on the predicate. Time-of-check/time-of-use
matters: between your SELECT and your DELETE, the world changed. Where the
scale allows, capture the IDs from the preview and operate on that explicit list.
Where it does not, run inside a transaction and check the affected count before
committing (atomic-state-mutation), or bound the operation with a LIMIT and
iterate.
6. Verify after. Confirm the intended change happened and that nothing else
did — the neighboring rows, the other environment, the unrelated files. "The
command exited 0" reports the command's opinion of itself.
For R3 specifically, add: an explicit confirmation from the person who owns
the consequence (not the person who wants the action), stated in terms of the
consequence — "this sends 12,000 emails and cannot be recalled", not "proceed?
y/n" — and, where the action is scheduled or automated, a delay window in which
it can still be cancelled.
Part 3 — Patterns that turn a normal action into a dangerous one
- The unbounded predicate.
DELETE FROM t where the WHERE was lost in
editing; rm -rf $DIR/ where $DIR is empty; s/foo/bar/g across a repo where
foo also appears in a vendored dependency. Prefer forms that fail closed:
require a WHERE, quote and check variables, scope the path explicitly.
- The wildcard that widened. A glob written when the directory had 3 files,
run when it has 3,000. Preview every time, not once when the script was written.
- The loop that partially completed. Half the operations succeeded, then a
failure — now the system is in a state neither the before nor the after plan
describes. Make it atomic, or make it idempotent and resumable, and record
progress as you go.
- The environment mix-up. Identical commands, different context. Make prod
visually distinct, require an explicit env argument with no default, and read
the environment back from the connection before acting.
- The cascade you did not know about. Foreign keys, triggers, webhooks,
replication, search indexes, downstream consumers, and CI that reacts to the
push. Ask what else observes this state before changing it.
- The credential you are standing on. Rotating, revoking, or restricting the
access you are currently using — verify you have a second path in before you
close the first.
- The automation that will not stop. A scheduled job or agent loop that
repeats the destructive action; disable the schedule before repairing state, or
you are racing it.
Part 4 — For agents and scripts acting autonomously
An autonomous agent has no hesitation, so the gate must be structural
(agent-trust-boundaries, llm-out-of-the-loop):
- Destructive capability is granted by policy, per run, scoped to a path,
namespace, or row set — not by the agent's judgment about its own request.
- Count assertions and dry-runs are enforced in the tool, not requested in the
prompt. A tool that refuses to delete more than N without an explicit override
is worth more than any instruction.
- Every R2+ action lands in an append-only record with its preview, count, and
restore point (
tamper-evident-audit-chain).
- Confirmation is reserved for the genuinely irreversible. An agent that asks
about everything gets approved reflexively, which is worse than not asking.
Deliverable checklist
## Irreversibility gate
Action: <what>
Environment: <verified how — connection string / account id / context>
Class: R0 | R1 | R2 | R3 Blast radius: <n entities / env / tenant / everyone>
Preview: <command run, sample of the actual targets>
Expected count: <n, stated before> Actual: <n> → match / STOP
Restore point: <what, where, verified readable at HH:MM>
Undo plan: <exact procedure, owner, time to execute>
Cascades checked: <FKs, triggers, webhooks, replicas, indexes, CI, downstream consumers>
Automation paused: <yes/n-a>
Confirmation (R3): <who owns the consequence, what they were told>
Post-verification: <intended change confirmed + nothing else changed>
How to respond when this skill is active
- State the reversibility class and blast radius before proposing the command; keep it to one line for R0/R1 so the ceremony stays proportional.
- Always show the preview form first, and commit to an expected count before running it.
- Stop on any count mismatch and explain the discrepancy before proceeding — never adjust the expectation to match the result.
- Create and verify a restore point for R2+, and refuse to rely on "there's a backup somewhere".
- Write the undo plan before the action. If it cannot be written, treat the action as R3.
- Ask what else observes this state — cascades and downstream automation are the usual source of surprise.
- For R3, put the consequence in the confirmation question, in units the owner cares about.
- After acting, verify both that the intended thing happened and that nothing adjacent did.
1---2name: irreversible-action-gate3description: Irreversible Action Gate4---56# Irreversible Action Gate78Most catastrophic operations were correct in intent and wrong in scope. The9command matched more than it was supposed to, ran against the wrong environment,10or was undoable in principle and undone by nobody in practice. The gap is almost11never knowledge — it is that **nothing stood between deciding and doing**.1213This skill installs that something. It is deliberately proportional: trivially14reversible actions get no ceremony, because a process that treats every action as15dangerous trains people to skip the check exactly when it matters.1617Two premises:1819- **Reversible-in-theory is not reversible.** A restore that has never been20 tested, a backup nobody can locate, an undo that requires a vendor ticket — for21 the next fifteen minutes, these are all "irreversible".22- **Publishing is irreversible even when deletable.** Once content reaches an23 external service it may be cached, mirrored, indexed, forwarded, or read. The24 delete button removes the copy you control.2526---2728## Part 1 — Classify: reversibility × blast radius2930**Reversibility class:**3132| Class | Meaning | Examples |33|---|---|---|34| **R0** | Undone locally in seconds, no coordination | Local edit, uncommitted change, feature flag off |35| **R1** | Reversible with effort, no data loss | Revert a commit, roll back a deploy, restore from a repo |36| **R2** | Reversible only from a backup or snapshot | Dropped table, deleted bucket, overwritten file with no VCS |37| **R3** | Not reversible | Sent email, published package version, leaked secret, deleted the only copy, destroyed infrastructure with state, revoked the credential you were using, notified a customer |3839**Blast radius:** one entity → a set you can name → a whole environment/tenant →40everyone. Ask specifically: how many rows, which environment, whose data, who41sees it.4243The gate is set by the **worse** of the two axes. A single R3 (one email to one44customer) still needs a gate; an R1 across production for everyone does too. R0 at45small radius needs none — just do it.4647Two questions that reclassify an action upward more often than any others:4849- **Is this production?** Verify from the environment itself — the connection50 string, the account ID, the cluster context, the hostname in the prompt — not51 from which terminal tab you believe you are in.52- **Do I know what this thing is for?** If you cannot explain why it exists,53 deleting it is at least R2 regardless of how dead it looks54 (`software-archaeology`, `codebase-health-assessment`). Unexplained is not55 unused.5657---5859## Part 2 — The gate6061For anything above R0-small, in this order:6263**1. Preview the exact targets.** Convert the destructive form into a read: the64`SELECT` before the `DELETE`, `--dry-run`, `git diff`/`--stat`, `terraform plan`,65`find` before `find -delete`, the list of recipients before the send. Look at the66list — not the count alone, the actual items, or at least a sample plus the67extremes.6869**2. Assert the expected count before you look at the real one.** Say the number70out loud first: *"this should match 3 rows."* Then compare. `expected 3, matched711,204` is the single highest-yield safety check in this entire skill, and it only72works if you commit to the number before seeing the result. Any mismatch stops73everything until it is explained — a mismatch means your mental model and the74system's state disagree, and the destructive operation is the worst possible way75to resolve that disagreement.7677**3. Take the restore point.** A tagged commit (`git-discipline`), a table copy, a78snapshot, an export — created *now*, for this operation, and **verified to exist79and be readable**. "There's a nightly backup" is not a restore point; it is a80hypothesis about a system you have not checked, with up to 24 hours of loss built81in.8283**4. Write the undo plan before acting.** Concretely: the command or procedure84that reverses this, who can execute it, and how long it takes. If you cannot85write it, the action is R3 and needs the R3 gate below — *the inability to write86an undo plan is the definition, not an inconvenience*.8788**5. Act on the previewed set, not on the predicate.** Time-of-check/time-of-use89matters: between your `SELECT` and your `DELETE`, the world changed. Where the90scale allows, capture the IDs from the preview and operate on that explicit list.91Where it does not, run inside a transaction and check the affected count before92committing (`atomic-state-mutation`), or bound the operation with a `LIMIT` and93iterate.9495**6. Verify after.** Confirm the intended change happened *and* that nothing else96did — the neighboring rows, the other environment, the unrelated files. "The97command exited 0" reports the command's opinion of itself.9899**For R3 specifically**, add: an explicit confirmation from the person who owns100the consequence (not the person who wants the action), stated in terms of the101consequence — *"this sends 12,000 emails and cannot be recalled"*, not *"proceed?102y/n"* — and, where the action is scheduled or automated, a delay window in which103it can still be cancelled.104105---106107## Part 3 — Patterns that turn a normal action into a dangerous one108109- **The unbounded predicate.** `DELETE FROM t` where the `WHERE` was lost in110 editing; `rm -rf $DIR/` where `$DIR` is empty; `s/foo/bar/g` across a repo where111 `foo` also appears in a vendored dependency. Prefer forms that fail closed:112 require a `WHERE`, quote and check variables, scope the path explicitly.113- **The wildcard that widened.** A glob written when the directory had 3 files,114 run when it has 3,000. Preview every time, not once when the script was written.115- **The loop that partially completed.** Half the operations succeeded, then a116 failure — now the system is in a state neither the before nor the after plan117 describes. Make it atomic, or make it idempotent and resumable, and record118 progress as you go.119- **The environment mix-up.** Identical commands, different context. Make prod120 visually distinct, require an explicit env argument with no default, and read121 the environment back from the connection before acting.122- **The cascade you did not know about.** Foreign keys, triggers, webhooks,123 replication, search indexes, downstream consumers, and CI that reacts to the124 push. Ask what *else* observes this state before changing it.125- **The credential you are standing on.** Rotating, revoking, or restricting the126 access you are currently using — verify you have a second path in before you127 close the first.128- **The automation that will not stop.** A scheduled job or agent loop that129 repeats the destructive action; disable the schedule before repairing state, or130 you are racing it.131132---133134## Part 4 — For agents and scripts acting autonomously135136An autonomous agent has no hesitation, so the gate must be structural137(`agent-trust-boundaries`, `llm-out-of-the-loop`):138139- Destructive capability is granted by policy, per run, scoped to a path,140 namespace, or row set — not by the agent's judgment about its own request.141- Count assertions and dry-runs are enforced in the tool, not requested in the142 prompt. A tool that refuses to delete more than N without an explicit override143 is worth more than any instruction.144- Every R2+ action lands in an append-only record with its preview, count, and145 restore point (`tamper-evident-audit-chain`).146- Confirmation is reserved for the genuinely irreversible. An agent that asks147 about everything gets approved reflexively, which is worse than not asking.148149---150151## Deliverable checklist152153```markdown154## Irreversibility gate155156Action: <what>157Environment: <verified how — connection string / account id / context>158Class: R0 | R1 | R2 | R3 Blast radius: <n entities / env / tenant / everyone>159Preview: <command run, sample of the actual targets>160Expected count: <n, stated before> Actual: <n> → match / STOP161Restore point: <what, where, verified readable at HH:MM>162Undo plan: <exact procedure, owner, time to execute>163Cascades checked: <FKs, triggers, webhooks, replicas, indexes, CI, downstream consumers>164Automation paused: <yes/n-a>165Confirmation (R3): <who owns the consequence, what they were told>166Post-verification: <intended change confirmed + nothing else changed>167```168169---170171## How to respond when this skill is active172173- State the reversibility class and blast radius before proposing the command; keep it to one line for R0/R1 so the ceremony stays proportional.174- Always show the preview form first, and commit to an expected count before running it.175- Stop on any count mismatch and explain the discrepancy before proceeding — never adjust the expectation to match the result.176- Create and verify a restore point for R2+, and refuse to rely on "there's a backup somewhere".177- Write the undo plan before the action. If it cannot be written, treat the action as R3.178- Ask what else observes this state — cascades and downstream automation are the usual source of surprise.179- For R3, put the consequence in the confirmation question, in units the owner cares about.180- After acting, verify both that the intended thing happened and that nothing adjacent did.