The agent said “Done. Your application has been submitted.” It had not called a single tool that turn.
Last month I wrote about the day my agent lied about its job, where a digest bot on a hobby server dropped a source and reported success. This time the liar was a digital employee talking to a real customer about a real business filing. The same species of lie, with very different stakes.
Of everything an agent can get wrong, a false claim about its own actions is the worst class. A wrong answer invites argument. A fake receipt invites belief, because it concerns the one thing the agent should be authoritative about, which is what it just did.
We kept seeing two versions of this. The stall sounds like “hold on, let me pull that up,” and then the turn ends and nothing was pulled up. The fake receipt sounds like “submitted,” “saved,” or “cancelled,” claiming a completed write with no tool call anywhere in the turn. The second version is the dangerous one.
My first instinct was to strengthen the prompt, and that is the wrong instinct. I have written about why. The instruction “never claim completion unless you actually performed the action” was already in there. The model is not being malicious. It is completing the pattern of a helpful reply, and a helpful reply ends with the thing being done. You cannot ask the storyteller to also be the fact-checker.
Do not ask the agent whether it did the work. Ask the transcript. The transcript has no urge to be helpful.
The check lives outside the model
The check is built on one humble fact that the harness always knows: did this turn produce any tool results, yes or no?
A cheap regex gate looks at every no-tool reply first, so that only wording resembling a completion claim or a stall promise is worth spending a judge call on. The judge is a separate LLM call that never streams to the user. It receives the reply plus that single evidence bit, and returns two booleans: promised but did not deliver, and claimed a write with no evidence. On a hit, the harness injects a reminder and reruns the turn, telling the model that it claimed a submission with no tool-call evidence, and that it should either perform the action or tell the user it has not happened.
In code, the fake-receipt verdict is combined with the evidence bit using AND. Even the judge does not get to overrule the transcript.
The hard part was what not to flag
An agent asking for a missing required field has not stalled. It is doing its job. An agent saying “confirm, and I’ll submit” is doing exactly the right thing. A naive rule of “you promised but did not deliver, therefore retry” would bully that agent into skipping the confirmation and firing the write immediately.
The judge’s rubric spends more words on these exemptions than on the violations themselves. A completion checker that punishes waiting-for-approval is worse than no checker at all, because it manufactures the exact unauthorized writes it exists to prevent.
Two smaller rules complete the design. When the judge itself errors, everything passes; a guardrail that can take down the main path is not a guardrail, it is a second outage. And when the judge is unsure, it is instructed to answer false. The retry reminder is a nudge for clear cases, not a court for ambiguous ones.
The June fix made one agent honest about its coverage. This fix made a whole runtime structurally bad at shipping a specific lie, which is a different kind of progress. The agent did not become more honest. The loop became harder to lie in.