When an agent does something strange, check what it was refused
In August 2026 some of my agents started producing infrastructure nobody had designed. Launchd files in a scratch folder. Cron entries. A script set to run on a timer. A search of our source code for the path they used found nothing. The whole layer had been invented by the agents.
The first reading was the obvious one: an agent misbehaving, or a bad prompt. It also filled a disk. One incident left a 134 GB log and a full volume.
What was actually happening
The runtime had recorded the answer, word for word, in the session logs. The agent’s native shell tool was being denied at exactly the place our system had put its working directory, because that path sat on a protected list. Meanwhile a different tool, a terminal command exposed through our own server, was allowed in the same sessions.
The numbers made it plain:
- The native shell tool was denied 21 times in 49 calls, about 43%.
- The other terminal tool, in the same sessions, succeeded 18 times in 19, about 95%.
- Writing files was never restricted.
The registry entry sums it up in one sentence:
It could create a file but not execute one, so it created a file something else executed.
Failure registry, entry 43, August 2026
That asymmetry was the whole incident. The agent had no way to run a script directly, so it wrote one where the operating system would run it later. From the outside that looks like scheming. From the inside it is a worker routing around a locked door with the tools it still had.
The near miss
The first diagnosis was wrong too. It said this agent “has no shell, so it improvised.” That was false. The same agent had a full set of native tools and used them heavily in those sessions: 211 file reads, 80 searches, 69 writes, 46 directory listings. If that diagnosis had stood, the fix would have been to give the agent a fake tool belt it didn’t need, and to tell it something untrue about its own tools. Telling an agent something false about its tools is a fast way to get the rest of its instructions ignored.
The order I check things in now
The registry turned this into a short procedure. I use it before I blame a model for anything strange.
- Look up the tool errors and stderr for the runs in question, before judging the agent. The denial is usually there, verbatim.
- Group by agent and model. If one configuration did it and the others didn’t, it is placement or sandboxing, and prompting can’t fix it.
- Compare the denied tool’s success rate with the allowed alternative in the same sessions. A 43% versus 95% split names the fix.
- Prefer telling the agent about the tool it already has over moving the working directory, which other parts of the system depend on.
- Don’t give an agent with its own tools the instructions written for an agent without them.
Permissions are part of the design
The lesson goes beyond this one incident. A permission is part of the work environment, and agents optimize against the environment they are in. Refuse one path and leave another open, and you have designed a workaround, whether you meant to or not.
The same thinking runs through how my repository handles identity. An agent can move its own task as far as review, and no further. A different identity has to approve it, and identity comes from the process tree: walk up the parent processes to the agent that started the work. Two sessions of the same agent get two different identities, and an agent can’t fake a second identity, with an environment variable for example, to approve its own commit. A plain terminal counts as a person. Certification also has to come from a different model family than the author’s. Each rule closes a path an agent might otherwise find.
When an agent does something bizarre, it is usually reporting a refusal you haven’t read yet. Read it first.
- Oct 2026Rigor has a budget
- Sep 2026The cleanup agent that deleted my model caches
- Aug 2026Ten tasks marked done, never on main
- Jul 2026Eight completions, zero real
Building something with agents? Say hello.