TASK SETS · GRADERS · LEADERBOARDS · CONTAMINATION

AI benchmarks:
what the numbers actually measure

ONE LEADERBOARD ROW· mid-2026 ·
resolve rate·····································68.4%
WHAT THE CELL DOES NOT SAY
task set·····································SWE-bench Verified, 500 tasks
harness·····································one agent scaffold, 1 attempt
graded by·····································pytest, in a container per task
reported by·····································whoever ran it

A benchmark is a fixed set of tasks, a program that puts a model through them, and a rule that decides whether each answer counts. The cell above is what comes out: one number, with everything that produced it left off the page. This tutorial explains each of those pieces in order, using real tasks, real Python and real leaderboards.

Each step carries a diagram, and some carry a code sample. open a short definition.

00

What a benchmark is, and what one test looks like

Before any of the arguments about leaderboards make sense, it helps to see one benchmark taken apart. This chapter defines the three parts every benchmark has, shows one real test from SWE-bench in full, names the three kinds of grader, and explains how a set of per-task results becomes the single percentage that ends up in a table.

STEP 01 / 05
A BENCHMARK IS THREE THINGS
every benchmark, however it is dressed up
TASK SET
the questions
500 fixed tasks
RUNNER
the program that answers them
model + harness
GRADER
the rule that marks an answer
pytest exit code
one score: 68.4%
change any one of the three and the score changes

A is a fixed set of tasks, a program that puts a model through them, and a rule that marks each answer. Nothing else. The task set is written once and then reused, which is what makes two models comparable at all. The program that runs it is called the . The rule that marks the answers is the grader. Change any one of the three and the published number changes, which is why most of this tutorial is about telling them apart.

a benchmark score is a property of a task set, a harness and a grader together, not of a model on its own
STEP 02 / 05
WHAT ONE TEST ACTUALLY LOOKS LIKE
one task, exactly as the model receives it
astropy__astropy-12907given to the model
repo · astropy/astropy @ d16bfe0
issue · “Modeling’s separability_matrix does not compute separability correctly for nested CompoundModels” · 340 words
test patchsealed until grading
3 fields handed over · 1 field withheld · 0 hints

Here is one real task from SWE-bench, the best known coding benchmark. A task is four fields: a repository, the commit it is pinned to, the text of a GitHub issue, and a set of tests. The model is given the first three. Instance astropy__astropy-12907 gives it the astropy repository at one commit, plus about 340 words describing a bug in how nested models compute separability. The tests are withheld until the model has answered. They are the tests the human who really fixed this bug wrote, taken from the pull request that closed the issue.

ONE TASK, READ OUT OF THE PUBLISHED TASK SET
from datasets import load_dataset

# The task set is a public download. Anyone can read every question in it.
tasks = load_dataset("princeton-nlp/SWE-bench_Verified", split="test")
task = next(t for t in tasks if t["instance_id"] == "astropy__astropy-12907")

task["repo"]               # 'astropy/astropy'
task["base_commit"]        # 'd16bfe0...', the commit the repo is pinned to
task["problem_statement"]  # the text of the GitHub issue, about 340 words
task["patch"]              # the fix a human really merged. Never shown.
task["test_patch"]         # the tests that grade the answer. Never shown.
task["FAIL_TO_PASS"]       # tests that must go from failing to passing
task["PASS_TO_PASS"]       # tests that were already passing and must stay so

# The model is handed the first three fields, and nothing else.
prompt = f"""Repository {task['repo']} at commit {task['base_commit']}.
Fix the issue below. Reply with a unified diff.

{task['problem_statement']}"""
a SWE-bench task is a repository, a commit, an issue and a hidden test suite, and it is downloadable by anyone
STEP 03 / 05
WHO OR WHAT DOES THE GRADING
who decides the answer is right
ANSWER KEY
graded by · a program
seen on · MMLU, SWE-bench
needs a known answer
HUMAN VOTE
graded by · a person
seen on · LMArena, WebDev Arena
measures preference
MODEL JUDGE
graded by · another model
seen on · MT-Bench, most evals
needs its own check
the first is reproducible · the second is not repeatable · the third is both, and wrong sometimes

Three kinds of grader exist, and every benchmark you will read about uses one of them. An compares the answer to a stored correct one, either by string match or, as in SWE-bench, by running a test suite. A person compares two answers and says which is better, which is what arenas do. A second model reads the answer against a written rubric and returns a verdict, called an . The first needs a question with a known answer. The second and third are what you reach for when no such answer exists.

THE THREE GRADERS, WITH THE SAME JOB
def grade_with_answer_key(answer: str, expected: str) -> bool:
    """Multiple choice, maths, anything with one correct string."""
    return normalize(answer) == normalize(expected)


def grade_with_tests(repo: Path, test_patch: str, must_pass: list[str]) -> bool:
    """SWE-bench. The answer key is a test suite instead of a string."""
    apply_patch(repo, test_patch)
    return run_pytest(repo, must_pass).returncode == 0


def grade_with_human_vote(answer_a: str, answer_b: str) -> str:
    """Arenas. No key exists, so a person picks. "a", "b", "tie" or "both bad"."""
    return show_side_by_side_and_wait(answer_a, answer_b)


def grade_with_model_judge(answer: str, rubric: str) -> bool:
    """A cheaper stand-in for the person, and it needs its own accuracy check."""
    verdict = judge_model.complete(JUDGE_PROMPT.format(rubric=rubric, answer=answer))
    return verdict.strip().lower().startswith("pass")
the grader is the part of a benchmark that decides what the number means, and it is rarely printed beside the number
STEP 04 / 05
THE SCORE IS AN AVERAGE OF PER-TASK RESULTS
one task, one bit · resolved or not resolved
342 resolved158 not resolvedmean of 500 bits = 68.4%

Each task produces one result. On SWE-bench that result is a single bit: the tests passed, or they did not. There is no partial credit for a patch that fixes half the bug, and no bonus for a patch tidier than the human one. Run all 500 tasks and you have 500 bits, of which 342 come back resolved. The is their mean, 342 divided by 500, or 68.4%. That average is the entire content of the sentence “68.4% on SWE-bench Verified”.

every leaderboard percentage is the mean of a column of per-task results, most of them yes or no
STEP 05 / 05
THE FOUR SHAPES A BENCHMARK COMES IN
four shapes, and this tutorial covers all four
FAMILYEXAMPLEGRADED BY
task setsSWE-bench, Terminal-Benchanswer key
question banksMMLU, GPQA, HLEanswer key
arenasLMArena, WebDev Arenahuman vote
live contestsICPC, AtCoder, IOIanswer key
three of the four have a right answer · one of them does not

Task sets give the model a job to do in a controlled environment and check the result, as SWE-bench and Terminal-Bench do. Question banks ask hard exam questions with known answers, as MMLU, GPQA and Humanity’s Last Exam do. Arenas show two answers to a person and record which one they prefer. Live contests put models into competitions written on the day, such as the ICPC world finals. The rest of this tutorial walks through all four, because the same word “benchmark” covers them all and they mean different things.

three of the four benchmark families have a correct answer to compare against, and arenas have none
deep dive: the words people use for scores, and what each one measures

Benchmark, eval, test suite. The words overlap. A benchmark is public and shared, so that numbers from different groups can be compared. An eval is usually private and specific to one product. A test suite checks code you wrote against behaviour you specified. The machinery is much the same; the difference is who the result is meant to convince.

Why a bit and not a score. Grading a patch on a scale would need a judge with an opinion, and opinions are not reproducible. Reducing the outcome to “the tests passed” makes the grade something two strangers can recompute and agree on. The cost is that a nearly correct answer scores the same as no answer at all.

Accuracy, resolve rate, rating. Question banks report accuracy, the share of questions answered correctly. Task sets report a resolve rate, the share of tasks completed. Arenas report a rating on an invented scale with no natural zero. All three get called “the score”, and only the first two are percentages of anything.

The task set is a file. SWE-bench Verified is a dataset on HuggingFace. GPQA is a CSV. Anyone can download either one and read every question and every answer. Keep that in mind for chapter 07, which asks what happens when the model being tested has read them too.

01

Where the tasks come from

The tasks in a benchmark have to come from somewhere, and where they come from decides what the score can tell you. This chapter follows one task set from the pull requests it was mined out of, through the container it runs in and the human screening that threw two thirds of it away, to the six different task sets that share the name “SWE-bench”. It ends with what a task looks like in benchmarks that have nothing to do with GitHub.

STEP 01 / 05
THE GRADER IS A TEST SUITE, NOT A MODEL
grading runs pytest · two lists decide the task
FAIL_TO_PASS · must flip to passing
test_separable[compound_model6-result6]
test_separable[compound_model9-result9]
PASS_TO_PASS · must stay passing
· test_coord_matrix
· test_cdot
· test_cstack
· … 15 more already green
grader: pytest exit codes · judge models involved: 0

Grading applies the withheld tests, called the , to whatever the model produced, and runs them. Two lists decide the outcome. names the tests that were failing before and must now pass, which is the proof the bug is fixed. PASS_TO_PASS names the tests that were already passing and must stay that way, which is the proof nothing else broke. Both lists must be satisfied. No rubric is consulted and no model is asked for an opinion, so two people grading the same patch get the same answer.

THE ENTIRE GRADING RULE FOR ONE TASK
def resolved(task, model_patch: str) -> bool:
    """One task in, one bit out. No partial credit anywhere in this function."""
    with docker_container(task["instance_id"]) as repo:   # pinned OS and versions
        if not apply_patch(repo, model_patch):
            return False                                  # the patch did not apply

        apply_patch(repo, task["test_patch"])             # sealed tests arrive now
        f2p = json.loads(task["FAIL_TO_PASS"])
        p2p = json.loads(task["PASS_TO_PASS"])

        report = run_pytest(repo, f2p + p2p)
        return all(report[test] == "PASSED" for test in f2p + p2p)
the SWE-bench grade is a pytest exit code, so the score contains nobody’s opinion about anything
STEP 02 / 05
ONE CONTAINER PER TASK
one Docker image per task · 500 images for 500 tasks
baseubuntu 22.04 + python 3.9.19
pinnednumpy 1.25.2 · scipy 1.11.1 · pytest 7.4.0
repoastropy @ d16bfe0, editable install
entrypointconda env “testbed”, activated
unpin one version and the score moves without the model changing

Each task ships as its own Docker image: an operating system, a Python version, every dependency pinned to the version that was current when the issue was filed, and the repository installed in place. This is not incidental packaging. The environment is half the benchmark, because a test that passes under scipy 1.11.1 and fails under 1.12 turns a correct patch into a zero. Benchmarks that let dependencies float produce scores that drift with the package index, which is why serious task sets pin every version.

a dependency resolving to a different version can move a published benchmark score with no model involved
STEP 03 / 05
2,294 TASKS, AND 500 SURVIVED
93 Python developers screened SWE-bench, one task at a time
SWE-bench, full
2,294 tasks
flaggedproblem statement underspecified38.3%
flaggedtests fail valid solutions61.1%
SWE-bench Verified
500 tasks
68.3% of the original tasks filtered out · 500 survived

Tasks are mined automatically, and a lot of them are unfair. The original SWE-bench holds 2,294 of them, taken from merged pull requests. OpenAI paid 93 experienced Python developers to read every one. They flagged 38.3% for problem statements too vague to solve from, and 61.1% for tests that would mark a valid solution wrong. In total 68.3% were filtered out, leaving the 500-task called SWE-bench Verified. That is the one every launch post quotes. The best known benchmark of agentic coding is the third of a benchmark that survived paid human review.

two thirds of the original SWE-bench tasks were thrown out as underspecified or unfairly graded
STEP 04 / 05
ONE NAME, SIX TASK SETS
six task sets · one name · no shared scale
EVERYTHING CALLED “SWE-BENCH”
Full·······································2,294 tasks, Python
Verified·······································500 tasks, human-screened
Lite·······································300 tasks, the cheap subset
Multimodal·······································front-end issues with screenshots
Multilingual·······································nine languages beyond Python
Pro·······································longer commercial tasks
a score on one says nothing about a score on another

“SWE-bench” names at least six different task sets. Full is the original 2,294. Verified is the screened 500. Lite is a cheap 300-task subset. Multimodal covers front-end issues with screenshots, Multilingual covers nine languages beyond Python, and Pro holds longer commercially sourced tasks. Their scores are not comparable to each other. A number quoted without its split is missing the piece that says which exam was sat.

a benchmark name without a split name does not identify which task set was actually run
STEP 05 / 05
WHAT A TASK LOOKS LIKE ELSEWHERE
one task, in three benchmarks that are not SWE-bench
Terminal-Benchgraded by · a test script checks /out
the archive at /data is corrupted. recover the files and put them in /out.
GPQA Diamondgraded by · one stored letter
a four-option physics question written by a PhD holder in that field
ARC-AGI-2graded by · exact grid match
three coloured grids showing a rule, plus one grid to complete
different subjects · the same task set, harness and grader underneath

A task is not always a GitHub issue. On Terminal-Bench it is an instruction, a container and a test script: install this package, recover this corrupted archive, get this server responding. On GPQA it is a multiple-choice question written by a PhD holder, hard enough that expert non-specialists with web access score around 34%. On ARC-AGI it is a handful of coloured grids showing a rule, plus one grid to complete. Different subjects, same three parts from chapter 00.

GPQA questions are written so that experts outside the field score about 34% even with the web open
deep dive: how a task set is mined, and what mining leaves out

Where tasks come from. A script walks a repository’s merged pull requests looking for ones that close an issue and touch test files. The code changes become the reference solution, the test changes become the hidden tests, and the issue body becomes the prompt. Nothing is written by hand, which is how you get 2,294 tasks cheaply, and also how you get 1,794 unusable ones.

The issue text is the entire specification. There is no ticket description, no reproduction script and no maintainer to ask. If the human who fixed this bug had context from a mailing list thread, that context is gone. This is the largest source of unfairness in mined benchmarks, and it is what the 38.3% flag was measuring.

PASS_TO_PASS is the regression guard. Without it, a patch that deletes the failing assertion would score. With it, the model has to leave the other tests in the file alone. It is also the list most likely to fail for boring reasons: a warning promoted to an error, a timestamp in an assertion, a test that was already flaky before anyone got involved.

What “solvable” hides. Many Verified tasks have a reference fix of one or two lines. Screening removed the unfair tasks, not the easy ones, so a high resolve rate mixes genuine debugging with a fair number of one-line edits that grep would find.

Task sets wear out. A set that everybody has trained against stops separating models, and a set whose tasks are too easy stops separating them too. Both problems get a chapter of their own later: contamination in chapter 07, and saturation in chapter 08.

02

Running the benchmark

Running a benchmark is a batch job, and the settings of that job are worth as much as the model is. This chapter covers what the harness does 500 times over, and how much it costs to do the same thing yourself. Then the settings that get declared, the amount the number moves when the run is repeated, the rules a submitter promises to follow, and the error bar that never appears next to the score.

STEP 01 / 06
FIVE HUNDRED CONTAINERS, ONE NUMBER
the harness loop · repeated 500 times, once per task
1build the image, check out the commit
2hand the model the issue text
3let it work, up to the turn cap
4take whatever patch it has
5apply the test patch, run pytest
6record one bit
resolved
342 / 500
the turn cap is set by the harness, not the model · 342 ÷ 500 = 68.4%

The does the same six things 500 times: build the image, hand the model the issue, let it work until it stops or hits the turn cap, take whatever patch it has at that moment, apply the withheld tests, record one bit. Notice who owns the turn cap. The harness does, not the model, so “how long is the model allowed to work on this” is a benchmark setting rather than a model property. Some hours later, 342 tasks have come back resolved and the run is over.

500 RESULTS IN, ONE PERCENTAGE OUT
results = {task["instance_id"]: resolved(task, patches[task["instance_id"]])
           for task in tasks}

resolve_rate = sum(results.values()) / len(results)   # 342 / 500

print(f"{resolve_rate:.1%} on SWE-bench Verified")    # 68.4% on SWE-bench Verified
the harness decides how many turns the model gets before its answer is taken, and that setting is not in the table
STEP 02 / 06
ANYONE CAN RUN THIS AT HOME
reproducing a published score, in two halves
GRADINGcheap
· pip install swebench
· docker, tens of GB of images
· a few hours of CPU
· no API calls
ANSWERINGexpensive
· 500 model runs
· each one a full agent session
· paid per token
· this is the real bill
checking somebody’s grade is easy · checking their answers is not

Nothing about running a benchmark is privileged. The task set is a public download, and the harness that grades it is a package the benchmark’s own authors publish. Install it, point it at a file of answers, and it builds the containers and grades them on your machine. Grading is cheap: it costs disk, Docker and a few hours. Producing the answers is the expensive half, because that means 500 model runs, and on a frontier model that is a real bill. This is worth knowing because it makes third-party checking possible, which chapter 05 is about.

GRADING 500 TASKS ON YOUR OWN MACHINE
pip install swebench    # the harness, published by the benchmark's own authors
docker info             # required. One container image per task, tens of GB.

# preds.jsonl holds one line per task: {"instance_id": ..., "model_patch": ...}
python -m swebench.harness.run_evaluation \
  --dataset_name princeton-nlp/SWE-bench_Verified \
  --predictions_path preds.jsonl \
  --run_id my_first_run \
  --max_workers 8

# Grading the 500 tasks costs nothing but time and disk.
# Producing preds.jsonl is the expensive half: that is 500 agent runs.
the official SWE-bench harness is a pip package, so any reader can regrade a published claim themselves
STEP 03 / 06
PASS@1 IS A DECLARATION, NOT A DEFAULT
same model, same 500 tasks, two declared protocols
PASS@1BEST@5
predictions per task15
selection stepnonepick by tests
metadata attempts12+
resolve rate68.4%higher
API bill×1×5
metadata.yaml · tags.system.attempts · you declare which one

means one answer per task, scored as submitted. means several attempts plus a step that picks among them, and on maths boards the same idea appears as cons@64 or maj@k. The gap between them is large enough to reorder a leaderboard, so SWE-bench makes submitters say which one they did: tags.system.attempts is either 1 or 2+, and the checklist in the pull request repeats the question in words. A table that omits this column may be comparing a model that got one try against a model that got sixty-four.

WHAT THE @K IN PASS@K IS DOING
import math


def pass_at_k(n: int, c: int, k: int) -> float:
    """Chance that at least one of k attempts passes, given c of n did.

    From the Codex paper. Estimating it this way, instead of asking whether any
    of k tries happened to pass, removes the bias in a small sample.
    """
    if n - c < k:
        return 1.0
    return 1.0 - math.prod((n - c - i) / (n - i) for i in range(k))


pass_at_k(n=10, c=1, k=1)   # 0.10, one attempt allowed
pass_at_k(n=10, c=1, k=8)   # 0.80, eight attempts at the same task
a model that solves a task one time in ten looks eight times better when it is allowed eight attempts
STEP 04 / 06
RUN IT AGAIN AND THE NUMBER MOVES
same model · same 500 tasks · same config · five runs
run 1336 / 50067.2%
run 2342 / 50068.4%
run 3345 / 50069%
run 4341 / 50068.2%
run 5346 / 50069.2%
66%the rail is four points wide70%
spread across five runs: 2.0 points · Terminal-Bench requires all five

Nothing here is deterministic. Sampling is random, models time out at different points, and container builds fail in ways that get counted as failures. Run the same model over the same 500 tasks five times with identical settings and the results land a couple of points apart. That is one reason reported scores commonly differ by two to three points between groups who think they measured the same thing. Terminal-Bench draws the obvious conclusion and refuses single-run submissions: five runs, or no row.

Terminal-Bench will not accept a single run, because one run of a random process is not a measurement
STEP 05 / 06
THE RULES YOU AGREE NOT TO BREAK
the submission checklist, pasted into your own pull request
[x]pass@1 submission, one prediction per task
[x]no use of FAIL_TO_PASS or PASS_TO_PASS
[x]no use of the hints field
[x]web browsing absent, or prevented from finding the fix
verified byyou, about yourself

Four boxes go into the pull request. The run was pass@1. Nobody read or PASS_TO_PASS from the dataset. Nobody used the hints field, which holds the maintainer comments from the original issue thread. The model either had no web access, or had it with steps taken to stop it finding the commit that fixed the bug. Each of these is checkable in principle from the recorded run and checked in practice by nobody, because the person confirming it is the person who ran it.

the rule against looking up the real fix is enforced by a checkbox the submitter ticks about themselves
STEP 06 / 06
THE NUMBER HAS NO ERROR BAR ON THE PAGE
what the board prints, and what 500 binary trials support
PRINTED ON THE LEADERBOARD
68.4%
SUPPORTED BY THE SAMPLE SIZE
68.4% ±2.1
rival, 70.1%
√(0.684 × 0.316 ÷ 500) = 0.021 · the two intervals overlap

Five hundred yes-or-no trials at a rate of 0.684 carry a standard error of 2.1 points, which puts the 95% interval at roughly 64.3% to 72.5%. So a rival reporting 70.1% is inside the interval of a model reporting 68.4%, and the difference between them is not evidence of anything. The leaderboard prints 68.4% and a rank. This is the first of three places in this tutorial where a benchmark reports a single number for a quantity it measured with visible noise, and it comes back in chapters 06 and 08.

THE INTERVAL THE TABLE LEAVES OUT
import math

n, resolved_count = 500, 342
p = resolved_count / n                    # 0.684

se = math.sqrt(p * (1 - p) / n)           # 0.0208, so 2.1 points
lo, hi = p - 1.96 * se, p + 1.96 * se     # 0.643 to 0.725

print(f"{p:.1%}, 95% interval {lo:.1%} to {hi:.1%}")
# 68.4%, 95% interval 64.3% to 72.5%
# A rival reporting 70.1% is inside that interval. The table still ranks it above.
the 95% interval on a 500-task benchmark is about four points wide, and no leaderboard prints it
deep dive: the run settings that never fit in the table

Turn caps and token budgets. A harness stops the model after some number of turns, or some number of tokens, or some wall-clock limit. Whichever it is, the limit is a benchmark setting nobody prints. Doubling it changes the score without changing the model.

Build failures count against you. If the container will not build or a dependency has vanished from the index, the task is scored as not resolved. That is the correct choice for reproducibility and it means a percentage point or two of every published score is infrastructure rather than capability.

Timeouts are silent failures. A model that was on its way to a correct patch when the clock ran out is indistinguishable, in the results file, from one that had no idea. The recorded run shows the difference, which is one reason boards ask for it.

best@1 is not pass@1. Generating sixty-four answers, picking the best with a verifier, and reporting the result as a single-attempt score is the most common way a table becomes misleading without anybody stating a falsehood.

Re-running somebody else’s submission is not free. Grading is cheap, but reproducing the answers means paying for 500 model runs at current API prices. That figure is why independent verification is rare, and it is the subject of chapter 05.

03

The score belongs to the model plus its harness

A leaderboard row names a model, and that is a compression. What got measured was a model plus the program driving it, and the program is worth about twenty points either way. This chapter shows that program as code, then takes the same weights through a second benchmark with a second set of rules, and explains why the two published numbers about the same model do not have to agree.

STEP 01 / 04
ONE MODEL, THREE HARNESSES, THREE NUMBERS
identical weights · three agent programs around them
minimal, 2 tools
bash + edit, 30 turns
54.8%
standard, 6 tools
+ search, tests, 75 turns
68.4%
tuned, 6 tools
+ retry on red tests, 120 turns
74.2%
19.4 points between the best and worst wrapper · one model throughout

Take one model and wrap it in a with fewer tools, a lower turn cap and no retry when the tests come back red. The resolve rate falls to 54.8%. Wrap the same weights in a more generous one and it climbs to 74.2%. The model did not change. What the agent loop does between turns changed, and that was worth 19.4 points. Terminal-Bench responded by giving the scaffold its own column, so a row there names a model and an agent, because the pair is what got measured.

THE HARNESS. EVERY ARGUMENT HERE IS WORTH POINTS
def solve(task, model, tools, max_turns: int = 40) -> str:
    """The harness around the model. Every argument here changes the score."""
    messages = [{"role": "user", "content": build_prompt(task)}]

    for _ in range(max_turns):
        reply = model.chat(messages, tools=tools)
        if not reply.tool_calls:
            return extract_diff(reply.content)
        messages += run_tools(reply.tool_calls)

    return ""   # out of turns. An empty patch is graded as not resolved.


TOOLS = ["read_file", "grep", "edit", "run_tests"]
# Take run_tests away and the same model cannot check its own work before
# answering. Nothing about the weights changed, and the score falls.
a Terminal-Bench row names a model and an agent, because the pair is the thing that was scored
STEP 02 / 04
89 TASKS, FIVE RUNS REQUIRED
Terminal-Bench 2.0 · 89 tasks · 5 runs required per submission
software engineeringsecurityscientific computingdata sciencedebugginggames… 10 more categories
one submission = 89 tasks × 5 runs = 445 graded attempts

Terminal-Bench 2.0 puts a model at a shell inside a container and gives it 89 curated tasks across 16 categories: software engineering, security, scientific computing, data science, debugging, games. Nearly a hundred contributors built it under Stanford and the Laude Institute, and they sized it deliberately, keeping the tasks hard enough that frontier performance stays under half. A submission runs the whole set five times through the Harbor framework and sends in the job directories.

Terminal-Bench 2.0 was sized on purpose so that no model would clear half of it
STEP 03 / 04
THE SETTINGS THAT MOVE MORE THAN THE MODEL
the settings that move a score, and how the table reports them
thinking budget···························not stated
tool access···························sometimes “with tools”
context window···························not stated
retries per task···························not stated
temperature···························not stated
scaffold version···························not stated
6 settings · 1 of them ever printed beside the number

Thinking budget, tool access, context window, retries, temperature, scaffold version. Six settings, each able to move a resolve rate by several points. One of them occasionally appears beside a number, as the phrase “with tools”. That phrase is doing a lot of work. A model given a Python interpreter and a model given none are two different systems, and on maths benchmarks the gap between them is routinely larger than the gap between model generations. A table that lists one number per model has picked one configuration per model and thrown the rest away without saying so.

“with tools” and “without tools” are two different benchmarks under the same column header
STEP 04 / 04
THE HARNESS THAT ONLY WORKS ON ONE BENCHMARK
the same three scaffolds, ranked by two different task sets
SWE-BENCH VERIFIED · PYTHON, PYTEST
1tuned scaffold74.2%
2standard scaffold68.4%
3minimal scaffold54.8%
TERMINAL-BENCH 2.0 · 16 CATEGORIES
1standard scaffold41.6%
2minimal scaffold38.9%
3tuned scaffold33.1%
the order inverts · the scaffold tuned for one task set is last on the other

Nothing stops a team from tuning the scaffold to the shape of the task set. SWE-bench Verified is Python, pytest, one repository, one issue at a time, so a scaffold that assumes all four does well there and then falls over on a benchmark where the model has to install a package, read a manual page and drive a game. Rankings invert between task sets for exactly this reason. It is allowed, it is cheap, and it is the main reason two boards disagree about which system is best.

tuning the harness to one task set is allowed and cheap, and it is the main reason boards disagree
deep dive: the harness settings that move a score

What Harbor standardizes. Terminal-Bench runs submissions through a shared execution framework, so the container, the timeout and the transcript format are the same for everyone. It does not standardize the agent. That is the point: the agent is a competitor, not part of the fixture.

Mini-agents versus frameworks. A 200-line loop with four tools often scores within a point or two of a large framework, and sometimes above it. Extra machinery buys reliability and observability more than it buys resolve rate.

Why the columns split. Once a board admits that a score belongs to a pair, it needs two columns, and then it needs a policy about which pairs may be compared. Boards that never split the column are comparing pairs that share only one member, without saying so.

Version your harness like a dependency. A scaffold without a pinned commit is a number nobody can reproduce, including the team that published it three months later.

The cost of five runs. Terminal-Bench asks for five runs of 89 tasks, so a submission is 445 model runs before anything is published. The rule exists because a single run is noise, and it prices small teams out of the board.

04

Who builds them, who runs them, who publishes the table

This is the chapter that answers who these organisations actually are. Benchmarks are mostly built by universities and non-profits, mostly run by the labs whose models are being measured, and mostly published by whoever felt like making a table. Nobody applies to a central body, because there is no central body. What follows is the practical route a number takes onto a page, for code benchmarks and for arenas, and what a badge on that row does and does not promise.

STEP 01 / 07
FOUR JOBS, AND NOBODY HOLDS THEM ALL
four jobs behind one leaderboard row
AUTHOR
writes the tasks and the grader
Princeton NLP, ARC Prize, Epoch AI
RUNNER
executes the model against them
usually the lab that made the model
HOST
publishes the table and vets rows
swebench.com, tbench.ai, LMArena
REPEATER
copies the number elsewhere
aggregators, launch posts, press
nothing requires these four to be four different parties

Four separate jobs sit behind a published score. Someone writes the tasks and the grading rule. Someone runs a model against them. Someone publishes a table and decides which rows go on it. Someone repeats the number elsewhere. The authors are mostly universities and non-profits: SWE-bench came out of Princeton with collaborators at Stanford and Chicago, Terminal-Bench out of Stanford and the Laude Institute, ARC-AGI from the ARC Prize Foundation, FrontierMath from Epoch AI. The runner is usually the lab that made the model. Those two facts together explain most of this tutorial.

the people who write a benchmark and the people who run it are almost never the same people
STEP 02 / 07
FOUR PLACES A TABLE CAN LIVE
four places a benchmark table lives
the benchmark author
swebench.com + a GitHub repo
runs a spot check
the framework
tbench.ai, via Harbor
validates your job dirs
the aggregator
llm-stats, CodeSOTA
transcribes numbers
the lab itself
the model’s launch post
ran it on itself
the dashed one publishes tables and executes nothing

A lives in one of four places. The benchmark’s authors keep one: swebench.com publishes the table and SWE-bench/experiments holds the submissions behind it. A framework keeps one, like tbench.ai for Terminal-Bench, where the board and the runner ship together. Aggregators keep one, collecting hundreds of models into a single sortable table. And the lab keeps one, in its launch post, which is where most numbers appear first and where most readers meet them.

the number you read most often lives on the launch post of the lab that produced it
STEP 03 / 07
GETTING A ROW: A PULL REQUEST
the directory your pull request adds to SWE-bench/experiments
evaluation/verified/20260803_myagent/·······················
all_preds.jsonl·······················one patch per task
logs/<instance>/·······················patch.diff · report.json · test_output.txt
trajs/·······················every step the agent took
metadata.yaml·······················model, org, attempts, report link
README.md·······················the system, and who built it
5 required paths · then paste the get_results output into the PR

For a code benchmark, applying means opening a pull request against SWE-bench/experiments with one directory named evaluation/<split>/<date>_<model_name>. Inside it: all_preds.jsonl with one answer per task, a logs/ folder holding patch.diff, report.json and test_output.txt for every task, a trajs/ folder of , a metadata file and a README. Then run python -m analysis.get_results and paste its output into the pull request. The application form is a pull request, and the reviewer is one maintainer.

applying to the best known coding leaderboard means opening a pull request against a GitHub repository
STEP 04 / 07
GETTING A ROW: AN API ENDPOINT
applying to an arena · no predictions are uploaded
what you supply···················an OpenAI-compatible endpoint
for how long···················30 days, minimum
shown to voters as···················an anonymous codename
results during preview···················private to the provider
what you sign···················this model equals the one you ship
the last line was added after a preview model turned out not to be the shipped one

Arenas work the other way round. Nothing is uploaded, because there is nothing to grade offline. A provider hands over an API endpoint, preferably OpenAI-compatible, for at least 30 days, and the arena spends those days sending it real user prompts. During the preview the model appears to voters under an anonymous codename, and results go back to the provider privately until release. The provider must confirm in writing that the previewed model is the one they intend to ship. That clause has a date attached: in April 2025 a lab put a variant tuned for human preference on the board while shipping a different model to the public. Models tuned specifically for the arena are now barred.

a model enters an arena as a live endpoint under a codename, not as a file anyone can inspect
STEP 05 / 07
WHAT A SUBMITTER HANDS OVER
metadata.yaml · what you disclose to get a row
info:
name:my-agent + the model it drove
site:source code, if it is open
report:paper or blog post · mandatory
authors:the humans, by name
tags:
model:the LiteLLM model names used
os_model:are the weights open?
os_system:is the scaffold open?
system.attempts:1, or 2+
no technical report, no row · the maintainer also wants push access

The metadata file is the disclosure. It carries the entry name, a link to the source if the system is open, and a link to a technical report. It also carries the contributors by name, the exact model identifiers used, whether the weights are open, whether the harness is open, and the attempts count. The report is not optional, and submissions without adequate documentation are rejected. Since November 2025 the Verified and Multilingual boards narrowed further, taking submissions only from academic teams with peer-reviewed publications, so for those two the answer to “how do I apply” is now “publish a paper first”.

since late 2025 the SWE-bench Verified board takes submissions only from academic teams with peer-reviewed work
STEP 06 / 07
THE AGGREGATOR RUNS NOTHING
one number, four pages · executions along the way: 1
the launch post
68.4%
ran it
aggregator A
68.4%
copied it
aggregator B
68.4%
copied A
a news article
68.4%
copied B
containers run by these four pages0

An aggregator reads a percentage off a launch post, puts it in a row, sorts by it and adds a rank. A second aggregator copies the first. A news article copies the second. Four pages now carry the number, and it was executed exactly once, by the party it describes. This is how most tables anyone has ever scrolled are built. It is also why a figure appearing in many places is not corroboration: the copies are downstream of one run, and none of the copying parties owns a container.

most leaderboard rows you read were never executed by whoever published the table
STEP 07 / 07
WHAT A BADGE ACTUALLY CERTIFIES
five badges, five boards, no shared definition
os_modelthe weights are downloadable
os_systemthe scaffold source is public
checkeda maintainer re-ran a random subset
Verified (ARC)trusted source, reproducible, splits agree
no badgemost rows on most boards
“verified” means something different on each one

The badges are not a standard. On SWE-bench, os_model and os_system say which halves of the system are open, and “checked” means a maintainer re-ran the submission on a random subset and got the same answer. On ARC Prize, “Verified” means something stricter: the run came from a trusted source, it is reproducible, and the public and semi-private scores agree within a stated tolerance. Two boards, two words, no shared definition, and most rows on most boards carry no badge at all.

there is no shared definition of a verified benchmark result across boards
deep dive: reading a submission directory

What the results script validates. analysis/get_results reads the submitted answers and logs, recomputes the resolve rate from the per-task reports rather than trusting the summary, and cleans the directory into the layout the leaderboard renders from. If a claimed number and its logs disagree, this is where it surfaces.

Trajectories matter more than answers. A patch tells a reviewer what the system produced. A trajectory tells them how, including whether it opened a browser, read a test file it should not have, or got the answer on the first try because the fix was in its training data. Boards that require trajectories are asking for the only artifact that makes the honour-system checklist auditable.

Hosted leaderboards come and go. HuggingFace ran the Open LLM Leaderboard for years and retired it once the benchmarks under it stopped separating models. A board is a maintained service, and when maintenance stops the table stops being a record of anything current.

Some submissions arrive by email. Terminal-Bench asks for five runs plus the job directories, sent to the maintainers for validation. It works, and it also shows how much of this infrastructure is a few people reading attachments.

Rejection is normal. Missing report, missing logs, an implausible jump with no explanation, a harness nobody can inspect. The gate is one maintainer’s judgment, which is both the weakest part of the system and the reason the worst submissions never appear.

05

Nobody is required to check the number

The default is that a lab runs a benchmark on its own model and publishes the result, and no one else touches it. Independence is not a property of the system. It is something a specific party pays for, one run at a time. This chapter sorts numbers by who produced them, shows what happens when a second group runs the same tasks, and looks at two cases where the money and the task set came from the same place.

STEP 01 / 06
THE DEFAULT IS SELF-REPORTED
who is required at each step, by any rule anywhere
build the task set·······················the benchmark author
run the model·······················you
grade the output·······················your machine
publish the number·······················you
check the number·······················nobody
provenance of the 945 attempts so farself-reported

Walk the pipeline and ask who is required at each step. The authors built the task set. The lab ran the model. The lab’s own machine graded the output. The lab published the number. Who checks it? Nobody, and no rule anywhere says otherwise. There is no accrediting body for benchmark results, no audit requirement and no registration. Almost every number in circulation is . That is not a scandal, it is the ordinary condition of the field, and it is the reason the rest of this chapter exists.

there is no accrediting body for AI benchmark results, and there never has been
STEP 02 / 06
FOUR POSTURES OF PROVENANCE
four postures · strength increases downward, and so does cost
1self-reportedthe lab, on its own model
2author-runthe benchmark’s own authors
3third-partya neutral harness, every model
4held-outtasks the model can never see
only the last one survives a model that has read the questions

Sort every number you read by who produced it. Self-reported: the lab ran the benchmark on its own model. Author-run: the benchmark’s own authors ran it, which removes the incentive but not the harness question. Third-party: an outfit such as Epoch AI, Artificial Analysis or Vals AI runs every model itself on one harness, so at least the differences between rows are real. And : the model is scored on tasks it can never have seen. Confidence increases down that list, and so does cost, which is why almost everything sits at the top of it.

four postures of provenance, and only the last one survives a model that has already read the questions
STEP 03 / 06
SOMEONE ELSE RUNS THE SAME 500
same model · the same 500 tasks · two runners
us, on our own accountself-reported68.4%
our scaffold, our API version
a neutral harnessthird-party64.9%
their scaffold, their retry policy
3.5 points apart · neither party did anything wrong

A neutral group re-runs the identical 500 tasks against the identical model and reports 64.9% where the lab reported 68.4%. Neither party did anything wrong. Their harness has different tools and their API version is two weeks newer. Their retry policy gives up sooner, their sampling settings differ, and refusals count as failures on their side. A third-party number is not automatically the true one. It is a second measurement, and two measurements are how anyone finds out the spread.

the same model on the same 500 tasks scores differently depending on who runs it
STEP 04 / 06
WHO PAID FOR THE BENCHMARK
FrontierMath · the order events actually happened in
·a lab funds the benchmarknot disclosed at the time
the benchmark is builtproblems written and held
the same lab gets the problemsverbal no-train agreement
a record is announcedfunding disclosed the same day
one holdout set retained · the record itself never independently verified

FrontierMath is a hard mathematics benchmark built by Epoch AI. In December 2024 Epoch disclosed that OpenAI had funded it, under an agreement that kept the funding quiet until the o3 announcement, the same announcement that set a record on it. OpenAI also had access to the problem set, covered by a verbal agreement not to train on it, and Epoch retained a separate holdout set. Epoch stated it had not been able to verify the result independently. Contributors said afterwards that they had not known who was paying. Nothing here requires bad faith to be a problem. The structure alone is enough to make the number hard to interpret.

the lab that set the record on FrontierMath had also funded it, and the funding was disclosed on announcement day
STEP 05 / 06
THE CLEAN SET WAS BUILT BY A CONTESTANT
SWE-bench Verified · who did which job
mined the 2,294 tasks···················an academic team
hired the 93 screeners···················a frontier lab
chose the surviving 500···················that lab’s annotation process
competes on the 500···················that lab, and every other
current mitigation: zero-data-retention terms and held-out splits

The same shape sits at the centre of this tutorial. SWE-bench Verified, the 500-task everybody quotes, was assembled by OpenAI, with 93 hired developers deciding which tasks were fair. Every lab now competes on a set that one of them curated. This is structural rather than an accusation, and the field’s answer to it is contractual rather than technical: terms on the APIs that see unpublished tasks, held-out splits nobody gets, and written no-train agreements where a verbal one used to do. Chapter 07 is where those measures get real teeth.

the “clean” 500-task set everyone quotes was assembled by one of the labs competing on it
STEP 06 / 06
THE ROW YOU CAN DEFEND
the six fields that make a number comparable
RESOLVE RATE · 68.4% · WITH ITS FOOTNOTES
split·······································SWE-bench Verified, 500 tasks
scaffold·······································my-agent @ 4f21ac9
attempts·······································pass@1
artifacts·······································logs + trajectories published
run by·······································us, on our own account
dated·······································2026-08-03
the average launch post publishes two of the six

Publishing a number of your own takes six fields to make it comparable to somebody else’s. Name the split. Name the harness and pin its commit. Declare the attempts. Publish the logs and the trajectories, which is the cheapest form of independence available to anyone. Say who ran it. Date it, because every figure in this field has a half-life. A table missing one of the six is not comparable to another table, and the average launch post publishes two.

six fields make a benchmark number comparable, and the average launch post publishes two of them
deep dive: the economics of an independent run

What zero-data-retention covers. It is a contract term binding a provider not to store or train on the requests sent to it. It does not bind the model that already read the public internet, and it cannot be verified from outside. It is a promise with a signature on it, which is more than a verbal agreement and less than a measurement.

Who pays for third-party runs. Somebody has to buy 500 model runs per row. Third-party evaluators fund that through subscriptions, grants or their own products, which means their coverage follows their funding, and models nobody pays to evaluate go unevaluated.

Aggregate indices hedge the runner. An index that averages several benchmarks inherits the provenance of each one. Averaging four self-reported numbers does not produce an independent number; it produces a self-reported average.

Logs are the cheap form of independence. Publishing every trajectory costs nothing but disk and lets a stranger check the claim without buying the run. A submission with logs is a claim anybody can attack. A submission without them is a claim nobody can.

“We could not verify this result.” When a benchmark author writes that sentence next to a record, it is not a formality. It means the strongest claim available about that number is that someone reported it.

06

When there is no right answer: human judges

What is being measured when there is no correct answer to compare against? Everything so far has had a grader that could be right: the tests pass or they do not. Arenas throw that away and ask people which of two answers they prefer, then turn millions of those choices into a rating with an interval on it. This chapter covers who those people are, whether anyone pays them, how their votes become a number, and what happens when a model is asked to do the same job.

STEP 01 / 06
A BENCHMARK WITH NO CORRECT ANSWER
one prompt · two anonymous answers · no answer key anywhere
model A · name hidden
model B · name hidden
A is betterB is bettertieboth are bad
graders: pytest 0 · humans 1 · what is measured: preference

A visitor types a prompt. Two models answer side by side with their names hidden, and the visitor picks A, B, tie, or both bad. In WebDev Arena the two answers are rendered web pages rather than text, so what is being compared is two running interfaces. There is no hidden test here and no grader, because there is nothing to be right about: “build me a landing page for a bakery” has no reference solution. What accumulates is preference, which is a real signal about a different question than the one SWE-bench answers.

ONE BATTLE, AS THE ARENA STORES IT
PROMPT, TYPED BY A VISITORbuild me a landing page for a bakery, with a menu and an order form
MODEL A, NAME HIDDENa running page: warm colours, a photo grid, the form validates
MODEL B, NAME HIDDENa running page: plainer, faster, the form posts nowhere
THE VOTEA · B · tie · both bad. one click, no explanation asked for
WHAT IS RECORDEDthe pair, the winner, the prompt. no correctness anywhere
no test patch · no answer key · no exit code · nothing here can be re-run
an arena has no answer key, so what it ranks is which answer people preferred
STEP 02 / 06
WHO VOTES, AND WHETHER THEY ARE PAID
the two kinds of person who grade AI answers
ARENA VOTERS
· anyone who opens the site
· unpaid, unscreened, anonymous
· millions of votes, no expertise claim
· they judge what they prefer
HIRED ANNOTATORS
· screened for the subject
· paid per item or per hour
· hundreds of items, not millions
· they judge against a written rule
bias controls ·names hiddensides randomisedbots filteredstyle regressed out

The voters are whoever opens the site. They are not paid, not screened for expertise and not identified, and their votes are the entire dataset. That sounds fragile, and the arena’s answer is not to trust any single vote. Model names are hidden, so brand preference cannot operate. Which model appears on the left is random, so position preference averages out. Duplicate and automated voting is filtered. The remaining bias is real and structural: on a question the voter cannot check, a confident wrong answer beats a hedged right one, and no amount of blinding fixes that.

nobody is paid to vote in the public arenas, and nobody checks whether a voter knows the subject
STEP 03 / 06
PAYING EXPERTS INSTEAD
the same votes, scored two ways
DEFAULT RANKING
1model V
2model W
3model X
4model Y
5model Z
length and markdown count
STYLE CONTROL
1model X
2model V
3model Z
4model W
5model Y
length and markdown removed
model X moves from third to first once formatting stops counting

The other way to use people is to hire them. SWE-bench Verified exists because OpenAI paid 93 experienced Python developers to screen tasks. Humanity’s Last Exam was assembled from questions written by close to a thousand subject experts, with prizes paid per accepted question. Hired annotators are screened, briefed with a written rule and measured against each other for agreement, which makes their labels usable as an answer key. They also cost money per item, so there are hundreds of them rather than millions. Arenas buy scale; hired experts buy reliability.

paid experts are used to build task sets and answer keys, and unpaid volunteers are used to rank live models
STEP 04 / 06
FROM VOTES TO A RATING
Bradley-Terry rating · refit over every battle ever played
1,200 rating1,400 rating
100 votesrating 1,284±62 points
a model needs at least 1,000 votes before its rating settles

Ratings come from the , a win-probability model fitted over the whole battle history at once. That is the difference from chess Elo, which updates incrementally and therefore depends on the order the games were played. Here a vote cast today can move a rating computed for a model last year. The intervals come from refitting on resampled battles, and battles are reweighted so one heavily-voted pairing does not dominate. A model needs at least a thousand votes before its rating stops drifting, and usually many more.

THE FIT THAT TURNS VOTES INTO RATINGS
import numpy as np
from sklearn.linear_model import LogisticRegression

# One row per battle: +1 for the model on the left, -1 for the one on the right.
X = np.zeros((len(battles), n_models))
y = np.zeros(len(battles))
for i, (left, right, winner) in enumerate(battles):
    X[i, left], X[i, right] = 1, -1
    y[i] = 1.0 if winner == "left" else 0.0

fit = LogisticRegression(fit_intercept=False).fit(X, y)
ratings = 400 / np.log(10) * fit.coef_[0] + 1000   # rescaled to look like Elo

# The interval comes from refitting on resampled battles, over and over.
draws = [fit_ratings(resample(battles)) for _ in range(100)]
lo, hi = np.percentile(draws, [2.5, 97.5], axis=0)
ratings are refitted over every battle ever played, so a vote cast today can move a rating from last year
STEP 05 / 06
RANK 3 AND RANK 7 ARE THE SAME MODEL
five ranks · 95% intervals · vote counts on the right
rank 1
84,000 votes
rank 2
61,000 votes
rank 3
38,000 votes
rank 5
7,200 votes
rank 7
4,900 votes
rank 3 and rank 7 overlap · the gap between them is 13 rating points

Interval width is driven mostly by vote volume. A model with 84,000 battles carries an interval of a few rating points; a model with 4,900 carries one of 30 or more. So a table where rank 3 sits 13 points above rank 7 is showing two models it cannot tell apart, and it says so in a column most readers skip. Ranks are assigned by significant separation rather than raw order, which is why several models share a rank number. Formatting also moves votes: longer answers with headers and bold text win more often, so the arena publishes a second board with applied, and models move several places between the two.

a 25-point gap on an arena can be a statistical tie, and the board says so in a column most people skip
STEP 06 / 06
THE MODEL AS JUDGE
the third grader: a model marking another model
the question · what was asked
the answer · what came back
the rubric · what counts as good
judge model
no answer key
pass / fail
a personminutesthe standard
a model judgesecondsagrees most of the time
a judge is only usable once someone has measured how often it matches people

The third grader from chapter 00 is a model. Give one the question, the answer and a written rubric, and it returns a verdict in seconds for a fraction of a cent. That is the only way to grade thousands of open-ended answers on a schedule, and it is how most private evaluation is done. It also has known failure modes: judges prefer whichever answer they read first, prefer longer answers, and prefer text from their own model family. The fixes are mechanical. Ask twice with the sides swapped and keep only the verdicts that agree with themselves, then measure the judge against human labels before trusting it. The evals tutorial covers that measurement in depth.

A MODEL JUDGE, AND THE CHECK IT NEEDS
def judge(question: str, answer_a: str, answer_b: str) -> str:
    """Ask a model which answer is better. Returns "a", "b" or "tie"."""
    reply = judge_model.complete(JUDGE_PROMPT.format(q=question, a=answer_a, b=answer_b))
    return parse_verdict(reply)


def judge_both_ways(question: str, answer_a: str, answer_b: str) -> str | None:
    """Judges favour whichever answer they read first, so ask it twice."""
    first = judge(question, answer_a, answer_b)
    second = judge(question, answer_b, answer_a)     # same pair, sides swapped
    return first if first == flip(second) else None  # None: it disagreed with itself


# A judge is worth using only once you know how often it matches people.
agreement = sum(judge_both_ways(*pair) == label for pair, label in labelled) / len(labelled)
a judge model favours whichever answer it reads first, which is why serious setups ask it twice with the sides swapped
deep dive: preference data and its biases

Preference and accuracy come apart on hard prompts. On a question the voter cannot check, a confident wrong answer beats a hedged right one. That failure mode is structural, it gets worse as prompts get harder, and it is the main reason arena rank and benchmark rank disagree at the top.

Category boards are much thinner. The overall board may have tens of thousands of votes per model. The coding, maths, vision and WebDev subsets are slices of that. Same method, far wider intervals, and the same rank column presented with the same confidence.

What a tie vote does. Ties carry information about two models being close, and they are included in the fit rather than discarded. “Both are bad” is different again: it says something about the prompt, and pairs where both sides fail show where the frontier actually is.

Who is voting. The population is people who chose to visit an LLM arena. Their preferences are real and they are not a random sample of your users, so an arena rating is evidence about general appeal rather than about your product.

Why the arenas matter anyway. They are the only large public evaluation where the questions are written fresh by users every day, so there is nothing to leak in advance. Chapter 07 takes that idea further.

07

Has the model already seen the answers?

This chapter answers the question that makes people distrust benchmarks in the first place: can a model already know the answers? It can, and often does, because the questions are published and the training data is a scrape of the same internet. What follows is how anyone detects that, and the three things benchmark authors do about it: date-stamping every question, holding a set back, and running contests written after every training cutoff.

STEP 01 / 07
THE QUESTIONS ARE A PUBLIC FILE
how an exam ends up inside the thing it examines
the task set · a public dataset, downloadable by anyone
someone posts it · blog walkthroughs, GitHub forks, papers with the answers
the crawler reads it · a training corpus is scraped from the same web
the model reads it · now the exam is somewhere in the weights
nobody has to cheat for this to happen

A benchmark has to be public to be shared, and a model is trained on a scrape of the public web. Those two facts collide. SWE-bench Verified is a dataset anyone can download, reference fixes included. MMLU and GPQA questions sit in repositories, papers, blog posts and tutorials with the answers beside them. If any of that was in the training data, part of the score is recall rather than reasoning. This is called , and it needs nobody to cheat. It is the default outcome of publishing an exam on the internet that the candidates read.

a benchmark has to be published to be useful, and publishing it is what eventually breaks it
STEP 02 / 07
REBUILD THE EXAM AND SEE WHO DROPS
the same maths, asked twice: published questions, then new ones
model Agap 1 points
published82%
rebuilt81%
model Bgap 13 points
published79%
rebuilt66%
a gap means the score was partly memory · no gap means the skill is real

Authors stamp a into the file so corpus builders can filter it out, which helps only if they bother and does nothing about the copies reposted without it. Labs run their own filter, usually dropping any training document that shares a run of about 13 words with a test question, but that needs the training corpus and only the lab has it. So the public measurement is indirect: write new questions matched to the old ones for style and difficulty, and see which models drop. Scale AI did this with GSM1k, 1,250 fresh grade-school maths problems mirroring GSM8K. Some families fell by as much as 13 points, notably Mistral and Phi, while the frontier models from OpenAI, Anthropic and Google showed little gap.

THE THREE WAYS ANYONE CHECKS FOR A LEAK
def overlaps_training_data(question: str, corpus_ngrams: set[str], n: int = 13) -> bool:
    """The usual filter: one shared run of 13 words is treated as a leak."""
    words = question.split()
    runs = (" ".join(words[i : i + n]) for i in range(len(words) - n + 1))
    return any(run in corpus_ngrams for run in runs)


# That filter needs the training corpus, which only the lab that trained the
# model has. So the public measurement is indirect: rebuild the benchmark from
# scratch with new questions of the same difficulty, and compare.
gap = accuracy(model, gsm8k) - accuracy(model, gsm8k_rebuilt)

# Or keep only the questions published after the model stopped reading.
fresh = [p for p in problems if p.release_date > MODEL_TRAINING_CUTOFF]
rebuilding GSM8K with new questions of the same difficulty cost some model families up to 13 points
STEP 03 / 07
FIX ONE: DATE-STAMP EVERY QUESTION
every problem carries a release date · the model carries a cutoff
problems released earliertraining cutoffscoreable
score the model only on the problems to the right of the line

LiveCodeBench records the publication date of every problem it collects, so a model can be scored only on problems that appeared after its training data was gathered. Nothing is hidden and the filter is one comparison per problem. The cost is that the usable set shrinks as models get newer, so it has to be refilled continuously from live sources. A rolling benchmark is a subscription rather than a file, and somebody has to keep paying for it.

LiveCodeBench date-stamps every problem so a model can be scored only on work published after it stopped reading
STEP 04 / 07
FIX TWO: THE SET NOBODY SEES
ARC-AGI-2 · three splits, calibrated to be statistically alike
publicopen, downloadableanyone, including trainers
semi-private120 tasksAPIs under zero-retention terms
private120 tasksthe competition sandbox only
the private run: Kaggle, 12 hours, four L4 GPUs, no internet

ARC-AGI keeps three sets. A public one anyone may train against, a one behind the published leaderboard, and a fully private one that never leaves the machines it is graded on. Runs against the private set happen offline, on hardware with no internet connection, so the tasks cannot leak through an API. Where a model can only be reached over the network, the fallback is contractual: terms saying the provider will not store or train on what it is sent. One of those is a physical guarantee. The other is a promise.

the private ARC set is graded on machines with no internet, so the tasks cannot leak out
STEP 05 / 07
FIX THREE: PROBLEMS WRITTEN THAT MORNING
ICPC World Finals 2025, Baku · 12 problems, five hours
hour 1 of 512 of 12 solved
problem K: solved by a model, solved by no human team

A live contest is the strongest version of the idea: problems written for one room on one day, with no prior existence anywhere. No amount of scraping helps, because the problems did not exist when the training data was collected. At the 2025 ICPC World Finals in Baku, an OpenAI reasoning system solved all twelve problems inside the five-hour window, and Gemini 2.5 Deep Think solved ten, one of which no human team solved. Twelve problems is a tiny sample. It is also the least contaminable evidence in this tutorial, which is the trade you make when you move from task sets to contests.

at the 2025 ICPC World Finals a model solved a problem that no human team solved
STEP 06 / 07
THE HUMAN WHO STILL WON
AtCoder World Tour Finals 2025 · heuristic track · ten hours
Psyho, human
45.2 billion points
the model entry
43.0 billion points
the human finished about 5% aheadIOI 2025, same year: a model placed 6th of 330, at gold-medal level

The same year, at the AtCoder World Tour Finals in Tokyo, the heuristic track ran humans and an OpenAI entry against the same optimisation problem for ten hours. Przemysław Dębiak finished first with roughly 45.2 billion points to the model’s 43 billion, about 5% ahead. At the International Olympiad in Informatics that year a model placed sixth out of 330 entrants, at gold-medal level. Read those two results together: the model is inside the top ten of the strongest human field available, and the top of that field was still ahead in the track that rewards ten hours of judgment.

in the 2025 AtCoder heuristic finals one human finished about 5% ahead of the model, after ten hours
STEP 07 / 07
THE PRICE CAP IS PART OF THE BENCHMARK
ARC Prize reports accuracy and cost in the same row
SEMI-PRIVATE EVALUATION · CAP $10,000 PER RUN
entry A·······································18.6% · $3.40 per task
entry B·······································21.2% · $47.10 per task
entry C·······································24.9% · capped out mid-run
entry C bought more search than the cap allows, so it has no score

ARC Prize caps a single semi-private evaluation run at $10,000 and prints cost per task next to accuracy. That is not administrative tidiness. On a benchmark where more compute per task buys a harder search, accuracy without cost is not a result, because any score can be bought by anyone willing to pay enough per puzzle. The cap turns the benchmark into a question with two axes, and it is the clearest example here of a benchmark author designing against the way their own measure would otherwise be gamed.

ARC Prize refuses to run an evaluation that would cost more than $10,000
deep dive: keeping a task set clean, and what it costs

Contamination is a spectrum, not a switch. At one end a model has memorised the answer string. In the middle it has read a walkthrough of that exact problem. At the other end it has read a thousand similar problems, which is just called learning. No detector separates the third case from the second cleanly, which is why the honest measurements are comparisons rather than verdicts.

Contests are strong evidence and weak measurement. A dozen problems on one day cannot be a stable estimate of anything. What they can do is rule out the explanation that the model had seen the questions, which no 500-task benchmark can do.

Held-out sets decay too. Every time a private set is used, a little information about it leaks through the scores it produces. Sets get rotated and rebuilt for that reason, and each rebuild costs the money and expert time that made the original.

Fresh questions do not fix a bad harness. Contamination is one failure mode among several. A perfectly clean task set is still run by a harness the submitter chose, scored by a grader somebody wrote, and published by whoever felt like it. Chapters 03 to 05 keep applying.

Rolling benchmarks are expensive to keep. A file needs no maintenance. A benchmark that stays uncontaminated needs new questions written forever, which is a running cost with no obvious owner. That is why so few of them exist.

08

Reading a score you did not produce

The last chapter is about reading somebody else’s number, which is what most people do most of the time. State of the art asserts one thing: the highest published score on a named benchmark, on a named split, under a named protocol, as of a named date. Everything difficult about the phrase comes from how easily those four qualifiers fall off. After that come the two questions worth asking before the rank: does this benchmark still have room to separate models, and are the models at the top actually distinguishable?

STEP 01 / 05
SOTA NAMES A TABLE CELL
“state of the art” · the four qualifiers under the claim
the headline···························“state of the art on coding”
benchmarkSWE-bench, not Terminal-Benchwhich skill
splitVerified, not Full or Prowhich 500 tasks
protocolpass@1, one scaffoldhow many tries
date3 August 2026how long it holds
drop all four and the sentence still parses, which is the problem

asserts exactly one thing: the highest published score on a named benchmark, on a named split, under a named protocol, as of a named date. Four qualifiers. A headline keeps the model name and drops all of them, and the sentence still reads fine, which is why two “state of the art on coding” claims from the same week routinely turn out to be about different task sets measured different ways. Attach the four and most disagreements about which model is best dissolve into which cell somebody was quoting.

state of the art is a property of a table cell, not a property of a model
STEP 02 / 05
THE REGISTRY THAT HELD 9,327 BOARDS
Papers with Code · shut down without notice, July 2025
benchmark leaderboards···························9,327
paper-to-code links···························79,817
successor registry···························none
data frozen on GitHub and HuggingFace · nothing added since
the domain now redirects to a trending-papers page

For a decade the nearest thing to a canonical registry was Papers with Code: 9,327 benchmark leaderboards and 79,817 links between papers and their implementations, free to use, the place you went to check whether a claimed record was actually a record. Meta shut it down in late July 2025 without notice. The data is frozen on GitHub and HuggingFace, the domain redirects to a trending-papers page, and nothing has replaced it. So when somebody says a model is state of the art, there is now no shared place to look it up, and the claim rests on whoever is repeating it.

the closest thing to an official SOTA registry shut down in July 2025 and nothing replaced it
STEP 03 / 05
WHEN THE EXAM STOPS DISCRIMINATING
best published score, at launch and now · the ceiling is 100%
MMLU
launched 2020 · 43% then
92%
SWE-bench Verified
launched 2024 · 33% then
72%
Humanity’s Last Exam
launched 2025 · 2.7% then
25%
headroom left, top to bottom: 8 points · 28 points · 75 points

A benchmark is useful while it has . MMLU launched in 2020 with the best models near 43% and now sits above 90% for everything at the frontier, which is : it can no longer tell two good models apart. Humanity’s Last Exam was built as the reply, 2,500 questions from nearly a thousand experts across more than 500 institutions in 50 countries. At launch in early 2025 the best score was 2.7%. A year later it is around a quarter. The evals tutorial covers what saturation does to your own test suite; here the point is narrower. Check headroom before you read a rank.

Humanity’s Last Exam went from 2.7% solved to roughly a quarter solved in about a year
STEP 04 / 05
THE CLUSTER AT THE TOP
the top six rows, drawn with the error bar the board omits
1st, 72.6%
2nd, 71.8%
3rd, 70.9%
4th, 70.1%
5th, 69.2%
6th, 68.4%
4.2 points from 1st to 6th · every interval overlaps its neighbours

Draw the top six rows of a 500-task board with the error bar from chapter 02 and they form one blur. First place is 4.2 points above sixth, every interval overlaps its neighbours, and the ordering would shuffle if everyone re-ran tomorrow. The arena has the same shape for a different reason: intervals there are set by vote volume, and the thinly-voted rows carry ±30 rating points. Rank is the least informative column on either page, and it is the one every summary quotes.

when six models sit inside one error bar, the rank column is sorting noise
STEP 05 / 05
THE AXES THAT CANNOT SATURATE
METR’s 50% time horizon · the task length a model finishes half the time
1 s1 min10 min1 h4 h16 h
measured against timed human experts on 100+ tasksdoubling roughly every seven months since 2019 · unreliable above 16 h

Two responses to the ceiling problem. Aggregate indices, like Epoch AI’s capabilities index, combine many benchmarks into one versioned scale, so no single saturated exam decides the ordering. And continuous axes measure something with no maximum: METR’s 50% is the human-expert task length a model completes half the time, measured on over a hundred software, machine-learning and cybersecurity tasks against timed human baselines. It has doubled roughly every seven months since 2019, faster since 2024, and METR states that measurements above 16 hours are unreliable with the current task suite. A duration cannot reach 100%, so this metric does not expire the way a percentage does. It just gets harder to measure.

METR measures capability in task length instead of percent, because a clock has no ceiling
deep dive: aggregate indices and continuous axes

An index needs a version number. Combining benchmarks means choosing weights, and changing the weights changes the ordering. An index with public, versioned methodology is a measurement; one that reweights without saying so is an opinion with a chart.

Doubling times are fragile. A trend fitted over six years is sensitive to how tasks are sampled, how human baselines are timed, and which models count as frontier. Treat “doubling every N months” as a description of the data collected so far rather than a law.

Report the score with its cost and date. Three fields together are a result: what it scored, what it cost per task, and when. Any one of them alone is decoration, and cost is the one most often missing precisely because it is the one that constrains the claim.

Negative headroom exists too. A benchmark where every model scores near zero also fails to discriminate, and it is easy to mistake for a hard benchmark that is working. The useful window is the middle, which is exactly why Terminal-Bench sized itself to keep frontier scores under half.

Benchmarks have half-lives now. MMLU took about four years to saturate. Humanity’s Last Exam has moved an order of magnitude in one. Plan for any benchmark you adopt to stop discriminating within roughly two years, and write your reporting so that swapping it out does not invalidate your history.

What to check before you believe a number

Everything above, compressed. The first table is the questions this tutorial set out to answer, with the chapter each one was settled in. The second is the practical version: the fields that have to be stated before two published scores can be put side by side.

THE ELEVEN QUESTIONS, AND WHERE EACH ONE WAS ANSWERED
what is a benchmark·······································a fixed task set, a runner, and a grading rule00
what does one test look like·······································a repo, a commit, an issue, and hidden tests00
who writes the tasks·······································universities and non-profits, mostly01, 04
who runs the model·······································usually the lab that built the model02, 05
can I run it myself·······································yes. the harness is a pip package02
what decides pass or fail·······································a test suite, a person, or another model00, 06
are the human judges paid·······································arena voters are not. task screeners are06
how is bias handled·······································blind pairs, random sides, style control06
who checks the number·······································nobody, unless somebody pays to05
has the model seen the answers·······································sometimes. that is what chapter 07 measures07
what does state of the art mean·······································one cell: benchmark, split, protocol, date08
BEFORE COMPARING TWO NUMBERS, CHECK THAT BOTH STATE THESE
task set and split·······································SWE-bench Verified, not “SWE-bench”
harness·······································named, and pinned to a commit
attempts·······································pass@1, or say how many
grader·······································tests, human vote, or model judge
runner·······································who executed it
date·······································every figure here has a half-life
logs·······································published, so a stranger can check

Written as code, the comparability rule is short enough to keep in your head. Two results belong in the same table only when the task set, the split, the harness, the attempts and the grader all match. Any table that puts two rows together without those five agreeing is comparing different exams.

WHEN TWO PUBLISHED NUMBERS MAY BE COMPARED
from dataclasses import dataclass
from datetime import date


@dataclass(frozen=True)
class Result:
    task_set: str    # "SWE-bench"
    split: str       # "Verified". Lite and Pro are different exams.
    harness: str     # the agent driving the model, pinned to a commit
    attempts: str    # "pass@1"
    grader: str      # "pytest", "human vote" or "model judge"
    runner: str      # who executed it, usually the lab that made the model
    published: date  # every figure in this field has a half-life
    score: float


def comparable(a: Result, b: Result) -> bool:
    """Two scores belong in one table only when all five of these match."""
    fields = ("task_set", "split", "harness", "attempts", "grader")
    return all(getattr(a, f) == getattr(b, f) for f in fields)
Read next. AI evals for the test suite you build for your own product, once you have decided the public boards cannot answer your question, and Claude Code for what the harness in chapter 03 is actually doing between turns.