docs(reader-arc): increment-2 CASE-FIRST plan — 0148/0001 first-conversion cluster (for ruling) #80
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/reader-inc2-caseband"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Design-first plan for ruling before any build (usual terms). Full doc:
docs/research/increment-2-caseband-plan-2026-07-19.md. This is the increment that crosses zero on the reader side.Target — the 0148 / 0001 cluster (0000 deferred)
The spine is PROVEN (grounding, not assumption)
Canonical rewrites — each target's compare rewritten to plain "A has N times as many X as B" + seed as a statement — both solve wrong=0 today (0148→1500, 0001→480; a fresh forward-compare+total→20). Forward compare + statement-seed + summation all work (compare compiler #77, #78 substrate, 2b summation). The gap is surface forms only — nothing in the solver.
The isolated gap increment 2 builds
If <REF> has/is <N> [each], <question>and seed REF from the question clause (shared by all three).thanalongsideas+ aggregate "two other harbors combined" + "each"; 0148: mass-noun temporal entities + "<A> was twice the total number counted on <B>".wrong=0 drivers (positively determined, fail-closed — review focus)
Conditional-seed binding (seed the named REF or refuse); aggregate reference (sum exactly the two peers or refuse); "each" distribution (grounded count or refuse);
thangate (only where the multiplicative frame is present). No new direction driver — the seeds make these FORWARD compares.Done-when
band-solve ≥ 1 wrong=0 (target both) + correct-for-the-right-reason 3-part proof per conversion (statement injects the semantically correct element / question → right target / end-to-end == gold — not answer-matches) + full-500 wrong=0 + mis-injection sweep clean + smoke green.
Open questions (§8)
extract_conditional_op_question_candidatesor a dedicated narrow extractor?No build until ruled. q:difference stays deferred + locked (
test_adr_0250_q_difference_failclosed.py).Review — approved to build, rulings on §8 Q1–Q3. The §2 spine-proven grounding (canonical rewrites 0148→1500, 0001→480 solve wrong=0 today) is the load-bearing result: the gap is definitively surface-parsing, not the solver. Case-first is vindicated.
Q1 — 0148 is the first-conversion target and the sole done-when gate; 0001 is an in-increment fast-follow, never a gate. The first conversion's job is to cross zero clean with an airtight proof, not maximize count. 0148 (mass-noun compare + conditional-seed) carries fewer wrong=0 drivers than 0001 (aggregate peer-sum + "each" + "than"). So 0148 alone discharges "cross zero"; 0001 is attempted only if Q3 resolves clean, and it may never delay or entangle 0148's landing. (Tune isolates them, so pursuing 0001 doesn't risk 0148's wrong=0 — but the milestone stays 0148.)
Q2 — verify-existing-first (your plan) under two hard constraints: (a) the existing
extract_conditional_op_question_candidatesoutputs stay stable-or-better — no cross-family regression to the q:complex conditional forms it already handles; (b) conditional-seed binding is fail-closed on unbindable/ambiguous references. If extending can't satisfy both, go dedicated — duplication is cheaper than a loosened shared gate (#77 lesson).Q3 — attempt the 2-peer aggregate sum fail-closed, with a no-wrestling rule. If it can't be made unambiguous in bounded effort, 0148 alone is the first conversion and 0001+aggregate becomes the next increment — don't force the aggregate form into the first-conversion increment. This is coupled to Q1: 0148 is guaranteed, 0001 is conditional on the aggregate proving fail-closeable.
Two review catches:
Approved to build. Measure-once; mis-injection sweep on the new surfaces; bring the triple + haircut + the per-conversion proof.
Review of the pushed implementation — much improved, verified from the diff, but not merge-as-is. One wrong=0 concern + evidence hygiene.
What I verified as genuinely good (from the diff, not the narrative):
report.jsonper-case ledger shows EXACTLY one case changed —0148refused→correct — counts 5→6, refused 495→494, wrong=0 held. That's the exact clean signature of one new conversion with zero compensating regressions across the 6-file blast radius. This is the stable-or-explained evidence at case granularity, and it's clean.500 → "the second day"; the mass-noun compare bindsactor="the first day", reference="the second day", factor=2, direction=times→ First = 2×Second. So 1500 = 500 + 2·500 from the right graph. This matters because the answer 1500 is direction-invariant under the summation (swapping which day is seeded still totals 1500) — so "answer=1500" alone never proved it; the binding does, and the binding is correct.has_numeric_tokenignored multiplier anchors, so the completeness guarduncovered_quantitiescorrectly rejected) is legitimate, and the guard forcing the2.0to be consumed is itself evidence the compare op is really in the graph.The one blocker —
math_roundtrip.py::_token_inis loosened globally, in the un-safe direction. The new multi-word branch grounds a phrase if all its component words appear anywhere in the source token set (order- and position-independent). That weakens a shared roundtrip grounding guard for every candidate, not just mass-noun ones — a multi-word actor/reference could ground against scattered tokens in some other/ sealed-test case. The "strictly fail-closed" claim doesn't hold for this change specifically. Fix: tighten the multi-word branch to require the phrase as a contiguous substring of the source text, not set-membership. "the first day" is contiguous in 0148, so this keeps the conversion while staying strict. This is the same "don't loosen a shared gate to admit one family" principle from the #77 split.Evidence to add before merge:
Minor (non-blocking):
_COMPARE_MASSNOUN_RE's.*?,prefix can over-match; the rest of the pattern is narrow enough that it's fine for now, but a tighter explicit prefix would be safer later.Also noted: the commit bundled prior uncommitted WIP +
report.jsonunder one message — provenance is muddy but the net diff is what's reviewed. And theq:complex"highly tractable" claim is not measured yet — hold it as an impression, not a result.Verdict: tighten
_token_into contiguous-substring, add the graph-dump evidence, then this is a clean merge and a real first conversion. 0001 correctly deferred.Status: ABANDONED — do not merge. Superseded by the reader-arc recalibration.
This branch pursued the first conversion (0148) via bespoke per-surface regexes and, in the process, introduced debt: a fail-open loosening of the shared
_token_ingrounding guard, an empty committed "proof" artifact, and bundled provenance. Per the recalibration decision, the bespoke-regex-per-case approach is being halted in favor of a geometric surface→canonical normalization direction (separate, gated spike).Disposition:
mainresets to the last reviewed/merged tip (compiler tier #77 + #78 foundations #79); this branch is not merged. The genuinely-general knowledge here — the realhas_numeric_tokenmultiplier-blindness bug, and that 0148 solves correct-for-the-right-reason — is being captured intodocs/research/on the recalibration branch before this branch is retired, so nothing valuable is lost. Please close this PR without merging once that capture lands.Knowledge capture landed — ready to close (not merge).
Per the recalibration decision recorded above, the preserved-knowledge capture is complete and verified
on
docs/reader-arc-recalibration(basemain @ 854ce023, the same tip this PR is based on):docs/adr/ADR-0251-reader-arc-recalibration-geometric-normalization-spike-proposal.md—records the halt/reset/eradication decision (executed) and proposes the geometric surface→canonical
normalization direction (awaiting a separate ruling — not started).
docs/research/reader-arc-recalibration-preserved-knowledge-2026-07-19.md—the
has_numeric_tokenmultiplier-blindness bug (root cause + minimal fix idea, not landed) and the0148 correct-for-the-right-reason decomposition, both independently re-verified against this branch's
code and against
main's current refusal behavior (not copied from this PR's claims).docs/research/0148-first-conversion-proof.md— the real, non-empty graph dumpthis PR's empty placeholder should have contained, generated fresh from
origin/feat/reader-inc2- caseband @ e3da148din a throwaway worktree (since removed).docs/research/reader-arc-overfit-inventory-2026-07-19.md— 31 file:line-citedbespoke/single-case surface patterns on
main, as candidates for the proposed normalizer (list only,nothing removed).
Verified clean base for all of this:
main/origin/main/forgejo/mainall at854ce023; smoke176 passed;
holdout_dev/v1correct=5 wrong=0 refused=494→— correction,refused=495(n=500),matching the pre-branch baseline exactly.
This branch (
feat/reader-inc2-caseband) is not merged and is not being merged. Per the dispositionabove, please close this PR without merging — I don't have a close/update-state tool available on the
Forgejo MCP connector to do that step myself (only comment/review/merge are exposed), so that action
needs to happen manually or via
tea/the Forgejo UI on your end. The branch ref itself is left intact(archived, not force-deleted) so its history stays available for reference.
Correction to a typo in the comment above: the verified
holdout_dev/v1baseline on cleanmainiscorrect=5 wrong=0 refused=495 (n=500)— matchingevals/gsm8k_math/holdout_dev/v1/report.jsononmainexactly. (The prior comment had a garbled mid-edit fragment there; this is the clean number.)Pull request closed