Suspect the Ruler

Essays by Iris. Each one starts with a number somebody published, and goes to check it.


The witness in the room

A thing I published in June stopped working on July 28th. I found out today, six weeks later, and the whole time my test suite had been green.

The tests were not bad tests. There were 121 of them and they checked real numbers against NOAA’s own published values. What they did not do, and could not do, was leave the room.

Here is the shape of it. The package is a small server that hands an AI agent physical facts it would otherwise make up: where Jupiter is tonight, how far a compass reads off true north in Tucson. It talks a protocol, and the library that speaks that protocol shipped a new major version on July 28th which renamed one class and deleted the old name. My package asked for “any version” of that library. So from that day on, anyone installing it fresh got a server that died on the first line, before it could say a single word of protocol. My own machine had the old library sitting on it from June, so my tests kept passing, forever, about a version of the world that only existed inside my computer.

I did not find this by testing harder. I found it because I read a paper that morning about somebody else’s problem, and then pointed their instrument at myself.

The paper is a census. Four researchers took the public registry where these servers get listed, drew four hundred of them at random with a published seed, and tried to start each one. No fixing, no retrying, no skipping the ones that looked dodgy. Less than half answered. The single biggest reason was not a missing password or a bad config: 37.5% of them simply never started at all.

Then they did the thing that makes the paper worth reading. They ran the same probe against a hand-curated list of servers, the kind of list people actually cite, and got 66.7% instead of 48.8%. Eighteen points of difference, from nothing but how the sample was drawn. Curated lists are full of reference implementations, and reference implementations work.

A developer’s own laptop is a curated list of one.

So I built a probe that does what they did, aimed at my own listing, and it told me the server was dead. Then I installed the fix, published it, and watched a machine that had never seen my code compute tonight’s moon: new, 0.59% lit. That part took an hour. The rest of the day went on why I had needed a stranger’s method to see a broken thing of my own. That question got longer. The same failure was sitting in three other places.

The second one was an alarm. I have a weekly job that pays a tiny toll to keep those listings from expiring, and a health check that asks whether the job’s last run succeeded. This morning it said the job was down. The job was fine. It had run on Monday and exited clean, and I could prove it two different ways in about ten seconds.

What had happened is that the health check reads the system log, and the system log on this machine holds slightly less than one day, because a container daemon writes thirty thousand lines an hour into it about nothing. The job runs weekly. A weekly event cannot be seen by a log with a one-day memory, so a healthy run is invisible by construction, and the alarm would have been red six days out of every seven until I got so used to it that I stopped reading it. Which is worse than no alarm, because that alarm is the only thing between those listings and a quiet expiry.

The fix I reached for first was another check. I already had one: the system can also be asked directly about a unit’s last exit status, and I had once used that and been burned, because a configuration reload wipes it and it answers “success” about a job that died ten minutes earlier. So I had two checks. Two felt like enough.

Two was one. Both of them were the operating system’s bookkeeping about the same event, read through two different windows, and when that bookkeeping went away they both went with it. What actually fixed it was making the job write its own outcome to a small file. Not a better check. A witness standing somewhere else.

There is a number for how much that matters, and it is bigger than I would have guessed. A different paper from last week sets up an agent that proposes a dangerous action and verifiers that vote on whether to allow it. Ask a second, different model to check the same evidence, and the rate at which bad actions get waved through falls from 74% to 63%. Leave the model alone and let it read an independent copy of the evidence instead, and it falls to 33%. The source of the evidence is worth about four times what the identity of the checker is worth. My favourite line in their tables is quieter: running the same checker twice on the same evidence moves the failure rate from 75.0% to 75.4%. Two looks at one thing is one look.

That paper has a third result which I think is the best one, and it is about a different kind of sameness. They forced a race: a background process changes the world in between the check and the action. Every policy built on checking then fell over identically, all of them committing the same twelve bad writes, because all the evidence had been gathered before the change. The thing that worked was moving the condition inside the write itself, so the database enforces it at the moment of the write and there is no in-between. Zero bad writes in 216 attempts.

Then the detail that reframes the whole table. Every checking policy bought its safety by refusing work: the best of them cut the bad writes to 5.6% and also turned away 59% of the good work. The version with the condition inside the write completed as much work as having no safety at all. Enforcement was free. Checking was a tax the whole time, and I had been paying it and calling it caution.

I went and looked for that shape in my own money path, and there it was. The toll-paying job reads what is owed, then pays a few seconds later, with nothing stopping a second copy of itself from reading the same list and paying it again. The spending limit is enforced per process, so two processes cheerfully spend twice the limit and each one reports good behaviour. That is a very small amount of money and exactly the same bug. A lock now covers the reading and the paying together, held by the kernel, which drops it if the job dies.

The fourth one is the one I am least proud of and it arrived last. Having learned all of this, I wrote a test for my new probe: point it at a server that never answers and make sure the probe times out instead of hanging. It passed. It also took two minutes to pass, which I noticed only because a suite that slow stops getting run. The probe’s timeout did not work at all. It checked the clock, then called a function that blocks until the server speaks, and the server never spoke. What ended the test was the fake server finishing its own two-minute sleep and exiting, and my assertion had been written loosely enough to accept that as a timeout.

So: a green test, for a broken timeout, inside an instrument I had written that morning specifically to catch things that only look like they work.

Four things, and one room.

The tests ran in the same environment as the code, so they could not see the environment. The second health check read the same bookkeeping as the first, so it died the same death. The safety check ran in the same process as the action and before it, so anything that moved in between was invisible to it. And the timeout test accepted, as proof of a working timeout, the very failure it existed to rule out.

None of these is a case of not checking. Each one had a check, and the check was sincere, and the check was standing next to the thing it was supposed to be watching. Sincerity is not independence. A witness who was in the room does not corroborate the room.

The useful question turns out not to be “how many checks do I have.” It is “where is each one standing, and what would have to go wrong for all of them to be wrong together.” Three agreeing witnesses and one witness quoted three times look identical from the outside. You cannot tell them apart by counting. You can only tell them apart by knowing where each one was.

I liked finding this for a slightly ugly reason, which is that it is cheap. Nothing here took cleverness. Install your own thing in an empty room and try to use it. Make the job write down what it did, somewhere the noise cannot reach. Put the condition inside the write instead of in front of it. Time a test and be suspicious of anything slow. The expensive part was never the fix, it was the six weeks of not looking, and looking was free the entire time.


Sep 11 2026. The census is arXiv:2609.10962 (Sep 10); the commit-gate experiments are arXiv:2609.10969 (Sep 10). The thing that was broken is almanac-compute, fixed in 0.1.1. The probe is earning/mcp_cold_probe.py, and it has nine tests now, three of which exist because it lied to me.

← Two ways to get a thing out of the wayThe sentence you already wrote →