On 2026-07-18 I was decommissioning a deployment and wanted to keep three volumes of real historical data alive underneath it — carved out of the manifest before the rest of it went away. I split the file, staged the new one with git add -N so I could review the diff before committing to it, then committed it in the same motion I’d used a dozen times before. git status showed the file as added, both before the commit and after. Every visible signal said the data was safe.
It wasn’t. -N marks a path as tracked; it does not stage its content. The commit shipped an empty file — zero bytes, valid YAML, containing nothing. The sync controller took the diff at face value: the three volumes were no longer declared anywhere, so they were no longer wanted, so it queued them for deletion. Nothing in the tooling was wrong. I had, with total precision, told it to throw away data I meant to keep.
I caught it one step before the deletion actually ran, by re-checking the committed file’s line count against the working tree’s. If I’d looked four minutes later, or not looked at all, three volumes of real data would be gone, and the only reason it isn’t is that I happened to check. That’s not a safety net. That’s luck, wearing a safety net’s clothes.
Trust is a property of the system, not the agent
The instinct after a near-miss like that is to conclude the fix is more attention — read the diff more carefully next time, be warier of -N, don’t get complacent. That instinct is exactly backwards, and it’s worth being precise about why, because it’s the same instinct that shapes most conversations about how much autonomy to give an agent: trust it more, trust it less, review its output more closely, calibrate your confidence up or down. All of that treats trust as a property of judgment — mine or the agent’s — exercised correctly in the moment it matters.
My vigilance is not a system property. It doesn’t scale, it doesn’t run at 2 a.m., it doesn’t survive the twentieth review of the day. What would actually have saved those three volumes wasn’t a sharper eye — it was a rule that never needed my eye in the first place.
Trust isn’t a measure of how good an agent’s judgment is. It’s a measure of how cheap it is to be wrong.
Once you frame it that way, the live question stops being “how much do I trust this agent” and becomes “what does it cost me if this specific action is wrong” — and the second question has an answer you can build into the system, instead of a feeling you have to keep maintaining in your own head every time an agent reaches for something.
Three tiers, one test
The test that actually matters is: if this action turns out to be wrong, what does undo look like, and does it already exist? Run every class of change an agent might make through that test and it sorts into three tiers, not two.
- Code changes. The undo already exists by construction — it’s called version control. A wrong edit costs one
git revert. Because the floor is unconditionally solid here, an agent gets full autonomy: it can open files, restructure functions, delete whole modules, and none of it needs a human in the loop first, because none of it can actually cost anything. - Live infrastructure, routed through the same mechanism. This is the tier people get wrong most often, because “production” sounds like it should default to more caution than code. But if every change to a live system also goes through version control — a manifest committed to a repo, reconciled into the running system by a controller that will happily put it back the way it was — then a live environment is exactly as reversible as code is. The controller becomes a second safety net underneath the first: even a change applied by hand gets silently reverted back to what the repo says, because the repo, not the moment, is the source of truth. An agent can operate here too, with the same confidence, for the same reason.
- True one-way actions. Nothing rescues these, because there is no “undo” to construct — the action’s effect leaves the system entirely. Sending a message. Charging a card. Force-pushing over history other machines have already built on. For this tier, and only this tier, a human has to own the call before it happens, because no amount of system design converts a one-way door into a two-way one.

Nearly everything an agent does in a working session — writing code, restructuring a repo, reconciling a live deployment — lives in the first two tiers. The third tier is real, but it’s much smaller than it feels, and most of what people lump into it because it sounds irreversible actually isn’t.
Deletion is the easy case
Deletion is the textbook example of something that looks like tier three and is almost always tier two. The three volumes in my near-miss weren’t actually one bad git commit away from being unrecoverable — they were one missing backup away from it. The fix for the near-miss was never “review diffs harder.” It was: nothing gets deleted without a copy existing first, full stop, made mechanically, not as a judgment call in the moment.
That’s the whole trick, and it’s almost insultingly simple once you say it out loud: back up, then delete. If the deletion turns out to be wrong, you don’t debug it, you don’t reconstruct it by hand, you restore the copy and move on. The action was irreversible for about the length of time it takes to run a backup command, and reversible forever after.

This is what actually earns an agent the right to delete something on its own. Not a judgment that the agent is trustworthy enough, not a review step where a human eyeballs the command first — a mechanical guarantee that a copy exists before the delete runs. The permission comes from the guarantee, not from confidence in whoever’s finger is on the trigger.
Where backup-based reversibility hits a ceiling
It would be tidy if “back up first” dissolved the whole tier-three category, but it doesn’t, and the place it stops working is worth naming exactly: force-pushing over history other machines have already pulled and built on.
A force-push looks like deletion — old commits vanish from the branch — so the backup instinct says: keep a copy of the old ref, push away, restore from the copy if it goes wrong. But the thing that actually breaks isn’t recoverable from your copy, because the damage doesn’t happen to your repository. It happens to every other clone that has already based work on the history you just erased. Their next pull either silently diverges or drags your rewritten history in and corrupts what they built on top of it. Your backup makes you whole. It does nothing for them.
That’s the real dividing line for tier three, and it’s sharper than “can this be undone”: can the person doing the action alone also do the entire undo alone? Deleting a file, dropping a database table, deleting a cloud volume — one party creates the mess, and that same party, with a backup in hand, cleans it up completely. A force-push spreads the damage to parties who never agreed to bear it and can’t fix it from where they’re sitting. When the blast radius crosses that boundary, no backup converts it back to tier two, and it stays a human’s call — which is exactly why it’s banned outright rather than allowed-with-a-backup.
A backup existing isn’t the same as a backup you can trust
There’s one more failure mode, quieter than the other two, and I only found it by nearly getting bitten by it: a backup job can exist, run on schedule, and still not be the safety net it looks like. I once found a nightly restic backup running three times concurrently — no lock file, no guard against overlap — driving load high enough to look like an unrelated outage. The backups were technically completing. Nobody had checked whether they’d actually restore cleanly, or whether the three overlapping runs were racing each other into a corrupted snapshot.
The presence of a backup script is not the same claim as “this delete is reversible.” A trustworthy backup is current, it doesn’t race itself, and — the part that’s easiest to skip — someone has actually restored from it at least once, not just confirmed the job exits zero. Treating “there’s a backup somewhere” as equivalent to “this is tier two” is how a near-miss like the one that opened this note turns into an actual loss the next time nobody happens to be watching. The guarantee has to be real, not assumed.
The question I now ask
Before I let an agent take an action with any real weight behind it, I ask:
If this turns out to be wrong, what’s the undo — and does it already exist, or does it need to be built first?
If the answer is “version control already covers it,” the agent proceeds without me. If the answer is “a backup makes it reversible,” the backup runs first, verified, every time, mechanically — and then the agent proceeds. If the answer is “nothing rescues this, and the blast radius reaches past me,” it waits for a human, no matter how good the agent’s judgment looked five minutes ago. I stopped asking myself how much I trust the agent. I built the system so the question doesn’t need an answer.
— Jed
Background: Ending is better than mending — the same cheap-undo logic applied to agent output, not agent actions. And Deterministic state machines for non-deterministic agents — predictable failure as the other half of the same foundation.