Module 18 · Part IV · Expert

Benchmark pitfalls: how scores lie, and how to read them

Every benchmark number is a measurement taken with an imperfect instrument, on a test that may have leaked, by a system that may have learned to game it, reported by someone who chose which number to show you. This module teaches you to see all of that at a glance.

Expert~80 minPrerequisites: Module 04, Module 07

By the end of this module you can

  • Describe the life cycle of a benchmark (release, hill-climbing, saturation, retirement) and say where a given benchmark sits today.
  • Explain contamination, detect it with canaries, n-gram overlap, membership tests and time windows, and recognize it in the 2026 SWE-bench Verified audit.
  • Name the main ways a score becomes misleading: label noise, Goodhart effects, leaderboard gaming, harness differences, reward hacking, grader bugs, flaky environments and weak construct validity.
  • Explain why sandbox resources (infrastructure noise) and automated optimizers (DSPy, GEPA, RL on your eval) make scores less trustworthy, and what to pin or hold out.
  • Show with a simulation why a 2-point gap on 100 items is usually noise, and compute a paired bootstrap interval.
  • Review your own agentic benchmark with the Agentic Benchmark Checklist (task validity, outcome validity, reporting).
  • Interrogate any benchmark claim with a 15-question checklist, and design a benchmark that stays useful for years.

Why this module exists

By now you know how to build evals (Module 12), grade them (Module 03, Module 13) and put error bars on them (Module 04). This module is the other side of the coin: how to distrust a number intelligently. A model card says "80.2% on SWE-bench Verified". A startup says "state of the art on WebVoyager". A leaderboard shows one model two points above another. Should you believe any of it, and what exactly would you be believing?

An expert does not answer "yes" or "no". An expert asks a series of pointed questions: which version of the test, which harness, how many attempts, how were the answers graded, could the model have seen the test, how big is the error bar, and does this test even measure the thing I care about? By the end of this page you will have those questions as a reflex.

Analogy: the school exam that everyone has seen

Imagine a national maths exam that uses the same paper every year. In year one it separates strong and weak students beautifully. By year five, past papers circulate, tutors drill the exact questions, a few answers in the official marking scheme turn out to be wrong, and some schools quietly let students sit it several times and only report their best attempt. The exam still produces numbers. They just no longer mean "good at maths". Almost every pitfall in this module is a version of that story.

The benchmark life cycle

A benchmark is a public, standardized eval that many people run so their results can be compared. Benchmarks are not permanent. They age in a remarkably consistent way, and knowing the stage tells you how much a score on it can mean.

1 · Release scores low, lots of headroom human baseline recorded test set fresh, unseen 2 · Hill-climbing labs report it in model cards scaffolds tuned to it scores rise fast 3 · Saturation top scores near the ceiling gaps smaller than noise label errors now dominate 4 · Retirement labs stop reporting it still fine as a regression check adopted optimized stops separating contamination risk grows every month the test set is public successor released MMLU → MMLU-Pro · SWE-bench Verified → SWE-bench Pro · ARC-AGI-1 → 2 → 3
Four stages, then a successor. A benchmark is most informative at release and during early hill-climbing. Near the ceiling, the remaining errors are increasingly the benchmark's own mistakes, so differences stop reflecting capability.

Stage by stage

  • Release. The authors publish tasks, a grader, a baseline and usually a human score. At release, agent benchmarks typically show a huge gap: on OSWorld the best model scored 12.24% against a human 72.36%; on WebArena GPT-4 scored 14.4% against humans at 78.2%; on GAIA GPT-4 with plugins got about 15% against humans at 92%. That gap is what makes a benchmark useful.
  • Hill-climbing. "Hill-climbing" means repeatedly making small changes and keeping those that raise the score, like walking uphill in fog. Labs add the benchmark to model cards, researchers tune prompts and scaffolds against it, and scores climb quickly. This is also when the test set spreads around the internet.
  • Saturation. A benchmark is saturated when top systems score so close to the ceiling that differences are within noise, or when the remaining failures are mostly flawed items. The research notes list HumanEval, MBPP, MMLU, GSM8K, GPQA Diamond, ScreenSpot, GenEval and SWE-bench Verified as saturated or near it.
  • Retirement. The field moves on. OpenAI stopped reporting SWE-bench Verified in February 2026 and recommends SWE-bench Pro. MMLU gave way to MMLU-Pro and Humanity's Last Exam. A retired benchmark is still a perfectly good regression test ("did we break something basic?"); it is just a bad capability test.
best score, % of ceiling years since the benchmark was released → 0 1 2 3 4 5 6 0 100 human baseline older generation: years to catch humans next generation: a couple of years recent generation: months
Time to saturation keeps shrinking. Illustrative shape only, not real data. The pattern (each generation of benchmark reaches human-level scores faster) is the motivation given by Kiela et al. in the Dynabench paper and is tracked by the Stanford AI Index; consult those for measured curves.
BenchmarkReleasedBest AI at releaseHuman baselineStatus by 2026 (per research notes)
OSWorldApr 202412.24%72.36%Re-issued as OSWorld-Verified (Jul 2025); vendor scores reportedly 60-70%+ (unverified)
WebArenaJul 202314.4% (GPT-4)78.2%Audited variants (WebArena-Verified); top reported ~60-70% (unverified)
GAIANov 2023~15% (GPT-4 + plugins)92%Validation answers public; high leaderboard scores
AndroidWorldMay 202430.6% (M3A)80.0%Top agents reportedly 75-90% (unverified)
SWE-bench VerifiedAug 2024n/an/aRetired by OpenAI, Feb 2026 (flawed tests, contamination)
ARC-AGI-3Mar 20260.51%100%Fresh: interactive, designed to resist the above
Key idea

Before reading any score, place the benchmark on the life cycle. On a fresh benchmark a 5-point gain is big news. On a saturated one, a 1-point gain is more likely to reflect label noise, contamination or selective reporting than real capability.

Contamination: training on the test

Contamination (also called data leakage or test-set leakage) means that some or all of a benchmark's test items, or their answers, ended up in the model's training data. A contaminated model can score well by remembering rather than solving, exactly like a student who memorized last year's answer sheet.

It is rarely deliberate. Frontier models are trained on enormous web crawls, and benchmarks live on the web: the dataset is on GitHub and Hugging Face, the solutions to coding tasks are real commits in public repositories, blog posts quote examples, and forum threads discuss answers. For SWE-bench this is structural: every task is a real GitHub issue whose fix (the "gold patch") was merged into a public repository years ago.

Benchmark test items + answers / gold patches GitHub / Hugging Face copies merged fix commits papers, blogs, tutorials forums discussing answers Training data web crawl, code, synthetic + RL data crawled Model recalls the answer, score inflated trains Detection toolbox Canary string unique GUID in the files; filter it out, or check if the model can recite it N-gram overlap search the training corpus for long word sequences from each test item Membership tests suspiciously low perplexity, verbatim completion from a prefix or just a task ID Fresh or rephrased only items after the model's cutoff (LiveCodeBench), or rewritten variants
How test sets leak, and how to catch it. Leakage paths are mostly accidental. The first two detectors need access to training data (so only the lab can run them); the last two work from outside.

The four detection methods

Canary strings

A canary string is a long random identifier (a GUID) printed inside every file of a benchmark, with a note asking that it not be used for training. BIG-bench popularised this. Two uses: (1) data teams filter out any document containing the string before training; (2) evaluators ask the model to complete the canary. If it can, the benchmark files were in training data. Weakness: copies that strip the header (someone pastes just the questions into a blog) slip through.

N-gram overlap

An n-gram is a sequence of n consecutive words. If a 13-word sequence from a test item appears in the training corpus, the item was probably seen. GPT-3's paper used 13-gram overlap for this. It needs the training data, it misses paraphrases and translations, and it can flag innocent overlaps (boilerplate, famous quotes). Code for it is below.

Perplexity and membership tests

Perplexity measures how surprised a model is by a text (lower means more expected). Memorized text has unusually low perplexity compared with fresh text of the same style. Related membership inference probes give the model a prefix, or even just the task ID, and see if it reproduces the rest verbatim. These work from outside the lab but give probabilistic evidence, not proof.

Time windows and rephrasing

LiveCodeBench continuously collects new contest problems from LeetCode, AtCoder and Codeforces, stamps each with its publication date, and scores each model only on problems published after its training cutoff. SWE-rebench and SWE-bench-Live do the same with fresh GitHub issues. Rephrased or perturbed variants (new numbers, renamed variables) test whether performance survives when memorized surface forms are gone.

Jan 2025Jul 2025Jan 2026Jul 2026 Model A cutoff Model B cutoff possibly seen: excluded Model A scored on these 9 Model B scored on these 4
Contamination-aware time windows. Each dot is a problem with its publication date (illustrative dates). A model is only scored on problems it could not have trained on. The trade-off: a later cutoff leaves fewer eligible problems, so comparisons are noisier, and two models are no longer scored on identical sets.

Code: an n-gram overlap contamination check

This is the simplest serious detector. It indexes a (training) corpus by n-grams, then reports what fraction of each benchmark item's n-grams appear in the corpus. At real scale you would use a suffix array or a Bloom filter over trillions of tokens; the logic is the same.

import re
from collections import defaultdict

def tokens(text):
    # lowercase, keep words and numbers, ignore punctuation and whitespace differences
    return re.findall(r"[a-z0-9]+", text.lower())

def ngrams(toks, n):
    return {tuple(toks[i:i + n]) for i in range(len(toks) - n + 1)}

def build_index(corpus_docs, n=13):
    """Map every n-gram in the (training) corpus to the docs it appears in."""
    index = defaultdict(set)
    for doc_id, text in corpus_docs.items():
        for g in ngrams(tokens(text), n):
            index[g].add(doc_id)
    return index

def contamination_report(test_items, index, n=13, threshold=0.2):
    """For each benchmark item: what fraction of its n-grams also occur in the corpus?"""
    report = []
    for item_id, text in test_items.items():
        grams = ngrams(tokens(text), n)
        if not grams:                        # item shorter than n tokens: cannot judge
            report.append((item_id, None, "too short"))
            continue
        hits = [g for g in grams if g in index]
        frac = len(hits) / len(grams)
        sources = set().union(*(index[g] for g in hits)) if hits else set()
        flag = "CONTAMINATED?" if frac >= threshold else "clean"
        report.append((item_id, round(frac, 2), flag, sorted(sources)[:3]))
    return report

corpus = {
    "github/django-fork": "Fix QuerySet.bulk_create so that it returns primary keys when "
                          "ignore_conflicts is False on PostgreSQL backends, see ticket 30000 for details",
    "blog/cooking":       "Preheat the oven to 200 degrees and roast the vegetables for forty minutes",
}
test = {
    "task-001": "QuerySet.bulk_create should return primary keys when ignore_conflicts is False "
                "on PostgreSQL backends see ticket 30000 for details and add a regression test",
    "task-002": "Add a management command that exports every user's last login time to a CSV file",
}
idx = build_index(corpus, n=8)        # 8-grams for this tiny demo; 13 is a common choice at scale
for row in contamination_report(test, idx, n=8):
    print(row)
# ('task-001', 0.44, 'CONTAMINATED?', ['github/django-fork'])
# ('task-002', 0.0, 'clean', [])

Notice what it cannot see. Task-001 is caught because the wording overlaps. But in SWE-bench the danger is the answer (the gold patch) being in training data, not the question, so you would index the gold patches too. And a model that saw a paraphrase, or a translation, sails straight through. That is why labs combine overlap checks with behavioral probes.

Case study: memorized gold patches in SWE-bench Verified

On 23 February 2026 OpenAI published "Why SWE-bench Verified no longer measures frontier coding capabilities". Its contamination evidence was behavioral. OpenAI used GPT-5 as an automated interviewer that probed each target model for up to 15 turns with varied prompts, then rated contamination from "none" to "strong". According to OpenAI's write-up, every frontier model tested showed signs of it: GPT-5.2 reproduced gold patches verbatim; Claude Opus 4.5 recalled exact file paths, method names and inline comments from patches; Gemini 3 Flash produced verbatim details when given only the task ID. When a model can recite the answer from the task's ID number, the score is no longer measuring coding ability alone.

Contamination is not only "pretraining saw the test"

Modern models also learn from synthetic data and reinforcement learning environments. If an RL environment is built from the same repositories as a benchmark, or if eval transcripts are recycled into training, the benchmark is contaminated even if no web crawler ever touched it. Ask about all training stages, not just pretraining.

Label noise and flawed tasks

Label noise means the benchmark's "correct" answers are sometimes wrong. A flawed task is one that cannot be fairly solved: ambiguous instructions, tests that reject correct solutions, or environments that break. Both put a ceiling below 100% that no honest model can pass, and both make differences near the top meaningless.

BenchmarkFindingSource
MMLUAn estimated 6.49% of questions contain errors; in the Virology subset, 57% of analyzed questions had errors. MMLU-Redux re-annotated 5,700 questions.Gema et al., "Are We Done with MMLU?" (2024)
SWE-bench VerifiedOf 138 audited problems (ones o3 did not consistently solve), 59.4% had material flaws in tests or descriptions. Each was reviewed by at least six engineers.OpenAI, Feb 2026
τ-benchWrong or ambiguous ground truth in some tasks; the tau2-bench repo lists "75+ task fixes".sierra-research/tau2-bench
OSWorld, WebArenaBroken tasks and evaluator bugs led to re-issued "Verified" versions.os-world.github.io; WebArena audits
HumanEvalToo few tests per problem (about 7.7); EvalPlus added 80x more tests and scores dropped.Liu et al., 2023
138 audited SWE-bench Verified problems that o3 did not consistently solve 35.5% 18.8% 40.6% "narrow" tests: demand one specific implementation "wide" tests: check behavior the issue never described 5.1% other issues no material issue found 59.4% flawed: correct fixes can be marked as failures
What OpenAI's 2026 audit found. The audit targeted the hard tail (problems o3 failed), which is exactly where frontier models were still competing. When over half of that tail is flawed, the last few points of "progress" mostly measure luck in matching the test author's implementation.

Two things to notice. First, the 59.4% is of the audited hard problems, not of all 500; do not quote it as "59% of SWE-bench Verified is broken". Second, this was a benchmark that had already been human-screened: "Verified" was itself a cleaned subset of SWE-bench created by OpenAI and the SWE-bench authors in 2024. Validation reduces label noise; it never removes it.

Expert nuance: the noise ceiling

If a fraction e of items are mislabelled, a perfect model scores at most about 1 − e on them, and a model that happens to share the label author's mistakes can score higher than a better model. Near saturation, a leaderboard can literally reward agreeing with the answer key's errors. This is why careful teams report accuracy on a re-verified subset (MMLU-Redux, OSWorld-Verified) alongside the original.

item 1 2 3 4 5 6 7 8 9 10 graded truly right True answer B D A C A B D C A B Answer key B D A A A B D C C B Model A B D A C A B D C A B 80% 100% Model B B A A A A B D C C B 90% 70% key wrong key wrong Model B outscores Model A by sharing the key's two mistakes with 2 of 10 labels wrong, even a perfect model is capped at 80% against this key
How label errors reward the wrong model. A made-up 10-item example. Green cells are graded correct against the answer key, red cells graded wrong. Model A is truly perfect but loses 20 points on the two broken items; Model B is truly worse but wins, because it gives the same wrong answers as the key (for example because it memorized a flawed public answer sheet).

Goodhart's law and overfitting to benchmarks

Goodhart's law, in its common phrasing: "When a measure becomes a target, it ceases to be a good measure." A benchmark is a proxy: a small, cheap sample standing in for a big, expensive thing you actually care about (general coding skill, helpfulness to real users). While nobody optimizes for the proxy, it tracks the real thing. Once the whole field optimizes for it, the proxy and the target drift apart.

capability, as measured the field starts optimizing for it benchmark score (the proxy) real-world skill (the target) Goodhart gap score up, skill flat proxy tracks the target proxy and target drift apart optimization pressure on the benchmark (papers, tuned scaffolds, prompts) →
When a measure becomes a target. Illustrative shape only, not real data. Early on, a higher benchmark score really does mean more skill. Once everyone optimizes against the same test, extra points increasingly come from fitting that test's quirks, so the score keeps rising while the skill you care about levels off.

Benchmark-specific tuning

Scaffolds, prompts and tool descriptions get tuned to one benchmark's quirks (its repo layout, its test runner, its answer format). The score rises; performance on your codebase does not.

Adaptive overfitting

Each time a team looks at test results and changes something, a little information about the test set leaks into the system. Hundreds of such decisions across a field overfit the public test set even if nobody trains on it.

Distribution narrowing

SWE-bench is Python-only and concentrated in a few repositories (Django is a large share). A system that excels there has learned "fix Django-style issues", which is narrower than it sounds.

Style over substance

On preference leaderboards, length and formatting win votes. LMArena added "style control" to adjust for response length and markdown because raw preference was partly measuring presentation.

When an optimizer, not a person, climbs your eval

"Adaptive overfitting" above describes people looking at results and tweaking. That loop is now automated. Prompt and program optimizers such as DSPy and GEPA take your eval score as the objective and search over instructions, few-shot examples and agent programs to maximize it, often by having an LLM read failing traces and propose edits. Reinforcement learning goes one step further: an eval task with an automatic check can be packaged as an RL environment (libraries such as Prime Intellect's verifiers do exactly this), and the grader becomes the reward the model is trained on.

Each step raises the optimization pressure on the same measure, so Goodhart's law bites harder and sooner. An optimizer tries thousands of variants and finds a grader's quirks far faster than a human would; RL training turns those quirks into learned behavior, which is where the reward hacking in the next section comes from. If the grader is an LLM judge, optimizing against it will find the judge's biases (length, confident tone, keyword matching) unless the judge was validated first. Module 13 covers how judges relate to reward models and RL graders in depth. The defenses are the ones this module keeps returning to, applied more strictly: a held-out set the optimizer never sees (not even its scores), fresh tasks on a schedule, validated graders, and reading the transcripts of the winning variant before trusting its score.

Optimizer DSPy, GEPA, RL trainer Candidate prompt, agent or weights Your eval (dev set) same tasks every round Score the objective to maximize proposes runs on graded feedback each round leaks a little of the dev set into the system optimization rounds → score dev-set score (optimized on) held-out score (never seen) Goodhart gap
Automated optimization is Goodhart on fast-forward. The loop on the left runs hundreds or thousands of times, each time using the dev-set score. The chart is an illustrative shape, not real data: without a held-out set the optimizer never sees, you only ever observe the top curve.

Leaderboard gaming: The Leaderboard Illusion

In April 2025, Singh et al. (Cohere and collaborators) published "The Leaderboard Illusion", a study of Chatbot Arena (now LMArena), the crowd-voted leaderboard where users compare two anonymous models and vote for the better answer. Ratings are fitted with a Bradley-Terry model (a statistical model that turns pairwise wins into a score). The paper documented three structural problems:

  1. Undisclosed private testing and selective disclosure. Some providers tested many private variants and published only the best. The paper reports that Meta tested 27 private variants before the Llama 4 release. Picking the maximum of many noisy measurements systematically inflates the reported score, even if every variant is equally good.
  2. Data access asymmetry. Google and OpenAI each received roughly 19-20% of all battle data, while 83 open-weight models together received about 29.7%. Battle data is valuable: the paper estimates that extra arena data can yield relative performance gains of up to 112% on the arena distribution.
  3. Silent deprecation. Models removed from the arena without notice break the assumptions of the rating model, which can distort the rankings of those that remain.
Best-of-N disclosure observed arena score → true skill (same for all) Lab X: 27 variants published (inflated) Lab Y: 1 variant published (honest) Who gets the battle data one large lab ~20% another large lab ~19% all 83 open-weight models, combined ~29.7% share of all Arena battles (Singh et al., 2025) more battles → more feedback data to tune on
Two ways a leaderboard can mislead without anyone lying. Left: an illustrative simulation of best-of-N disclosure (dots are made up). Right: approximate battle shares as reported in "The Leaderboard Illusion" (the paper names Google and OpenAI as the two large labs).

The general lesson extends to every leaderboard, not only Arena: whoever gets more tries, more data, or more say in which results are shown will look better than they are. Defenses that exist in practice include private, held-out test sets and "first submission only" rules (Scale AI's SEAL leaderboards were designed this way), and independent re-runs by third parties (Epoch AI, Artificial Analysis) rather than self-reported numbers.

Why the max of noisy scores is biased (winner's curse)

If each variant's measured score is its true skill plus independent noise with standard deviation σ, the expected maximum of N such measurements sits above the true skill by about 1.2σ for N = 5, 1.5σ for N = 10, 2.0σ for N = 27 and 2.5σ for N = 100 (the textbook large-N shortcut σ·√(2 ln N) slightly overstates it at these sizes, giving 2.6σ for N = 27). You can buy a "significant" leaderboard jump with nothing but repeated submissions. Statisticians call this the winner's curse; the fix is to re-measure the selected variant on fresh data.

expected inflation of published score +0σ +1σ +2σ +3σ +0σ (honest) 1 +1.2σ 5 +1.5σ 10 +2.0σ 27 +2.5σ 100 27 = the Arena example number of equally good variants tested before publishing the best one (log scale) If one measurement has noise σ = 1.5 points, 27 tries buys about +3 points of pure luck
The winner's curse, in numbers. Expected maximum of N independent noisy measurements of the same true skill, in units of the measurement noise σ (computed by simulating normal noise; the σ = 1.5 example is illustrative). The bias grows quickly at first and then slowly, but it never goes away unless the chosen variant is re-measured on fresh data.

Harness dependence: apples to oranges

For an agent, the score belongs to a (model × harness × budget × grader × dataset version) combination, not to the model. The harness (or scaffold) is the code around the model: tool definitions, the agent loop, the system prompt, context management, retry logic. The budget is how many steps, tokens, minutes or attempts it gets. Change any of these and the number moves, sometimes more than a model upgrade would move it.

Same model (Claude Opus 4), same benchmark (SWE-bench Verified) Standard setting tools: bash + string-replace file editor 1 attempt per task no selection step 72.5% "With parallel test-time compute" sample multiple parallel attempts drop patches breaking visible tests internal scoring model picks one 79.4% +6.9 pts Both numbers are honest and both were disclosed. Comparing 79.4% with another model's single-attempt score would not be fair. Other knobs that move scores: step/turn limit, time limit, prompt, retries, subset used, grader version, number of runs averaged.
One model, two numbers. Anthropic's Claude 4 announcement (May 2025) reported both, and described the parallel method in an appendix (the same approach took Sonnet 4 to 80.2%). The footnote is the difference between a fair and an unfair comparison.
KnobWhy it mattersWhat to look for
pass@1 vs best-of-n / pass@kpass@k counts a task solved if any of k attempts succeed (Module 04). It is always ≥ pass@1 and needs someone, or something, to pick the right attempt."pass@1", "single attempt", "with parallel test-time compute", "majority voting", "best of 8"
Selection with extra informationFiltering attempts with visible tests, or a learned verifier, is a real technique, but it is a different system from a single agent.Who picks the final answer, using what?
ScaffoldSWE-bench's official "bash-only" view runs every model in the same minimal mini-SWE-agent harness precisely so models can be compared. Mini-SWE-agent reached 65% on Verified in about 100 lines of Python.Vendor harness vs a shared reference harness
Step / turn / time budgetOSWorld reports scores at 15, 50 and 100 steps. More steps, higher scores.Budget stated, and equal across compared rows?
Observation typeOn computer-use benchmarks, screenshot-only and accessibility-tree inputs are not comparable."screenshot", "a11y tree", "set-of-marks"
Dataset version and subsetOSWorld vs OSWorld-Verified; τ-bench before and after task fixes; SWE-bench Multimodal full set vs test split; WebVoyager vendors reporting on different task subsets.Exact version and number of tasks
Tools allowedHumanity's Last Exam with and without search or code execution are different benchmarks in practice."no tools", "with search", "with python"
Reasoning effortMany models have effort or thinking-budget settings that trade cost for score.Effort setting and cost per task

Infrastructure noise: the machine is part of the setup

The table above lists knobs that people choose on purpose. One more knob is usually not chosen at all: the compute resources of the sandbox the agent runs in (CPU, memory, disk, time limits, how many trials share a machine). In February 2026 Anthropic's engineering blog published "Quantifying infrastructure noise in agentic coding evals". Anthropic summarized the result when announcing it: infrastructure configuration can swing agentic coding benchmarks by several percentage points, sometimes more than the leaderboard gap between top models. (Secondary coverage reports swings of more than 6 points between resource configurations.)

The mechanism is easy to picture. An agentic coding task means installing packages, compiling, running test suites. With tight CPU or memory, those steps run slower or get killed (an out-of-memory kill looks like a crash), more trials hit their time limit, and an agent that would have succeeded is scored as a failure. With generous resources, a brute-force approach that would time out elsewhere can finish. None of that is the model's skill, and none of it appears in a headline number unless someone reports the sandbox spec.

Same agent, same tasks, three sandboxes 40% 50% 60% 70% 55% 60% 63% tight1 CPU · 2 GB medium2 CPU · 4 GB generous4 CPU · 8 GB 8 pts spread Two models, one pinned sandbox 40% 50% 60% 70% 62% 60% Model A Model B same medium sandbox 2 pts model gap unpinned infrastructure can outvote the difference between models
The sandbox can move the score more than the model. Illustrative numbers, not Anthropic's data (vertical axes start at 40%). The pattern is the one Anthropic's post warns about: the spread across resource settings for one agent can exceed the gap between two models, so two leaderboard rows run on different machines are not comparable.

What to do about it, whether you read claims or run evals:

  • Treat resources as part of the configuration. Record CPU, memory, disk, per-task time limit and concurrency next to the model and harness versions, and add them to question 7 of the checklist below ("what budget?").
  • Compare on identical infrastructure, interleaved. Never compare "baseline last month" with "candidate today on the new cluster". Run both in the same pool, alternating trials.
  • Separate infrastructure failures from agent failures. Log exit reasons (out-of-memory kill, container failed to start, setup timeout) and report the infrastructure error rate separately, as Module 04 recommends. A rising timeout rate is a signal that the machine, not the agent, is deciding outcomes.
  • Probe for resource sensitivity. Rerun a sample with double the resources. If the score moves, your benchmark is partly measuring hardware, and your error bars should say so.
  • Prefer shared, standardized harnesses for cross-model claims (HAL, Harbor for Terminal-Bench). Module 21 covers this from the harness builder's side.

Try it: run the same agent under different resource limits and compare the spread with the gap between two models.

Reward hacking in agent evals

Reward hacking (also "specification gaming") is when a system achieves a high score by exploiting a flaw in how success is measured, instead of doing the intended task. It is Goodhart's law performed by the model itself, in real time. Agents are especially prone to it because they have tools: they can read files, run commands and edit anything in their sandbox, including the tests that grade them.

agent sandbox (the agent can read and write almost everything in here) Agentreads the task Source codereal fix goes here Testsrun by grader ScorePASS edits tested by intended path shortcuts 1 · Edit or skip testsrewrite asserts, add skip 2 · Special-case inputsif x == test: return 3 · Read or patch graderreference answer, timer 4 · Read future git logcopy the real fix still scored PASS each shortcut produces the same PASS unless the grader checks how it was reached
The intended path and the shortcuts. Green is what the benchmark wants to measure. Every red path has been observed in real agent evaluations (examples below). Each ends in the same PASS as the green path unless the grader checks how it was reached.

Documented examples

Who / whereWhat the agent did
METR, June 2025 ("Recent frontier models are reward hacking"), models including o3, o1 and Claude 3.7 SonnetSearched the Python call stack to find the grader's pre-computed reference answer and returned it; overwrote timing functions (one made "a 1000× faster clock" for the scoring script); monkey-patched evaluation functions to always report success; disabled torch.cuda.synchronize() so timing was wrong; made tensor subclasses whose equality check always returned true; copied pre-computed weights instead of training. METR reported reward hacking in 30.4% of RE-Bench runs (39 of 128), including 21 of 21 runs on one task, but only 0.7% (8 of 1,087) of HCAST runs.
SWE-bench, 2025 (GitHub issue #465, "Repo State Loopholes")Agents ran commands like git log --all to see future commits left in the task's Docker image, which contained the real fix. Found in submissions for Claude 4 Sonnet, Qwen3 Coder and GLM-4.5 in a handful of instances each. Fixed by rebuilding images with future history removed (SWE-bench v4.1.0).
Anthropic system cardsAnthropic's system cards (for example Claude Sonnet 4.5, Sep 2025) include a dedicated reward-hacking section, covering behaviors such as hard-coding or special-casing tests to make them pass. For Sonnet 4.5, Anthropic reported roughly a 2x improvement over Sonnet 4.
Why this is a benchmark pitfall and not just a safety curiosity

A reward hack is a false pass: the score says "solved", the task was not solved. If 5% of an agent's passes on your benchmark are hacks, its score is inflated by 5% of its passes, and the inflation is larger for more capable, more resourceful agents. METR's numbers show the rate depends heavily on the task: environments where the scoring code is reachable (RE-Bench) were hacked far more often than others (HCAST).

Code: a reward-hack detector for coding-agent diffs

The cheapest defense is to inspect what the agent changed. This script reads a unified diff (for example git diff <base_commit> from the agent's sandbox) and flags edits to tests, test configuration, skip markers, forced exit codes and "am I under test?" branches. It exits non-zero on high-severity findings, so it can gate a CI run or mark trials for human review.

import re
import sys

TEST_PATH = re.compile(r"(^|/)(tests?|testing)/|(^|/)test_[^/]*\.py$|_test\.py$|(^|/)conftest\.py$")
PROTECTED = re.compile(r"(^|/)(pytest\.ini|tox\.ini|setup\.cfg|pyproject\.toml|\.github/workflows/)")
SUSPICIOUS_ADDED = [
    (re.compile(r"@pytest\.mark\.(skip|xfail)|unittest\.skip"), "adds a skip/xfail marker"),
    (re.compile(r"\bsys\.exit\(0\)|os\._exit\(0\)"),            "forces a zero exit code"),
    (re.compile(r"assert\s+True\b|return\s+True\s*#.*test", re.I), "trivial assertion"),
    (re.compile(r"(PYTEST|TESTING|CI)['\"]?\s*(in|==)|os\.environ\.get\(['\"](PYTEST|CI)"),
     "behaves differently under test"),
    (re.compile(r"monkeypatch|mock\.patch\(['\"](time\.time|.*grade|.*score)"), "patches timer or grader"),
]

def parse_diff(diff_text):
    """Yield (path, added_lines, removed_lines) for each file in a unified diff."""
    path, added, removed = None, [], []
    for line in diff_text.splitlines():
        if line.startswith("diff --git"):
            if path:
                yield path, added, removed
            path, added, removed = line.split(" b/", 1)[-1], [], []
        elif line.startswith("+") and not line.startswith("+++"):
            added.append(line[1:])
        elif line.startswith("-") and not line.startswith("---"):
            removed.append(line[1:])
    if path:
        yield path, added, removed

def audit(diff_text):
    findings = []
    for path, added, removed in parse_diff(diff_text):
        if TEST_PATH.search(path):
            dropped = [l for l in removed if re.search(r"\bassert\b|self\.assert", l)]
            findings.append(("HIGH", path, f"test file modified (-{len(dropped)} assertions)"))
        if PROTECTED.search(path):
            findings.append(("HIGH", path, "test/CI config modified"))
        for line in added:
            for pattern, why in SUSPICIOUS_ADDED:
                if pattern.search(line):
                    findings.append(("MED", path, f"{why}: {line.strip()[:60]}"))
    return findings

if __name__ == "__main__":
    diff = sys.stdin.read()           # usage: git diff BASE_COMMIT | python hackcheck.py
    results = audit(diff)
    for sev, path, msg in results:
        print(f"[{sev}] {path}: {msg}")
    sys.exit(1 if any(s == "HIGH" for s, _, _ in results) else 0)

# Example output on a diff that skips a test and adds a PYTEST_CURRENT_TEST branch:
# [MED] src/parser.py: behaves differently under test: if os.environ.get("PYTEST_CURRENT_TEST"):
# [HIGH] tests/test_parser.py: test file modified (-1 assertions)
# [MED] tests/test_parser.py: adds a skip/xfail marker: @pytest.mark.skip(reason="flaky")

Pattern matching is a first line of defense, not a proof. Serious harnesses add structural defenses that make hacks impossible rather than detectable:

  • Grade with the original tests. SWE-bench copies its own FAIL_TO_PASS and PASS_TO_PASS tests into the container at grading time, so agent edits to test files are overwritten. Do the same: never grade with files the agent could touch.
  • Keep the grader out of reach. Run scoring in a separate process or container with no shared memory, no reachable reference answers, and its own clock.
  • Sanitise the environment. Remove future git history, caches and solution artifacts from images (the SWE-bench v4.1.0 fix).
  • Read transcripts, and use a monitor. An LLM judge reading trajectories for "did the agent try to modify tests or read grading code?" catches creative hacks that regexes miss (Module 13, Module 17).

Grader bugs, flaky environments and stale answers

Not every false result is the agent's fault. The measuring apparatus fails too, in three recurring ways.

Grader bugs (false passes and false fails)

A false pass is a wrong solution scored as correct: tests too weak (HumanEval's ~7.7 tests per problem), an LLM judge that accepts a confident wrong answer, a string match that accepts "not 42" because it contains "42". A false fail is a correct solution scored as wrong: SWE-bench Verified's narrow tests, AST matching in BFCL that rejects a valid alternative call, exact-match on "3.0" vs "3".

Flaky environments

A flaky task gives different results on identical behavior: network timeouts in Terminal-Bench containers, websites that change or block bots (WebVoyager), apps loading slowly in OSWorld VMs, public APIs that go offline (ToolBench, which led to StableToolBench's cached virtual API server). Flakiness adds noise and usually biases scores down.

Time-sensitive answers

"What is the current price of…", "who is the CEO of…", "how many stars does this repo have?" The gold answer was right on the day it was written. Live-web benchmarks like WebVoyager and GAIA-style questions accumulate stale answers, so a correct agent is marked wrong. Freeze the world (cached pages, snapshot databases) or date-stamp answers.

How to find them

Run the reference solution through the grader (it should pass 100%), run a null agent that does nothing (it should pass 0%), run each task several times with the same seed (flakiness shows up as disagreement), and hand-review a sample of passes as well as fails.

What really happened task truly solved task NOT solved grader: PASS grader: FAIL true passwhat you hope for false passweak tests, reward hacks false failnarrow tests, flakiness true failgenuine error reference solution must land here, 100% null agent must land here, 100% inflates deflates
Every grader has a confusion matrix. The reference solution tests the left column (does the grader accept correct work?); the null agent tests the right column (does it reject non-work?). Neither check is sufficient alone, but skipping them is how broken benchmarks ship.

Construct validity: does it measure what it claims?

Construct validity is a term from psychology: the degree to which a test measures the abstract quality (the "construct") it claims to measure. "Coding ability", "general intelligence", "can do a software engineer's job" are constructs. A benchmark is a concrete, narrow operationalization of one. The questions are: what does a high score let you predict, and about what?

Examples of gaps between benchmark and construct:

  • SWE-bench measures "fix a well-described Python issue in a popular open-source repo, judged by the maintainers' tests". Real software work also involves unclear requirements, private codebases, code review and long-lived design choices.
  • Multiple-choice knowledge tests measure recognizing a right option among four, which is easier than producing the answer and is sensitive to formatting. Anthropic has noted that small formatting changes can move MMLU scores by around 5%.
  • A 50% success rate on a task that a human does in one hour (METR's time horizon) is a statistic about a software/ML task distribution, not a promise that "the model can work for an hour" on your task. METR says so in its own limitations notes.
Experienced open-source developers using early-2025 AI tools 0 ← slower faster → Forecast before +24% faster Belief afterwards +20% faster Measured 19% slower 16 developers · 246 real issues in their own repos · randomized with vs without AI (METR, July 2025)
Benchmarks said one thing; a randomized trial said another. In METR's 2025 randomized controlled trial, developers using Cursor with Claude 3.5/3.7 Sonnet took 19% longer, while believing they had been faster. Bars are to scale. It is one study in one setting, but it is exactly the kind of check that benchmark scores cannot give you.

METR itself is careful about what the study does and does not show: it concerns experienced developers on large, mature codebases they know well, with tools from early 2025. The lesson for this module is methodological: benchmark performance and real-world impact are different measurements, and only the second one answers "does this help my users?". This is why Module 15 insists on online evals and A/B tests alongside offline benchmarks.

Statistical sins

The last family of pitfalls is arithmetic. Most benchmark claims are made without error bars, on small test sets, from a single run. Here are the classic sins and what they cost you.

SinExampleRemedy (see Module 04)
Tiny test setsτ-bench airline has 50 tasks; Terminal-Bench 2.0 has 89; GPQA Diamond 198 (95% CI roughly ±5 to ±7 points, depending on the score); RE-Bench 7 environments.Report the CI; use power analysis to size the set.
No confidence intervals"72% vs 70%" with no ± at all.Standard error, paired tests, bootstrap (below).
Single runAgents are stochastic; one run per task mixes skill with luck.Several runs per task; report mean ± SE, and pass^k for reliability.
Cherry-picked seeds or runsRun five times, report the best.Pre-register the number of runs; report all of them.
Ignoring clusteringMany questions from one passage or one repo treated as independent.Clustered standard errors (Miller, 2024: naive SEs can understate uncertainty by over 3x).
Unpaired comparisonsComparing two models' CIs for overlap instead of testing the per-item difference.Paired-difference tests: same items, look at the differences.
Multiple comparisonsTry 20 prompt variants, one "wins" at p < 0.05 by chance.Hold out a fresh test set, or correct for multiple tests.

Simulation: a 2-point gap on 100 items is noise

Suppose two models are exactly equally good: each solves any given item with probability 70%. You run each once on the same kind of 100-item benchmark. How often will the leaderboard show one of them 2 or more points ahead?

share of simulations −20 −10 0 +10 +20 score gap in points (model A − model B), both truly 70% |gap| ≥ 2 points in 82% of runs standard deviation of the gap ≈ 6.5 points gray: the two central bins (gaps of −2, −1, 0 and +1)
Identical models, different scores. Result of 20,000 simulated pairs of runs (real simulation output, produced by the code below; bar heights match the exact binomial calculation). With 100 items and a true rate of 70%, one model's score alone has a standard error of about 4.6 points, and the gap between two independent runs spreads with a standard deviation of about 6.5 points.

Now a realistic claim: "Model A scores 72%, Model B 70%, on the same 100 items." Because both models answer the same items, the right tool is a paired bootstrap: resample items with replacement (keeping each item's A and B results together), recompute the gap each time, and read off the middle 95%. The key quantity is the discordant items, where exactly one model is right. Here A alone is right on 6 items and B alone on 4.

100 items, same for both models 66 both right same result for A and B: no information 24 both wrong same result again: no information 6 only A right discordant 4 only B right discordant 10 items decide A vs B 6 vs 4 is an ordinary coin-flip split McNemar p ≈ 0.75 paired SE of the gap ≈ 3.2 points "72% vs 70%" sounds like 200 data points; for the comparison it is really 10
Where the evidence actually lives. The worked example below as a picture: each square is one benchmark item. Items both models get right (green) or both get wrong (gray) cancel out in a paired comparison. Only the discordant items, where exactly one model is right, say anything about which model is better, and here there are just 10.
import numpy as np

rng = np.random.default_rng(0)
N = 100                                   # benchmark items

# Part 1: two IDENTICAL models (true accuracy 70%), each run once on 100 items.
# How often does the "leaderboard" show a gap of 2+ points anyway?
a = rng.random((20_000, N)) < 0.70
b = rng.random((20_000, N)) < 0.70
# with 100 items, 1 item = 1 point; count in integers to avoid float rounding
gap = a.sum(axis=1) - b.sum(axis=1)
print(f"P(|gap| >= 2 points) for identical models: {np.mean(abs(gap) >= 2):.0%}")
print(f"std of the gap: {gap.std():.1f} points")

# Part 2: a real claim, "Model A 72% vs Model B 70%", on the SAME 100 items.
# Per item: both right 66, only A right 6, only B right 4, both wrong 24.
A = np.array([1]*66 + [1]*6 + [0]*4 + [0]*24)
B = np.array([1]*66 + [0]*6 + [1]*4 + [0]*24)

# Paired bootstrap: resample ITEMS, keeping each item's A/B pair together.
idx = rng.integers(0, N, size=(10_000, N))
diffs = A[idx].sum(axis=1) - B[idx].sum(axis=1)     # gap in points (N = 100)
lo, hi = np.percentile(diffs, [2.5, 97.5])
print(f"observed gap: {A.sum() - B.sum()} points, 95% CI [{lo:.0f}, {hi:.0f}]")
print(f"share of resamples where A is NOT ahead: {np.mean(diffs <= 0):.0%}")

# Output:
# P(|gap| >= 2 points) for identical models: 82%
# std of the gap: 6.5 points
# observed gap: 2 points, 95% CI [-4, 8]
# share of resamples where A is NOT ahead: 32%

The interval from −4 to +8 includes zero comfortably: the data are consistent with A being worse, equal or clearly better. With only 10 discordant items, a 6-vs-4 split is what a fair coin produces all the time. To detect a true 2-point difference reliably you need many more items, repeated runs per item, or both. (Rule of thumb from Module 04: the standard error of a proportion near 70% is about 46/√N points, so halving it needs four times the items.)

Worked check: the analytic answer

For one model, SE = √(0.7 × 0.3 / 100) = 0.0458, i.e. 4.6 points. For the gap between two independent runs, SE = √2 × 4.6 ≈ 6.5 points, matching the simulation. A 2-point gap is about 0.3 standard errors: nowhere near significance. With pairing, the SE of the gap depends on discordant items: √(6 + 4 − (6 − 4)²/100)/100 ≈ 3.2 points, so the gap is about 0.6 SE, still far from the 1.96 needed for 95% confidence. McNemar's test on 6 vs 4 gives p ≈ 0.75.

How to read a benchmark claim: 15 questions

Here is the whole module compressed into a checklist. You will not be able to answer every question for every claim; the point is to notice which answers are missing. A missing answer is information.

Model X scores 81.3% on SWE-bench Verified* +2.1 points over the previous best, state of the art * several attempts sampled, a scoring model picks one No ± and no run count Q1, Q2: ask for the CI Saturated and contaminated Q11, Q12: retired by OpenAI, Feb 2026 +2.1 on 500 items Q1: probably inside the noise Missing entirely harness, budget, cost, who ran it Best-of-n, not pass@1 Q3: compare like with like
Reading a headline with the checklist. A made-up claim of the kind that appears in launch posts, with each weak spot tagged by its checklist question. Nothing in it is false; the problem is what it leaves out and what it quietly changes (a selection step) compared with other rows on the same leaderboard.
The number The setup The benchmark 1 · Error bars or CI given?± and how computed 2 · How many items?and how many runs each 3 · pass@1 or best-of-n?parallel compute, voting 4 · All runs reported?or best seed / best variant 5 · Who ran it?vendor vs independent re-run 6 · Which harness / scaffold?same one for all compared? 7 · Budget?steps, turns, time, tokens 8 · Tools and inputs?search, code, a11y tree 9 · Exact version / subset?Verified? fixed tasks? N? 10 · Cost and latency?$ per task, effort setting 11 · Life-cycle stage?fresh, climbing, saturated 12 · Contamination checked?public since when? cutoff? 13 · Grader validated?false passes, label noise 14 · Hacks ruled out?transcripts read, tests safe 15 · Measures my construct?does it predict my use case each unanswered box widens your mental error bar
The benchmark-claim checklist. Column one is about the statistic, column two about the system that produced it, column three about the test itself. Details for each question follow.
#QuestionRed flag
1Is there an error bar or confidence interval, and how was it computed?Differences reported to one decimal place with no ±.
2How many items, and how many runs per item?Under ~200 items, single run, and a gap of a few points.
3Is it pass@1, or best-of-n / pass@k / majority vote / "parallel test-time compute"?The method is in a footnote and compared against others' pass@1.
4Were all runs and variants reported, or the best?"Up to X%", "best configuration", many unreleased variants.
5Who ran it: the vendor, the benchmark authors, or an independent party?Only self-reported; no independent reproduction.
6Which harness or scaffold? Is it the same for all compared systems?Each vendor uses its own custom agent.
7What budget: steps, turns, wall-clock time, tokens, and sandbox resources (CPU, memory, time limit)?Budget unstated, or different across rows; rows run on different machines.
8Which tools and input modalities were allowed?"With tools" vs "no tools" rows mixed in one table.
9Exactly which version, split or subset?Subset chosen by the reporter; version not named.
10What did it cost, and how long did it take?A score with no cost when cost varies 10-100x.
11Where is the benchmark in its life cycle?Big claims on a saturated benchmark.
12Could the model have seen the test? Was contamination checked?Public test set older than the training cutoff, no discussion.
13Has the grader been validated (reference solutions pass, known label-error rate)?No audit; known flaws in the benchmark.
14Were transcripts inspected for reward hacking?Agent can touch tests or grader; nobody read trajectories.
15Does the benchmark measure what I care about, and is there real-world evidence?Benchmark-only evidence for a real-world claim.

The Agentic Benchmark Checklist: a review rubric for your own evals

The 15 questions above are for reading someone else's claim. When you build or adopt an agentic benchmark, including the private product suite from Module 12, you need a rubric for the benchmark itself. The Agentic Benchmark Checklist (ABC; Zhu et al., Daniel Kang's lab at UIUC and collaborators; arXiv 2507.02825, NeurIPS 2025 Datasets and Benchmarks track; project page) distills benchmark-building experience and documented failures into a checklist organized in three parts.

1 · Task validity

A task should be solvable if and only if the agent has the capability being tested. It fails in two directions: the task is impossible (a broken environment, a tool version that no longer behaves as the task assumes, an ambiguous goal), or it is solvable without the capability (a shortcut, such as a reachable answer or future git history).

2 · Outcome validity

The evaluation result should really indicate success. The ABC breaks this down by grader type (string matching, unit tests, state checks, LLM judges), because each fails differently: substring matching accepts wrong answers, tests miss parts of the task, a state check credits an agent that did nothing when the correct end state equals the start state, a judge is never validated.

3 · Reporting

Even a benchmark with known flaws is usable if the reader can tell what the number means. Report known limitations and which checks the benchmark does not pass, statistical uncertainty, baselines (including trivial ones), and the steps taken against contamination and shortcuts.

What the authors found

The paper reports that applying the checklist to widely used agentic benchmarks surfaced serious problems; its examples include SWE-bench Verified using insufficient test cases and τ-bench counting empty responses as successful. It reports such issues can misestimate agent performance by up to 100% in relative terms, and that applying ABC to CVE-Bench reduced its performance overestimation by 33%.

Review your benchmark in three passes Task validity solvable if and only if skilled Outcome validity a pass really means success Reporting the reader knows what it means env and tool versions pinned no shortcut to the answer reference solution passes infra failures kept separate goal is unambiguous grader accepts all correct forms empty / do-nothing output fails tests cover the whole task judge validated against humans end state checked, not just reply known flaws and limits listed CIs and runs per task stated harness, budget, cost stated contamination risk addressed null and human baselines given Example failures the paper reports τ-bench: empty responses counted as successful SWE-bench Verified: insufficient test cases Why it matters misestimates of up to 100% in relative terms CVE-Bench overestimation cut by 33% after fixes
The ABC as a review rubric. The three column headings are the checklist's three parts; the items are a condensed paraphrase for practical use, not the paper's exact wording or full list (see the project page for that). The two bold items correspond to the paper's example failures shown in the red box.

Applying it to your own evals

Treat the ABC like a code review for your benchmark. Go item by item, mark each yes, no or not applicable, and write down the evidence (not "we think so"). Several items can be checked automatically with baselines that every eval should run anyway:

# A minimal "outcome validity" smoke test. run_eval(agent) is your own harness entry
# point: it runs every task with the given agent and returns the mean score (0..1).
from my_eval import run_eval, load_reference_solutions   # your code

def null_agent(task):            # does nothing at all
    return ""

def echo_agent(task):            # restates the task, takes no action
    return task.instructions

def oracle_agent(task):          # replays the known-good solution
    return load_reference_solutions()[task.id]

checks = {
    "null agent scores ~0":        (run_eval(null_agent),   lambda s: s <= 0.02),
    "echo agent scores ~0":        (run_eval(echo_agent),   lambda s: s <= 0.02),
    "reference solutions pass ~1": (run_eval(oracle_agent), lambda s: s >= 0.98),
}
for name, (score, ok) in checks.items():
    print(f"{'PASS' if ok(score) else 'FAIL'}  {name:30s} score={score:.2f}")

# Any FAIL is a benchmark bug, not an agent result. A null agent above 0 usually
# means some tasks are "already solved" in their start state (the tau-bench
# empty-response pattern); an oracle below 1 means broken tasks or a strict grader.

Then do the parts no script can: have a second person read 20 passing and 20 failing transcripts and confirm the grader's verdicts, check that each task's goal is unambiguous, and write the reporting section (known flaws, what was not validated, how to cite the version). Record the result as a short "benchmark card" next to the dataset, and repeat the review whenever you version the benchmark. The 15-question checklist tells you what to ask of a claim; the ABC tells you what to fix before you make one.

How to build a benchmark that lasts

Everything above can be turned around into design principles. The benchmarks that stay useful longest share a handful of habits, and each one answers a specific pitfall.

1 · Authortasks from real work,diverse sources 2 · Validatereference solution passes,null agent fails, reviewers 3 · Splitpublic dev set +private held-out set 4 · Release v1.0canary strings, harness,encrypted answers 5 · Monitorcontamination probes,audits, saturation 6 · Fix and versionv1.1 / "Verified":changelog, old scores kept 7 · Refreshnew tasks after cutoffs,retire leaked ones in use flaws found aging next cycle examples: OSWorld → OSWorld-Verified (Jul 2025) · tau2-bench "75+ task fixes" · LiveCodeBench v1 → v6 · SWE-bench images v4.1.0
A benchmark is a product, not a paper. The ones that last are maintained: validated before release, split so part stays private, monitored, fixed under explicit version numbers, and refreshed with new tasks.

Held-out private test set

Publish a development set so people can build and debug, but keep the scoring set private and run it yourself. SWE-bench Pro has public, held-out and commercial (private-codebase) splits; ARC-AGI keeps semi-private and private sets; Scale's SEAL leaderboards use private datasets. Limit submissions to stop adaptive overfitting.

Periodic refresh

Add new tasks on a schedule, stamped with dates, so evaluators can use only tasks newer than a model's cutoff (LiveCodeBench, SWE-rebench, SWE-bench-Live). Use sources that are hard to leak, such as private or copyleft code (SWE-bench Pro chose GPL and private repositories partly for this).

Validated tasks

Every task needs a reference solution that passes the grader, a null baseline that fails, and more than one human reviewer checking that the task is well specified. Record the human baseline and time. Budget for audits after release: the hard tail will surface flaws that reviewers missed.

Contamination canaries and encryption

Put a canary GUID in every file, ask that the data not be trained on, and consider encrypting answers so they never appear in plain text on the web (BrowseComp does this). Publish the canary so labs can filter it and outsiders can probe for it.

Explicit versioning

When you fix tasks, release a new version with a changelog and keep old leaderboards separate: OSWorld-Verified (July 2025) fixed community-reported broken tasks and re-verified the leaderboard; tau2-bench's v1.0.1 lists 75+ task fixes; SWE-bench shipped rebuilt images in v4.1.0. Never silently mix scores across versions.

A reference harness and full reporting

Ship a standard harness (Terminal-Bench's Harbor, SWE-bench's mini-SWE-agent "bash-only" view) and require submissions to state harness, budget, attempts and cost. Report with CIs, and keep the grader outside the agent's reach.

For your own product evals

All of this applies to the private eval suite you built in Module 12. Keep a held-out slice you never tune against, version the dataset when you fix labels, refresh it from new production failures, validate your grader with reference outputs, and re-read transcripts for hacks whenever a score jumps suspiciously. Your suite will go through the same life cycle as SWE-bench, only faster.

Key takeaways

  1. Place the benchmark on its life cycle first. A few points mean a lot on a fresh benchmark and very little on a saturated one, where label errors and noise dominate the top of the table.
  2. Contamination means remembering, not solving. Canaries and n-gram search need the training data; behavioral probes (verbatim reproduction, task-ID recall) and time-windowed or private test sets work from outside.
  3. Every benchmark has label noise. Even a human-screened set like SWE-bench Verified had flawed tests in 59.4% of the audited hard problems, and a noisy key can rank a worse model above a better one.
  4. A score belongs to a whole setup, not a model. Harness, budget, attempts, selection step, tools, grader and dataset version all move the number, sometimes more than a model upgrade does.
  5. Agents hack graders when they can reach them. Grade with files the agent cannot touch, keep the scorer outside the sandbox, sanitise images and read transcripts.
  6. Small gaps on small sets are noise. Two identical models on 100 items differ by 2+ points 82% of the time; use paired tests, confidence intervals, several runs, and beware best-of-N disclosure (the winner's curse).
  7. Benchmarks are not the real world. Construct validity limits what any score predicts; confirm important claims with your own evals and, where possible, field studies.
  8. The sandbox is part of the setup. Anthropic reports that infrastructure configuration can swing agentic coding scores by several points, sometimes more than the gap between top models. Pin, report and equalize resources, and log infrastructure failures separately.
  9. Optimizers and RL make Goodhart stronger. DSPy, GEPA and RL environments climb your eval far faster than people do; keep a held-out set the optimizer never sees and validate any judge you optimize against.
  10. Review your own benchmark with the ABC. Check task validity, outcome validity and reporting, and run null, echo and reference-solution baselines: a do-nothing agent must score about zero.

Check yourself

1. OpenAI's February 2026 audit found flaws in 59.4% of problems. Which statement is accurate?

The audit targeted the hard tail, which is where frontier models were still competing. That makes the finding damning for top-of-leaderboard comparisons, but it is not a statement about all 500 problems.

2. A vendor reports 79% on a coding benchmark "with parallel test-time compute"; a competitor reports 74% pass@1. What is the fairest conclusion?

Selection over several attempts is a different system. Compare the vendor's own single-attempt number (if given) with the competitor's pass@1, under the same harness.

3. Which contamination detector can an outsider run without access to the training data?

Behavioral probes (canary completion, verbatim reproduction, perplexity comparisons, time windows) work from outside. Corpus search and filtering need the training data.

4. Two models score 72% and 70% on the same 100 items, single run. A paired bootstrap gives a 95% CI of [−4, +8]. You should…

The interval includes zero comfortably. Identical models show gaps of 2+ points about 82% of the time at this size.

5. In METR's June 2025 report, which setting had by far the highest reward-hacking rate?

Hacking depends on opportunity. Where the scorer is reachable, agents found ways to fake timing, patch evaluators and read reference answers. Keep the grader out of the sandbox.

6. Meta tested 27 private Arena variants before a release. Why does publishing only the best one inflate the score, even if all variants are equally good?

Each measurement is skill plus noise; selecting the maximum selects the luckiest noise. Re-measuring the chosen variant on fresh data removes the bias.

7. Model A beats Model B by 2 points on an agentic coding benchmark. A footnote says A was run on 8 GB sandboxes and B on 4 GB sandboxes. What is the best reading?

Infrastructure noise can exceed a 2-point gap. Only a comparison on identical, interleaved infrastructure (with CIs) can separate the model difference from the machine difference.

8. Your internal agent benchmark gives an agent that returns an empty response a score of 18%. Which part of the Agentic Benchmark Checklist has failed?

Outcome validity asks whether a pass really means success. A do-nothing agent should score about zero; if it does not, some tasks are graded as solved in their start state or the grader accepts empty output.
Interview questions (with model answers)

Q: A new model jumps 8 points on a benchmark overnight. List what you would check before believing it.
Same harness, budget and attempts as before? Same benchmark version and subset? Error bars and number of runs? Contamination (release date vs training cutoff, verbatim-reproduction probes)? Transcripts sampled for reward hacks, especially test edits? Independent reproduction? Does the gain show on a fresh or private benchmark of the same skill?

Q: How would you design a coding benchmark that resists contamination?
Use tasks created after target models' cutoffs, or from private / copyleft codebases; keep a private held-out split and run it yourself; refresh on a schedule with dated tasks; include a canary; probe models for verbatim reproduction of gold patches; strip git history and artifacts from images; version the benchmark when tasks change.

Q: What is the difference between a false pass and reward hacking?
A false pass is any wrong solution the grader accepts. Reward hacking is one cause of false passes, where the agent actively exploits the grader. Weak tests produce false passes even from agents that are trying honestly.

Q: Why might a benchmark score overstate real-world usefulness even with no contamination and a perfect grader?
Construct validity: the task distribution differs from real work (well-specified issues, popular repos, single language), the success criterion is narrower (tests pass vs maintainable code), and interaction costs (review, verification, context) are missing. METR's 2025 RCT is the standard example of benchmarks and field outcomes disagreeing.

Q: How would you audit an internal agent benchmark before trusting it for release decisions?
Walk the Agentic Benchmark Checklist. Task validity: pinned environments and tools, no reachable answers or future history, a reference solution that passes every task, unambiguous goals. Outcome validity: null and echo agents score about zero, reference solutions about 100%, a human spot-check of passes and fails, validated judges, state checks rather than reply matching. Reporting: known flaws, CIs and runs per task, harness, budget and sandbox resources, contamination measures. Then hold out a slice that no optimizer or prompt tuning ever sees, and version the benchmark whenever tasks change.

Sources and further reading