Skip to content

Adversarial review: the harness had the hole it was written to close - #277

Merged
adamjohnwright merged 2 commits into
mainfrom
evaluation-ab-review
Sep 20, 2026
Merged

adamjohnwright merged 2 commits into
mainfrom
evaluation-ab-review

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Follow-up review of #276. Builds on it, so merge #276 first.

The harness had the vacuity hole it exists to stop

precondition is only worth the discrimination in it, and nothing checked for any. lambda: True reads like a guard, passes every sample, and proves nothing — the same shape found three times elsewhere today, sitting in the file written to prevent it.

After each sample, with that arm's configuration still active, the other arms' preconditions are evaluated. If one also holds, the two cannot tell the arms apart and the comparison is refused with its own message. That is the rule the rest of this work arrived at — an assertion that something is absent proves nothing until the same check has shown it can be present — applied to the guard rather than only to the thing guarded.

It immediately caught the test helper in this very file, whose preconditions returned a constant. The helper now reads a shared marker the arms actually set, and a separate always-true arm exists solely to be caught.

Two smaller ones

  • Re-using arms across runs silently merged two distributions into what looks like one noisy measurement. Refused now.
  • Ordering reversed rather than rotated, so with three or more arms the middle one stayed in the middle and carried a systematic warming bias — precisely the effect the alternation exists to remove, left in place for every case except two arms.

The sabotage needed its own precondition

All three verified by sabotage, and the first attempt is worth recording: removing the discrimination check appeared to fail nothing, because the edit had silently not applied. Re-done with the sabotage itself asserted, the test fails as it should.

That is the same class of error the harness is about, one level up. The check needed a check.

595 passed, 1 skipped; mypy over 151 files, ruff clean.

🤖 Generated with Claude Code

adamjohnwright and others added 2 commits September 20, 2026 04:41
Three mistakes have recurred in this repository's measurements, each more
than once, and knowing the lesson has not prevented repeating it within hours
-- the cross-window comparison was corrected in one PR and repeated in the
next.

`src/evaluation/ab.py` makes each one structurally hard.

Arms alternate. There is no mode that runs them in sequence, because the
second arm otherwise inherits a warm process and whatever the API is doing
that minute. That has twice produced a result in the direction the author was
hoping for, and once showed a real improvement as a regression.

Every arm declares a `precondition`, checked after each sample, and the
summary refuses to draw a conclusion when one fails. A prompt that asked for
"exactly 1" alternate returned four; a ContextVar set around a call was
overwritten by the node inside it. Both produced ordinary-looking numbers
that were measuring the baseline twice.

And the summary reports p50 with min and max, never a percentile the sample
cannot support. A p90 was quoted from fifteen samples, where it is about the
second-highest value.

The configuration is applied before every sample rather than once per arm,
because a setting applied once and mutated in between fails the same way as
never applying it.

Each test recreates a mistake actually made here, and both guards were
verified by sabotage: removing the alternation fails the alternation test,
ignoring preconditions fails the refusal test. Exercised on the real graph
too, not only on stubs -- expansion on 6.22s against off 2.86s, precondition
held.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`precondition` was only worth the discrimination in it, and nothing checked
for any. `lambda: True` reads like a guard, passes every sample and proves
nothing -- the same vacuous shape this repository has found three times
elsewhere today, sitting in the file written to stop it.

After each sample, with that arm's configuration still active, the other arms'
preconditions are evaluated. If one of them also holds, the two cannot tell
the arms apart and the comparison is refused with a distinct message. That is
the rule the rest of the work arrived at -- an assertion that something is
absent proves nothing until the same check has shown it can be present --
applied to the guard rather than only to the thing guarded.

The new check immediately caught the test helper in this file, whose
preconditions returned a constant. The helper now reads a shared marker the
arms actually set, and a separate always-true arm exists solely to be caught.

Two smaller ones. Re-using arms across runs silently merged two
distributions into what looks like one noisy measurement, and is refused.
And ordering reversed rather than rotated, so with three or more arms the
middle one stayed in the middle and carried a systematic warming bias --
exactly the effect the alternation exists to remove, left in place for every
case except two arms.

All three verified by sabotage, and the first attempt at that is worth
recording: removing the discrimination check appeared to fail nothing,
because the edit had silently not applied. Re-done with the sabotage itself
asserted, the test fails as it should. The precondition needed a precondition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 57012f4 into main Sep 20, 2026
10 checks passed
@adamjohnwright
adamjohnwright deleted the evaluation-ab-review branch September 20, 2026 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant