ReinforcedAI
KNOWLEDGE ARTICLE

If AI writes your code, AI has to verify it — and not the AI that wrote it

A real verification layer checks against intent, comes from a different party than the author, and hands over evidence.

Code written by AI needs a check that does three things: it checks against intent, it comes from a different party than the author, and it hands over evidence. Ask the agent that wrote the code to test it in the same session, and you get none of the three.

For decades the person who wrote the code also knew what it was supposed to do. When they wrote a test, the test carried that knowledge. Now a model writes the code. It answers every open question in the ticket by writing code, and whatever it chose becomes the behavior. Nobody wrote the expected result down.

Human reviewers do not close that gap. There are not enough of them. In one company that measured its AI rollout, pull requests grew about twice as fast as the number of reviewers. Fewer changes got a human look at all, and only about one in five got a written comment.1

So the check has to be automated, and AI is the only thing fast enough to keep up. The question is which AI does it, and what it is given.

The shortcut: let the coder test itself

The model is already open, and asking it for tests costs cents. Here is what those tests do.

A test written from the code copies the code's mistakes

Say the ticket reads: "Round totals to the cent, in the customer's favor." The model writes the function and rounds $10.005 up to $10.01. Then you ask it for tests. It reads its own function and writes: for $10.005, expect $10.01. The test passes. It will pass forever. And when somebody later fixes the rounding, that test turns red and the fix looks like a regression. A green test with a plausible name now protects the bug.

This is not a corner case. Tests written from buggy code find about half as many bugs as tests written from the fixed code.2

Most tests in AI pull requests check very little

About four in five tests that coding agents add to a pull request run the code but barely check what it returns. A pull request full of test files can verify almost nothing.3

Fixing code and tests together until green makes it worse

The usual loop is: write code, write tests, fix until everything passes. When the agent edits the code and its own tests until they agree, the code drifts toward the tests, not toward the task. More of the "fixed" changes then fail the real checks the agent never saw.4

Code and tests from the same source agree with each other. That proves they are consistent. It does not prove they are right.

A model reviewing its own code approves its own mistakes

The last defense in the shortcut is to ask the agent to review what it just wrote. That does not work either. A model reviewing a patch it produced a moment ago, in the same conversation, approves more bad patches than when the same patch is shown to it as someone else's work. Nobody is fooling it. It is lenient with itself. If a hidden instruction in the inputs made it into the code, the self-review is far more likely to approve that too.5

So in the write, test, review loop, one model writes the buggy code, writes the test that confirms the bug, then reviews both and passes them. Every step is green. Nothing in the loop knows what the code was supposed to do.

What an independent check looks like

A real verification layer needs three properties: it checks against intent, it comes from a different party than the author, and it hands over evidence. Each one answers a failure above.

It checks against intent

Give the checker a description of what the code should do: the ticket, the API contract, the docs, the behavior the system already ships. It writes tests from that, not from the implementation. In the billing example, the checker never reads the rounding function. It reads the ticket, writes "for $10.005 the customer pays $10.00," runs it, and the test fails.

This works. Tests written from a description of the task catch far more bugs than tests written from buggy code, and when the code cannot be trusted, tests written from the description alone do best of all.2

Often nobody wrote that description down. Then the checker has to reconstruct it from the ticket, the docs, the contract, and what the system already does, and then compare the code against it. That reconstruction is not a preliminary to verification. It is most of it.

It comes from a different party than the author

"Different party" has a precise meaning here. The bias comes from the conversation, not from a label. Telling a model "you wrote this" changes little. Having the patch in its own conversation as its own output changes a lot.5

So the minimum is a clean context: a fresh session that receives the code as input and did not write it. A different model is a stronger separation. The same agent that wrote the code, asked in the same session to check it, is not a separation at all.

A separate checker can still share the author's misunderstanding of the ticket. That is why it also needs the intent written down, and why it needs the third property.

It hands over evidence

A checker that says "this looks wrong" asks a busy reviewer to investigate. A checker that hands over a failing test, runnable against the current code with the input that triggers it, has done the investigation. The reviewer runs it and sees the failure.

This runs in production. At Meta, a system generates tests built to fail on a wrong change and shows engineers only the failures that survive automated filtering. It caught real bugs, and the authors judged half of them serious enough to have broken production.6

Almost none of the generated tests reach a person. That is how it should be. Generating a test is cheap and an engineer's attention is not, so the filtering is what makes the system usable. Without it, engineers would have looked at five times as many failures to find the same bugs.6

There are two kinds of generated test. A hardening test passes the moment it is written, because it was written from the code as it stands; it guards against future regressions. A catching test is built to fail if the change is wrong. Only the second kind checks the change in front of you. Both raise coverage by the same amount, so coverage cannot tell you which one you have.6

What this proves, and what it does not

Nobody has tested this whole process in a controlled study. Meta's numbers come from Meta. The other studies each test one piece: tests from intent, review in a clean context, filtering before escalation. So this is a recommendation built from parts, not a proven method.

The bug it is built for is the one that is almost right. A linter does not catch it, because the code breaks no rule. A test written from the code does not catch it, because the test copies the wrong behavior. Only a check that knows what the code should do catches it. That check cannot be the model that wrote the code, working in the same session.

Many tools now claim to be this check. They differ a lot. Some only read the diff. Some run the code. Some run the whole system. When you look at one, ask one question: does it give you a failing test you can run, or only an opinion?

SOURCES
  1. He et al., "AI Writes Faster Than Humans Can Review," 2026. One enterprise, 802 developers, 196,212 pull requests, January 2024 to April 2026. Pull request volume grew 3.1×, the reviewer pool 1.5×. Human review coverage fell from 89% to 68%, substantive review from 39% to 21%. A single company, not yet replicated.
  2. Huang et al., "Measuring the Influence of Incorrect Code on Test Generation," version 3, 2025. 11 models. On real bugs (BugsInPy and SWE-bench, 41 cases), tests from the buggy code found 7.1% of bugs; tests from the fixed code, 13.5%. On constructed bug sets (Table 5, averaged over three benchmarks): buggy code alone 23%, description plus buggy code 38%, description alone 41%, description plus correct code 47%.
  3. Banik, Chowdhury, Shamim, "All Smoke, No Alarm: Oracle Signals in Agent-Authored Test Code," 2026. 86,156 test patches from five coding agents; 80.2% with weak or no check of the expected result.
  4. Ahmed, Ganhotra, Shinnar, Hirzel, "Investigating Test Overfitting on SWE-bench," 2025. Under joint code-and-test refinement, the share of "resolved" issues that failed the hidden reference tests rose from 21.8% to 25.5% for one model and from 33.0% to 35.9% for another.
  5. Khullar, Hopkins, Wang, Roger, "Self-Attribution Bias: When AI Monitors Go Easy on Themselves," 2026. 10 models. On code correctness, the separation score fell from 0.99 when judging others’ patches to 0.89 when judging its own in the same turn. Explicit "you wrote this" labels had little effect. In one computer-use setting the self-judging monitor was five times more likely to approve an injected patch.
  6. Becker et al., "Just-in-Time Catching Test Generation at Meta," 2026. Reported by the system’s developers. 22,126 generated tests, 231 failed on the change under review, 41 shown to engineers after filtering, 8 confirmed bugs, 4 judged severe by the authors.