Reconciles ChatGPT's four independently-branched extraction PRs (#451/#452/ #453/#454) into one coherent generate/derivation/extract.py. They each rewrote the same file + same new test off main, so they conflicted pairwise and needed integration, not a merge. Integrated (span-tracked, most-specific-pass-first so numbers are never double counted): - EX-1 word-numbers (#452): reuses WORD_NUMBERS; tens-one hyphen compounds; factor-bearing half/third/quarter excluded. - EX-4 list-unit inheritance (#451): bare numeric list with one trailing unit. - EX-5 sentence-final numbers (#454): bare final number with empty unit. Deferred: EX-3 multi-word units (#453). Its greedy lowercase span reads "6 apples and 4 apples" as unit "apples and", regressing GB-2's test_same_unit_list_sums, and still can't recover real multi-word units from 0024-class text ("jumping jacks on"). Needs a tighter rule; see docs/handoff/AUDIT-ADR-0179-EX-RECONCILE.md. Verification (sealed lane only; chat/ does not import this module): - Serving frozen: lane-SHA 8/8 match, generate_claims --check OK -> 3/47/0 byte-identical, wrong=0 held. - Sealed practice improved 4/2/44 -> 4/1/45: case 0025 flips wrong->refused. EX-1 reads "three", so completeness sees a quantity the 6x50 chain omits and refuses the spurious 300 (gold 1200) instead of committing it. - No new test failures (3 pre-existing on main). Also fixes stale test drift from EX-2 (#447): TestDecimalGroundingGapIsDeferred asserted decimals still refuse, but #447 made $0.75-class resolve to 864. Renamed to TestDecimalGroundingResolves and updated to assert the flip. Honest scope note: EX-4 does NOT unblock real case 0024 (its PR test used a fabricated bare-list paraphrase). TestRealCase0024StillBlocked pins the true boundary.
3.9 KiB
ADR-0179 EX-1/3/4/5 reconciliation (Claude, at the library)
Reconciles the four sealed-lane extraction PRs ChatGPT opened from the remote
brief (docs/handoff/CHATGPT-REMOTE-BRIEF.md) into one coherent
generate/derivation/extract.py. Each PR was branched independently off main
and rewrote the same file + the same new test, so they conflict pairwise and
could not be merged as-is — they needed integration, not a fast-forward.
Disposition
| PR | Sub-phase | Verdict | Action |
|---|---|---|---|
| #452 | EX-1 word-numbers | Integrated | folded in (reuses WORD_NUMBERS, factor-words excluded) |
| #453 | EX-3 multi-word units | Deferred — regressed GB-2 | not integrated; see below |
| #451 | EX-4 list-unit inheritance | Integrated | folded in (span-tracked; does not flip real 0024) |
| #454 | EX-5 sentence-final numbers | Integrated | folded in (empty unit, span-excluded) |
| #450 | Stream A lookback audit | Sound | merged (read-only, feeds GB-3) |
The four EX source PRs are superseded by the integration commit and closed with a pointer here; their authored content survives in the merged file/tests.
Why EX-3 (multi-word units) was deferred
The brief required "keep units tight" and "don't regress GB-1/GB-2 tests." EX-3's
greedy lowercase unit span ([a-z]+(?:\s+[a-z]+)*) does both wrongs:
- Regresses GB-2.
compose_sequential("She picked 6 apples and 4 apples.")expects10.0. Greedy units read the first unit as"apples and"(it swallows the connective up to the next digit), so_same_unitsees two distinct units and the composer refuses. The existing testtest_same_unit_list_sumsflips from pass to fail. - Doesn't even recover real multi-word units. Real gold case 0024 is
"20 jumping jacks on Monday, 36 on Tuesday, …". Greedy lowercase reads"jumping jacks on"(stops only at the capitalMonday), so the intended"jumping jacks"unit is never produced anyway.
EX-3's own tests pass only because they place the unit at a clause end ("12 jumping jacks.") where punctuation halts the greedy run. That is a contrived
shape, not the GSM8K shape. A correct multi-word-unit extractor needs a tighter,
non-connective-crossing rule; tracked as future work, not shipped here.
Honest note on EX-4 and case 0024
EX-4's PR test asserted it "unblocks 0024" using a fabricated input
("20, 36, 40 and 50 jumping-jacks"). The real case interleaves numbers with
temporal phrases ("36 on Tuesday, 40 on Wednesday"), so the bare-list regex
never fires and 36/40/50 do not inherit the unit. EX-4 is still a real, safe
orthographic primitive (some GSM8K problems do state a unit once after a bare
list), but it does not flip 0024. TestRealCase0024StillBlocked pins this so
no future change silently re-claims the unblock without proving 438 end-to-end.
Verification (run at the library)
- Serving frozen: lane-SHA gate 8/8 match;
scripts/generate_claims.py --checkOK → serving3/47/0byte-identical. wrong=0 held. - No new test failures: the 3 failures present (
0163pronoun, telemetry round-trip, and the now-fixedms3decimal-deferred) all pre-existed onmain. - Sealed practice improved:
build_search_reportwent 4/2/44 → 4/1/45 (one wrong eliminated, no correct lost). Case 0025 flipped wrong→refused: EX-1 now reads"three", so the completeness check sees a quantity the 6×50 chain doesn't consume and refuses the spurious300(gold is1200). Richer reading → the gate refuses rather than commits a wrong answer. This is the intended direction.
Drift fixed in passing (cleanup-as-you-find)
tests/test_adr_0176_ms3_search.py::TestDecimalGroundingGapIsDeferred asserted
decimals were "currently refused." EX-2 (#447) landed decimal grounding and made
that case resolve to the correct 864; the test was stale on main. Renamed to
TestDecimalGroundingResolves and updated to assert the flip.