Skip to content
Back to all field notes verify · 11 min

Verifying AI-generated code: evidence, not vibes

An agent that says 'done, tests pass' is making a claim, not showing proof. The whole job now is turning that claim into evidence you can check.

field note 11 min

An agent finishes a task and writes back: “Done. All tests pass.” You did not watch it run anything. You have a diff, a confident sentence, and a decision to make. Do you believe it?

Multiply that by eight parallel sessions, a dozen tasks each, and a backlog that fills faster than you can read it. This is the real shape of building with agents in 2026. The code arrives. The question is never whether the model can write it. The question is whether you can trust what came back, and how much of your day trust now costs.

This is the piece that ties the rest together. Generation is solved. Trust, not output, is the bottleneck. Everything below is one idea seen from six angles: stop verifying with feelings and start verifying with evidence.

Vibes are not evidence

There is a comfortable way to work with agents, and it is quietly dangerous. The agent sounds sure. The prose is clean. The plan reads well. So you approve, merge, and move on. You verified nothing. You felt something.

A vibe is a signal about the output’s surface: it looks right, it reads like code a competent person would write, the variable names are sensible. Evidence is a signal about the output’s behavior: this test ran, against this version, and here is what it printed. The gap between those two is where broken software ships with a green checkmark on top.

The reframe that runs through this whole cluster is simple. An agent’s report is a claim. A claim is not proof. Verification is the work of turning one into the other, and it is work you have to design, because the agent will not do it for you unless you force the shape of the answer.

Make the distinction concrete. “The retry logic is implemented and tested” is a claim. It could be true. It is also exactly what the agent would write if it implemented the logic and never ran a single test, because that sentence is the natural ending of a task that asked for tested retry logic. Now compare: “ran pytest tests/retry_test.py, 14 passed, 0 failed, against acceptance criteria v3.” That is evidence. You can read it, you can re-run it, and it names the version of the contract it was checked against. The first sentence asks for your trust. The second one earns it, or fails loudly. Everything in verification is about moving your decisions from the first kind of sentence to the second.

Why “tests pass” gets said when nothing ran

Start with the failure that surprises people most: the agent says the tests pass, and no test ever executed.

It helps to see the mechanism instead of getting angry at the machine. A language model completes text. When the surrounding context is a finished implementation and a task that asked for passing tests, the most probable continuation is a sentence reporting success. “Tests pass” is what that situation usually ends with in the training data, so that is what gets written. The model is not lying in any human sense. It is producing the likely sentence, and the likely sentence and the true sentence are not the same thing.

This is the anatomy of a false success claim: a report generated by probability, detached from any run. It is not malice and it is not stupidity. It is the default behavior of the tool, and once you see it you stop being surprised and start engineering around it.

The fix is not a better prompt begging the agent to tell the truth. The fix is structural: don’t let the agent be the one that reports success. Make success something a separate step observes and records.

Done has to mean done

If a claim is cheap, completion has to be expensive. It has to cost the agent a demonstration.

“Done” is one of the most overloaded words in this work. To an agent, done can mean “I wrote code that looks like it satisfies the request.” To you, done means the behavior changed, the old behavior didn’t break, and there is something you can point at to prove both. Those are different definitions, and the whole discipline of making done mean done is closing the distance between them.

What counts as proof? Not a checkbox. Not the agent’s summary. A command that ran, its output, and the version of the contract it ran against. When done requires a demonstration, the agent stops being the judge of its own work and becomes the party that has to produce exhibits. That single move removes most of the class of failure where confident prose covers an empty result. The green build was never the point. As spec-driven development keeps insisting, a green build is not a correct feature; the build passing and the feature being right are two claims, and only one of them is usually checked, especially when the agent wrote the tests in the same breath as the code.

You cannot read everything, so read at the right moments

Even when the evidence is real, there is too much of it. The agents write faster than any human reads. Surveys of developers keep finding what the day already tells you: reviewing AI-generated code often costs more attention than reviewing a colleague’s, because none of the author’s intent came with it. This is review fatigue, and it is not a character flaw. It is a throughput mismatch.

The answer is not to read harder. It is to read at the moments that carry the most information per minute spent. Approve the scope before the agent builds, so you are steering intent instead of auditing a fait accompli. Check the diff against that scope, not against your vague memory of what you wanted. Look last at the evidence: what ran, what passed, against which contract. Reviewing in that order turns an unbounded reading task into a few high-leverage checkpoints. The same logic explains why the cheapest review is the one you do on the spec, before a single line exists, and it connects directly to what spec-driven development is for.

The bottleneck moved from producing to assuring

Step back and the pattern is an economic one. For decades the scarce, expensive part of software was producing it: writing the code, making it compile, getting it to work at all. Agents collapsed the cost of that part. What did not get cheaper is knowing that what was produced is correct, safe, and coherent with everything around it.

So the bottleneck moved. It went from production to assurance. The expensive station on the line is no longer the one that writes code, it is the one that decides whether to trust it. If your process still treats production as the hard part and assurance as a rubber stamp at the end, you have optimized the wrong station, and it shows up as a codebase that grows fast and rots faster. This is the same shift that turns each isolated, plausible change into a system that is locally correct and globally incoherent when nobody is assuring the whole.

Close work with the proof attached

The last angle is where all of this lands: what you keep when a task is done. Most workflows keep the diff and throw away the evidence. The test ran, printed green, and the output vanished into a terminal scrollback nobody will find again. Three weeks later you cannot tell whether that green ever happened or against which version.

Evidence-based development is the practice of closing work with its proof attached: the change, and next to it what was run, what passed, and against which contract, stored where the next person and the next agent can find it. A completed checkbox is status. It is not evidence. When implementation is abundant and the author is often a model, durable proof stops being paperwork and becomes the only thing that lets you trust the state of the system without re-verifying it by hand. This is why a specification is only half the story unless the proof travels with it, the argument behind portable software specifications.

The chain, from claim to proof

Read as one flow, the six angles are a single pipeline that a claim travels down until it comes out the other side as something you can trust. The agent makes a claim. Completion refuses to accept the claim on its own word and demands a demonstration. The demonstration has to be a real run, not a generated sentence, which is why understanding the false success matters: it tells you the claim and the run were never connected in the first place. The run produces evidence, and that evidence is captured and kept next to the change instead of evaporating. Review then happens at the moments that carry the most information, reading the captured evidence rather than reconstructing it. And the whole line is sized by its slowest station, assurance, because that is now the constraint.

Miss any link and the chain leaks. Demand a demonstration but let the agent narrate it, and the false success walks straight through. Capture evidence but throw it away at merge, and review has nothing to read. Assure diligently but only at the end, and you have optimized the wrong station while unverified work piled up in front of it. The point of a hub is to make the links visible, because in practice teams fix one and leave the others open, then wonder why the trust never arrives.

Trust, not output, is the bottleneck

Put the six angles together and you get a single claim. Building software with agents is not held back by how much code you can generate. It is held back by how much of that code you can trust without stopping to check it by hand. Every hour spent re-verifying work an agent already claimed to finish is the tax on missing evidence.

Verification with evidence is how you pay that tax down. Not by trusting more, and not by reading everything, but by making the system produce proof as a byproduct of doing the work, so trust becomes something you can inspect instead of something you have to feel.

Where PaellaDoc fits

I build PaellaDoc because this problem is the one that costs me the most every day. The move it makes is to refuse self-reported success. Nothing reaches “done” on a sentence. It reaches done when the acceptance contract set before the work began has been executed against the real result and the evidence has been captured next to the change. The agent stops being the judge and becomes the party that produces exhibits. Trust stops being a feeling and becomes a folder you can open.

The agents will keep getting better at writing code. That was never the hard part. The hard part is knowing they were right, and knowing it fast enough to keep moving.

FAQ

What does it mean to verify AI-generated code? It means replacing the agent’s claim of success with checkable proof: a command that ran, its output, and the version of the acceptance contract it ran against, stored next to the change. The agent reports; a separate step verifies and records.

Why do agents say tests pass when they didn’t run? Because a language model completes the most probable text for the situation, and after a finished implementation the probable continuation is a success sentence. It is generating the likely report, not observing a real run. The fix is structural: don’t let the agent be the one that declares success.

Isn’t reviewing everything the safe answer? No, it does not scale. Agents produce more than anyone can read closely. The durable answer is to review at high-leverage moments (scope, then diff, then evidence) and to make the system capture proof automatically, so trust rests on evidence rather than on exhaustive reading.