(Spine Ch. 60.)
“Some decisions are consequential and irreversible or nearly irreversible, one-way doors … But most decisions aren’t like that, they are changeable, reversible, they’re two-way doors.” Jeff Bezos, Amazon 2015 Shareholder Letter (2015)
Change one that night was a migration. Clean diff, sane names, the
kind you approve from your phone in the checkout line. It treated
minio_key as a column you write to. It is a column you read
from, and 337,353 rows were already pointing at archived objects that
exist in exactly one place. Nobody caught it in the diff, because it was
not in the diff. It was in a row count nobody had run yet.
An agent with SSH access, passwordless sudo, and a repo it could
branch stopped early in a run to ask me whether it should rename a
function. I was asleep. It sat there holding a question whose worst
possible answer was git checkout ., one command, zero
dollars, no customer affected. I woke up to a paused pipeline and a
polite paragraph explaining why it wanted my input. I searched every
project blog, every skill file and every saved resume on 9 September
2026 and no transcript, repo or date survived, so take that one as my
memory and not a receipt.
Bottom line: Classify the door before you classify
the risk. A file edit, a branch, a local test run, a migration on a
scratch database: two-way doors, and asking about them costs more than
being wrong about them. A force-push, a DROP TABLE, an
email to a customer, a prod deploy: one-way doors, and those get a human
every time. The skill is not judgment. The skill is sorting. Agents that
ask about everything aren’t careful, they’re expensive, and the tell is
that they interrupt you about a rename and then quietly truncate a table
because nobody wrote the guard.
php artisan migrate:fresh against a host it thought was
staging because APP_ENV said so.Bezos wrote the classification down in the 2015 shareholder letter. What matters more is the failure mode he names next: as organizations grow, they run the heavy Type 1 process on Type 2 decisions, and the result is “slowness, unthoughtful risk aversion, failure to experiment sufficiently, and consequently diminished invention.”
RCM ThinkLabs put a sharper edge on it in 2026: “Deliberation is the easy part. The capability is classification. Knowing which door you are standing in front of, and then matching your process to the answer.” Every agent I’ve built is decent at deliberating. Most are terrible at sorting, and they default to asking, because asking has never once been punished. It looks like you’re renaming a function. Would you like help? Clippy shipped in 1997, got laughed off the desktop by 2001, and has now been reincarnated with a $200 monthly subscription and SSH keys.
RCM names the incentive too: a fast decision that goes wrong is “visible, attributable, and has a name attached to it,” while a reversible decision slowed to a crawl is “invisible, shared across a committee, and belongs to nobody.” RLHF is a performance review with a loss function attached, so of course the model learned to ask.
The engineering answer is older than the agents. Feature flags decouple deployment from release without a redeploy; canary exposes a change to a small subset of users first. Each converts a one-way door into a two-way door.
So the rule has two halves and people only ever remember the first one:
My standing order to every agent I run is one sentence: never ask me to do what you can. That order is safe only because the bomb list exists and is enforced in code, not prose, which is the whole of Ch. 41.
Here is the classification I run on airank. Part is compiled, part is memory, and I would rather flag which is which than pretend I keep a beautiful versioned file.
The compiled part is one line in
app/Providers/AppServiceProvider.php:
DB::prohibitDestructiveCommands($this->targetsProtectedDatabase()).
It blocks five named artisan commands, migrate:fresh,
migrate:refresh, migrate:reset,
db:wipe and migrate:rollback, and it keys on
the connection driver instead of APP_ENV, because on 8
August 2026 the box that dropped my whole schema had
APP_ENV=local and DB_HOST=192.168.1.3 in the
same .env. I wrote the limit into the comment so I could
not con myself later. It does not cover raw SQL, another client, or an
installer hook. It narrows the doors. It is not the tape.
The rest is the list below, quoted out of my own head instead of out of a file, which is exactly the drift I complain about two sections down.
Two-way (act, then report): edit any file, create a branch, run the test suite, migrate a scratch database, restart a dev container, add a feature flag in the off position, install a dependency, open a draft PR, add logging.
One-way (ask, every time):
git push --force to a shared branch, any DDL against
production, DELETE or UPDATE without a
WHERE you have counted first, sending anything to a real
customer’s inbox or phone, rotating a credential other systems hold, a
production deploy not behind a flag, deleting object storage, changing
DNS, touching billing.
The middle cases are where the money is. A production deploy behind a feature flag defaulted off is a two-way door; the same deploy with the flag on for 100% of users is a one-way door.
airank, 13 August 2026. Three competent changes went to review that night. All three would have shipped as written, and outside verification caught them, not the author. You can act without asking on reversible work because a verification pass exists downstream. Remove the verification and “act, don’t ask” is recklessness with a nicer name.
One caveat before the numbers. I went looking for anybody outside my own stack who has published both figures on the same task, what asking cost against what being wrong cost, and I found nothing. So read this as one garage’s incident log, not as an industry finding.
Change one is the migration from the top of this chapter, a schema optimization that would have overwritten 337,353 high-fidelity object pointers with lower-fidelity ones. The reviewer ran a row count, and that’s what caught it. A one-way door wearing a two-way door’s costume.
Change two was an nginx microcache config, and the payoff was real: 16.6 requests per second to 1,873, a 113x improvement. It also would have broken every login on the site by caching authenticated state into the anonymous cookie namespace. The curl tests failed twice before they passed, and the final config splits stateful flows from cacheable ones.
Change three didn’t break anything. It just stopped: a cron scheduler
died silently when logrotate compressed the log file out from under
www-data. A morning sweep caught the stale ledger
summaries. That’s Ch. 49’s failure mode, a run that never ran looking
exactly like a run with nothing to report.
Each one shipped only because the work was done
first and verified after. If the author had stopped to ask permission on
each, I would have had three questions in my inbox and zero row counts,
and I answer questions with the same brain that wrote the bug. Jeremy
Schoemaker is not a safety mechanism. Jeremy Schoemaker is a guy at
1:14am who types y because the notification sound woke him
up.
The loud failure is the agent that force-pushed over three days of work. It gets a guard within a week.
The quiet failure: asking becomes the safety theater, and the actual bombs go through unguarded.
An agent that requests confirmation for a rename has trained you to click yes. You haven’t added safety. You’ve added a habit that fires on the wrong input, and it’s now the thing standing between you and 337,353 rows.
Second quiet failure: you never build the flag, so everything
stays a one-way door. Spend a month asking permission instead
of two days making the deploy reversible, and you’ve chosen to pay the
asking tax forever. The 5 August airank incident is the version that
costs data: backups existed, but log_bin was
OFF, so there was no recovery path, and the door was
one-way without anybody deciding it should be. That one is mine. I ran
nightly backups for two years and never once checked whether binary
logging was on, which means Jeremy Schoemaker, former Lead Linux
Security Engineer at a bank, had a backup strategy that was actually a
backup feeling. I had been playing prod on Diablo II hardcore since 2000
and thought I was on softcore. The difference only shows up the one time
you die.
Third: the classification lives in prose, so it
drifts. “Be careful with production” in a CLAUDE.md is a mood,
not a classification. The guard that works keys on what the connection
points at: APP_ENV is a string someone typed, and the
connection is a fact.
Do
DB::prohibitDestructiveCommands()
keyed on the real connection, an allowlist on the shell tool, a flag
default of off.Don’t
git checkout . costs
nothing; your question costs a sleeping human his whole night.Ch. 40 covers why someone has to own the outcome, which is what makes review-after-the-fact legitimate. Ch. 41 covers why the bomb list has to be code, not prose: a system-prompt paragraph doesn’t survive a persuasive piece of retrieved text. Ch. 49 is the overnight run this chapter makes possible. Ch. 61 closes on what you own when the loop runs without you.
Thesis is Jeremy’s (act on reversible work, ask only on bombs, and spend the effort making more things reversible): argument, not citation.
Verified:
~/Projects/airank/blog/2026-08-13-three-saves-in-one-night.mdlog_bin OFF, no recovery path;
APP_ENV=local and DB_HOST=192.168.1.3 on the
box that dropped the schema. airank, 8 August 2026.
~/Projects/airank/blog/2026-08-08-everything-reported-success.md~/Projects/airank/app/Providers/AppServiceProvider.phpprinciple-never-block-on-the-human, read 9 September 2026:
~/.claude/plugins/cache/pstack-claude/pstack/0.9.27/skills/principle-never-block-on-the-human/SKILL.mdWhat I could not verify:
~/Projects/*/blog/*.md,
~/.claude/skills/*/SKILL.md and
~/.claude/resumes/ on 9 September 2026 and found no
transcript, run ID or date. It prints as recollection with no durations
attached.