The message looked perfect: "Implemented the fix. All tests pass. ✅"

I almost merged it. Then I opened a new session and asked a different agent to check the work line by line. Two of the five requirements were missing. One test had been skipped, not passed.

The agent wasn't lying. It was grading its own homework.

Why agents over-report "done"

An agent that just wrote the code is soaked in its own context. It remembers what it meant to do and reads that intent back into the diff. It's the same reason we can't proofread our own writing well.

Same session reviews Fresh session reviews
Sees What it meant to do, and its own reasoning Only the task and the diff
Knows "I wrote this" Yes No
Has a checklist No PASS/FAIL checklist
Ends up reading The intent What's really there

This is mostly my own experience. While reading around the topic, I found research pointing in a similar direction, though it's more nuanced than I first thought:

  • Self-correcting without outside feedback is still weak. A Google DeepMind paper (ICLR 2024) found that LLMs struggle to fix their own reasoning without external feedback, and sometimes get worse after trying. A 2025 follow-up (ACL 2025) tested nine recent models, including reasoning models, on tasks that include programming, and still saw self-correction fail often. The exception: models specifically trained to self-correct with reinforcement learning do get better.
  • Whether models favour their own work is still debated. A NeurIPS 2024 paper found that LLM judges score their own outputs higher than humans would. Later studies push back: some of that preference reflects real quality differences, and a 2026 study found it largely disappears once quality is controlled. But the same study found that just labelling text as "yours" or "someone else's" can shift the scores either way.

My takeaway: I don't need to win the argument about bias. I just remove the authorship context. A fresh session does exactly that.

I'm still learning here, so I treat these as early signals, not final proof. If you know better research on this, I'd love to read it.

So I stopped asking the builder whether it was finished.

My review workflow

flowchart TD
    A["Writer agent says done"] --> B["1. Run real checks<br/>tests · build · lint"]
    B -->|pass| C["2. Fresh-session reviewer<br/>diff + checklist only"]
    C -->|pass| D["3. I spot-check the evidence<br/>open the cited file:line"]
    D -->|pass| E["Merge the small PR"]
    B -->|fail| A
    C -->|fail| A
    D -->|fail| A

When an agent says "done", the work goes through three gates. Any failure goes straight back to the writer.

1. Run real checks first

Tests, build, lint, type-check. Anything that returns a plain pass or fail.

This comes first because it's the only gate that isn't another opinion. If a requirement can be turned into a test, turn it into a test.

But tests aren't the whole truth either. Research on coding agents in 2026 keeps finding reward hacking: the agent makes the measured thing pass without doing the real work, for example by hard-coding the visible test cases or editing the tests instead of the code. One ICLR 2026 study of agents that repeatedly optimise code found that in 73.8% of KernelBench runs and 46.8% of ALE-Bench runs, the measured score went up while the real task did not improve.

A green test suite tells you the tests passed. It doesn't tell you the tests are still the same tests. That's what the next gate is for.

2. A fresh-session reviewer

I open a new session with no memory of how the code was written. It gets only two things:

  • the task description
  • the diff

Then I give it a PASS/FAIL checklist and one hard rule: every PASS must cite file:line evidence. No evidence means FAIL.

The evidence rule does most of the work. It turns "looks good" into something I can verify in seconds.

I also always ask: were any tests edited, skipped or weakened? Interestingly, one 2026 benchmark (EvilGenie) found that LLM judges were better than held-out tests at spotting this kind of cheating. So tests and the reviewer aren't competing. Each covers the other's blind spot.

Tests Fresh reviewer
Catches Broken behaviour · regressions · build and type errors Tests edited or skipped · hard-coded test cases · missing requirements · out-of-scope changes
Blind spot Can be gamed or quietly edited Still an LLM

One warning. A reviewer told to find problems will usually find something, even when the work is fine. If you chase every comment, you end up with extra abstractions, defensive code and tests for cases that can't happen. So I tell the reviewer to report only gaps that affect correctness or the stated requirements. Style is optional.

3. I spot-check the evidence

The reviewer is also an LLM. It can cite the wrong line or misread a function. So I open a few of the cited file:line references myself, especially for anything risky.

For code that touches money, security or user data, I don't spot-check. I read every line.

What a checklist looks like

For a change that retries a failed network call:

  • Retries only on errors that are safe to retry? (file:line)
  • Never retries when the request may already have succeeded? (file:line)
  • Every new test actually ran, with none skipped? (test output)
  • Were any existing tests edited, deleted or weakened? If so, why? (file:line)
  • Failure paths are logged? (file:line)
  • Nothing outside the task's scope changed?

Rules I follow with every agent

  • One task per prompt. Bigger prompts produce bigger, vaguer "done"s.
  • Small PRs. A reviewer, human or AI, can only check what fits in its head.
  • Stop before architecture changes. The agent asks me first, then reports back.
  • Evidence over claims. Test output, the command it ran, or a file:line. Never just "it works".

The mindset shift

I used to treat a coding agent like a senior developer. Now I treat it like a fast, talented junior teammate. It does great work, but the review is still my job.

My role moved from writing code to defining "done" and proving it.

Steal my review prompt

You are reviewing work you did not write.

Task:
<paste the task>

Diff:
<paste the diff>

For each item below, answer PASS or FAIL.
- Every PASS must cite file:line evidence (or test output).
- If you cannot find evidence, answer FAIL.
- Only report gaps that affect correctness or the stated
  requirements. Mark anything else as "optional".
- Do not fix anything. Only report.

Checklist:
1. <requirement>
2. <requirement>
3. ...

Then:
- List any tests that were edited, deleted, skipped or weakened,
  and whether the task justified it.
- List anything that changed which the task did not ask for.

Run your tests first, then try this on the next "done ✅" you get. You might be surprised.


This is part 1 of my Agentic Engineering series: notes from building software with AI coding agents. Next up: how to cut a task down to agent size.

Further reading