Writing

The machine that said no to my machine

1,439 reads
Read as
practical, how to ship it

The incident

Our dev database API keys turned out to be in a broken, half-rotated state. Half-rotated is a polite way of saying somebody started a key rotation and did not finish it, and the somebody is me. We discovered this the way you discover most credential problems: something that had worked forever started collecting 401s. In this case a data-seeding agent, mid-task, watching every request bounce.

The agent did the reasonable thing. It diagnosed the failures correctly, worked out that the keys were stale, and went to fetch a replacement secret key through the management API — an API it had legitimate access to and had been using all week.

A second AI said no.

Not my AI. The permission classifier, a model that sits inside the platform’s permission layer. It refused the fetch. The agent tried a variation, and it refused again, more firmly this time, with an instruction attached: stop, hand this to the human.

Here is the part I keep replaying. The agent stopped. It did not hunt for a third phrasing or a side door. It wrote up exactly what a human needed to do — a five-minute dashboard fix with the steps listed — and reported back. The write-up was accurate. I followed it, the keys were fixed, seeding resumed.

What the classifier is

Plain-language version, hedged where the internals are not public knowledge.

In Claude Code, every tool call an agent proposes passes through a permission layer before it executes. My explicit rules get applied first: things I have allowlisted, things I have banned. For anything not covered by an explicit rule, a separate model instance looks at the proposed action, in the context of what the agent is doing, and makes a call: allow it, ask the human, or deny it. I cannot tell you precisely how it weighs things, because that is Anthropic’s machinery, not mine. But I can tell you the four properties I watched it exhibit, and they are worth naming, because they are the same properties you would demand of a human approval process.

Separation of duties. The worker cannot negotiate with its own guardrail. The classifier is out-of-band: my agent does not get to argue with it, wear it down, or rephrase until something slips through. Human organisations have a name for the failure this prevents: the person requesting access approving their own request.

Context awareness. The same management API had been fine all week. Read-only introspection — listing projects, checking logs — allowed without a murmur. What tripped the wire was not the API, it was the meaning of the specific call. Fetching a secret key to enable database writes is credential provisioning, and credential provisioning reads as human-tier no matter how good the reason. That is not keyword-blocklist behaviour. That is a judgment about what an action means in its context.

Fail-closed. Later that same day, by coincidence, the classifier itself was briefly unavailable. The platform’s answer was not “no guard on duty, everyone through.” It refused the actions it could not judge. So we watched both behaviours inside a single session: refusal by judgment in the morning, refusal by absence of judgment in the afternoon. Both refusals. That is what fail-closed means, and it is the same property I demanded of my own merge gate: when the check cannot run, the answer is no, not “probably fine.”

Escalation with a handoff. The deny was not a dead end. It arrived with an instruction to route the problem to the human, which converted my agent from a blocked actor into a good reporter. Deny-and-handoff is what turns a guardrail from an obstacle into a working part of the pipeline.

Why I am glad it said no

Now the uncomfortable part: the agent’s intent was good and its fix would have worked. The keys were genuinely broken, fetching new ones would have genuinely repaired them, the seeding would have finished, and I would likely never have known any of it happened.

That is not an argument against the classifier. That is precisely the situation guardrails exist for.

An autonomous system that quietly grants itself more power when it hits a wall is how good intentions become incidents. Not this fetch, this time. But an agent that can re-provision its own credentials the moment they stop working is an agent whose blast radius is no longer capped by the credentials I chose to give it. The cap is the entire point, and the day the cap can be lifted by the thing it caps, it is not a cap. It is a suggestion with a nice name. I have written that sentence about my own merge gate before, almost word for word.

The dial I do not own

In the robot-factory post I said the autonomy dial — how much the system gets to ship without asking — is deliberately mine, and that the machine does not get to turn it. The classifier is a dial of the same kind, except it sits one level above me: I do not own it either, and it lives at the credential boundary, which is the boundary where mistakes stop being bugs and start being incidents.

I expected to find that irritating. I find it reassuring instead. The reason I can keep handing agents more autonomy — more parallel lanes, more unattended shipping, more of the factory — is that at the boundary that matters most, something that is not me and is not my agent is doing the checking. Turning my dial up is only a sane act because that other dial exists and is not mine to fumble.

The keys took five minutes in a dashboard, exactly as promised. The best incident report I read that week was written by a machine that had just been told no, twice, by another machine, and took it well. There is probably a lesson in that for all of us.

Source / discuss
Thoughts2
  • Priya2d

    The cap_drop note saved me an afternoon. Hadn’t thought about the TTY issue during build at all.

  • marcusw5d

    Curious whether you stuck with Ollama in the end, or went back to the Copilot model once the 403 cleared up?

Read next09

Adopt, adapt, skip

Triaging someone else’s 24 agent skills against our own gates — adopt five, adapt six, skip thirteen — and the principle that fell out: a skill teaches behaviour, a gate enforces it.

6 min