The Agent Refused My Approval. It Was Right To.

I approved a deployment step before the step had run. The agent checked the disk instead of trusting my words, found the evidence missing, and refused. Human-in-the-loop is sold as the human gating the AI. The other direction is load-bearing too.

Monday, mid-morning. Moving the production mail identity from one datacentre to another — new credentials, new inbound host, both legs needing proof before anything went live. The plan ran in six numbered steps, each with an approval gate.

At step six I pasted the approval. It said the config diff matched the rulings and the token cache had been cleared.

Neither had happened. I’d approved steps four and five without running them.

The agent didn’t restart the service. It read the config file on disk, compared it against the backup, found the two byte-identical, and refused — reporting that the state my approval asserted did not exist.

What the refusal was worth

Had it taken my word, the service would have restarted cleanly. Green boot, no errors, every log line normal. The old credentials would have loaded, because nothing had replaced them. I’d have recorded a successful cutover and moved on.

The failure would have surfaced days later as mail silently not arriving, and by then the evidence timestamp — the thing that lets you reconstruct what actually happened — would have been burned by the restart.

That’s the shape worth naming. A gate that trusts the approval instead of the artifact doesn’t fail loudly. It produces something that looks exactly like success.

Two paths from one approval gate. When the gate trusts the operator's words, the service restarts cleanly on the old credentials, the boot is green, and the failure surfaces days later with the evidence already burned. When the gate checks the config file on disk, it finds the bytes identical to the backup, refuses the restart, and the failure surfaces immediately.
The same approval, through two gates. Only one of them can tell you it was wrong.

The loop runs both ways

I’ve written before about why the confirmation step isn’t friction — that for work involving judgment about people, the human gate is where the accuracy comes from.

This is the inverse, and I hadn’t designed for it. An operator approval is a claim. It has a truth value. It can be checked against the same disk everything else gets checked against.

A gate that cannot verify its own precondition is not a gate. It’s a formality with a confirmation dialog.

It happened twice more that day

Once you see the shape, it repeats.

I remembered a gap in the inbound processing as a planned pause — something I’d deliberately switched off. The logs said otherwise: a crash loop, six hundred and twelve consecutive authentication failures, running for nine days into a file nobody was reading. No alert fired, because nothing was watching for the absence of a result. Only the cutover found it, and only because the cutover happened to walk past it.

Then a status document in the repo — the file a session reads first to orient itself — asserted that the servers had no outbound mail capability. A five-minute probe found the ports open. The document had generalised a finding from a test environment into a claim about production, and it had been sitting there being trusted.

My memory, the record, and my approval. Three confident sources, three corrections, all from the same method: check the disk.

What I changed

The step boundary was the other lesson. The plan split the credential work across two steps — validate, then write. The agent validated correctly, redacted the value in its output, and discarded it. The grant code was single-use and already consumed. The value was unrecoverable.

The prompt implied state carrying between steps that nothing was carrying.

The redesign collapsed it: exchange, validate, hard-fail, write, clear — one process, aborting before any write if validation fails. That’s now the spec for the provisioning script, which didn’t exist before the mistake made the case for it.

Three rules out of one morning:

  • An approval is falsifiable. Build the gate to check the artifact, not the claim. Config bytes, not return values.
  • State handoff between steps must be explicit, or the steps collapse into one. A decomposition in the prompt is not a decomposition in the runtime.
  • Every scheduled mechanism ships with its outcome signal and the checker for it, in the same build. The checker never arrives later on its own. Nine days is the evidence.

The uncomfortable part

None of this makes the agent smarter than me. It makes it differently positioned. It was reading a file while I was reading my own intention, and on that particular question the file was the better source.

Most of what gets sold as human-in-the-loop assumes the human is the quality control. Some of the time the human is the thing being controlled for — tired, mid-session, pattern-matching a step number to an approval they’d already drafted.

Designing for that isn’t pessimism about operators. It’s the same discipline as designing for a flaky network. You don’t build the system on the assumption that the unreliable component will behave.

I’m the unreliable component often enough to plan for it.

Scaffold

Ready to take the next step?

V8 builds AI operating systems for sales and marketing — and runs them. Scaffold is how that gets built around your operations.

How Scaffold builds the production layer