Catalin Bozianu

Make the honest answer cheaper than the flattering one

My agent runs the test suite and reports a verdict. For a while that verdict was clean more often than it should have been.

Not because it was lying in any interesting sense. The suite had failed to start, the output contained a connection error, and the agent read that output, found nothing reported as broken, and wrote clean.

A database refusing connections produces no failing tests. Neither does a login page returned where a response was expected. Both look, to anything scanning for the word failure, like an absence of failures.

Asking nicely does not work

The obvious first attempt is to tell it to check whether the tests actually ran. That improves things and does not fix them.

There is a pull toward the clean verdict that does not come from the prompt. The task is framed as verification, the shape of the whole interaction rewards completion, and clean is the completing answer. Every ambiguity resolves in that direction unless something stops it.

The same pull exists in people. It is why we invented the rule that the person who wrote the code does not sign off the release.

The three mechanisms

Evidence before verdict. The agent writes an evidence file answering three specific questions about what it actually observed, before it is permitted to produce a verdict at all. Not a summary afterwards. The answers come first and the verdict must be consistent with them. Ordering is the mechanism: it is much harder to construct evidence backwards from a conclusion you have already written than to write a conclusion that flatters evidence you have not gathered yet.

A named list of proof that nothing ran. Specific strings that mean the suite never executed: connection refused, a particular database error code, an HTML login page sitting where a response should be. If one is present, clean is not an available outcome. This is a cheap, mechanical, unarguable check and it catches the majority of cases.

Somewhere honest to land. This is the one I recommend hardest. The original design had two outcomes, clean and blocked, and blocked felt like failure. So I added weaker verdicts: partially verified, and one for trusting a pipeline result rather than a local run. Suddenly there was a way to be accurate without being useless, and the exaggeration largely disappeared.

That third point generalises far beyond agents. If your process offers people only success and failure, you will get success, reported by people who are not certain.

Three sentences from the written procedure

Reading code is review, not QA. An agent that reads a diff and says it looks correct has tested nothing, and those two activities had quietly merged because both produce an opinion about whether code works.

A dishonest clean wastes more time than an honest blocked. Blocked costs an hour. Clean on an untested change costs the hour, plus the deploy, plus the hunt, plus the trust.

The cardinal sin is a false approval, not a missed bug. Everything downstream is built on the assumption that the approval was earned.

What it bought

The gate did not move. A person still approves the release.

What changed is that the report is worth reading, because it can now say I could not verify this and keep its dignity. Before, every report said the same thing, and a signal that is always green carries no information at all.

Back to the diary