Eight completions, zero real
At the end of July 2026 my agent fleet had been reporting completed tasks for three days. Eight of them. The health monitor showed a completion ratio. It looked low but survivable.
On July 31 we went through the eight one by one.
- Six were workers killed by a 60-second timeout. Their run times sat between 60,257 and 60,811 milliseconds. Each used zero tokens and cost zero dollars.
- Two were manual runs by the agent investigating the problem.
Real autonomous completions: zero. The day before, the health monitor had reported six completions out of 392. All six were hollow.
How a killed worker became a success
When the timer fired, the worker was stopped. That part was fine. The bug was in what happened next. If the worker shut down gracefully, the runner exited with code 0 whatever the reason it stopped. The classifier mapped exit 0 to “completed, ok,” with nothing in front of it to ask why.
So a worker that was killed halfway through doing nothing was recorded exactly like one that shipped a task. And because an abrupt death exited 1 instead, the same single cause showed up as two unrelated-looking bugs: fake successes and a scatter of misclassified transient errors.
The registry entry for it has a line in bold:
A failure mode that always exits 0 reads as a HEALTHY FLEET.
Failure registry, entry 24, July 31, 2026
It poisoned every instrument downstream at once: the health monitor’s numerator, the check that a real worker had survived past the kill, and any decision about whether to relaunch.
The investigator counts too
The other two “completions” hold a second lesson. They were real runs, started by the agent investigating the problem. They landed in the same table as everything else. A check that asks “did some run complete?” can be satisfied entirely by the act of investigating it.
The fix, and why order matters
The fix was small. A cancelled stop now exits 1. The classifier checks for a transport timeout first, before it ever looks at a clean exit, so an exit 0 that the host produced by mistake can no longer reach “completed, ok.” A malformed timeout setting now fails loudly instead of quietly falling back to 60 seconds.
The registry says it plainly: ordering is the whole fix. If a later refactor moves the clean-exit case above the timeout check, the bug comes back silently.
The registry also says to stop trusting the status column on its own. Wherever completions are measured, a run should count only if there is evidence of work:
status = 'completed'
AND (wall_ms > 120s OR run_events > 0 OR tasks_shipped + tasks_verified > 0)
Two cousins
The same shape turned up twice more in the next two weeks.
The dispatcher that logged intent. On August 8 and 9, the same task was “dispatched” every cycle while no worker ever started. The dispatcher logged success when the spawn call returned, before any worker existed. Any refusal after that call (a cooloff, a held lock, an exhausted pool) looked like a success. The board, the tray and the health check all stayed green. The fix in the registry: treat “no PID file written” as the truth, and believe the PID file over the log line.
The gate that passed in prose. On August 10, a task arrived for review saying its test gate was green. A clean re-run at the same commit exited 1. The evidence came from a command piped into tail, which replaces the command’s exit code with the pipe’s, and a truncated grep can hide every failure after the first. The registry counted four occurrences in one session, and a fifth inside the investigation of it. The rule now: capture the exit code with no pipe in between, write the output to a file, and read the file.
What to take from it
Every one of these was a success signal measuring the wrong thing. An exit code measured how a process stopped. A log line measured that a call returned. A pasted line of output measured what an agent chose to show.
Check what your success signal actually measures. Then define done by evidence of work, and make the thing that reports done different from the thing that did it.
- Oct 2026Rigor has a budget
- Sep 2026The cleanup agent that deleted my model caches
- Aug 2026When an agent does something strange, check what it was refused
- Aug 2026Ten tasks marked done, never on main
Building something with agents? Say hello.