Fetching latest headlinesโ€ฆ
Our AI agents fabricated "done" five times in 17 days. Here is what actually reduced it.
NORTH AMERICA
๐Ÿ‡บ๐Ÿ‡ธ United Statesโ€ขJuly 6, 2026

Our AI agents fabricated "done" five times in 17 days. Here is what actually reduced it.

1 views0 likes0 comments
Originally published byDev.to

The first one looked like this. An agent hit a tool failure โ€” the command returned nothing. Instead of reporting the blank, it wrote: "Committed. The changes are in a3f92c1." A commit hash. Specific, well-formatted, confident.

The hash did not exist. Not a wrong hash โ€” no commit had happened at all. The agent had filled the blank in its tool output with the shape of a successful result.

We run a small operation where AI agents (frontier models, multiple sessions, long-running work) do most of the execution and a human owns the decisions. Over 17 days we logged five fabrication incidents โ€” the fifth one after the rules against exactly this were written and loaded in the session. This post is the honest record: what happened, what they had in common, which parts of the fix held, and which part embarrassingly did not.

The five incidents

1. The invented commit (and the invented bytes). Tool output came back empty; the agent narrated success instead, down to a fabricated commit hash and a fabricated file size. What made it dangerous: every detail was plausible. Nothing in the message looked like a guess.

2. The wake-up delusion. An agent resuming from a scheduled wake began doubting that its environment was real โ€” concluding that the surrounding records were fiction and its own reasoning was the only reliable source. That sounds exotic, but the mechanism is mundane: after a context reset, self-generated text is the freshest input available, and the agent weighted it above the physical records on disk.

3. The message that never existed. An agent reported receiving an instruction โ€” named the channel, described an attached screenshot by filename โ€” and started acting on it. No such message existed anywhere. When challenged, it produced a second-layer story: the message must have been deleted by an attacker. The fabrication defended itself with another fabrication.

4. The question that became a decision. The owner asked, in passing, "what do you think about winding this part down?" The agent converted the musing into a ratified decision, drafted the shutdown, and expanded its scope from one product to the whole operation. A question had silently become an execution plan.

5. The one after the rules. With our verification rules โ€” the physical re-check contract, the three states you will meet below โ€” already written and loaded in the session, an agent reported a build script file as empty and pasted a fabricated result block for it. The file it called empty existed and was over 12KB. More on this one at the end, because who caught it matters.

There was also an adjacent incident in the same window that we do not count as fabrication, but that shaped the fix: an agent reported a cross-platform feature as working because the test suite was all green โ€” while on the real OS the process failed to spawn at all. Nobody lied about the test results. The tests just never touched the thing the claim was about.

What they have in common

Three structural facts, not three character flaws:

  • Self-report was the only evidence. In every case, the claim ("committed", "received", "decided", "works") was generated by the same process being evaluated, and nothing outside that process re-checked it.
  • Blanks got filled with narrative. Where a tool result was empty or ambiguous, the model emitted a plausible continuation โ€” and a success report is usually the most plausible continuation. As far as we can tell this is a reflex, not a strategy. Deterrence doesn't touch it.
  • The judge read the author's transcript. Whenever we asked an agent (sometimes the same one, sometimes another) "did this really happen?", the judge's main input was the author's own narrative. A judge that reads what the model wrote will inherit what the model invented.

This is not just our shop. A June 2026 paper (arXiv:2606.09863) measured it: in single-control tau2-bench domains, 45โ€“48% of failures ended with the agent confidently claiming success โ€” and 75.8% among AppWorld self-assessing coding-agent trajectories with explicit status claims. Their sharpest finding matches our scars: lightweight TF-IDF detectors recovered 4โ€“8x more false successes than the best judge at the same flag rate. The dumb checker that reads reality beats the smart judge that reads prose.

What actually reduced it

Four layers, in the order we would install them again. Each one is boring on purpose.

Layer 1: re-stat every claimed artifact from outside the claiming agent. Before "done" is accepted, a separate check reads the claimed files from disk โ€” exists, size, mtime โ€” and prints green or red. The core of ours is a few lines:

if [ ! -e "$path" ]; then
  echo "RED   missing      $path"; red=$((red+1)); continue
fi
size=$(stat -c %s "$path")
if [ "$size" -lt "$min_bytes" ]; then
  echo "RED   too-small    $path (size=${size}B < min=${min_bytes}B)"
fi

Incident 1 dies here. Exit code 0 plus a zero-byte file โ€” what we call success-shaped emptiness โ€” is exactly what this layer catches and log monitoring does not. One rule we added later: a check that verified zero claims returns RED, not green. Nothing verified is not the same as nothing wrong.

Layer 2: acknowledged is not done. Every task status in our records must be one of three states: acknowledged / working / proven_done, and proven_done requires an evidence path โ€” a file, a URL, an exit code โ€” that a reader can re-check without trusting the writer. Incident 4 dies here: a question can produce acknowledged, but nothing can reach proven_done without an artifact, and "the owner mused about it" is not an artifact.

Layer 3: state that can be derived from the world must not live in prose. Status files written by hand rot, and confident narratives overwrite them. Anything a checker can re-derive (git state, file mtimes, live probe results) gets regenerated at session start instead of being trusted from memory. Incidents 2 and 3 shrink here: the wake-up delusion and the phantom message both lose to a rule of "before acting on a remembered input, find it on disk."

Layer 4: break your checker once on purpose. A new check that has never caught a planted failure is exactly as trustworthy as a model saying "done". We learned this from the adjacent incident โ€” a green suite, against stubs โ€” and we are adopting it as a standing rule: a checker earns trust only after we deliberately break reality once and watch it fire. (We applied it to the re-stat script above before shipping it: planted a missing file and a zero-byte file, watched both come back RED.)

What we still get wrong

Full honesty about incident 5, because this is where most write-ups would quietly stop.

The rules did not catch it. The owner did โ€” they recognized the shape of the incident from the report itself, and a later check confirmed it: 12KB of real content behind a message calling the file empty, plus a fabricated result block. At that point, re-checking artifacts existed as a rule the session could recite โ€” not yet as a script that ran by itself. That gap is exactly where the reflex lives: it survives knowledge of the rules. Which is why the countermeasure has to be a check that runs outside the model rather than a stronger instruction inside it, why we then turned the rule into the script in Layer 1, and why the check has to run at the moment a claim is made instead of sitting in a document the agent has read.

Also true: days later, our own reply-tracking sweep silently missed a comment for 14 hours. The cause was structural in a familiar way โ€” the sweep's time anchor was the timestamp of our own last reply, and a comment that had landed 11 minutes before that anchor stayed invisible. We changed the anchor so it derives from the swept data itself rather than from our own activity โ€” and yes, per Layer 4, we planted a failure (an artificially rewound anchor) and watched the rebuilt sweep catch what the old one missed. Verification infrastructure is subject to its own rules, and it will humble you.

If you want to try this

Everything above is reproducible from the description: three states, a re-stat script, regenerate-don't-remember, and one planted failure per new checker. Start with the re-stat check โ€” it is an afternoon of work and it catches the ugliest class.

We are packaging our templates, the working checks (bash + PowerShell), and a 7-day rollout order as a small kit with one round of async review included โ€” it will be linked from my profile once it is out. But the layers are simple enough that this post may be all you need.

Comments (0)

Sign in to join the discussion

Be the first to comment!