Agent evaluation · eleven reproduced defects · updated 22 August 2026

Green that lies

Eleven evaluation tools and leaderboards that reported a score over a set that was not the set they claimed. Every one reproduced from the outside, from public code and what the vendor itself prints.

k n n is the part nobody prints

Every score in machine learning is a fraction. Almost every dashboard prints the quotient and hides the denominator. So when the denominator moves — because rows were dropped, because a buffer swallowed a block, because a suite collected nothing, because a run covered a different set — the number stays green and no reader can tell. That single failure mode is what this page is a register of, and it is the only thing I work on.

The same seam, opened deliberately

In April 2026 a Berkeley RDI team — Hao Wang, Qiuyang Mang, Alvin Cheung, Koushik Sen and Dawn Song — pointed an automated agent at eight leading agent benchmarks and took near-perfect scores on all of them without solving a single task. It never touched the problems. It attacked the harness. Their summary is one sentence:

The implicit promise is simple: a higher score means a more capable system. That promise is broken. Berkeley RDI, April 2026

Terminal-Bench 89 tasks → 100%  ·  SWE-bench Verified 500 → 100%
SWE-bench Pro 731 → 100%  ·  WebArena 812 → ~100%
FieldWorkArena 890 → 100%  ·  CAR-bench → 100%
GAIA 165 → ~98%  ·  OSWorld 369 → 73%  ·  tasks actually solved: 0

The part that concerns this page is how SWE-bench Verified fell. Not a clever solver: a ten-line conftest.py whose pytest hook rewrites every outcome to passed. Five hundred instances resolved, nothing solved.

That is the same seam as the LangSmith entry in the register below, which I reported to LangChain on 13 August and which they confirmed in writing the next day — a pytest status hook reporting a clean green over a suite that had lost seven of its ten cases. One was an exploit; one was an accident. From the number, a reader cannot tell them apart. That is the whole problem, and it is the reason the accident is the more expensive of the two: an adversary has to be invited in, while a renamed directory arrives on its own, on a Tuesday, in your own repository.

Check your own run, before it ships

No recomputation after the fact can catch the worst case. Once seven of ten items are gone, the report and the recheck agree — both read 1.0000, because both are dividing by seven. Catching it needs the intended denominator pinned before the run, and the receipt recomputable afterwards by someone who trusts neither party.

That is a zero-dependency CLI you can install and run in the next minute:

pip install runreceipt
runreceipt selftest  →  probes all seven rules both ways before you trust it
runreceipt fix  →  pins the intended set and its hash, before the run
runreceipt verify  →  recomputes the denominator from the run’s own artefact
runreceipt recheck  →  re-derives a receipt you were handed, trusting neither party

The self-test is not decoration. Every rule is probed in both directions — it must fire on the defect and stay silent on the honest run — against twelve fixtures, seven of which are the defects in the register below, reproduced from shipped releases. If a rule has stopped working, the tool says so and refuses, with its own exit code: 0 consistent, 2 findings, 3 artefact unreadable, 4 the instrument failed its own probe. There is deliberately no code that means probably fine.

It has no dependencies on purpose: it is the thing that checks the other tools, so it imports none of them. Live on PyPI, one page of documentation at runreceipt.surge.sh, and the recheck page reproduces a verdict from a receipt in your browser, with nothing uploaded anywhere.

And for a number someone has already published, without any pipeline at all: paste a rate and the population it claims to be over, and every integer is enumerated — either a list of counts that produce it, or the closed fact that none does. Seven controls have to pass before a verdict is shown, two of them added on 21 August, when the tool cleared nine figures it should have refused. The denominator check →

The register

Each entry was reported to the vendor before it appeared here, with the reproduction and the fix. Where a vendor has confirmed it in writing, that is marked. Nothing below is a vulnerability, a leak, or anyone’s private data: every one of these is recoverable from a public page or a public repository.

Inspect (UK AISI)
accuracy 1.000, stderr 0.000 3 of 10 scored measured 22 Aug 2026

The best-behaved framework I have tested still hands the leaderboard a survivor mean, and calls it stderr 0.000

Inspect is the one that does most of this right, and the entry is here for that reason rather than in spite of it. Its default is safe: fail_on_error defaults to None, and a run that hits a sample error stops with status: error and publishes no results at all. I verified that first, because a gate that only ever fires has not been tested. It also does something no other tool in this register does — it writes down what it lost, in results.completed_samples beside results.total_samples.

The gap is one step downstream, and it is a gap you walk into by doing the sensible thing. A long agent run cannot use that default: one flaky container and hours of work are discarded, so real evals set a tolerance. With the tolerance above the actual error rate — the only setting that survives contact with a real harness — ten samples go in, seven raise inside the solver, three are scored, and this is what comes back:

fail_on_error=None  →  status=error, no results   (correct)
fail_on_error=0.10  →  status=error, no results   (correct)
fail_on_error=0.75  →  status=success   accuracy 1.000   stderr 0.000
                       completed_samples 3  ·  total_samples 10

Two things travel out of that run. The counts, which stay in the log file. And the number, 1.000, which is what a leaderboard row, a system card, or a CI threshold actually consumes. Nothing downstream is obliged to open the log. The stderr is the sharper end of it: 0.000 is a claim of certainty, computed over three items, at the exact moment the run is least certain it measured anything.

I ran a second, identical evaluation with nothing failing. Ten of ten scored, accuracy 1.000, stderr 0.000, status: success. The two headline lines are byte-for-byte the same. That is the finding: not that Inspect discards the count, but that the count it keeps never reaches the place where someone decides whether a model is ready.

So this entry ships with the fix rather than the complaint. A gate reads any .eval log and replaces the point estimate with the interval the metric could occupy over the set that was asked for — the survivors’ sum is fixed, each lost sample lands somewhere in [0, 1], and nothing in the arithmetic assumes anything about why a sample died:

always_right.accuracy        reported 1.0000
denominator                  3 scored of 10 asked for   [OVER SURVIVORS]
over the set that was asked for, the same run is somewhere in [0.3000 .. 1.0000]

It carries seven controls that probe the gate itself in both directions, including a negative control that feeds the checker a deliberately wrong answer and demands the word DEAD back — and it exits 4, refusing to give any verdict, if one of them stops working. Both fixtures are real Inspect logs, generated against mockllm/model at no API cost, so anyone can rerun the whole thing in a minute without a key.

The full measurement, both runs and the source →

Vals AI
page says n = 1,223 3 rows solve to 1543 reported 15 Aug 2026

Three leaderboard rows were scored over 1543 items and ranked against rows scored over 1,223

public validation 20 + private validation 300 + test 1223 = 1543

The TaxEval v2 page documents its test set as 1,223 samples, and lists two validation sets alongside it. Three model rows do not fit 1,223 under any epoch count; they fit 1543 exactly — which is the sum of all three components the page itself lists, and is prime, so it is not 1,223 counted some whole number of times. Those rows sit in the same sorted column as 128 rows that do fit 1,223.

No row on the site publishes a sample count. Every row publishes accuracy and a standard error, and for a proportion those two printed numbers are a system of two equations in two integers, which has a unique solution over the range the printed precision brackets. The counts are therefore not estimated. They are solved. The control that rules out rounding: each model is scored twice, in two tabs, over the same set — and the recovered count agrees in 128 of 128 cases, and 47 of 47 on their GPQA board. A rounding artefact would wander between tabs. It does not.

Separately on that GPQA board, where the page states 198 questions, one model solves to 197 — and 197 is prime.

Epoch AI
34 of 813 rows disagree with the stated set reported 14 Aug 2026

The denominator was recovered from the decimal itself, and 34 rows do not match the set they claim

Same method, run against a published benchmark table: 34 rows out of 813 recover a denominator that is not the set size the page states. Two of the recovered values are 197 and 1319, both prime, so neither can be explained as the documented set scored a whole number of times.

The finding has a corollary that matters more than the rows: rounding a score to four decimal places or fewer destroys the denominator. Below that precision the recovery is no longer unique, and a reader who tries anyway will get a confident wrong answer. Publishing one integer per row costs nothing and removes the need for anyone to invert an error bar to learn what a score was computed over.

Langfuse
UI: 8 items, accuracy 1.000 92 items failed

92 items failed, so the run reported 8 items at perfect accuracy

The denominator counted survivors. Of 100 items, 92 failed before they could be scored and left the set entirely; the remaining 8 all passed. The dashboard therefore reported a complete run of 8 items at accuracy 1.000 — correct arithmetic over the wrong set, and no field anywhere in the view says the run was asked for 100.

promptfoo
threshold gate: pass tests collected: 0

A suite that collected zero tests passes its own threshold

passRate = 0/0 = NaN  ·  NaN < threshold → false → gate does not fire

The pass rate over an empty suite is not a number, and every comparison against not-a-number is false — including the one that is supposed to fail the build. So the single check whose job is to notice that quality dropped is structurally incapable of firing on the case where quality is unmeasured. A misconfigured glob, a renamed directory, a bad filter: the run goes green and reports nothing missing.

DeepEval
report: pass metric verdict: False

A metric returned False and the reporting layer wrote None over it

The judged verdict was negative. On the way from the metric to the report, the value became None — and a missing verdict is not treated as a failure, it is treated as nothing to report. The run reads as passing. The layer that overwrote the answer is the layer whose only job is to display it, which is why nobody inspecting the metric ever sees the problem.

LangSmith · LangChain
100% pass 3 of 10 cases ran reported 13 Aug 2026
confirmed by vendor

100% over a suite that shrank from ten cases to three

A pytest status hook reported every collected case as passing, over a suite that had lost seven of its ten cases before execution. The summary line is honest about the three and silent about the seven, and there is no printed total to compare it against. A green wall after a refactor that quietly stopped collecting most of the suite looks exactly like a green wall after a good run.

LangChain confirmed it in writing the next day:

We agree that the pytest plugin can present an incomplete test run misleadingly, and we're tracking a product-quality fix to report incomplete suites accurately. LangChain, 14 August 2026

The same reply contains the sentence I now quote more often than the finding itself. Declining to treat it as a security matter, they noted that the behaviour “does not alter pytest's underlying test results or exit status” — which is precisely correct, and precisely the reason a reader believes the green. The exit status is clean because the work that would have failed was never attempted.

Galileo
0 rows processed 128 rows processed

The flush buffer was doing double duty as the row counter

reported = rows % 128  ·  128 rows → “0 rows processed”

Rows are flushed in blocks of 128, and the number surfaced to the user is what is left in the buffer rather than what went through it. A run whose size is an exact multiple of the block size therefore reports that it processed nothing — and a run of 130 reports 2. The buffer is an implementation detail that became the published denominator.

SWE-bench
leaderboard: 3 rows above a 4th-place row was dropped

A bare except swallowed the row that belonged in fourth place

One submission’s row never reached the public leaderboard: the code path that assembles the table catches every exception without distinguishing them, so a single malformed field removes an entrant instead of reporting an error. The board looks complete. There is no count of rows attempted next to the count of rows shown, so the absence cannot be seen from the page.

A broader read of the same leaderboard turned up seven separate defects, including one submission appearing as two rows, and independently verified entries ranking below unverified ones because the sort ignores the verification flag.

Ragas
SKIPPED counted as success 11 months in the open

A skipped case counted toward the success total for eleven months

A case that never ran was tallied with the cases that passed. The behaviour sat in released code for eleven months, which is the part worth dwelling on: this class of defect does not announce itself, does not page anyone, and does not appear in a bug tracker, because the observable symptom is a number that looks fine.

Weights & Biases
monitor: green nothing was being watched
confirmed by vendor

A monitor that stays green over a set that shrank to nothing

All three points I raised were confirmed. Their own summary of the behaviour is the clearest statement of the whole class:

…stays green until someone re-runs it. Weights & Biases, 11 August 2026

That is the definition of the problem. A status that can only be falsified by re-doing the work is not a status; it is a memory of a status, presented as a current one.

The same check, run outside machine learning

The arithmetic does not care what the population is made of, and the cheapest way to show that a method is a method rather than a knack is to run it somewhere else. Nine worked examples, each one free, each one recomputed live in your own browser with its controls visible — and each one a case where a number in commercial use cannot be rebuilt from what its own source prints:

Why the usual defences do not catch this

Signing does not help. A signature attests that a result is the one that was produced. It says nothing about what the result was produced over. There is no cryptographic operation that can attest to absence — that no row was dropped between the harness and the report — because absence leaves nothing to sign. Every finding above would survive a fully signed pipeline intact.

Alerting does not help either, because nothing crashed. In each case the software completed, returned, and printed a number in range. Monitoring watches for the run that fails; this is the run that succeeds over less than it was asked to cover.

And a passing test suite is exactly what a shrunken suite looks like. The green wall is produced identically by the good case and the empty one. That is why the check has to be arithmetic on the denominator, run before the result ships, by someone whose only job is to try to make it fail.

What this register does not claim

Three of these findings started as mistakes of mine and were caught by a control before they left the house: I once published a false finding by checking an object where I should have checked a field. That is why every claim here carries a control designed to fail, and why the report a vendor gets includes the controls that did fail on me first.

The arrangement

Standing monthly work on your agent evaluations, before the results ship rather than after: the harness read the way the Berkeley agent read those eight, the denominators recovered from what your runs report, the cases that silently left the set, and a reproduction for each one that you can run without me.

I am one person and this is the only thing I work on. The register above is seventeen days of it, two entries confirmed in writing by the vendors concerned, and one — Inspect — where the finding arrives with the gate that closes it.

Monthly
400 USD for up to three evaluation suites, benchmarks or boards.
With your pipeline
500 USD monthly, including the internal runs and the CI gate, not only what you publish.
First month
3 000 USD, which is the month that does the deep read of the harness.
If nothing is found
The entry month costs nothing. If the deep read turns up no case that left the set unreported and no score that disagrees with the set it claims, you owe nothing and we stop there.
By the hour
Also available as contract work on a specific harness, if a standing arrangement is the wrong shape for the problem.

Written to whoever owns the number, not to a support queue — and I would rather show you a week of it than describe it. igor@greenthatlies.com