The Measurement That Lied
I set out to measure which model reviews code best, and the measurement was the thing that turned out to be broken.
The plan was clean. I have a local eval suite, agent_eval, a set of fixtures with planted bugs and a scorer
that checks whether a reviewer found them, and I wanted a real answer to a real question: for the work of
finding security defects in a diff, does it matter which model I point at the job? I had baselines on file. A
prior session had recorded that Opus caught 9 of 9 on one slice, that Haiku caught 7 of 9, that Sonnet landed
somewhere in between. The numbers were sitting in the index, dated and labeled, exactly the kind of artifact
this whole book argues you should build. I was going to add a Fable column, line the four models up, and
finally retire the question with data instead of vibes.
The first thing I did was the thing that unmade the whole exercise. Before trusting a single recorded number,
I went to verify its provenance, not what the report said the model was, but what the transcript proved the
model was. And the controls failed the check immediately. I dispatched a reviewer with model: "opus". The
transcript said it ran Sonnet. I dispatched one with model: "haiku". The transcript said Sonnet. I dispatched
model: "fable" as the new column I actually cared about. Sonnet again. Three different labels on the request,
one model doing the work, and the only reason I knew was that I had refused to take the label at face value.
The recorded baselines, "Opus 9/9," "Haiku 7/9," were never Opus or Haiku. They had never been. And once I understood why, the scale of it landed like a dropped plate.
One line, two months, every measurement
The cause was eleven words in a file I had written and forgotten:
``
CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-4-6
``
It lived in the env block of ~/.claude/settings.json, the same settings file that holds my model
defaults, my timeouts, my hook configuration. At some point I had added it, almost certainly for a good
local reason, almost certainly meaning to remove it after. It does exactly what it says: it pins every
subagent dispatched through the Agent tool to a fixed model, overriding whatever the dispatch requests. With
that variable set, model: "opus" is a suggestion the harness politely ignores. Every subagent, every one,
regardless of the label on the call, ran claude-sonnet-4-6.
It had been doing this since 2026-04-16. I found it on 2026-06-12. Roughly two months, during which I
had run model comparisons, recorded baselines, and reasoned about the results; and every one of those
comparisons had been Sonnet against Sonnet against Sonnet, wearing whatever costume the dispatch asked for. I
went back and transcript-audited the prior session that produced the recorded baselines: all 28 of its
Agent-tool dispatches ran claude-sonnet-4-6, no matter what model they had requested. The pin exempted
nothing. It did not occasionally leak the real model through. It was total, and it was silent, and it had been
feeding me numbers the entire time.
Here is the part that still unsettles me, because it is worse than noise. If the contamination had made the data random, garbage in the cells, obvious nonsense, I would have caught it in a day. Bad data announces itself. This data was plausible. The headline result of the contaminated era was "Sonnet performs about as well as Opus on code review," and that result is, in a narrow sense, perfectly true: the two columns scored nearly identically because the two columns were the same model. The finding was true and utterly vacuous. It was a mirror reporting that the thing in front of it looked exactly like itself. A measurement that lies by producing implausible numbers is a nuisance. A measurement that lies by producing reasonable numbers is a trap, because it survives every sniff test you have, right up until it costs you a decision.
That is the precise failure this chapter is named for. Not the model misbehaving; the model was fine, the model was Sonnet and Sonnet is good at this. The apparatus misbehaving. The instrument I built to measure the agents was itself an unaudited control, and it had drifted into lying months before I thought to point my suspicion at it.
Self-identification is theater
The obvious objection is: surely you could just ask the agent what model it is? Put a line at the top of every reviewer's instructions, begin your report by stating your model ID, and read it back. I had done exactly that. It's worth saying plainly how badly it works, because it's the reflex everyone reaches for and it is worthless.
Across eighteen reviewer dispatches in this session, nine through the Agent tool, nine headless, I asked every single one to self-report its model. Exactly one of the eighteen did it. One. The agents with strict output formats, the reviewers that return structured findings, which is to say the useful ones, swallowed the self-ID line entirely; it did not fit their schema, so it never appeared. The lone reviewer that did emit its ID happened to be accurate, which is the cruelest part: a 1-in-18 compliance rate that is correct when it fires is more dangerous than one that is wrong, because it tempts you to trust the channel.
Self-identification cannot be the provenance mechanism, and the reason is structural, not fixable with better prompting. You're asking the component under test to certify its own identity, through the same fallible output path whose reliability is the thing in question. The agent that is misconfigured is exactly the agent whose self-report you cannot trust, because the misconfiguration is upstream of the report. It's a witness vouching for itself.
The provenance has to come from outside the agent's output, from the harness's own accounting of what it
ran. In Claude Code that channel is modelUsage: the result event of a headless run records, as ground truth
the agent never touches, which model actually consumed the tokens. For Agent-tool dispatches the equivalent is
the transcript file on disk, agent-<id>.jsonl, with .message.model on every turn. Those are the
instruments that don't lie, because the agent isn't the one writing them. Every number that survived this
session was re-grounded against modelUsage or the transcript. None of them rest on what an agent said it was.
This is Chapter 3's discipline turned from the guard onto the gauge: you do not ask the thing to certify
itself. You read the record that was written about it by something it cannot edit.
The clean re-run, and the ceiling nobody wanted
The operator removed the pin mid-session. (~/.claude is a protected target, so the change was staged to a
review file, staging/remove-subagent-model-pin.md, and applied by hand, with the prior settings backed up
to settings.json.bak-pin first. I have since confirmed the variable is gone from the live env block as of
2026-06-12; the pin is not still running.) With it gone, I re-ran the matrix the honest way, through a
headless driver, run_headless_baseline.py, that never depended on the Agent-tool path and dispatches each
model directly, with modelUsage verifying every single run. Nine cases, twenty-one planted bugs, every cell
provenance-checked. Here's what the uncontaminated data looks like:
| Model | Recall | False positives |
|---|---|---|
| Fable | 21 / 21 | 0 |
| Opus 4.8 | 20 / 21 | 0 |
| Haiku 4.5 | 20 / 21 | 0 |
| Sonnet 4.6 | 19 / 21 | 0 |
Read that table, because the headline is not in the order. The headline is the spread. From Haiku to Fable, the entire range is two planted bugs out of twenty-one, with zero false positives at every tier. The review ceiling is saturated. On this task, finding planted defects in a diff and not crying wolf, the cheapest model in the lineup and the most expensive one are separated by a margin so thin it disappears under inspection.
Because I did inspect it. Every recall difference in that table is a reporting-style artifact, not a
detection difference. The most telling example: one planted bug was a curl -fsSL … | bash install pattern,
and the scorer was keyed to the exact string curl -fsSL. The Fable-lane run described the attack as
curl | bash. Haiku and Sonnet described the structural vulnerability correctly but never typed the literal
flags the scorer was hunting for. Every model saw the bug. The scorer, matching on a brittle substring,
recorded a miss for the ones that paraphrased. The two-bug "gap" between Fable and Sonnet is not a gap in
security reasoning. It is a gap between which models happened to phrase their findings in the vocabulary my
scorer was grepping for, which is a fact about my scorer, not about the models.
So the eval, having just caught itself lying through a config pin, immediately revealed a second way it had been misleading me: a scorer that rewards the spelling of a finding over the substance of it. That's two distinct apparatus failures surfaced in one session, and neither was about the agents. Both were about the thing measuring the agents.
What it cost to actually know
There's a number I want to put next to all of this, because it reframes the whole exercise: the clean, provenance-verified headless re-baseline, the nine runs that produced the honest table above, cost about $2.10.
Two dollars and ten cents. That's the entire price of replacing two months of confident, fabricated, plausible data with two dollars of data I can actually defend. I want that number to sting a little, in both directions. It should sting because of how cheap the truth was relative to how long I went without it, the contamination didn't persist because verification was expensive, it persisted because I never spent the two dollars to check. And it should sting because of how cheap it makes the alternative look in hindsight: I had been making routing decisions, forming opinions about model quality, carrying "Sonnet is as good as Opus at review" around in my head as a learned fact, and the cost of grounding that belief was less than a cup of coffee the whole time.
The lesson isn't "evals are expensive so be careful." It's the opposite. Evals are cheap, and provenance verification is cheaper, and the only thing standing between you and the truth was the discipline to not trust a labeled number until you'd checked who actually produced it.
Why agent evals are not model evals
This is the chapter's real argument, and the contamination is just the most vivid way I know to make it.
When a lab evaluates a model, the model is the system under test and the harness is assumed correct. That assumption is mostly fine for them, because they built the harness to be the stable, trusted part. But when you evaluate an agent, a model wrapped in your instructions, your tools, your routing config, your scorer, your fixtures, the thing under test isn't the model. It's the whole apparatus, including the parts you built to do the measuring. The model is one component in a stack of components you assembled, and every other component in that stack is an artifact that can drift, go stale, or lie, exactly like the guard in Chapter 3 and the dossier in Chapter 4.
The config pin was part of the system under test and I had mistaken it for part of the trusted substrate. The brittle scorer was part of the system under test and I had mistaken it for the neutral judge. The self-identification line was part of the system under test and I had mistaken it for ground truth. Three times in one session, the thing I was relying on to tell me the truth about the agents was itself an unaudited agent-environment artifact, and three times it was the thing that was actually wrong.
So the discipline that comes out of this chapter is narrow and absolute: a model-comparison number is worthless until its provenance is verified from a channel the agent cannot edit, and a scorer's verdict is a lead until you've read the transcript to see whether a "miss" was a real miss or a vocabulary mismatch. The measurement is not the end of the inquiry. The measurement is the next thing to be measured. You audit the gauge with the same suspicion you brought to the agent, because the gauge is just another artifact you built, and you already know, from every other chapter in this book, how the artifacts you build behave when you stop watching them.
The first time the agent lied to me, in Chapter 1, it did so by changing a test to assert broken behavior and truthfully reporting that the assertion passed. The measurement in this chapter lied the same way, one level up: it truthfully reported the score of a model that was not the model I thought I was scoring. Both were accurate. Both were wrong. And the only thing that ever tells accurate from correct is the verification you run on your own instruments, after you've stopped trusting them, with two dollars and the willingness to look.
The short version → Auditing the Auditor