evidence: cohort-split the benchmark, and withdraw the baseline claim that did not survive a fair comparison - #449
Conversation
Three of twelve held-out cases contain an expected fixing path in the task text itself: mongoose as "Location: lib/document.js:2339", svelte and yargs as a GitHub permalink to the exact file and lines. Any ranker with an explicit-file-mention signal answers those by reading the task, so pooling them into one rate lets them carry the headline generalization number. Classification is derived at evaluation time from the same task text the ranker reads, never stored in dataset.json, so it cannot drift away from the case it describes. Three tiers are recorded because they are not equally strong: a repo-root-anchored full path, a multi-segment path suffix (a tsc error naming src/query/react/buildHooks.ts), and a bare basename. The first two count as named; a bare "index.ts" does not and stays in the unmentioned cohort. Datasets are unchanged and the pooled top-level rates are unchanged, so the published figures still reproduce. This only adds the cohort breakdown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FixMap published a hit rate with nothing beside it, which left a reader's actual question unanswered: is this better than what an agent already gets by searching the repository itself? scripts/evaluate-baseline.mjs scores three baselines and FixMap on ONE scanRepo() result per case, so the only difference between arms is ranking. Both keyword arms are case-insensitive and expand camelCase, which favours the baselines deliberately — a handicapped baseline proves nothing. path-extraction path-shaped tokens read out of the task text lexical-literal literal keyword search, distinct terms then occurrences bm25 BM25 (k1=1.2, b=0.75); a retrieval baseline, not a grep fixmap rankContextFiles path-extraction scores exactly 0.000 on the unmentioned cohort of both suites and 0.667 on the mentioned one, which independently confirms the cohort classifier is measuring what it claims. Arms are compared with McNemar's exact test rather than by eyeballing two Wilson intervals: the arms ran on the same cases, and the pairing carries information that independent intervals discard. Wilson is extracted to scripts/lib/wilson.mjs so a published rate and its stated precision cannot come from two different formulas. That extraction is output-neutral: --check-recorded still exits 0 on both suites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Leads the README and site evidence page with the cohort that had to locate the file (held-out 44% Top-1 / 56% Top-3, n=9) instead of the pooled rate (58% / 67%, n=12), and puts a baseline number beside every FixMap number. The correction is stated as structural rather than as a measured effect size, because that is what the data supports: the regression suite barely moves under the same split and its named cases are 2/3 rather than 3/3, so with three cases per named cohort the value of a mention is not established. The baseline result is reported with the same restraint in the other direction. FixMap beats literal keyword search and BM25 on both suites and never loses a disagreeing case on the regression suite (McNemar exact, p = 0.004-0.016), but the held-out cohort cannot reach significance at all: with three disagreeing cases the smallest attainable two-sided p-value is 0.25. That is stated as a power limit rather than quietly omitted, and it is the strongest argument for growing the held-out suite. Site figures are read from the recorded JSON at build time, so the page cannot drift from the suites. Ten tests pin the classifier, including the permalink shape and the two false positives a bare substring match makes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first version of this comparison pointed every baseline at the raw scan
while FixMap ranked its own gated candidate set. rankContextFiles gates on
isSource && !isTest and then deprioritises documentation for implementation
tasks; the baselines had none of that, so they returned README.md,
CONTRIBUTING.md and issue templates first. For mocha the literal arm's top
five were all Markdown while the answer was lib/reporters/xunit.js.
That is not a baseline, it is a handicap, and the p = 0.004-0.016 result it
produced was an artifact of it. Withdrawn.
Each baseline now runs under three candidate policies - raw, source
(FixMap's own gate), and code (also dropping documentation) - and is
compared at its STRONGEST. The conclusion reverses:
held-out, unmentioned (9) top1 top3 top5
bm25:code 44.4% 55.6% 100%
fixmap 44.4% 55.6% 66.7%
Top-1 and Top-3 are exact ties (McNemar p = 1.0, two disagreements each
way). At Top-5 the baseline wins three cases FixMap misses and FixMap wins
none: bm25:code has the fixing file in its top five for 9 of 9, FixMap 6 of
9, the misses being socket.io, vite and vue. FixMap still leads the
regression suite (69.2% vs 38.5% top-1) but that suite shaped the ranker,
and even there the lead is not significant against this baseline
(p = 0.125 top-1).
Also fixes path-extraction, which peeled no segments off a GitHub permalink
and so silently missed every URL-embedded path - the exact quantity that arm
exists to measure. It now scores 100% on the named cohort and 0% on the
unmentioned one, a clean separation that independently checks the classifier.
README, docs/BENCHMARKS.md and the evidence page all state the corrected
result, including that FixMap loses at Top-5 on unseen repositories.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@aryamthecodebreaker — requesting review before this lands. Two things worth a reviewer's attention specifically: 1. The headline reversed inside this PR. Commit The specific thing to check: is 2. CI is red for an unrelated reason. Datasets are unchanged, pooled rates still reproduce ( |
What this corrects
Three of twelve held-out tasks name their expected fixing file in the task text. They remain valid tests of explicit-file-mention handling, but they are no longer counted as generalization evidence.
The cohort flag is derived during evaluation from the same task text the ranker reads; it is never hand-maintained in dataset JSON.
Fair baseline comparison
All arms rank one shared
scanRepo()result per case. Literal retrieval and BM25 are reported under raw, source, and code candidate policies; the strongest recorded policy is used instead of handicapping a baseline with documentation FixMap itself deprioritizes.On the nine held-out tasks that did not name the file:
FixMap does not beat BM25-over-code on this unseen cohort. Top-1 and Top-3 are exact ties; at Top-5 BM25 wins three cases FixMap misses.
Review hardening
Codex review found and fixed four release-blocking inconsistencies before merge:
evaluate-baseline.mjs --check-recordedpreviously did nothing; it now rejects missing or drifted artifacts.A dated, citable narrative is in
docs/releases/2026-08-04-benchmark-self-audit.md.Verification
After merging the independent security fix #450 into this branch:
npm ci— 0 vulnerabilitiesnpm run ci— 430 tests, audit, lint, builds, Action bundle drift, smokes, evaluation, and scan benchmark all pass/evidenceNot in scope
This PR does not change the shipped ranker, add replacement held-out cases, release or deploy a version, or begin ranking optimization. The nine-case interval remains wide; expanding the mechanically selected cohort is follow-up evidence work.
🤖 Initial implementation generated with Claude Code; independently reviewed and hardened by Codex.