Carry a leading currency into the extracted unit string - #30
dyk1454683243-sudo wants to merge 2 commits into
Conversation
…e unit. Models write USD 5 /tCO2e with the currency in front; the extractor only captured /tCO2e, which parse_unit never splits, so twelve carbon_pricing answers were dropped as unscoreable. A leading currency token is prepended onto a trailing / or per unit. Foreign-only figures stay unscoreable — no invented FX — and a mixed sentence scores the USD figure when one is stated. Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>
compare.py and paired.py now reproduce the new coverage (Claude unscoreable 110 -> 98, within 10% 45.7 -> 46.2). FINDINGS.md figures from the previous scorer version are allowlisted as historical. Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>
|
Thanks David — the extraction fix is right, and it's well scoped. I checked out the branch and diffed scoring before/after across the whole corpus: 14 answers change, every one Two things to fix, one thing to rebase onto, and one number I'd like you to say out loud. 1. Please drop the allowlist entries and update the stale tables insteadThis is the blocker. The nine figures added to The allowlist's own
These are all derivable — they're what your own To be fair to the branch: those entries are scoped to So: revert
|
| model | pct_of_all now |
with your fix |
|---|---|---|
| claude-opus-5 | 30.8% | 32.3% |
| gpt-5.5 | 31.9% | 32.1% |
| grok-4.6 | 8.8% | 9.0% |
One of those flips a sentence, not just a number
Claude overtakes GPT-5.5 on total correct. So this, on the accuracy guide and repeated in its FAQ, becomes false:
GPT-5.5 gets the most questions right overall
It's Claude Opus 5 now, at 32.3% against GPT's 32.1%. The gate checks numbers, not claims, so nothing would have caught it — and it only flips once this PR lands, so it isn't wrong on the live page today. I'll update the live copy when you merge; flagging it because it's the kind of consequence worth looking for whenever the scorer moves, and because the same sentence is in the FAQ further down that page.
2. Please state the paired-arm change in the PR body
results/paired.json is in the diff: Claude unaided within-10% goes 37.1% → 35.9%, while the tool-assisted arm stays 98.7%. That's the abstract — "giving two models a sourced lookup tool moved accuracy from 37.1% to 98.7%" — and also README.md:67 and FINDINGS.md:86.
It's a real consequence of the fix: two more unaided answers become scoreable and both are wrong. Nothing is wrong with the number. But it widens the gap the study argues for, which is exactly the direction that should be said out loud rather than found in a JSON diff.
3. The trailing-currency case is fixed on main — please rebase
You were right that the prefix path needed the guard, but the guard only covers a currency written before the number, and every live instance is written after it:
'About EUR 65 /tCO2e.' -> None (your guard)
'About 65 EUR per tCO2e.' -> 65.0 scored as USD
'approximately 85.00 EUR per tonne CO2e' -> 85.0 scored as USD
CURRENCY in units.py gives usd, eur and gbp the same scale of 1.0, so reconcile was converting between them at 1:1 — sek: None shows the intent was always to refuse. Three answers were affected, all gemini-3.6-flash, one of them counted correct.
Rather than push that onto this PR I fixed it at the root: parse_unit now records which currency a numerator is, and reconcile refuses a mismatched pair. That's on main, with verify/test_currency_mismatch.py.
Two consequences for you:
- Rebase onto
main. Yourpref_famfilter inbest_valuebecomes redundant oncereconcilerefuses — harmless to keep, but_CURRENCY_FAMILYnow duplicatesCURRENCY_FAMILYinunits.py, and a currency added to one but not the other silently bypasses the guard. I'd drop the local copy and letunits.pyown it. - The figures you'll be asked for are these. I ran your
score.pyon top of the fix; both test suites pass, and this is where it lands:
| scoreable | ≤10% | ≤50% | >50% off | right src/wrong № | |
|---|---|---|---|---|---|
| claude-opus-5 | 327 | 46.2% | 78.0% | 22.0% | 53.7% |
| gpt-5.5 | 257 | 58.4% | 85.2% | 14.8% | 41.0% |
| grok-4.6 | 67 | 62.7% | 86.6% | 13.4% | 31.6% |
| gemini-3.6-flash | 312 | 42.0% | 77.9% | 22.1% | 58.8% |
| gemini-3.1-pro-preview | 46 | 65.2% | 80.4% | 19.6% | 35.3% |
Paired, Claude unaided: 35.9% within 10%, 25.0% off by >50%. gemini-3.6-flash is already updated on main and doesn't move again.
Worth adding the trailing spelling to test_foreign_only_is_unscoreable while you're there — it only tests prefix spellings today, so it reads as though it covers a case it doesn't.
What we fixed on our side
The gate should have caught all of this. Three holes, now closed on main:
strip_markupstripped TeX comments with a bare%.*before unescaping\%, so the first percentage in a line ate the rest of it. All 53 percentages in the preprint were invisible —paper okwas checking nothing.README.mdwasn't a checked surface.--offlineskips the four live pages and then printed "every published number". It now names what it skipped.
That's #31, now merged-pending-review, along with the currency fix below. Coverage went 64 → 139 claims offline and 249 → 285 online, and the full online run is green. With just the TeX fix, this branch fails honestly even with the allowlist entries in place:
paper 5 UNEXPLAINED
37.1%, 45.7%, 54.1%, 58.2%, 62.1%
Follow-ups, not blockers
Opening these separately; nothing to do here:
- Unenumerated dollar codes collapse to USD.
$has no left boundary, soA$50,C$80,R$100,S$25read as USD. No live hits — theA$/S$mentions in the corpus sit on date-truthreporting.*questions — but enumerating known codes means each new one silently becomes USD. reconcilenever compares numerator dimensions. Currency and mass are both scale1.0, soUSD 5 per kWhreconciles against akg CO2e/kWhtruth and returns5.0. Zero hits today. The currency guard is one condition away from closing it, and it's worth closing before the pre-registered holdout is ever scored.- The duplicated currency table, per above — trivial once you rebase.
Nice work on the core fix: the scoping to carbon_pricing.*, the NT$ handling and the refusal to invent FX on the prefix path are all right. It's the published-numbers half that needs the rework.
FINDINGS.md's scorer section opens "It was wrong six times, and each is recorded because on a benchmark the corrections matter more than the headline." The EUR-as-USD fix in the previous commit is a seventh, and leaving it out fails that document's own standard — so it is now entry 7, and README.md and the paper abstract say seven. Two details worth being straight about rather than smoothing over: - Six of the seven were found BEFORE the number was published. This one was not; it was found after, while reviewing a contributor's fix for the related extraction bug. The paragraph now says so instead of keeping the tidier claim. - The same section ended "mostly currency (a SEK answer against a USD truth)", which implied currency mismatches were handled. Only SEK was, and only by accident -- it is the one entry in CURRENCY with no scale. EUR and GBP were converted at 1.0. Reworded to say what the scorer now does. The bug-log entry quotes 41.9%, which no script produces any more. That is the one shape an allowlist entry is for, and it goes in `findings` next to the existing 46.7% for the same reason -- a historical figure in a narrated correction, with the current value named in the reason. Also: nothing ran verify/test_*.py. Three test files existed across this branch and #30 and CI executed none of them, which is worse than having no tests because it reads as cover. claims.yml now has a scorer-tests job that discovers them by glob and fails if the glob matches nothing -- a listed filename would have gone stale the first time one was renamed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRueWxopDXHoWY2dPvsmLG
|
Two follow-ups on the review above, both about CI. CI hadn't run on this PR at all. A fork PR needs a maintainer to approve the workflow run, and nobody had, so GitHub reported no checks. That's approved now. Some of the red is ours, not yours. Two of those — The other six are the published-numbers item from the review. Target numbers after you rebase onto
One heads-up on sequencing: #29 also moves Claude's row (it recovers four freight answers), so whichever of you rebases second will see slightly different numbers than the table above. Regenerate rather than copying these in by hand — |
|
One more sequencing note, and a reason to hold rather than act on the figures in my earlier comment. A test written for #29's It moves Claude, so the target table I gave you above is now stale. Please don't update the prose from it. The order is:
Step 4 is the reliable one — the gate reads the regenerated data, so it can't hand you a stale figure the way I just did twice. Everything else in the review stands: the extraction fix is right and well scoped, the allowlist entries need reverting rather than keeping, and the paired-arm change is worth stating in the PR body in your own words. No action needed from you until #36 lands. I'll comment here when it does. |
|
#36 has merged, so That last command is the one to work from — it names exactly which published figures need changing, read from your own regenerated data. Please don't use any table I've given you earlier in this thread; two of them are now stale. That's my fault, not yours: #36 moved Claude and Gemini after I wrote them. Three things from the review still stand:
Two notes on your code after the rebase. Also worth knowing: No rush. Ping me if you'd rather I did the regenerate pass. |
|
I ran your branch through the rebase locally to find the sharp edges before you hit them. Nothing pushed to your branch — this is just a report. What happens when you rebaseYour first commit ( All three resolve to take Two things to expect from that. The allowlist conflict is your nine "before #23" entries against four historical ones now on If you'd rather not deal with the conflict at all: All five test files pass togetherThat was the thing I most wanted to check: your prefix filter and the cross-currency refusal now in Where the figures land
The gate will then name the prose to update — currently One consequence worth knowingClaude overtakes GPT-5.5 on total correct, 32.5% against 32.1%. So this, on the live accuracy guide and again in its FAQ, becomes false:
That's my job, not yours — I'll update the live page right after the merge. Flagging it because it's the kind of thing worth looking for whenever a scorer change moves a ranking, and the claims gate checks numbers, not sentences. Still on you
Happy to do the whole pass myself if you'd rather — say the word. Otherwise it's yours and there's no hurry. |
Fixes #23.
candidates()only captured the text that trails a number. Models writeUSD 5 /tCO2ewith the currency in front, so the extractor saw/tCO2e,_clean()turned that intoper tco2e, andparse_unit()never split on a leadingper. The pair was dropped as unscoreable even though reconciliation already works once the currency is in the unit string.This prepends a currency token written immediately before the number onto a trailing
/orperunit (USD+/tCO2e→USD /tCO2e). Tokens come fromCURRENCYinunits.py, plus€/£(canonicalised so_cleandoes not strip them) and the NT$/NOK-style prefixes that appear in these answers so$cannot stealNT$.No exchange rates are invented.
EUR 65 /tCO2eorNT$300 per tCO2eagainst a USD truth stays unscoreable. If the same sentence also states a USD figure, that is the one scored. Trailing currencies already in the unit (85 EUR per tonne) are unchanged.Branched from current
main. Does not stack on #29 / #18 (tkm).Verification
python3 verify/test_currency_prefix.py— PASS (issue repro, US$/$spellings, NT$/NOK/EUR-only unscoreable, mixed sentence scores USD,€vs EUR truth)compare.py/results/comparison.json):carbon_pricing.*answers recover via a leadingUSD. The Dutch levy stays unscoreable: its USD figure is swallowed by2025,(thousands-separator comma) and scoring the leadingEUR 30would be invented FX. The twelfth coverage win is€4.5 per tCO2eagainst an EUR truth (same currency, same prefix bug).python3 verify/figures.py --check-fresh— PASSpython3 verify/check_claims.py --offline— PASS (FINDINGS.md figures from the previous scorer version are allowlisted as historical)Out of scope: #18
tkm, #24 long-prose matcher.