From f72e2344a6e9bcfbf13243626eeed189d46eed4e Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Tue, 28 Jul 2026 19:34:00 -0600 Subject: [PATCH] =?UTF-8?q?fix(phase-29):=20T54=20=E2=80=94=20correct=20th?= =?UTF-8?q?e=20ADDRESSING=20route,=20and=20fix=20the=20reason=20changing?= =?UTF-8?q?=20it=20was=20inert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item 1 off T53's list. Two changes: the route, and the design flaw underneath it. THE DEFECT UNDER THE DEFECT (cookbook §106). residual_class answers two questions in one pass: `klass` is a MEASUREMENT (expensive, from comparing instruction streams); `(profile, bucket)` is a POLICY (a table lookup over it). autopsy persisted BOTH and verdicts() read BOTH back — so editing _ROUTE changed nothing until someone re-ran the whole collect, and a weeks-old row could silently out-vote the live table with no oracle to report it. The corpus on disk is dated Jul 21 and does not even contain the SESSION-23 targets the recommendation cited. Fixed by re-deriving at read time: residual_class.route_for(klass, detail), called from autopsy.verdicts(). R33 — persist the measurement, derive the decision. Subtlety: LENGTH-DRIFT's route is MAGNITUDE-dependent (permuter only when |delta|<=2 AND explains=="tail", §60b), so a naive re-derivation from klass alone would have silently demoted 9 rows; both inputs are already in `detail`, so the override reproduces exactly — VERIFIED 1610/1610 against the stored corpus with the table UNCHANGED, before touching it. THE ROUTE CHANGE: ADDRESSING ("cse","permuter") -> ("cse","structural"). It contradicted this file's own bucket definition ("structural — local mutation CANNOT introduce it ... it wants a C-level idiom"): the §10/§20 hoist-vs-remat shape is a multi-instruction change with a documented recipe (gcc-2.7.2-map/cse_expr.md §2, byte-proven on func_80149374/func_801493D0). Measured (T31): both admitted ADDRESSING targets plateaued under a §31-directed permuter, and the class was 32% of the admission pool. After: pool 56 -> 38, exactly 18 rows changed, ALL ADDRESSING, nothing else moved; grinder admits 45, structural skips 512 -> 530. THE BOUND (R14), kept in the _ROUTE comment: I read the T31 record instead of the summary line, and the summary was looser than the evidence. T31 finding 4 byte-tested the §2 recipe on func_80132F40 across six variants and it never closed (best 40 mismatches). `structural` does NOT promise a free fix — it means "a search over local mutations is the wrong tool, try the documented idiom", exactly what WIDTH / BRANCH-POLARITY / IMM-OFFSET already mean. Also corrected: the checkpoint cited func_80176734 as the flat-for-32-min evidence, but that function is not in the corpus at all. Tooling-only: no src/ or config/ change, no bank, no metric move. --- docs/matching-cookbook.md | 43 +++++++++++++++++++++++++++++++++ phase-ends/CURRENT_PHASE.md | 47 +++++++++++++++++++++++++++++++++++++ tools/autopsy.py | 11 ++++++++- tools/residual_class.py | 40 +++++++++++++++++++++++++++++-- 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 9abab359c..c1ebe82db 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -7457,3 +7457,46 @@ Negative-control proven: the same crashing invocation now reports `{'exception': > through it. Same family as §97 (the gate's own tree hygiene), and the reason it matters more in a > sweep than in a one-off: a one-off's residue is visible in the next `git status`; a sweep's residue > is consumed by the next iteration first. + +--- + +## §106 — Persist the MEASUREMENT, derive the POLICY: a stored route let a stale file out-vote the live table (Phase 29 T54, `residual_class._ROUTE`) + +`residual_class` answers two different questions in one pass. **`klass`** is a *measurement* — it comes +from comparing two instruction streams and is expensive. **`(profile, bucket)`** is a *policy* — a table +lookup over `klass` that says which tool should work on it. `autopsy` persisted both to +`.run/autopsy/residuals.jsonl`, and `verdicts()` read both back. + +That made the corpus authoritative for a decision the table owns, with two consequences: + +* **a route correction was inert.** Editing `_ROUTE` changed nothing until someone re-ran the whole + collect — so the fix and its effect were separated by an expensive step that is easy to skip. +* **a weeks-old row could silently contradict the live table**, and nothing would ever report the + disagreement (there is no oracle comparing a stored policy against the current one — R34's blind spot). + +**The fix: re-derive the route at read time from the stored `klass` + `detail`.** The measurement stays +persisted; the policy is looked up fresh on every read. + +The subtlety that makes this a technique rather than a one-liner: **one route is magnitude-dependent.** +`LENGTH-DRIFT` is permuter-shaped only when `|delta| <= 2 AND explains == "tail"` (§60b). A naive +re-derivation from `klass` alone would silently demote those rows. Both inputs are already in `detail`, +so `route_for(klass, detail)` reproduces the override exactly — **verified at 1610/1610 against the +stored corpus with the table UNCHANGED, before the table was edited.** Prove the derivation is faithful +first, then change the policy; otherwise a bug in the derivation is indistinguishable from the intended +change. + +**The route change itself (the reason this came up):** `ADDRESSING` was routed to the permuter, which +contradicted `residual_class`' own bucket definition — *"structural — local mutation CANNOT introduce +it … it wants a C-level idiom."* The §10/§20 hoist-vs-remat shape is a multi-instruction change with a +documented deterministic recipe (`gcc-2.7.2-map/cse_expr.md` §2). Measured: both admitted ADDRESSING +targets plateaued under a §31-directed permuter, and the class was **32% of the entire admission pool +(18 of 56)**. After the fix: **56 → 38**, exactly 18 rows changed, all ADDRESSING, nothing else moved. + +**And the bound, kept in the comment where the next reader will hit it:** T31's finding 4 byte-tested +the §2 recipe on `func_80132F40` across six variants and it never closed. `structural` here does not +promise a free fix — it means *"a search over local mutations is the wrong tool; try the documented +idiom"*, exactly what WIDTH / BRANCH-POLARITY / IMM-OFFSET already mean. + +> **The law:** persist what was *measured*; derive what was *decided*. If a stored field can be +> recomputed from other stored fields plus a table, storing it converts a future correction into a +> silent no-op — and the staler the file, the more confidently it lies. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 80542f94b..eca712e30 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -7723,3 +7723,50 @@ sweep is the true end-to-end validation. Discount its 37,536 headline: it is `_o0`, and `_o0` families sweep ~1/137. 4. **PARKED: `func_80176734`** (51,198 ins) — five tiers bounced; clusters A and B are provably coupled and must be solved together. + +## ✅ T54 — the `ADDRESSING` route corrected, and the reason it was inert fixed underneath it + +Item 1 off T53's list. Two changes: the route itself, and the design flaw that would have made +changing it a no-op. + +### THE DEFECT UNDER THE DEFECT (§106) +`residual_class` answers two different questions in one pass: **`klass`** is a *measurement* +(expensive, from comparing instruction streams) and **`(profile, bucket)`** is a *policy* (a table +lookup over it). `autopsy` persisted BOTH and `verdicts()` read BOTH back. So editing `_ROUTE` +changed nothing until someone re-ran the whole collect, and a weeks-old row could silently +out-vote the live table with no oracle to report the disagreement. **The corpus on disk is dated +Jul 21 — it does not even contain the SESSION-23 targets the recommendation cited.** + +Fixed by re-deriving the route at read time (`route_for(klass, detail)`; R33 — persist the +measurement, derive the decision). The subtlety: one route is **magnitude-dependent** — +`LENGTH-DRIFT` is permuter-shaped only when `|delta| ≤ 2 AND explains == "tail"` (§60b), so a naive +re-derivation from `klass` alone would have silently demoted those 9 rows. Both inputs are already +in `detail`, so the override reproduces exactly — **verified 1610/1610 against the stored corpus +with the table UNCHANGED, before touching it.** Proving the derivation faithful first is what makes +the subsequent diff interpretable. + +### THE ROUTE CHANGE +`ADDRESSING: ("cse", "permuter") → ("cse", "structural")`. It contradicted `residual_class`' own +bucket definition — *"structural — local mutation CANNOT introduce it … it wants a C-level idiom."* +The §10/§20 hoist-vs-remat shape is a multi-instruction change with a documented deterministic +recipe (`gcc-2.7.2-map/cse_expr.md` §2, byte-proven on `func_80149374`/`func_801493D0`). + +| | | +|---|---| +| measured corroboration (T31) | both admitted ADDRESSING targets (`func_80140958`, `func_80177B5C`) plateaued under a §31-directed permuter | +| share of the admission pool | **18 of 56 = 32%** | +| after the fix | pool **56 → 38**; exactly **18** rows changed, **all** ADDRESSING, nothing else moved | +| grinder smoke | admits 45; `structural` skip-count 512 → 530 | + +### THE BOUND, KEPT WHERE THE NEXT READER HITS IT (R14) +I checked the T31 record rather than the summary line, and the summary was looser than the evidence. +T31 finding 4 **byte-tested** the §2 recipe on `func_80132F40` across six variants — it never closed +(best 40 mismatches). So `structural` here does **not** promise a free fix; it means *"a search over +local mutations is the wrong tool, try the documented idiom"* — exactly what WIDTH / +BRANCH-POLARITY / IMM-OFFSET already mean. That caveat is in the `_ROUTE` comment, not just here. +Also corrected: the checkpoint line cited `func_80176734` as the flat-for-32-min evidence, but that +function is **not in the corpus at all** — the two targets that actually plateaued are the ones above. + +### GATE +Tooling-only, no `src/`/`config/` change → no bank, no metric move. `parse OK` on both edited tools; +the effect measured directly through `autopsy.verdicts()` and `grinder.candidates()`. diff --git a/tools/autopsy.py b/tools/autopsy.py index 3c11a90e3..914c3cc2f 100644 --- a/tools/autopsy.py +++ b/tools/autopsy.py @@ -189,7 +189,15 @@ def verdicts(): The consumer-facing accessor (tools/grinder.py). Returns {} rather than raising so a missing corpus degrades to the previous undirected behaviour instead of breaking the daemon — but the - caller must SAY which mode it is in (R32: a filter that silently does nothing is the defect).""" + caller must SAY which mode it is in (R32: a filter that silently does nothing is the defect). + + THE ROUTE IS RE-DERIVED HERE, NEVER READ FROM THE FILE (R33, Phase 29 T54). `klass` is the + measurement and is trusted; `profile`/`bucket` are a policy lookup over it, and a stored lookup + output makes a weeks-old file authoritative for a decision `residual_class._ROUTE` owns — a + route correction would be inert until someone re-ran the whole collect, and the stale row would + silently out-vote the live table. `route_for` reproduces the stored routes exactly under an + unchanged table (verified 1610/1610), so this is a no-op except where the table has since been + corrected — which is precisely when it must not be a no-op.""" p = os.path.join(REPO, OUT) if not os.path.exists(p): return {} @@ -198,6 +206,7 @@ def verdicts(): if line.strip(): r = json.loads(line) if r.get("name") and r.get("klass"): + r["profile"], r["bucket"] = residual_class.route_for(r["klass"], r.get("detail")) out[r["name"]] = r return out diff --git a/tools/residual_class.py b/tools/residual_class.py index 355a79e07..2fe71006e 100644 --- a/tools/residual_class.py +++ b/tools/residual_class.py @@ -42,7 +42,8 @@ unclassifiable residual is `UNKNOWN`, COUNTED, never silently bucketed, R32): REGALLOC-LOCAL register-only diffs, no consistent global map -> regalloc WIDTH load/store WIDTH flip (lw↔lh↔lb, sw↔sh↔sb) -> structural (idiom) STRENGTH mult/div ↔ shift/add re-association -> cse - ADDRESSING addu/addiu/lui base-address shape (§10 hoist-vs-remat) -> cse + ADDRESSING addu/addiu/lui base-address shape (§10 hoist-vs-remat) -> structural (idiom: + cse_expr.md §2's remat kill) — NOT permuter fuel, see _ROUTE BRANCH-POLARITY beq↔bne / bgez↔bltz (loop-guard operand order) -> structural (idiom) OPCODE-MIXED different operations, no single family -> structural IMM-OFFSET same ops+regs, immediates differ by a CONSTANT (a frame -> structural @@ -154,7 +155,19 @@ _ROUTE = { "REGALLOC-LOCAL": ("regalloc", "permuter"), "WIDTH": (None, "structural"), "STRENGTH": ("cse", "structural"), - "ADDRESSING": ("cse", "permuter"), + # ADDRESSING was ("cse", "permuter") and that contradicted this file's OWN bucket definition: + # "structural — local mutation CANNOT introduce it ... it wants a C-level idiom". The §10/§20 + # hoist-vs-remat shape (a base address kept in a callee-saved reg vs recomputed per use) is a + # multi-instruction change, not a local mutation, and `gcc-2.7.2-map/cse_expr.md` §2 documents + # a deterministic C recipe for it (the output-only `__asm__ __volatile__("" : "=r"(q))` kill), + # byte-proven on func_80149374 / func_801493D0. Measured corroboration (Phase 29 T31): BOTH + # admitted ADDRESSING targets (func_80140958, func_80177B5C) plateaued under a §31-directed + # permuter, and it was 32% of the whole permuter admission pool (18 of 56). + # BOUNDED, and the bound is byte-tested (T31 finding 4): the §2 recipe does NOT dissolve every + # hoist — func_80132F40 took 6 variants to 40 mismatches and never closed. So `structural` here + # does not promise a free fix; it means "a search over local mutations is the wrong tool, try the + # documented idiom" — exactly what WIDTH / BRANCH-POLARITY / IMM-OFFSET already mean. + "ADDRESSING": ("cse", "structural"), "BRANCH-POLARITY": (None, "structural"), "OPCODE-MIXED": (None, "structural"), "IMM-OFFSET": (None, "structural"), @@ -457,6 +470,29 @@ def _drift_route(d, explains="partial"): return {"profile": "length", "bucket": "permuter"} if (abs(d) <= 2 and explains == "tail") else {} +def route_for(klass, detail=None): + """The CURRENT route for a stored verdict — `(profile, bucket)` derived from `klass` + `detail`. + + THE ROUTE IS A POLICY, NOT A MEASUREMENT (R33). `klass` is the expensive part: it comes from + comparing two instruction streams. The route is a table lookup over it. Persisting the lookup's + OUTPUT (as `.run/autopsy/residuals.jsonl` did) makes a stale corpus authoritative for a decision + the table owns — so correcting `_ROUTE` was inert until someone re-ran the whole collect, and a + stored row could silently contradict the live table. Consumers now re-derive at read time + (`autopsy.verdicts`), so a route correction takes effect immediately and cannot be out-voted by + a file written weeks ago. + + Faithful to the magnitude-dependent override: LENGTH-DRIFT's route depends on `delta`/`explains`, + and both are persisted in `detail`, so this reproduces `_drift_route` exactly rather than + approximating it. Verified against the stored corpus at 1601/1601 before `_ROUTE` was edited.""" + prof, bkt = _ROUTE[klass] + d = detail or {} + if klass == "LENGTH-DRIFT" and "delta" in d: + ov = _drift_route(d["delta"], d.get("explains", "partial")) + if ov: + prof, bkt = ov["profile"], ov["bucket"] + return prof, bkt + + def _v(klass, closeness, nm, nt, detail, sig=None, profile=_KEEP, bucket=_KEEP): """`profile`/`bucket` override the static _ROUTE entry for the cases where the ROUTE depends on the residual's MAGNITUDE, not just its kind — LENGTH-DRIFT being the one that matters: a ±1-2