Why AI Agents Report False Success
Why AI agents report false success: the bias that makes them grade their own work, the $1.78M bug it shipped, and the independent check that catches it.
>This builds the independent check. Agentic Coding: Build the Harness adds the recovery layer that rolls back the bad work and the eval layer that proves each fix.

Agentic Coding: Build the Harness
The Loop, Guardrails, and Verification That Make Your AI Coding Agent Reliable on Real Code, Not Just Demos
Summary:
- AI agents report false success because they grade their own work, and the grader is biased toward itself.
- A passing test is not a correct program: a scale bug can pass the test the agent wrote for it.
- The fix is an independent check the agent never saw, plus rolling back when it fails.
- One real bug like this drained roughly $1.78 million while every check stayed green.
Here is why AI agents report false success, and why it is scarier than an agent that crashes. An agent will write the code, write the test, run the test, watch it pass, and report “done,” all in thirty seconds, and every step of that looks like diligence. It is an optimist grading its own paper, fast. The crash you can see. The confident, wrong “done” sails right past you with a green checkmark on top.
The claim: the agent’s “done” is untrustworthy by construction
The reason is not that the model is dumb. It is that the model is biased toward itself in two specific ways.
The first is self-preferential bias. When you ask a model to evaluate work and the work is its own, it rates it higher than it should. It reads what it meant, not what is there. A judge that wrote the thing it is judging is not a judge.
The second is agentic laziness: the model declares a complex task done after only partial progress. An agent asked to handle a fifty-item review will address thirty-five of them and then announce it is finished, because the momentum of “I have been working and making progress” slides into “this is done.” Thirty-five of fifty looks like done from the inside.
Put those together and you get an agent that does most of the work, rates its own output generously, and reports success with total sincerity. “Be thorough, double-check your work” helps a little and fails the same way, because you are asking the biased judge to be less biased about itself, which is not how bias works. The fix is structural, not a better prompt.
The evidence: $1.78 million through green checks
This is not theoretical. A lending protocol shipped AI-assisted code that passed its tests and a professional security audit, and a single scale error drained real money.
| Fact | Value (from reporting) |
|---|---|
| Loss | ~$1.78 million in bad debt ($1,779,044 across markets) |
| Cause | A misconfigured cbETH price oracle: it used the raw cbETH/ETH rate instead of multiplying by the ETH/USD price |
| Mispricing | The oracle reported cbETH at ~$1.12 instead of its real ~$2,200 (a 99.9% discount) |
| Exploitation | Liquidators repaid roughly $1 of debt to seize 1,096.317 cbETH |
Instead of multiplying the cbETH/ETH feed by the ETH/USD price, the system used only the raw cbETH/ETH exchange rate. As a result, the oracle reported cbETH at around $1.12, and liquidators were able to repay roughly $1 of debt to seize a total of 1,096.317 cbETH. — Decrypt, reporting the post-mortem
The governance change behind it was co-authored by a frontier model. By every signal a normal team trusts, the code was fine: unit tests, integration tests, an audit. And it shipped a number off by a factor of about two thousand. The checks all passed because none of them was looking at the thing that was broken.
The data: a self-test that passes the bug
Watch it happen on a tiny function. apply_discount(price, pct) should take a percentage, so pct=20 means twenty percent off. The correct code is price * (1 - pct / 100). Moving fast, the model drops the / 100 and writes price * (1 - pct). Now a $100 order at 50 off returns negative 4,900 instead of 50.
Here is the trap. The agent writes its own test, and the test it writes passes against both the correct and the broken code:
correct = lambda price, pct: price * (1 - pct / 100) # right
buggy = lambda price, pct: price * (1 - pct) # dropped / 100
def agent_self_test(fn):
return fn(100, 0) == 100 # "0% off is full price"
def independent_check(fn):
return fn(100, 50) == 50 # 50% off a $100 order is $50
for label, fn in [("correct", correct), ("buggy", buggy)]:
print(f"{label:8} self-test={agent_self_test(fn)} independent={independent_check(fn)}")
correct self-test=True independent=True
buggy self-test=True independent=False
Read the buggy row. The agent’s self-test says True. The independent check says False, with Expected 50, got -4900. The self-test is happy with the broken code because it never tries a real discount, so the scale bug sits right behind a green checkmark. The agent cannot catch this, because it wrote both the bug and the test that misses it, with the same blind spot in both.

The fix: a check the agent does not control
Verification has two layers, and the second is the one that matters. The self-check (the agent reviews its own work) catches careless mistakes and is not a verdict. The external check compares the work against reality instead of against the author’s intentions: run the tests yourself, run a known-correct input the agent never saw, gate “done” behind it.
Not all checks are equal. Reach for them in this order:
| Feedback type | Example | Reliability | Latency |
|---|---|---|---|
| Rules-based | ”must return 50 for apply_discount(100, 50); got -4900” | highest, mechanical | none |
| Visual | render the page, diff against the expected screenshot | high | low |
| Model-as-judge | a second model reads the diff and votes pass/fail | lowest, a second optimist | high |
A hard rule beats a soft opinion. Then wrap the loop so “done” has to survive the check, and roll back when it does not:
def run_verified(task, checks, max_attempts=3):
run("git add -A && git commit -q -m 'snapshot' || true") # a clean point
for attempt in range(1, max_attempts + 1):
run_loop(task) # agent works and claims done
ok, report = external_check(checks) # you check, independently
if ok:
return True
run("git reset --hard -q HEAD") # recover: throw away the bad work
task += f"\n\nAn INDEPENDENT check failed:\n{report}\nFix the real bug."
return False # gave up, nothing merged
When it cannot prove the work is right, it leaves your repo exactly as clean as it found it. The note matters too, because a cornered model will try to make the check pass by editing the check. Tell it: fix the bug, do not change the test to match the wrong output.
You don’t need a full answer key
The obvious objection: I cannot write a correct answer for every task. You almost never need one. You need a check that touches the part most likely to be wrong. Test a property instead of an exact value:
def price_is_sane(reported, reference, max_ratio=2.0):
lo, hi = reference / max_ratio, reference * max_ratio
return lo <= reported <= hi
print(price_is_sane(2_200, 2_300)) # True: cbETH near ETH
print(price_is_sane(1.12, 2_300)) # False: caught
Nobody needed to know the exact price. They needed to check that it was not absurd, and no check was looking. The bugs that actually ship tend to be the big, dumb, obvious-in-hindsight ones, exactly what a crude independent check trips over.
One honest limit: verification catches what your checks look for and nothing else. An external check is a flashlight, not the sun. So spend your checks by risk: a string format does not need a fortress, but anything that moves money, deletes data, or ships to production gets every check you can think of and a human on top. The day you believe your verification is complete is the day you stop looking.
What should you actually do?
- If your agent says “tests pass, done” → run the tests yourself before you believe it. The agent reporting green and the tests being green are two different facts.
- If you can write one known-correct input → make it an acceptance check the agent never sees, and gate “done” behind it. One real case catches most shipping bugs.
- If you can’t write an exact answer → test a property that must always hold (in bounds, non-negative, within a sane ratio). Cheap, and it catches the orders-of-magnitude bugs.
- If the work moves money or data → do not let the agent grade itself at all. External check, then a human, then ship. The Moonwell oracle deserved a sanity check and a person staring at the number.
The bottom line
- A passing test is not a correct program. The agent wrote the test with the same blind spot it wrote the bug.
- The thing that decides whether work is done must not be the thing that did the work. Build the check outside the agent.
- Verify by risk, not evenly, and never believe your checks cover everything. The expensive bug is always in the dark you forgot to point the flashlight at.
Frequently Asked Questions
Why do AI agents report false success?+
Because the agent grades its own work, and it is biased toward itself in two ways: self-preferential bias (it rates its own output too highly) and agentic laziness (it declares a multi-part task done after partial progress). A better prompt does not fix a bias, structurally.
If the tests pass, isn't the code correct?+
No. A passing test is not a correct program. The agent writes the code and the test with the same blind spot, so a scale bug can pass its own test and still be wrong. An independent check the agent never wrote is what catches it.
How do you verify an AI agent's work?+
With a check the agent did not design and cannot fudge: run the tests yourself, run a known-correct input the agent never saw, or test a property that must always hold. Rules-based checks beat a second model's opinion.