The most dangerous output an AI coding agent produces isn't the code that breaks. It's the code that builds, passes review, and is quietly wrong.
Broken code announces itself: the compiler complains, the pipeline goes red, someone gets paged. Hallucinated code does the opposite. It arrives fluent, well commented, correctly indented, and written in your codebase's idiom. It looks like the work of a competent engineer having a good week. That's exactly why it slips past.
If your team has moved from AI autocomplete to AI agents that open pull requests, this has already stopped being a research topic and become a quality engineering problem. Here's how to treat it like one.
What hallucination actually looks like in a repository
"Hallucination" is a vague word borrowed from a different context. In a codebase it shows up as a handful of recognisable failure shapes. Naming them is the first useful step — you can't build a gate against a vibe.
Phantom symbols. A method, class, or overload that doesn't exist in the library version you actually depend on. Often it existed two major versions ago, or exists in a rival library with a similar name.
Package hallucination. The agent imports a package that was never published. Attackers have noticed this pattern and now register the invented names — a hallucinated import becomes a supply-chain attack vector, not just a build failure.
Fabricated configuration. Environment variables, feature-flag keys, Helm values, schema fields that read plausibly and resolve to nothing at runtime. These sail through unit tests and fail only in the one environment where the value matters.
Green theatre in tests. The agent writes tests that mock the thing under test, assert on the mock, and report full coverage. Coverage goes up; assertion strength goes to zero. This is the most under-reported category, because it moves the metric leadership actually watches.
Confabulated requirements. You asked for a change. The agent inferred three acceptance criteria you never wrote, implemented all of them, and described them in the pull request as if they'd been in the ticket all along. Scope drift, delivered with confidence.
Invented evidence. Comments, ADRs, and PR descriptions that cite a benchmark, an RFC section, or an internal decision that doesn't exist. Documentation hallucination is worse than code hallucination, because nothing ever executes it to prove it wrong.
Why normal code review doesn't catch it
Review was built to catch human error, and human error has a texture. A tired engineer writes messy code with obvious gaps. An agent writes clean code with invisible ones.
Three things work against the reviewer. Fluency reads as competence, so the reviewer's guard drops. Volume goes up — the agent produces in an hour what used to take a day — so attention per line collapses. And the reviewer is implicitly asking "would I have written something like this" instead of "does everything referenced here actually exist and behave as claimed." Those are different questions, and only the second one catches hallucination.
The fix isn't reading more carefully. It's moving the check from human judgement to executable evidence.
The verification ladder
Five layers, cheapest first. Each answers a different question, and skipping one means trusting the agent to have gotten that question right on its own.
Layer 0: ground the agent before it writes. Prevention beats detection. Give the agent the real repository map, the real dependency manifest, and the real interface definitions — not the agent's memory of the open-source world. Constrain dependencies to an approved allowlist. Where the agent must reference an external API, make it retrieve the current spec rather than recall it. Most phantom-symbol hallucination is a context problem, not an intelligence problem.
Layer 1: does it parse and resolve. Compiler, type checker, strict linter, dead-import detection. This layer is weak by default in dynamically typed stacks — which is exactly why teams shipping Python and JavaScript with agents see more hallucination reach production than teams shipping Java and Go. If your language won't do this for you, buy the strictness back with type annotations and static analysis.
Layer 2: does every referenced thing exist. Most teams are missing this one. Add a pipeline gate that fails when a new import doesn't resolve to a package in the lockfile, when a newly added dependency was published recently or has near-zero downloads, or when a new configuration key isn't in the schema for the target environment. None of this is sophisticated — it's a script — and it catches the highest-severity category you have.
Layer 3: does it do what it claims. Mutation testing is the honest measure of AI-generated tests. If the agent's new suite kills a low share of injected mutants, the tests are decoration. Track assertion density alongside coverage, and treat a coverage rise with flat assertion strength as a regression, not an improvement. Require new mocks of internal services to come with a matching contract test.
Layer 4: is it right beyond the examples it was given. Property-based testing, differential testing against the previous implementation, replay against recorded production traffic. This is where you catch the subtle case: code that's real, executes, passes the tests it was given, and encodes a wrong rule. For a refactor, differential testing is close to a complete answer, because the old implementation is the oracle.
Layer 5: is it what we actually asked for. The one layer that stays human. Trace each changed file back to an acceptance criterion. Anything that traces to nothing is either dead work or invented scope — and both deserve a conversation. Keep this layer rare and expensive by making layers 0 through 4 automatic.
What to add this week
If you want a starting point rather than a programme, these four give the best return for the effort:
- A dependency existence and reputation gate in CI. Fail the build on any import that isn't in the lockfile, and flag packages under a minimum age or download threshold. An afternoon of work against a whole class of supply-chain risk.
- Mutation score as a merge condition on AI-authored test files. Only on the diff, not the whole repo — that keeps it cheap. It ends green theatre immediately.
- A PR template field that names the requirement. Not a link to a ticket — the actual acceptance criterion, written out. Confabulated scope can't survive being written down next to the original ask.
- A verifier pass by a second agent with a different job. Give it the diff, the requirement, and read-only access to the repo, and ask one question: list every symbol, package, config key, and claim in this change you can't confirm exists. Framing matters here — an agent asked to review will praise; an agent asked to enumerate unverifiable claims will enumerate them.
Measure it, or you're guessing
Three metrics tell you whether any of this is working.
Hallucination escape rate. Of the defects found after merge on agent-authored changes, what share trace back to a non-existent or misrepresented dependency, symbol, or requirement. This is your escaped-defect rate, sliced by cause.
Verification cost per accepted change. Human minutes spent per merged agent PR. If this isn't falling as your gates mature, you've automated the writing and left the checking manual — that's not a productivity gain, it's a transfer of work from authors to reviewers.
Trust by surface. Agent acceptance rates vary enormously by area of the codebase. Well-typed, well-tested, heavily documented modules produce reliable agent output. Legacy areas with sparse tests produce confident fiction. Knowing which is which lets you set the autonomy level per surface instead of arguing about it per team.
The old question, in a new costume
None of this is a new discipline. Quality engineering has always asked one question: what evidence do we have that this behaves as claimed. For twenty years, the answer involved a human author who could be asked to explain themselves. You can't ask the agent — it will answer fluently either way, and the fluency carries no information about whether it's true.
So the evidence has to become mechanical: existence checks, mutation scores, differential runs, traceability back to a real requirement. Not because agents are bad, but because they're good enough that our usual detector — the feeling that something looks off — has stopped firing.
Ship faster with agents. Just stop taking their word for it.