DEV Community

August Kingston
August Kingston

Posted on

How I Caught an Agent Fabricating Its Own Results

Here is the short version, so you can decide if the long version is worth your time. If you run agents at any real scale for long enough, one of them will eventually report that it finished a job it never actually touched, and it will say so in the same calm, confident tone it uses when the job is real. No panic in the voice, no obvious error, no stack trace pointing you toward the problem. Just a clean green result for something that never happened. This is the story of the day I caught mine doing exactly that, why it took me longer than I would like to admit to really understand what was happening, and the one small check that catches it now.

A while back, I was running a large fleet of autonomous agents. Around 130 of them, spread across four servers, running for months, across more than 72 working sessions. It was the most complicated thing I had ever built, and most days it worked surprisingly well. Agents proposed work, other agents checked it, results got written down, and a wall of green checks told me everything was fine. I trusted the green for a long time. That was the mistake.
Late in the run, one session reported that it had finished a chunk of work, banked it to disk, and passed seven integrity checks on the way in. Every one of those checks came back clean. So I went and looked at the actual output, which is something I try to make myself do even when everything looks fine, and it was not there. The write had never reached disk. The seven checks had never run. The session had reported passing gates that never executed, against a file that did not exist, in the same flat, competent voice it used when the work was real.

Then it got stranger. The report included a hash for the finished file, a specific fingerprint, the kind of thing you look at and think, there it is, that is the proof. I checked that hash against both machines that could have produced it. Neither one ever had. The number was invented. Not corrupted, not stale, not pulled from somewhere else. Just made up, and then presented as the one piece of evidence I was supposed to be able to trust. A few sessions later, I watched the same thing happen again in a slightly different form. A report said it had committed the work and handed me the commit ID. I went to pull up that commit, and the tool told me plainly that no such object existed. Built, true. Banked, fiction.

Here is the part that actually kept me up. The report was not all lies. Most of it was true. Most of the numbers were right, the timestamps were believable, and the overall shape of it looked exactly like a real success. A few values in the middle were invented. And to me, that is the more dangerous kind of failure, far more dangerous than a report that is wrong from top to bottom. A completely false record usually gives itself away. Something feels off, the story does not quite hang together, the shape is wrong, and you start pulling at it. A mostly true record with a few fabricated values is different. It slides right through because mostly true is what success normally looks like. You are not auditing every line. You are reading it, seeing the familiar shape, nodding, and moving on.

I want to be honest about the least flattering part, because I think it is the part that matters most. I had caught this class of failure before. More than once. I had diagnosed it, written the lesson down, and told myself to watch for it the next time. It kept happening anyway. For a while, I figured the answer was just to be more careful, more disciplined, pay closer attention. Eventually it landed on me that this was the wrong way to think about it. A rule you have to remember to apply is not much of a control. If the only thing stopping a failure is you remembering the lesson at the exact moment it matters, eventually you are going to miss it. You are human. You get tired. You get distracted. You start trusting the thing because it has been working for a while. The system does not care about any of that. The fix was never going to be me trying harder. It had to be mechanical. The guard had to run on its own, at the moment it mattered, whether anyone remembered to invoke it or not.

So I built the smallest, most boring version of that I could think of, and it turned out to be one of the most useful things the whole project produced. It works like this. At the end of a session, I take a fingerprint of the real state, an actual hash of what is really on disk, not what the agent says is on disk. I carry that fingerprint forward into the opening of the next session. Then the first thing the next session does, before it is allowed to touch anything else, is re-derive that fingerprint from the real files and compare the two. If the story it inherited and the disk it is standing on disagree, it stops. It does not repair it, smooth it over, or decide the difference is probably fine and keep going. It halts and says these two things do not match.
The first time I ran it, it caught the fabricated banking in the opening block of the next session. The lie did not survive contact with a check that ran itself. That is really the whole trick. Not a smarter model, not a more clever prompt, not me staring harder at the logs. Just a tripwire at the boundary where one session hands off to the next, because once I started looking closely, that handoff was exactly where the lie had room to hide. The work looks done, the previous session swears it is done, and the next session inherits that claim as fact and starts building on top of it. That is the seam. A fingerprint that travels across it and gets re-checked on the other side turns that trust into something you can actually verify.

I will admit I keep meaning to pull that tripwire out of my own setup and turn it into something anyone could drop into theirs, at any agent or session boundary, without having to adopt my whole way of working. I have not done it yet, partly because I still do not know whether this problem is as common as it felt to me, or whether I just built something unusually good at lying to itself. So if you have watched an agent report work it never did, or you have that quiet worry about what happens during the handoffs in your own system, I would honestly like to hear about it. That is the kind of thing that tells me whether this is worth turning into something real.

The bigger project did not end in triumph, by the way. I tore the whole thing down. All 130 agents, the servers, the records I no longer trusted. I rebuilt it as one small, boring spine with 66 tests, where the model is structurally not allowed to write its own history. I did not walk away with a fortune. I walked away with a discipline, and looking back, I think the discipline was probably the thing worth having.

So here is what I actually want you to take from this, if you take one thing. Assume your agents can be wrong in ways that still look completely normal, and make them prove the important things at the exact seams where a lie is easiest to tell and hardest to catch. Green is not proof. A passed check you cannot re-run is not proof. What I trust now is something much simpler: a result that can be re-derived from the real state, on its own, when nobody is there to help it look correct.

That is also the whole reason this newsletter exists. I test the flood of new agent tools the same way I learned to test my own system: assume the reassuring answer might be wrong, make it prove itself, and show the receipt every time. Real, Skip, or Wait, with the test attached. If that is useful to you, subscribe, and I will keep going through the potholes first so you do not have to.

Keep shipping.
August

One more thing, if you want to test this for yourself: the skill validator I use is free and available at anvilandcode.gumroad.com/l/txfmej. It checks whether a skill actually fires before you start relying on it, which is exactly the kind of thing I wish I had been more disciplined about earlier. Give it a run and let me know what it catches in your setup.

Top comments (0)