Files
BFM-decomp/phase-ends/CURRENT_PHASE.md
T
Drew T a33c6f85f5 docs(phase-26a): A11 — distill + close the tooling-integrity audit (26-A COMPLETE)
Closed the inserted half-phase. tooling-audit.md: DIAGNOSIS -> AUDIT-CLOSE LEDGER
(A1-A10 outcomes + the payoff 66.5->68.6% instr + remaining/handoff); the "two
rules" -> R32/R33/R34 crisp for P10 ratification at the Phase-26 PhaseEnd.
decision-log: the A10 wall-re-test verdict (R31 -- the broken tools WERE the walls;
the payoff was banked by the fixes; the closeness-0 residual is genuine; the real
deliverable is the 3 rules + the derived-oracle pattern). SETUP: the A9d-A10 tool
changes (R21). Cookbook §51 verified complete; LAW 3 tagged R34.

Observables green: final R22 clean-fleet 136/136 BYTE-IDENTICAL; make report EXIT 0
(dedup 1840/0, C1 227211/227211 signed, lint_symbol_refs wired + passing);
audit-corpus 0 slices; audit-cdecl green. Zero src/config changes this session.

Phase 26 resumes at Task 7 (fresh session).
2026-07-15 00:23:31 -06:00

149 KiB
Raw Blame History

CURRENT PHASE — Phase 26: The Family-Endgame (finish the decomp)

Status: ACTIVE · plan approved by Drew (gate 1) 2026-07-11 · Generation Gen2 (18th phase of the arc) Opening brief: docs/family-endgame-megaplan.md (Phase-25 handoff). Fable5 window closes ~2026-07-13. Recovery note: a fresh session resumes from the ▶ CURRENT TASK below (P3 — autonomous between gates). Read the plan context here + the megaplan; per-task log at the bottom is the crash-recovery trail.

The plan in one paragraph

The Phase-25 h_seq reframe: the "unique tail" is really per-location families — crack ONE exemplar, template ×~120 siblings through the whole-binary byte-gate. Planning-session verification (2026-07-11) reproduced the survey from the sigs: 663 tail families; substantial (nins≥80) = 186 families / 1.85M ins (megaplan's "986" was a miscount). Design-stage discovery (byte-verified on specifics, V0 re-verifies — R14): the tail is ~90% a reloc-tracker blind spot — norm_stream/reloc_targets drop the pending lui-hi on any R-type write, but gcc-2.7.2's indexed-global idiom (lui; addu $idx; lw lo($at)) preserves it, so every D_x[i] access made its function "fleet-unique" and broke remap. Substantial-tail classes: PURE-same-addr 62 fams/1.55M ins · PURE-cross-addr 103/0.10M · IMM 8/0.10M. Free win: 63 families / 0.31M ins have a MATCHED exemplar and siblings that failed earlier sweeps only from the tracker bug → zero-crack banks once the ~40-LOC fix lands (= the validation corpus). Also found: remap() sequential substitution corrupts chained renames → must become single-pass simultaneous. Immediate substitution shrinks to an escalation tier (diff-driven, 3 tiers; probe only ambiguous values). Owner decisions: carried Fable5 queue (5 giants) deferred to END of phase (low priority); milestone = structural completion.

Task checklist (effort per R7 · one commit per completed task, Drew pushes — R6)

  • Task 0 — Bootstrap [xHigh] — this file + harness task list (R28). (completes with this commit)
  • Task 1 — Remap core: extended reloc tracker + single-pass substitution [xHigh] — family_remap.py: hi propagates through add/addu index adds; symbol_map/remap gained backward-compatible to_addr=None (cross-address) + imm_map hook; single-pass simultaneous substitution + self-rename. norm_stream/h_norm UNTOUCHED. Discovery → cookbook §40b (R30) + decision-log (R31). V0 PASS (.run/v0_reloc.py): 22/22 regression (func_80141100 NEW==OLD), 15/15 fix (func_801407F4 vs splat .s, recovers D_80187B88/90/B0), cross-addr symbol_map clean. V1 PASS (.run/v1_regression.py): 160 real h_norm pairs, 96 SAME, 0 lost, differences are strict indexed-reloc improvements. (committed)
  • Task 2 — tools/family_hseq.py + committed manifest [xHigh] — full-frontier survey; shared word-diff classifier added to family_remap.py (stream_words/reloc_indices/reg_fields/classify_member — PURE/IMM/STRUCT, register-drift aware). → .run/family_hseq.json + docs/family-hseq.md. VERIFIED: fleet 74.8/58.2/30.3 (= PhaseEnd_25 & progress.py to the decimal); tail cross-check 663 families / 186 substantial / 1.847M ins (exact); classification vs Plan-agent table PURE-same 62, IMM 8 exact, PURE-cross 114≈103; 890×134/562×134 PURE per-location + 952×113 #addr21 IMM confirmed; full frontier = 581 substantial families / 3.22M templatable ins, 345 matched-sibling PURE/IMM families / 1.14M ins = the V2/V3 zero-crack corpus. (committed)
  • Task 3 — Imm engine (T2a Tier 1) + cross-address delta (T2b) [xHigh] — family_remap.imm_value/imm_map_tier1/remap_hseq: diff-driven Tier-1 value-replace (asm-side ambiguity guard defers ambiguous values; C-literal swap preserving sign/hex-case) + T2b self-rename via the Task-1 to_addr. Tier 2 (probe) DEFERRED — build-if-needed per Task-5 measurement (byte-gate arbitrates; only ~8 low-weight IMM families). VERIFIED (.run/v3_imm.py, match_one reloc-masked): 0 DIFF on every compilable pair — 3 IMM + 2 cross-address MATCH; 4 remap-fails were correctly-deferred asm-ambiguous values; 45 compile-fail(isolation) → whole-TU gate in Task 5. (committed)
  • Task 4 — family_sweep --hseq mode [xHigh] — additive hseq_sweep() (h_norm path byte-untouched): consumes family_hseq.json, templates matched-exemplar members via remap_hseq (cross-address + imm), stages .run/sweep/<ov>/func_<TO_ADDR>.c grouped by (overlay,split), gates via harvest_verify (unique per-group verified-out — fixes a latent multi-split overwrite). Member pre-filter = remap_hseq refusal (STRUCT/unresolved). Static pin guard (__asm__("$N" → skip family; §42e — the ×1-only pinned cracks that cc1-crash sibling TUs). --band/--min-members/--stage-only/--only. VERIFIED: substantial → 29 families, 1507 clean members staged / 267 groups, 1643 correctly skipped as pinned (16/29 families pinned incl. the decision-log-named func_8016DF5C/8013D9B0/80133AB0); h_norm path intact; drafts byte-correct by construction (V3), type-using → whole-TU gate (Task 5). (committed)
  • Task 5 — Zero-crack validation harvest (substantial band) — GO/NO-GO = GO [xHigh] — ran the real byte-gate on the 29 substantial matched-exemplar families. Tooling fix discovered + applied (Task-5's "measure then fix"): remap_hseq.gather_externs carries file-scope externs for body-referenced symbols (extract_unit only grabbed adjacent ones) — the decl class that depressed the first run. Result: 532 members banked (byte-gated); per-family: 3 tracker-miss PURE families (0x8015d5e8/0x8015f118/0x801407f4) bank 100% ×133 = 399 byte-perfect (the free win), 1 cross-addr family 50%; 9 zero-bank families are type-using (Work8016/Prim/…) → §41 reconcile follow-on (Task 8), 16 families pinned → Task 7. VERDICT: the h_seq machinery is 100% correct on clean families — GO. decision-log 2026-07-11 (R31). R22 clean-fleet 136/136 GREEN; metrics 30.3→30.9% distinct (+375 fns) / 58.2→58.5% instr. (committed)
  • Task 6 — Step-A triage of the 119 draftable substantial exemplars [Ultracode] — Workflow (.run/wf_triage_full.js, 119 agents / 0 err / 5.3M tok): per-family m2c draft (+§8 jtbl) → match_one closeness → class + §31/§45 lever + seed (.run/phase26-seeds/). 119 triaged → cheap 29 (325k ins, 23 closeness-0 isolation-MATCH) / permuter 29 (341k) / fable5 61 (1.71M). Curriculum → docs/phase26-triage.md. ⚠ Banking caveat (verified): the 23 closeness-0 seeds gate 0/23 into ov077 — genuine function matches but carry standalone types + Ghidra sigs conflicting with the TU (§41 def-side wall: redefinition of struct Obj / conflicting types); banking needs the Task-8 --reconcile/canon_sig_reconcile pass. (committed)

🔬 PHASE 26-A — TOOLING-INTEGRITY AUDIT (INSERTED HALF-PHASE) — ✅ COMPLETE (2026-07-15)

CLOSED. A0–A11 done. Outcome + the fixed/remaining ledger: docs/tooling-audit.md (AUDIT-CLOSE LEDGER). Rules for P10 ratification at the Phase-26 PhaseEnd: R32/R33/R34. Payoff 66.5→68.6% instr (banked by the fixes A3f/g/h + A9a/b). Final R22 136/136 byte-identical; make report/audit-corpus/audit-cdecl green. Phase 26 RESUMES at Task 7 (fresh session — see the Log's A11 entry + the checklist below). (The A0–A11 checklist below evolved its sub-numbering across sessions; the authoritative per-task trail is the ## Log at the bottom. Do not re-derive from the checklist alone.)

Drew's call, 2026-07-14 (gate-1 approved): "audit as an inserted half-phase in the current phase and then resume phase 26." The Phase-3.5 precedent — a spike inserted mid-arc, not a new phase. Phase 26's Tasks 7–12 resume unchanged after A11. Effort: Max (Drew set it); the fan-out tasks (A9/A10) need an Ultracode toggle — R27: STOP, prompt, WAIT for the actual /effort command.

📄 THE PLAN IS docs/tooling-audit.md — 40 measured findings, the method, the priority order, the 7 already-fixed bugs, the 63 not-yet-audited tools. Do not re-derive it.

The case (the session's most important finding): the whole-binary byte-gate is a perfect correctness oracle and a null coverage oracle. It has never accepted a wrong match — and it is blind by construction to work never attempted (green since Phase 5, when 0% was decompiled, because INCLUDE_ASM pastes the ORIGINAL asm: a green byte-gate is compatible with any decomp %). Seven silent-skip bugs in one session were not typos — they are one structural blind spot: a scanner extracts N items from a corpus, the true count is M > N, and nobody ever compared N to M. The cost is not wrong answers. It is invisible work, and walls that aren't there — one 10% hole in the callee oracle made nine byte-exact functions look like an intrinsic compiler wall.

R33 governs the order: apply it to each tool BEFORE R32. Why is this tool re-deriving something the build already guarantees? The best outcome is a DELETED SCANNER, not a fixed regex.

⚠ ORDER CORRECTED (Drew, mid-session): the FULL AUDIT runs BEFORE the fix campaign. My first ordering put the 18-tool audit near the end — which contradicts docs/tooling-audit.md, whose stated priority is dedup_integrate → jtbl_family_bank → the SELECTION tools → masked_diff/match_one → then the 40 measured findings. The reason is the one that matters: a hole in a SELECTION tool makes work invisible to planning — the worst kind, because you never know to look. Fixing on top of unaudited selection tooling means re-running the fixes when the audit later finds the hole. A2 is now the full audit; A3–A9 (the fix campaign) are blocked on it.

Tool coverage: A1 (1) + A2 (18) + the fix campaign (~17 already-measured) ≈ 36 tools — not 82. The filter (deliberate, from the audit doc): does it PARSE something, and does it GATE or SELECT work? The rest are dead LLM-tier scripts.

  • A0 — Bootstrap [Max] — this block + the harness task list (R28) + the phase-boundary decision → docs/decision-log.md (R31). (committed commit:0585)
  • A1 — dedup_integrate.py: the false-green gate [Max] — audit priority #1: a fail-closed validator that can print a false green, which nothing downstream can catch. All three paths confirmed and closed (commit:0586): (1) 1808 groups claimed a DEFINE_func_* macro; only 1801 exist — the 7 ghosts printed [ OK ], hiding 532 member-instances / 22,344 instructions of real, unbanked work. (2) an absent .run/sig.*.jsonl degraded to "0 validated, 0 failed" and exit 0 — on a fresh clone the gate validated nothing and passed. (3) the bank claim was never checked at all. R33 applied: the bank claim is now derived from the build invariant (INCLUDE_ASM pastes the ORIGINAL asm ⇒ a member not wrapped in it is byte-exact) rather than trusted from the registry. The C1 sig check is kept but its docstring corrected (P9): a sig signs the ROM, so it is immutable w.r.t. src/ and cannot detect source drift, which it claimed to. THE CAUSAL CHAIN (the audit's thesis in one example): 3 of the 4 hidden fns are defined in ov_SC01_077_jr_8012ACE0.c — a _jr_* split file — and dedup_propagate.overlay_files allowlists only ("_a","_o0","_o0b","_after"). The propagator could not see them; the group was registered anyway; dedup_integrate greenlit the lie. Two silent-skip bugs compounding: one created the hole, the other hid it. Fuel → .run/audit/a1_harvest_fuel.json, banked in A6. Blast radius, MEASURED (R14): headline metrics unchanged to the decimal (66.5% / 46.8%) — weighted_metrics() derives from the invariant and was structurally immune; progress.py had already been taught to distrust the registry (commit:0574). A null result that CONFIRMS R33: the tool that refused to re-derive was the one that was right. Registry 1813 → 1806 groups; make report green; negative controls all fail-closed.
  • A2 — THE FULL AUDIT: the remaining 18 gate/select tools [Ultracode] — DONE. 38 agents · 2.24M tok · 0 err. 32 findings raised → 28 SURVIVED adversarial verification (4 REFUTED, 16 downgraded); 40 scanners measured CLEAN. Full write-up appended to docs/tooling-audit.md (ROUND 2). 🔴 THE ROOT CAUSE — one bug, ~10 times: a hand-maintained model of the corpus layout (a file allowlist, a single-.c assumption, a func_-only regex, a REGION_SUB dict) sitting on top of a filesystem that already answers the question. Every TU split silently widened it. Decay proven: .run/fuel_manifest.json (Jul 8) recorded 130 stubs; the same tool today returns 30 — the Phase-26 splits moved ~100 stubs out from under a dict literal last edited in Phase 22, and nobody noticed, because an un-nominated target produces silence, not an error. THE NUMBERS (measured, skeptic-confirmed): 91.6% of ALL remaining project gain is invisible to target selection (true 994,633 ins; the manifest sees 83,305) · 117 of 127 reach-134 functions never nominated · harvest_verify cannot see 56,742 of 58,717 (96.6%) open stubs · wave_targets hands 78 of 87 targets an asm path that does not exist. THE THREE THAT CHANGE WHAT WE BELIEVE: (1) The Phase-22 verdict "the permuter's fuel is exhausted" is UNSAFE. grinder banks through harvest_verify, which sees one TU — 1,290 of 1,298 (99%) of the grinder's own queue could never have banked, however good the permuter's output was. "0 banks since Phase 21" is equally consistent with the tool could not bank. Re-test before repeating it (A12). (2) The Phase-25/26 endgame plan is majority-fiction. docs/family-manifest.md advertises "2,758 multi-member families / 11.0 MB of hidden leverage." 1,071 of them / 6.80 MB are ALREADY FULLY MATCHED — phantom targets, 62% of the advertised byte-weight — because the matched-set oracle scans ONE overlay. The byte-weight ranking, the entire point of the file, is sorted mostly on dead work. True frontier: 1,475 families / 3.9 MB. (3) A corpus defect the byte-gate is structurally BLIND to. config/symbols.us.txt:981 declares listCdBuffer = 0x80180000 (a main-EXE data symbol) and every overlay's splat config loads that file — but in overlay space 0x80180000 is code. splat cuts 97 real functions in half and invents 96 phantom ones: 193 slices that can never be matched by anyone, in 97 of 134 overlays. They sit in the harvest queue as ordinary work, so agents burn on them forever and the failure reads as an intrinsic compiler wall — and the build stays byte-identical and green throughout, because the .s halves are pasted back verbatim. A perfect correctness oracle and a null coverage oracle. What saved us: sig_image was RIGHT (58,524/58,621 agreement with spimdisasm; correct on all 97 disagreements). A second independent oracle is the only reason this was visible at all.
  • [~] A3 — tools/corpus.py: ONE derived corpus oracle [Max] — IN PROGRESS. The oracle is built and 4 of ~10 scanners are deleted. tools/corpus.py (commit:0589) — derives from the FILESYSTEM (which .c files make up a binary; where a function's .s lives — the INCLUDE_ASM line is self-describing, its first argument is the asm subdir) and from the PROVEN INVARIANT (matched = sig − stubs, derived, never re-parsed from C text). Coverage-asserted (R32): every INCLUDE_ASM line must parse, every symbol must resolve (any C identifier — a func_-only regex silently misses the 100 curated listCdBuffer stubs), every stub must have a .s. The SECOND ORACLE (make audit-corpus) — cross-checks splat's boundaries against sig_image's independent ones. Reports 193 (96 PHANTOM + 97 TRUNCATED), reproducing the A2 audit's number exactly from an independently-written tool — a third confirmation. RED by design until A4; then it becomes a gate. R14 self-catch, recorded because the near-miss IS the lesson: run naively over all 136 binaries the same check reports 914 slices — 4.7× the truth. main/resident are signed by the Ghidra dumper (shorter boundaries by design; never analysed the linked PsyQ subsegs), so the comparison measures Ghidra's limits, not splat's errors. Only the overlays are signed by sig_image. sig_is_independent() now encodes that domain. A check applied outside its valid domain does not become more thorough — it becomes noise. build_fuel_manifest + wave_targets (commit:0590) — targets 30 → 263; reach-134 targets 10 → 127 (the entire high-ROI band); remaining gain 83,305 → 994,633 ins — the audit's predicted figure to the unit (a fourth independent confirmation). wave_targets' asm paths: 0 of 263 missing (was 78 of 87 pointing at nothing, whose wasted attempts were booked as matching failures and fed reserved_walls() — a silent skip compounding into a false wall). harvest_verify + gate_stage (commit:0591) — the byte-gate itself. It could only see ONE TU: 4.9% → 100% of ov_SC01_077 reachable; 96.6% of fleet stubs were unreachable. Safety argued explicitly: only the SPLICE LOCATION is derived — the verdict (make build + SHA1) is untouched, and since INCLUDE_ASM pastes the original asm, a bug can make it fail to bank but never falsely bank. Verified by an identity known-answer test across three different split TUs → d19c9580 BYTE-IDENTICAL, tree clean. STILL TO DO in A3: family_manifest/family_hseq (the matched-set oracle — 1,071 of 2,758 advertised families are already fully matched, 62% of the endgame plan's byte-weight is phantom) · DELETE census_conflict_callees (R33 — reconcile_tu answers its question from the build) · exemplar_miner/difficulty (inherited holes) · jr_isolate_all.jr_inventory (reads its banked-roster from an ephemeral gitignored scratch file). jtbl_family_bank (3 bugs found by hand, never audited) · the SELECTION tools family_hseq/family_manifest/wave_targets/exemplar_miner/worklist/build_fuel_manifest · the closeness oracle masked_diff/match_one that every crack agent trusts · the bank/gate path gate_stage/harvest_verify/bank_exemplar · dup_report/difficulty/sig_image/canon_draft_decls/derive_canonical_sigs/census_conflict_callees. METHOD (do NOT audit by reading regexes — that is the failure mode that WROTE these bugs): build a deliberately over-approximating candidate detector, run both over the real corpus, compute gap = candidates − parsed, and classify every item in the gap as a real silent skip or a justified exclusion. Pair each auditor with an adversarial skeptic told to refute it (in the first audit several magnitudes were wrong in both directions and one whole class was refuted). R33 before R32.
  • A3b — tools/cdecl.py: THE C-declaration oracle [Max] — DONE. make audit-cdecl GREEN; R22 clean-fleet 136/136. The audit prescribed a shape-aware alternation per tool (~15 coordinated regex edits). Rejected (R33, decision-log 2026-07-14): fifteen hand-maintained models are exactly what diverged — two tools in ONE pipeline already disagree about whether extern s32 D_a, D_b; is a declaration — and an alternation only ever covers the shapes somebody remembered. The thing being scanned has a GRAMMAR. C's declarator grammar is small, closed and total: it describes fn-ptr arrays, sized/2-D arrays, multi-declarators, fn-ptr params and K&R identifier-lists without being told they exist. ~250 lines of recursive descent — less code than the fifteen regexes it deletes, and exhaustive by construction rather than by memory. THREE ORACLES, whole corpus (a measurement, not a belief): coverage 2,952,246 depth-0 statements → 2,731,521 declarators, 0 parser defects; the real cross-gcc compiled each of 50,405 distinct declarations beside this parser's reconstruction of it — 0 rejected; differential vs the incumbents — 0 symbols they see at file scope that cdecl misses, 26 in engine_core.h they cannot see, 6 they wrongly promote from block scope. Two design decisions (cookbook §51g, LAWS 4–8): the candidate set is DERIVED — at file scope C admits nothing but declarations, so R32's over-approximating detector is every depth-0 statement, with no second model to rot; and gcc adjudicates my own coverage gap — a statement gcc also rejects is not C (my rejection is right, the input is corrupt), one it accepts and I don't is my defect. All 33 residual came back NOT-C, all in dead .run/drafts* scratch, none in src/. NEW findings (docs/tooling-audit.md): reconcile_decls.DATA_DECL_LINE_RE finds ZERO decls in engine_core.h (line-anchored; every decl there ends in \) — its authoritative tier has always been empty · two scanners count block-scope externs as file-scope canonicals (the §8d wall) · tu_ambient's func regex ([^()]* params) drops any callee with a fn-ptr parameter · an R14 near-miss: the 33 corrupt drafts were written by a recovery tool that prepended extern to an if statement — but the source bug was already fixed in Phase 19 (today's oracle: 0 garbage / 300 sigs). Mechanism confirmed, consequence nil. Scope (deliberate): NO consumer changed — so it cannot move a byte. The audit warns that making the parser see more ARMS dormant transforms (reconcile_decls.data_access_subs would mangle D_1[i]() the moment fn-ptr decls become visible to it). Migration is one tool at a time, each byte-gated.
  • A4 — The listCdBuffer corpus defect: 193 unmatchable slices [Max] — DONE (commit:0594). R22 CLEAN-FLEET: make clean → extract 136 → build 136 → check-all = 136 PASSED, 0 FAILED. make audit-corpus: 0 PHANTOM + 0 TRUNCATED (was 193). symbols.us.txt:981 declared listCdBuffer = 0x80180000 — a correct Phase-3 name for main's LIST.CD RAM buffer. But that address is outside main's image and inside the overlay slot, and every overlay stacks that file. High RAM is reused: an address that is a buffer to main is live CODE to an overlay. splat therefore cut 97 real functions in half and invented 96 phantoms — 193 slices unmatchable by construction (the phantom listCdBuffer.s literally begins lw $ra,0x10($sp) / addiu $sp,$sp,0x18 / jr $ra — splat cut a function immediately before its epilogue and called the epilogue a function). It had already contaminated real work: in ov_SC03_031 the cut landed where the epilogue was exactly jr $ra; nop, so the Phase-26 ×134 sweep banked the phantom as void listCdBuffer(void) {} — byte-correct, gate-green, and entirely fictitious — while leaving func_8017FFC4 permanently unmatchable. And it was hiding a real function: in ov_SC01_084/ov_SC02_041/ov_SC03_094/ov_SC06_008 there IS a genuine function at 0x80180000 (111/35/28/74 ins), reachable only via a fn-pointer table and never by jal — so splat cannot find it and needs the boundary declared. listCdBuffer had supplied it by luck. Now declared honestly, per-overlay, exactly where R13/R15 says it belongs. THE RULE (the mirror of R13/R15, never written down): a symbol whose address falls inside ANOTHER binary's vram window must never enter that binary's symbol stack. Fix: config/symbols.us.ram.txt, stacked only by main.
  • A5 — The closeness oracle lied on 155 functions [Max] — DONE (commit:0594). masked_diff._reloc_kind() knew 26/HI16/LO16; an over-approximating sweep of every reloc objdump emits across all 3,367 build objects found four — the missing one, R_MIPS_PC16, fell through to a full-word compare against an unresolved placeholder, which can never succeed. Decisive test (derived from the invariant, coverage-asserted): INCLUDE_ASM pastes the ORIGINAL asm, so every stub MUST score 0 — 2,741 scored: old mask 150 LIES, PC16 masked 4 (the 4 are the separate length-delta defect). A phantom non-zero sends an agent to grind at a wall that isn't there, and the wasted attempt is booked as a matching failure → reserved_walls() → a function that was never broken is permanently blacklisted.
  • NEW (found by cutting the R22 corner): a STALE OBJECT CAN PRODUCE A FALSE PASS [Max] — .o ← .s is not a dependency make can see (assembly arrives via INCLUDE_ASM → .include → maspsx/as, after cpp; -MMD tracks headers only). Re-extract + build incrementally ⇒ make links a stale object — and because INCLUDE_ASM pastes the ORIGINAL bytes, the stale object still yields the original image, SHA1 goes GREEN, and the split just changed is never exercised. A broken config change can be "verified" by an incremental build. Live proof: 8 of 136 linked stale objects here and failed loudly only by luck. R22/H3 already legislate this and I broke them — but a rule that needs a human to remember it is not a gate. extract now invalidates the objects that include what it rewrote. Structural, not advisory.
  • A6 — Wire reconcile_tu.py; retire the fleet-majority oracle [Max] (blocked on A2) — reconcile_decls.canonical_data_map asks "what does the FLEET call this symbol" when C's constraint is per-TU: 36.8% of symbols carry ≥2 conflicting spellings, so one fleet-wide answer is wrong for some TU by construction — it returns an actively WRONG canon for 3,717 symbols and disagrees with the TU's own decl on 21.7% of pairs (every one of 678 TUs has ≥1 broken answer). reconcile_tu.py is written + validated + NOT WIRED (commit:0580). Wire → bank_exemplar / jtbl_family_bank / gate_stage; byte-gate. Unblocks func_8017A4AC (287 KB), func_8013F350, func_80131340.
  • A5 — Family engine: 93 phantom exemplars, 1,834 free members [Max] (blocked on A2) — family_remap.extract_unit globs only src/<ov>/<ov>*.c, so it cannot read a DEFINE_func_* body out of engine_core.h → 93 of 218 (43%) "matched" exemplars are PHANTOM and 1,834 still-stubbed, PURE/IMM-clean, unpinned member templates are never attempted. Plus symbol_map naming an address-taken function D_<addr> (3 families × 37 members silently no-op, booked as byte failures), gather_externs' multi-line externs (9), family_sweep.stub_map's curated-name stubs (3 phantom families). Fix, assert, re-run the sweep, harvest.
  • A6 — dedup_propagate: 407 invisible files [Max] (blocked on A2) — a hardcoded suffix allowlist predating the jr splits sees 404 of 811 overlay .c; the gap holds 36,135 stubs + ~32,000 inline defs — half the corpus — and it gates all of dedup_propagate. ≥266 free ×N banks cannot even be nominated. Fix = glob, not allowlist (the next split family would re-open it), landed together with find_site's K&R blindness or the newly-visible half's biggest prizes (func_8015AE2C, func_80166994 — both K&R) are still dropped. Re-run --auto-from, harvest.
  • A7 — build_engine_types: dead on 81% of its own corpus [Max] (blocked on A2) — hard-exits on 573 of 709 type-bearing files (all 134 overlays) because 1,929 tagged-struct typedefs trip an overlap guard written when the source had only anonymous ones. inject_capped_externs routes every type-bearing body to this tool as the type-heavy tail's only sanctioned unblocker — and it cannot run on the corpus that tail lives in. This is the "3,098 type-heavy tail" and the 9 zero-bank type-using families. (Fail-LOUD, not silent — it went unfixed because the message read like an edge case rather than an 81% coverage failure.)
  • A8 — Split-infra: the silent byte-changers [Max] (blocked on A2) — jr_isolate_all._file_scope_decls silently drops 4,040 decls, of which 683 are function PROTOTYPES: in C89 an undeclared function is implicitly int f(), so the TU still compiles — with the wrong return type, and this project has byte-proven that return type drives delay-slot fill. A latent byte-changer armed for the next cut. (uint/code_fn are defined in engine_types.h, which every region includes; volatile is missing from a whitelist that has const; the comment ON _SAFE_TYPE documents the fix that was never applied.) Plus jr_inventory's curated-name blindness — the tool's core invariant "every jr gets its own region" is false in 4 overlays right now — and overlay_src_split's force_decl latch swallowing 2 real definitions in the exemplar overlay while its selftest passes green (a serialisation check masquerading as a coverage check).
  • A9 — lint_symbol_refs: a guard allowed to sit red does not exist [xHigh] (blocked on A2) — currently RED (43 false positives) and UNWIRED (make report never calls it, though its docstring claims it does). No __asm__("label") model; unions two symbol files instead of using each binary's splat stack; globs src/**/*.c so src/shared/*.h is never scanned — yet engine_core.h (10,360 func_/D_ tokens, ×134 overlays) is exactly where one dangling ref breaks every clean build at once. Fix → green on HEAD → wire into make report. It is the only detector for the R22 failure mode.
  • A10 — THE PAYOFF: re-test the walls [Ultracode/Max] — how many of the walls we "byte-proved" across 26 phases were lookup misses wearing a wall's clothes? Re-test on the repaired oracles: the def-side loose-typing wall (§20/§41, "triple-confirmed" in Phase 23) · the 159 arity/narrow-param conflicts (Phase-15 "documented dead-end") · the 3,098 type-heavy tail · the 9 zero-bank type-using families · the 780 h_seq rejections. Phase 16 byte-proved that genuinely contradictory typings do exist, so the wall is real in part — but "some of it was our tooling" is now the prior, not the long shot. A re-confirmed wall is as valuable as a dissolved one (P9).
  • A11 — Distill + close the audit [Max] — cookbook (the silent-skip class + the over-approximating-detector method) · R32/R33 written for Drew's P10 ratification · decision-log (R31) · SETUP (R21) · docs/tooling-audit.md DIAGNOSIS → fixed/remaining ledger · final R22 clean-fleet 136/136 + make report. Then resume Phase 26 at Task 7.

Phase 26 tasks — RESUME HERE after A11

  • Task 7 — Family-core crack waves (the Fable5 window) [launch at Max — R27; agents model:fable, parallel-isolated, distill-between] — biggest byte-weight first (890/562/536/493…); §45-templated prompts (read .greg/.lreg first; Lever-A merged-accumulator audit; 1-death in-out-asm; /s; gdb-on-cc1, tools/reference/gcc-2.7.2/); PIN-FREE (×134); harvest_verify arbitrates; distill each crack → cookbook §46+ (R30); cheap-Opus applies banked idioms between waves; cheap/permuter lists run in parallel [UC]. ~375k tok/crack guide; documented wall + levers = valid outcome (P9). ⚠ The Fable5 window (~2026-07-13) has almost certainly closed — re-plan this task's model tier at A11.
  • Task 8 — Mechanical harvest waves [Ultracode; continues post-window] — cracked exemplars → template ×members → gate → bank; h_norm draftable/matched-free retries with the new tracker (5.9 MB bucket); F-jumptable 4 (§8); M-linkwall 1; link-undef second pass. Batched commits; R22 clean-fleet before each commit batch (never while agents need asm/).
  • Task 9 — Mid-band + residue measurement [xHigh] — <80-ins families (0.68M ins) through the engine; measure h_seq-miss → GO/NO-GO fuzzy pass (build only if ROI; else Phase 27).
  • Task 10 — Carried Fable5 queue (END of phase — Drew's call) [Fable5 if window remains, else permuter/Opus + banked levers] — 801670E4 (diff-23), 80178004 (close-7), 801412A8 (close-29), 8016CBC0 (153/209), 8014D820 (LCS-226); specs .run/giants/*.opus.md + .run/t7b/close/. Crack ×134 or wall-document; post-window walls → Phase 27, accepted.
  • Task 11 — Step-D residue map [xHigh] — true singletons (~0.27M ins) + 5 behemoths → Phase-27 input doc. NO execution.
  • Task 12 — PhaseEnd [Max — Tier 1; R27 prompt] — P7 walk, milestone demo, gate 2, PhaseEnd_Phase26.md, worklog → logs/Phase26.md (R19), in-file recap (R25), decision-log current (R31).

⛳ SESSION-8 CLOSE (2026-07-14) — READ THIS FIRST

⚠ DREW'S DIRECTIVE (2026-07-14, binding): the TOOLING-INTEGRITY AUDIT comes BEFORE any further matching work

"I feel like we should do T14 now, before the rest of the work. but not in this phase."

📄 THE AUDIT PHASE'S INPUT DOCUMENT IS docs/tooling-audit.md — READ IT FIRST, IT IS THE PLAN

40 MEASURED findings (6 auditor agents + 6 adversarial skeptics, 1.2M tokens) with file:line evidence and candidate/parsed/skip counts per scanner — plus the METHOD, the priority order, the 7 bugs already fixed (do not redo), and the 63 tools not yet audited with the filter for which of them matter. Almost nothing in it is fixed. It is diagnosis. Everything below is a summary OF that document.

✅ DECIDED (Drew, 2026-07-14, session 9): option (b) — the audit runs as an INSERTED HALF-PHASE inside Phase 26 (the Phase-3.5 precedent), and Phase 26 then resumes at Task 7. Effort Max set. The plan is the 🔬 PHASE 26-A block in the task checklist above (A0–A11), built from docs/tooling-audit.md. (The alternative — close Phase 26 early on an unmet milestone and open the audit as Phase 27 — was declined: Phase 26's structural-completion milestone is still live and the audit is a prerequisite to reaching it, not a successor to it.)

WHY (the case for doing it first — this is the session's most important finding)

Seven silent-skip tool bugs in one session, and they were not typos — they are a structural blind spot:

a scanner extracts N items from a corpus, the true count is M > N, and nobody ever compared N to M. The whole-binary byte-gate is a perfect CORRECTNESS oracle (it never once accepted a wrong match) but it is blind by construction to work never attempted — it has been green since Phase 5, when 0% was decompiled, because INCLUDE_ASM pastes the ORIGINAL asm. A green byte-gate is compatible with ANY decomp %. One hole (SIG_IN_BODY_RE, 10% of the callee oracle) made nine byte-exact functions look like an intrinsic compiler wall. That is the cost of not auditing: not wrong answers — invisible work, and walls that aren't.

AUDIT SCOPE SO FAR: 19 of 82 tools (23%), chosen by risk. NOT comprehensive. The unaudited set includes the most dangerous ones:

  • dedup_integrate.py — the FAIL-CLOSED byte-honesty validator. A silent skip there prints "1813 validated, 0 failed" — a false green from a gate. Audit this FIRST.
  • jtbl_family_bank.py — three bugs found in it BY HAND this session; never formally audited.
  • family_hseq.py / wave_targets.py / exemplar_miner.py — target SELECTION. A hole here makes work invisible to planning — the worst kind, because you never know to look.
  • masked_diff.py / match_one.py — the closeness oracle every agent trusts.
  • (harvest_verify.py is the byte-gate but DERIVES from the build, so a parse hole makes it conservative, not wrong — the good kind of tool. See R33 below.)

Do NOT "audit all 82" — many are dead LLM-tier scripts. The filter is: does it PARSE something, and does it GATE or SELECT work? (~15 tools.) And per R33, for each one ask the better question first: why is this tool re-deriving something the build already guarantees?

🔑 THE COOKBOOK LESSON THAT SUPERSEDES THE ONE WE STARTED WITH (R33 candidate)

A metric DERIVED FROM A PROVEN INVARIANT beats a metric that RE-PARSES THE WORLD. progress.py has two metrics answering the same question. weighted_metrics() derives from the invariant — "not wrapped in INCLUDE_ASM ⇒ byte-exact, because the build is byte-identical" — and inherits the byte-gate's correctness for free. classify() re-derives the same fact by parsing C, and inherited a bug instead (it read a K&R definition as a forward declaration). Same question, two tools; the one that refused to re-derive was the one that was right.

🔴 AND AN HONEST SELF-CORRECTION (P9/R14 — do not repeat it)

I told Drew our headline numbers had been under-reporting by ~190k instructions. That was WRONG. weighted_metrics() never calls classify(), so it was structurally immune; the published 65.6% / 44.9% were CORRECT ALL ALONG — only the secondary REAL/fn-count report was wrong. I had verified the DEFECT against the bytes (R14) and still got the conclusion wrong because I did not verify its BLAST RADIUS. What caught it was a null result (+376 instructions, not +190,000) against a strong prediction — trivially easy to wave off as noise. "This tool is broken" and "this number is wrong" are different claims needing different evidence.

RULE CANDIDATES FOR PHASEEND (P10 — Drew ratifies)

  • R32 — Coverage assertion. A tool that scans the corpus must assert its own coverage (found vs. an over-approximating candidate set) and fail loud on unparsed input. A silent skip is a DEFECT, not a no-op. (Drew: "agreed", 2026-07-14.)
  • R33 — Derive, don't re-derive. Where a proven invariant answers a question, derive the answer from it rather than re-parsing the source. Before adding a coverage assertion to a scanner, first ask whether the scanner should exist at all.

🔁 SESSION-9 CLOSE — HANDOFF (2026-07-14). Read this, then docs/tooling-audit.md.

State: check-all 136/136 BYTE-IDENTICAL · make audit-corpus 0 unmatchable slices · dedup 1823/0 · fleet instr-weighted 66.5% → 66.7% · 0 NON_MATCHING. Tree clean, all work committed.

DONE (A0–A8 + two unplanned finds)

what outcome
A1 dedup_integrate — a fail-closed gate that printed false greens 3 paths closed w/ negative controls; 7 ghost groups purged
A2 THE FULL AUDIT (18 tools, 38 agents, 2.24M tok) 32 raised → 28 survived, 4 refuted, 40 scanners measured clean
A3 tools/corpus.py — ONE derived oracle + make audit-corpus (a second oracle that can disagree) targets 30→263 · reach-134 10→127 · gain 83k→994,633 ins · byte-gate reach 4.9%→100%
A4 the listCdBuffer corpus defect 193 unmatchable slices → 0; 4 real functions un-hidden; a banked phantom removed
A5 the closeness oracle (masked_diff PC16) 150 lies → 4 (coverage-asserted over 2,741 fns)
A6 dedup_propagate (glob + K&R find_site) 17 fns banked ×134 free, incl. all 4 the registry lied about
A7 family engine (extract_unit/symbol_map/gather_externs/stub_map) 96 phantom exemplars → 0 (216/216 real)
A7 build_engine_types ran on 73–81% of its corpus for the first time
A8 jr_isolate_all._SAFE_TYPE 683 dropped prototypes — a latent BYTE-CHANGER — fixed + coverage-asserted
➕ stale objects can produce a FALSE PASS extract now invalidates them. Structural, not advisory.

REMAINING — all fully specified on disk; nothing lives only in a dead session's context

  1. tools/cdecl.py — the ONE coverage-asserting C-decl parser. DONE 2026-07-14 (A3b above; make audit-cdecl green, 0 parser defects over 2.95M statements, 50,405 declarations gcc-verified). The 15 consumers are NOT yet migrated — that is the next step, one tool at a time, each byte-gated. Migration order by measured harm: reconcile_decls/canonical_data_map (3,717 actively-WRONG canonicals; and see item 2 — reconcile_tu supersedes its oracle) → gen_harvest_targets (DATA_DECL_RE/EXTERN_DECL_RE/tu_ambient) → sig_unify → canon_sig_reconcile (its _file_scope_statements is CLEAN and should simply delegate to cdecl.tu_statements; only its classifier regexes are broken) → the rest. ⚠️ reconcile_decls.data_access_subs must gain a fnptr kind IN THE SAME CHANGE that lets it see fn-ptr decls, or it will mangle D_1[i]() → ((u8 *)D_1)[i]() (audit, GROUP data-decls, CLEAN item 4 — a dormant transform that the fix ARMS).
  2. Wire tools/reconcile_tu.py (written + validated at commit:0580, still NOT WIRED) into bank_exemplar/jtbl_family_bank/gate_stage; retire reconcile_decls' fleet-majority oracle (3,717 actively-wrong decls; 21.7% of (TU,symbol) pairs). Unblocks func_8017A4AC (287 KB), func_8013F350, func_80131340. Then DELETE census_conflict_callees (already marked; reconcile_tu answers its question from the build).
  3. lint_symbol_refs — RED (43 false positives) and UNWIRED. Fix the 4 blind spots → green on HEAD → wire into make report. It is the only detector for the R22 rename-drift failure mode.
  4. overlay_src_split.scan_construct force_decl latch (swallows 2 real defs in the exemplar overlay, while its selftest passes green — a serialisation check masquerading as a coverage check) + jr_inventory's banked-roster read from an ephemeral gitignored scratch file (R33 violation).
  5. 🏆 A10 — RE-TEST THE WALLS. This is the payoff and the reason the audit was gated ahead of matching.
    • "The permuter's fuel is exhausted" (Phase 22) is UNSAFE. grinder banks through harvest_verify, which could see ONE TU — 1,290 of its own 1,298 queued fns could never have banked. "0 banks since Phase 21" is equally consistent with the tool could not bank. Re-run it against the fixed gate before repeating that conclusion.
    • The def-side loose-typing wall (§20/§41, "triple-confirmed"); the 159 arity conflicts; the 3,098 type-heavy tail + 9 zero-bank type-using families (build_engine_types can now RUN); the 780 h_seq rejections against the repaired callee oracle.
    • Any wall whose closeness came from the 155 wrong scores, or whose target was one of the 193 listCdBuffer slices (unmatchable by construction — no C exists for them).
  6. A11 — distill + close: docs/tooling-audit.md DIAGNOSIS→ledger (partly done), PhaseEnd, then resume Phase 26 at Task 7.

RULE CANDIDATES for Drew's ratification (P10)

  • R32 — Assert your COVERAGE. A tool that scans the corpus must compare what it found against an over-approximating candidate set and fail on the gap.

    ⚠️ This is a CORRECTION to the first draft ("fail loud on unparsed input"). build_engine_types failed loud every single time for four phases while hard-exiting on 81% of its own corpus — and was still invisible, because the message read like an edge case and nobody counted it. A loud failure that nobody counts is exactly as invisible as a silent one.

  • R33 — Derive, don't re-derive. Where a proven invariant answers the question, derive from it rather than re-parse. The best outcome is a DELETED SCANNER, not a fixed regex. (28 findings → one derived oracle + ~10 deleted scanners.)
  • R34 (new) — A second oracle, not a better assertion. When an oracle is structurally blind to a class of error, no assertion inside it can help. Add an independent oracle that can disagree with it, and make them argue. (The byte-gate is a perfect correctness oracle and a null coverage oracle; sig_image disagreeing with splat is what exposed the 193 slices. We had both all along and never compared them.)

Reusable method + laws: docs/matching-cookbook.md §51. Strategic why: docs/decision-log.md.


▶ SESSION-8 RESULTS (2026-07-13/14)

📊 SESSION-8 SCOREBOARD

Fleet: instr-weighted 63.0 → 66.5% · distinct-code 39.1 → 46.8% · fn-count 82.61% (all DEFENSIBLE — see the self-correction below). FINAL R22: make clean + extract-all + check-all → 136/136 BYTE-IDENTICAL, 0 coverage defects. 136/136 byte-identical after every single bank (R22). dedup 1813/0. 0 NON_MATCHING (G4). ~25 commits.

13 CORES CRACKED, incl. the four heaviest functions in the game. The 12-agent Ultracode wave: 11/12 MATCH first pass, every one adversarially verified by a skeptic (re-run match_one + the §8a jump-table check).

Core size × reach how it fell banked
func_8017BEBC 952 × 113 Fable5 + §47 live-length slider ×1
func_80178D40 890 × 134 cheap-Opus reading loop.c/jump.c/cse.c (§46) ×134
func_8015AE2C 562 × 134 §8d decl-scope fix unblocked the sweep ×134
func_8017A4AC 536 × 134 §49 LUID dial (sched.c) blocked — see below
func_8015A3C8 493 × 134 §48-A1 sink-the-init ×132
func_8015444C · func_8016AB6C · func_8013FFD8 · func_801380E0 wave ×134 / ×134 / ×129 / ×134
func_8013F350 · func_80131340 · func_8015B950 · func_8015C32C · func_80159C84 wave (MATCH) blocked on plumbing
func_80135EB0 289 × 134 21→6, WALLED with a named mechanism (§50-F) honest defer

🧰 THE TOOLKIT CROSSED A LINE — three ZERO-BYTE DIALS now cover the three passes that make every residual

Each emits nothing; each steers a tie; each has a diagnostic signature a cheap agent can recognise on sight. That is why 9/12 fell first-pass without Fable5. Fable5 DISCOVERS a class; everyone else APPLIES it.

signature pass dial
registers rotated global.c allocno priority §47 live-length slider · §48-A pricing dials
two insns swapped, SAME registers sched.c rank_for_schedule LUID tiebreak §49 LUID dial
structure right, instruction COUNT wrong loop peel / cross-jump §46 · §48-D
New this session: §46 (4 loop-structure levers) · §47 (the slider) · §48 (+A4 sink-the-consumer-call) ·
§49 (the LUID dial) · §50 (refinements that BOUND §47/§48 — read §50-B before using A1/A4: the
"cross_jump refunds the bytes" claim is FALSE for a 1-insn tail reached by two jumps, jump.c:1993 minimum=2).

THE HEADLINE — the three heaviest cores in the game are cracked, and the wave scaled

Core size × reach how state
func_80178D40 890 × 134 (477 KB) cheap-Opus reading loop.c/jump.c/cse.c → §46 BANKED ×134
func_8017BEBC 952 × 113 (430 KB) Fable5 + the §47 live-length slider BANKED ×1 (×113 sweep = IMM-class, Task 8)
func_8015AE2C 562 × 134 (301 KB) §8d decl-scope fix unblocked the sweep BANKED ×134
12-core Ultracode wave 2.29 MB §31/§46/§47 in the prompt + adversarial verify 9/12 MATCH, 3 near (close=2/2/21)

Metrics: instr-weighted 63.0 → 64.7% · distinct-code 39.1 → 42.8% · fn-count 82.43%. R22 136/136 after every bank. dedup 1813/0. 0 NON_MATCHING. ~15 commits.

THE LESSON OF THE SESSION — every wall was OUR TOOLING, and the R17 triage rule called it every time

Six silent-skip bugs found and fixed, THREE of them the same brace-placement class:

  1. scope_data_externs (NEW, §8d) — carried DATA externs were emitted at FILE scope, establishing a global the sibling TU never had → conflicting types. Demote to block scope when the TU has no file-scope decl above. Byte-neutral, never worse than raw. Unblocked the ×133 jr sweeps.
  2. extract_unit — not ln.rstrip().endswith(";") missed m2c's ...); /* extern */ form → a DECLARATION was read as a DEFINITION and the brace-scan swallowed the NEXT function's body. 15 of 35 substantial exemplars were phantom "matches"; 3 more templated garbage.
  3. scope_data_externs._body_open_brace — matched only an own-line { → silently no-op'd on every ANSI draft.
  4. gen_harvest_targets.SIG_IN_BODY_RE — required )\s*{, but an own-line brace has a \ before it, so 186 of 1801 (10%) of engine_core.h's shared signatures were MISSING from the canonical-callee oracle that cast_call_sites/sig_unify resolve against. This is why the wave's byte-exact cores would not bank: the draft kept its guessed sig, hit conflicting types, and the "recovery" pass reported nothing to fix. Fixing it turned func_8015A3C8 from 28-conflicts-unbankable into BANKED BYTE-IDENTICAL with zero hand edits. (commit:0561)
  5. jtbl_family_bank.revert() didn't restore the splat config → an isolation's residue rode into a commit as a DUPLICATE code-subseg line (harmless to splat, so R22 stayed green) → the NEXT isolation walked the object twice → "segments out of order". Now reverts the config + jr_isolate_all FAILS LOUD on a non-ascending/duplicate subseg list.
  6. jr_isolate_all emitted an EMPTY region 0 when the object's first item IS the first cut (cutting a non-leader out of an already-isolated region) → duplicate config line.

Standing rule (cookbook §40): a tool that silently no-ops on input it cannot parse is indistinguishable from a tool that had nothing to do. Prefer fail-loud on unparsed input, and regression-gate any change to a "proven" text scanner by snapshotting its output over the whole corpus before/after.

NEW TOOLING + KNOWLEDGE (all committed)

  • tools/scope_data_externs.py (§8d) · tools/bank_exemplar.py (bank a cracked exemplar ×1 through the same stage ladder) · jtbl_family_bank --raw (template from the RAW crack — REQUIRED when the exemplar banked at the reconciled stage, since a reconciled body is TU-specific; byte-proven: D40 banked reconciled → sweep 0/4 until --raw, then 132/132).
  • Cookbook §46 (four LOOP-STRUCTURE levers: the PEEL rule — a break must never land on the loop's own fall-through label; the surviving-copy EBB rule; the merged store; the non-replaceable giv). §47 (the LIVE-LENGTH SLIDER: measure pri = int(floor_log2(n)·n/L·10000) in the .lreg/.greg dumps; a zero-byte asm("") between two existing volatile asms shifts L by +1 and splits an allocno tie — and the split direction is FORCED toward the target). §48 (the allocno-PRICING dials: sink-the-init-into-the-arms; the local-alloc $s0 occupant; per-case temps as a tie gate. The EBB rule generalized. The C type selects the addressing mode. The cross-jump RATCHET.)

▶ NEXT — IN ORDER (Drew's directive: the AUDIT comes first)

0. [FIRST — Drew's call] The Tier-1 phase-boundary decision (see the top of this file), then the TOOLING-INTEGRITY AUDIT in plan mode, Max. ▶ Plan it FROM docs/tooling-audit.md — 40 measured findings, the method, the priority order, the already-fixed list, and the not-yet-audited list are all in there. Do not re-derive them. Order: dedup_integrate.py FIRST (a fail-closed validator that can print a FALSE GREEN) → jtbl_family_bank → the SELECTION tools (family_hseq/wave_targets/exemplar_miner — a hole there makes work invisible to planning) → masked_diff/match_one → the 40 findings by severity. Apply R33 to each BEFORE R32: why is this tool re-deriving something the build already guarantees? The best outcome is a deleted scanner, not a fixed regex. Then re-test the walls diagnosed on top of the broken oracle (the real prize): the def-side loose-typing wall (§20/§41), the 159 arity/narrow-param conflicts, the 3,098 type-heavy tail, the 9 zero-bank type-using families. Some of that "intrinsic wall" may have been lookup misses all along.

1. [THEN — the cheapest matching work on the board] The CONSOLIDATED INTEGRATION FIX PASS. Nine byte-exact cracks; six cannot bank — for PLUMBING, not matching (~1.2 MB sitting behind it). Four distinct fixes, all identified, one already built:

  • tools/reconcile_tu.py — WRITTEN + VALIDATED, NOT YET WIRED IN. Replaces reconcile_decls' oracle. That tool asks "what does the FLEET call this symbol"; the only question that matters is "what can THIS TU SEE" — 34.4% of fleet symbols carry ≥2 mutually incompatible spellings, so one fleet-wide answer is provably wrong for some TU by construction (and it returns ACTIVELY WRONG decls for 3,717 symbols). reconcile_tu reconstructs the TU's visible file-scope environment from BOTH §8c sources (col-0 decls AND engine_core.h macro-injected externs — 544 visible syms from 1801 macros) and parses the fn-ptr forms reconcile_decls is structurally blind to. Validated on func_8017A4AC: resolves D_801DA75C (fn-ptr), D_80126B58 (struct), D_801DA734 (ptr). Wire into bank_exemplar.py + jtbl_family_bank.py as a stage, byte-gate it. Unblocks func_8017A4AC (287 KB), func_8013F350, func_80131340.
  • Strip scalar typedefs from the draft before splicing (match_one does this via masked_diff.SCALAR_TYPEDEF_RE; the BANK path does not) → redefinition of 's16'. Unblocks func_8015C32C.
  • canon_sig_reconcile is not K&R-aware — raises no definition found in draft, so the def-side (§41) conflict never gets a recovery attempt. Unblocks func_8015B950.
  • func_80159C84: carve STILL non-contiguous after isolation (0xb09a4 / 0xb09c4) — a real edge case where a region ends up hosting two carves with an unmatched jtbl between them. ⚠ func_8013F350 is NOT a plumbing bug — it is a real class. Its D_8011511C must be struct-typed to force la+offset (§48-C1): the type IS the code. The TU declares it u16 at file scope, and no cast fixes it (the cast folds back to lui/%lo). It needs an EBB-separated pointer re-crack (§48-B) or a fleet decl migration.

2. Re-run the 780 h_seq rejections against the REPAIRED callee oracle (commit:0561) — large mechanical recovery expected for ~0 agent tokens. 3. The next crack wave — 32 unmatched heavy-jr ov077 cores remain (.run/wave_targets.json, ranked, with per-core asm subdirs). Plus 32 PINNED families (1.09 MB) needing pin-free re-cracks — the toolkit is pin-free by construction now. Recipe = the 12-core wave: §31/§46/§47/§48/§49 in the prompt + an adversarial verifier + the mandatory §8a jump-table check. 4. func_8017BEBC's ×113 sweep (IMM-class, scattered addrs → the immediate engine, not the PURE path). 5. func_80135EB0 — the one documented wall (§50-F): needs a lever that injects a reload-deleted no-op reg copy inside [lhu 4($a2) … sh %lo(D_801152AC)]. That is the entire remaining delta.


▶ SESSION-8 (earlier) — the ×133 sweep blocker

THE ×133 SWEEP BLOCKER IS FIXED. func_8015AE2C (562 ins) BANKED ×134 — 133/133 siblings, 0 failures. R22 clean-fleet 136/136 GREEN (from make clean, 534 changed src files). dedup-check 1813 validated / 0 failed. Metrics: instr-weighted 63.0 → 63.6% · distinct-code 39.1 → 40.5% (+256 unique fns / +79,957 ins) · fn-count 82.31%. One core × 133 siblings = +0.6% instr-weighted for ~0 agent tokens. Tree clean, all work committed.

The fix — §8d, tools/scope_data_externs.py (the session-7 diagnosis was HALF RIGHT — R14)

Session 7 blamed reconcile_decls' fleet-majority oracle and planned to teach it a TU-visible one. Reproducing one sibling by hand (rather than trusting the handoff) gave a sharper picture and a smaller, safer fix:

  • The isolated region builds [ OK ] WITHOUT the body → §8b isolation was never implicated. The templated body is the sole cause.
  • family_remap.gather_externs prepends carried decls at FILE scope. D_801812A4 is a fn-ptr dispatch table the sibling declares four incompatible ways at BLOCK scope inside its own later functions. The carried file-scope decl establishes a global the TU never had → every later block-scope extern must now agree with it → conflicting types. Byte-proven asymmetry: BLOCK(int) → BLOCK(struct*) → FILE(void*) builds; FILE(void*) → BLOCK(int) errors.
  • It was the ONLY hard error in the build. All 27 carried function externs were fine raw — cast_call_sites was not needed at all (session 7 thought it was doing the heavy lifting).
  • FIX: demote, don't reconcile. Emit a carried D_ extern at block scope inside the function body when the TU has no file-scope decl of it above the insertion point. Byte-neutral (an extern emits no code; type + access opcodes unchanged), never worse than raw ⇒ needs no oracle, no type comparator, no fn-ptr parser. It also restores fidelity — the original declares these symbols at block scope in exactly this way. Wired as the scoped stage: raw → scoped → recovered → reconciled (and scoped is the base for the later recovery stages). Cookbook §8d + decision-log + SETUP row (R30/R31/R21).
  • reconcile_decls is the wrong instrument for this class, twice: its oracle answers "what does the FLEET call this symbol" when the question is "what can THIS TU see"; and its DATA_DECL_LINE_RE cannot parse extern void (*D_x[])(void *);, so it silently skipped the very symbols that were failing. (This is the "reconcile fn-ptr-extern gap" logged 2026-07-12 — filed as a small separate lever, it was the blocker.)
  • The R17 triage rule (committed commit:0550) paid off immediately: conflicting types = the compiler REFUSED TO COMPILE = a C front-end diagnostic = our Python. Reading cse.c/global.c would have taught nothing. ("Wrong BYTES" → read gcc; "won't COMPILE" → read our tooling.)
  • Diagnostics gotcha: gcc-2.7.2 does not prefix errors with error: — grepping a build log for error finds only make's Error 33. Grep the diagnostic text (conflicting types / undeclared / parse error).

NEXT (priority order — byte-weight first; top-20 jr cores = 4.12M of the 5.53M templatable bytes)

  1. func_80178D40 (890 × 134 = 477K bytes — THE heaviest core). Crack in hand at .run/phase26-cracks/func_80178D40.c, close=39, ALL 39 inside ONE case body (0x5C); every other case is byte-exact. The 4 residuals are named in the file header (cross-jump merge of D_801DAB2C = 3; $v0→$v1 copy; loop2 keeping old q in $v1; gcc peeling loop3's first iteration where the original doesn't — loop.c). All §31-map classes ⇒ cheap-Opus + permuter, NO Fable5. Exemplar = ov_SC01_077, PURE per-location ⇒ straight jtbl_family_bank sweep once closed.
  2. func_8017BEBC (952 × 113 = 430K bytes) — close=2. THE genuine R17/Fable5 target (R27: PROMPT DREW). Two addius TRANSPOSED in the loop preheader; registers already correct. Allocation order and preheader emission order are coupled (both follow creation/LUID order) but the target needs them to DIFFER: allocation (sz0, sz2, sz1) with emission (sz0, sz1, sz2). Permuter cannot reach it (not statement-permutable; ran 25 min, no close). Agent localized it to global.c's allocno-priority TIE and named the move: a §45-B gdb-on-cc1 read of allocno_live_length (the original's length quantization plausibly split the tie), then hunt a byte-neutral L-shifter that survives cse. Full spec: .run/phase26-cracks/func_8017BEBC.md. ⚠ Exemplar is ov_SC01_000 (NOT ov077), class IMM, addr_tag scattered → its sweep runs from ov_SC01_000 through the immediate engine, not the plain PURE template path.
  3. The rest of the 191 substantial jr cores (5.53M templatable bytes). Next by weight: func_8017A4AC (536×134), func_8015A3C8 (493×134), func_8013F350 (490×134), func_801380E0 (438×134) — all ov077 PURE per-location. Recipe = the session-7 five-step (unchanged) + the new scoped stage.
  4. Re-check for cracked-but-unswept jr cores whose ×N sweeps never ran because of this blocker — use family_remap.extract_unit as the definition oracle, not a hand-rolled regex (mine false-matched extern lines — R14).

Banking a heavy jr core — the FULL recipe (unchanged from session 7, + the scoped stage)

  1. jr_isolate_all --only <core> (also cuts every already-banked jr in the same object — one .rodata carve per region). 2. jtbl_carve --func <core> (trims trailing .align pad — §8a-pad). 3. If an engine_core.h thunk calls the core with zero args, the def must be K&R and the thunk's extern must drop (void) → (). 4. Gate stages: raw → scoped (§8d) → recovered (cast_call_sites+reconcile_decls) → reconciled. 5. Whole-binary gate → jtbl_family_bank ×N → R22 → commit. Commit each family before sweeping the next (the per-sibling revert restores from HEAD).

▶ SESSION-7 CHECKPOINT (2026-07-13, cont.) — superseded by SESSION-8 above

Heavy-jr crack waves RUN (Drew approved top-3 + distill). 1 core BANKED, 2 cracks in hand. R22 136/136 GREEN, tree clean, all work committed. The §8b wall stayed broken throughout.

The three Fable5 cracks (all PIN-FREE, all independently re-verified against the bytes — R14)

Core Size × reach Result State
func_8015AE2C 562 × 134 MATCH 562/562 Exemplar BANKED (commit:0547, d19c9580, R22 green). ×133 sweep BLOCKED — see below
func_80178D40 890 × 134 close=39/890 — 851 exact; all 39 in ONE case body (0x5C) crack at .run/phase26-cracks/func_80178D40.c; 4 named residuals in a 44-ins block, agent calls it permuter-tractable
func_8017BEBC 952 × 113 close=2/952 — two addiu TRANSPOSED, same registers (pure emission order) crack at .run/phase26-cracks/func_8017BEBC.c; permuter ran 25 min, did NOT close it

⛔ THE ONE BLOCKER — the func_8015AE2C ×133 sweep (precisely diagnosed, NOT yet fixed)

The remapped sibling body's DATA externs conflict with the sibling's §8b carried decl layer (conflicting types for D_801812A4; the layer's decl is the "previous declaration"). reconcile_decls resolves against a fleet-majority canonical oracle, not against the TU's actually-visible decl, so it picks a type that still conflicts. Fix direction: reconcile the body's externs against the TU's carried layer (which is authoritative — it reproduces the original TU's decl environment), or drop body externs the layer already provides and cast at use. cast_call_sites already fixes the function-callee half (27 callees); only the ~4 DATA symbols remain. Everything else in the sweep works (isolate → carve → remap → gate). Bank flow now has 3 stages: raw → recovered (per-sibling cast+reconcile) → reconciled.

Banking a heavy jr core — the FULL recipe (all byte-proven this session)

  1. jr_isolate_all --only <core> — now also cuts every already-banked jr in the same object (a region may host at most ONE .rodata carve; an object's .rodata is a single contiguous section).
  2. jtbl_carve --func <core> — now trims trailing .align pad words (§8a-pad).
  3. If an engine_core.h thunk calls the core with zero args, the def must be K&R/unprototyped and the thunk's extern must drop (void) → () (byte-neutral fleet-wide; R22-verified for func_8015AE2C).
  4. Recovery: cast_call_sites (callees) + reconcile_decls (data) against the isolated region TU.
  5. Whole-binary gate → jtbl_family_bank ×N → R22 → commit.

Six tool bugs found + fixed this session (each silently corrupts and each is committed)

canon_sig_reconcile void→s32 not byte-neutral (§41d) · extract_unit swallowing the §8b decl layer · jtbl_family_bank cross-address naming · jtbl_carve.func_subseg reading the stale asm tree · jtbl_carve/revert deleting the committed overlays.mk carve var · jr_isolate_all --only erasing the banked set + carve-ownership read from a .s splat never emits for matched fns + one region hosting two carves · p16_permute hide_asm eating GTE #defines (permuter silently no-op'd 0s on ALL renderer drafts) + hardcoded to one overlay.

NEXT (in priority order) — with the R17 routing call (Drew asked, 2026-07-13)

Do NOT read the gcc-2.7.2 source for the sweep blocker. R17 (read the compiler internals) is for codegen residuals — regalloc / scheduling / cross-jump / CSE — i.e. things no C change reaches. The sweep blocker is a C FRONT-END diagnostic (conflicting types: two incompatible file-scope decls of one identifier in one TU). gcc is correctly rejecting plain C89; the bug is in OUR Python (reconcile_decls picks a fleet-majority type instead of the type the TU can actually SEE). Reading cse.c/loop.c/global.c would tell you nothing. Triage rule: "the compiler produced the wrong BYTES" → R17. "the compiler refused to COMPILE" → our tooling.

  1. Unblock the ×133 sweep (above) → banks 562×133 ≈ 75K templatable ins. Highest ROI, well-diagnosed. Python fix, NOT a gcc read.
  2. func_8017BEBC close=2 (×113) — THIS is where the gcc-source read belongs. The permuter failed (25 min, no close). The agent localized it to global.c's allocno-priority TIE and named the exact move: a §45-B gdb-on-cc1 read of allocno_live_length (the original's length quantization plausibly split the tie). Two instructions from a 107K-ins bank — the best R17 target in the queue.
  3. func_80178D40 close=39 (×134) — all 39 in case 0x5C; agent named 4 concrete residuals (incl. gcc peeling a loop iteration the original doesn't → likely loop.c). Cheap-Opus/permuter first, R17 if it walls.
  4. Then the rest of the 191 heavy jr cores. R27: prompt Drew before any further Fable5.

▶ SESSION-6 CHECKPOINT (2026-07-13) — superseded by SESSION-7 above

THE §8b SCOPING WALL IS BROKEN. Heavy-jr harvest is UNBLOCKED. Effort: Max. Full 54-jr isolate-all on ov_SC01_077 → d19c9580 byte-identical; R22 clean-fleet 136/136. All tools committed. Baseline clean (no config/src changes).

What session 6 delivered (committed):

  1. Declaration-environment reconstruction (overlay_src_split.py + jr_isolate_all.py) — the fix. Session-5's "gcc block-scope-extern TU-persistence" diagnosis was WRONG (R14): DEFINE_func_* macros expand at FILE scope, so their leading externs are genuine file-scope decls that merely live in engine_core.h — invisible to any col-0 text scan. Four decl sources must be carried forward, and the byte-gate found three of them: (1) col-0 decls · (2) macro leading externs (1,377 macros / 3,929 lines / 1,462 syms) → D_80126B3E undeclared · (3) a definition is itself a declaration for everything below it → func_8012B2CC undeclared · (4) file-local typedefs used by a carried proto → parse error before '*' (Vec3s). Rejected the approved "global symbol→type map + shadow set" design — it would hoist loose-typing block-scope shadows to file scope and create the conflict the shadow-set then dodges. Faithful forward-carry is conflict-free by construction (see cookbook §8c + decision-log 2026-07-13).
  2. LAZY per-core isolation wired (jtbl_family_bank): jtbl_carve NON-CONTIGUOUS fail-loud → jr_isolate_all --only <core> → re-extract → re-carve. Proven on func_80178D40 (890×134, the heaviest core): carve blocked → isolated (byte-neutral d19c9580) → carve lands in its own subseg.
  3. Two latent bugs fixed (both would have corrupted the heavy sweeps): jtbl_carve.func_subseg derived the owning subseg from the asm tree, which make extract never prunes → after an isolation it returned the STALE owner and silently re-created the collision (now derived from the config); and the sweep's revert() deleted the shared overlays.mk carve var unconditionally → would have destroyed a committed carve (all 134 overlays have one) on any failed sibling (now restored to its committed value). jtbl_family_bank also now refuses to start on a dirty config/+src/ — commit each family before sweeping the next.

DE-RISK COMPLETE (Drew's call, session 6): func_80182268 cracked + banked ×3 (the whole family) through the LAZY path — the composition is proven end-to-end. Cracked first try (31-ins jr: shared-tail fallthrough where jtbl cases 3+7 enter case 4's tail, + the (s8)(*(u16*)(p+0x70) >> 8) sign-extend idiom). Its carve collided with the committed func_801734BC carve → lazy isolation fired → carve into its own subseg → whole-binary gate d19c9580. Siblings ov_SC02_000/ov_SC02_003 (cross-address, @0x8017FCB0) banked via jtbl_family_bank. The de-risk paid for itself — it exposed 3 more bugs that would each have silently capped the heavy sweeps:

  1. canon_sig_reconcile's void→s32 return promotion is NOT byte-neutral (cookbook §41d, R14): for a void body with no return it costs ONE instruction. It turned a perfect 31-ins MATCH into 32 → the extra word made the isolated object's .text 4 B long → every data symbol shifted +4 (~271k differing bytes). match_one said MATCH; only the whole-binary gate caught it. Fix (generalizes §19): every recovery pass is a FALLBACK — jtbl_family_bank now gates RAW first, reconciled only on failure.
  2. extract_unit swallowed the §8b carried decl layer (it walks backward absorbing extern/comment lines, and the layer sits above a region's first item) → the template dragged ~140 unrelated externs into every sibling → gate-fail. Fixed with an explicit end-marker + a stop in extract_unit (also guards the Phase-17 canon layer).
  3. jtbl_family_bank used the EXEMPLAR's name for the sibling's carve/isolate/stub → cross-address families never resolved. Now derived from to_addr. (The first two banked jr families were same-address, so it hid.)

Backlogged: func_801549F8 (31-ins jr, reach ×134 — the other cheap ×134 target) is a genuine §31 loop-IV / strength-reduction residual: the target does NOT strength-reduce (recomputes script+(i<<2) each iteration), but plain arr[i] with constant per-branch increments makes gcc build a pointer GIV. A variable-stride step defeats biv recognition and gives the correct address shape (17/31, seed at .run/backlog_drafts/func_801549F8.seed.c) but gcc then keeps step in a register instead of folding K into each branch's addiu. Needs the C shape that keeps CONSTANT per-branch increments yet fails the giv worth-while test (loop.md L1). Permuter/Fable5 class — do NOT hand-grind it.

NEXT (R27 BOUNDARY — prompt Drew before launching): Task 7 = the heavy-jr core crack waves. The 191 jr family cores / 5.53M templatable ins are now bankable. Per core: Fable5 crack (whole-binary gate — NOT plain rtu_match, jr false-match risk §8a) → lazy isolate → jtbl_carve → jtbl_family_bank ×134 → parallel R22 → commit. Targets by byte-weight: func_80178D40 (890×134 = 477K ins, isolation already proven), func_8017BEBC (952×113), func_8015AE2C (562×134), func_8017A4AC (536), func_8015A3C8 (493). Fable5 window now 7/19. Optional cheap de-risk first: the 45 small jr families (~129K ins, no Fable5) to exercise the full lazy bank end-to-end (isolate→carve→remap→reconcile→gate) on a cheap target — its composition is proven in parts but the end-to-end bank with a cracked body gates at the first real core. Ghidra MCP NOT needed (matching uses cached asm).


▶ SESSION-5 CHECKPOINT (2026-07-13) — superseded by SESSION-6 above

Stage-2 isolation build — parser DONE + single-cut PROVEN; full isolate-all hit the gcc-2.7.2 scoping wall. Effort: Max. Drew's decision (2026-07-13, Fable5 window extended to 7/19 so no time pressure): BUILD declaration-completion + apply isolation LAZILY per-core (not upfront-×134 — avoids the ~7,200-file cost; same tool, isolate only cores that crack). Tools committed this session.

Built this session (baseline reverted + intact — ov_SC01_077 rebuilds d19c9580; only the 2 new tools are untracked, NOT yet committed):

  1. tools/overlay_src_split.py — overlay-.c-aware partition (header = includes + Phase-17 canonical-sig layer; per-address items = preamble + body; robust def/decl/K&R/DEFINE_func/SETTER/RETCONST classification). VALIDATED fleet-wide: 404/404 overlay .c, 341,902 items — round-trip exact / 0 unresolved / 0 non-monotonic. This is solid, reusable, commit-worthy.
  2. tools/jr_isolate_all.py — multi-cut resegment (config split at every jr boundary, source repartition + INCLUDE_ASM path repoint, banked-jr carve repoint via config + overlays.mk --order, -O0-object skip, ambient file-scope decl carry). SINGLE-cut isolation BYTE-IDENTICAL (func_8013FFD8 in main → clean make build = d19c9580, R22). Full 54-jr isolation on the dense _after object hit the wall.

THE WALL (byte-verified, decision-log 2026-07-13): mechanical TU-splitting breaks gcc-2.7.2's block-scope-extern TU-persistence — func_801734BC uses D_80126B3E, declared extern s16 ONLY inside DEFINE_func macros in engine_core.h; the original _after.c compiles because an earlier macro's block-scope extern persists to file scope for the whole TU. Splitting separates the core from that macro → undeclared. (7 distinct C-scoping edge cases surfaced + fixed incrementally; this last one is the deep one — col-0 ambient-carry can't reach macro-injected decls.)

THE FIX TO BUILD (Drew-approved, fresh session): declaration-completion — build a global symbol→type map from engine_core.h macro externs (the DEFINE_func/SETTER/RETCONST bodies) + all overlay col-0 decls; then for each isolated region emit a file-scope extern <type> <sym>; for every D_/func_ symbol the region USES, EXCLUDING the type-shadowed set (symbols with inconsistent decls across the codebase, e.g. D_80115118 = unsigned short vs struct S115118 — kept block-scope in bodies). This makes every region self-contained regardless of where the original declared the symbol (col-0, block-scope body, or macro). Est. ~40–60 LOC on the proven overlay_src_split.py; the whole-binary byte-gate (G3/P9/R22) arbitrates. Wire it into jr_isolate_all._render_region REPLACING the col-0-only ambient carry (which can't reach macro-injected decls).

NEXT (fresh session): (1) build declaration-completion (above); (2) re-prove full isolate-all byte-identical on ov_SC01_077; (3) switch isolation to LAZY per-core — integrate a single-core isolate into the bank flow (jtbl_family_bank), isolating a heavy jr core only when banking it (fewer files than upfront-×134); (4) then the heavy-jr-core crack waves — Fable5 (window now to 7/19) → §8 carve + template ×134 → R22. Committed tools this session: tools/overlay_src_split.py, tools/jr_isolate_all.py. Ghidra MCP NOT needed (matching uses cached asm). R27 boundary: prompt Drew to re-confirm effort/Fable5 before the crack waves.


▶ SESSION-4 CHECKPOINT (2026-07-12 later) — superseded by SESSION-5 above

Stage 1 in progress — multi-jtbl --order BUILT + proven cross-subseg at fleet scale. Effort: Max (this session, Tier-1 tooling build).

What session 4 delivered so far (committed + R22 136/136)

  1. ld_interleave.py --order — address-ordered N-piece multi-jtbl sandwich (text → [data|.rodata carves, address-ordered] → bss). Legacy --front/--tail path byte-untouched (main + the 133 single-carve siblings unaffected). Empty code-object sections parked byte-neutrally with .text.
  2. jtbl_carve.py rewritten additive/regenerate-from-config — parses the current tail data-region + existing .rodata carves, adds the new fn's jtbl (splitting its containing data piece), re-emits the address-ordered pieces + the --order interleave arg. Same-subseg collision → fails loud (→ isolation). Idempotent.
  3. jtbl_family_bank.py fix — bank() now make extracts BEFORE the carve (so the sibling asm matches the reverted committed config; the old error-string retry was fragile). Revert-on-carve-fail.
  4. Family-1 banked: func_801734BC (34-ins PURE jr, in ov_SC01_077_after) ×134 — matched in ov077 (shared-tail switch: case N: t=-N; break; default: goto after;), banked 133/133 siblings, R22 136/136 clean-fleet byte-identical. This is a CROSS-subseg multi-jtbl (func_8012ACE0 in _a + func_801734BC in _after) — the checkpoint's primary Stage-1 goal, proven fleet-wide.

The same-subseg cases — TWO flavors (both surfaced this session; the de-risk paid off)

Two matched jr-fns in ONE code object emit their jtbls CONTIGUOUS (gcc source order). So:

  • (a) CONTIGUOUS jtbls (adjacent in the island) → MERGE. jtbl_carve now merges adjacent same-subseg carves into one spanning .rodata piece (+ a bound-fix: a new jtbl's end is bounded by the next raw dlabel OR the next existing carve start — an already-carved adjacent jtbl is gone from the data asm, so raw dlabels alone over-extend it → false "non-contiguous"). BUILT + config-proven (func_80171B4C jtbl_801D8C48 merges with func_801734BC jtbl_801D8C68 → one _after carve [0xb0af0..]). Byte-proof PENDING a matched adjacent pair — func_80171B4C is struct-heavy (m2c gives arg0->unkXX + ratan2 + 8-case switch), a real match, not a quick de-risk. NO-OP for family-1/cross-subseg (single carve per subseg) — committed configs unaffected (verified by reasoning: R22 rebuilds static configs; jtbl_carve only runs on new carves).
  • (b) NON-CONTIGUOUS jtbls (unmatched jtbl between) → ISOLATE one fn into its own code subseg (whale _o0b precedent). tools/jr_isolate.py written (config split + func_subseg-derived carve, no rename bookkeeping; isolating F preserves carves BELOW F, so bank ascending). BLOCKED — the real finding: split_src_region can't partition the overlay .c — it has 922 non-address top-level items (the Phase-17 global canonical-sig extern layer + per-function callee-extern blocks + DEFINE_func_X() dedup macros + // @class annotations). Making it overlay-.c-aware (header = includes+global-extern-layer; attach leading externs to the following fn-block) is the Stage-2 build item.

⚠ Stage-2 unblock decision (Drew's call): isolation is a HARD Stage-2 prerequisite

Because func_801734BC is now committed in _after of every sibling, banking any heavy core (also _after, non-adjacent) hits case (b) → needs isolation. Options: (i) fix split_src_region for the overlay .c + finish jr_isolate (robust, needed regardless — Stage-2 has many _after cores → same-subseg even without func_801734BC); (ii) a one-time "isolate-ALL-jr-per-sibling" resegment (scalable: ~134 resegments then fast fills, vs per-fn ×134 splits) — likely the right Stage-2 architecture. Also open: byte-proof the MERGE with a matched adjacent pair.

NEXT: pivot to Stage 2 (this is an R27 effort/model boundary — prompt Drew to re-approve + toggle). Stage 2 = the heavy 191 jr cores (Fable5 crack-core → template ×134), which first requires the isolation build-out (option i/ii above). Cross-subseg multi-jtbl (the primary Stage-1 goal) is DONE + committed + R22 136/136.


▶ SESSION-3 CHECKPOINT (2026-07-12 late) — superseded by SESSION-4 above

Committed baseline = commit:0534, R22 136/136 (now ~50s, parallel). Session-3 commits: commit:0531 (tiny-band), commit:0532 (§8 PoC), commit:0533 (§8 ×134 automation), commit:0534 (R22 parallelization). Phase-26 total: distinct-code 30.3→39.1%, instr-weighted 58.2→63.0%.

What session 3 delivered (all committed, R22 136/136)

  1. Tiny-band harvest — 17,975 member-matches; distinct 35.2→39.1%.
  2. §8 jtbl-rodata tooling (the strategic unlock) — overlay switch (jr) functions can now bank as C for the first time. tools/jtbl_carve.py (per-overlay jtbl-rodata carve) + ld_interleave.py --section + <bin>_JTBL_INTERLEAVE Makefile hook; cookbook §8a + SETUP. Reconcile-per-sibling + extract_unit now carries single-line typedefs. tools/jtbl_family_bank.py = the sibling sweep driver. func_8012ACE0 banked ×133/133, 0 fail.
  3. R22 parallelized — make clean && make extract-all && make check-all = ~50s (was ~9m), 136/136 byte-identical. JOBS ?= 16.

⚠ THE STRATEGIC REFRAME (decision-log 2026-07-12, Drew's steer) — READ BEFORE PICKING THE NEXT TARGET

§8 did NOT just unblock a small mop-up — it unblocked the single heaviest byte-weight chunk of the game. Measured (.run/family_hseq.json): 9 of the 10 heaviest unmatched family cores are switch (jr) functions (func_80178D40 890×134 = 477K ins, func_8017BEBC 952×113, func_8015AE2C 562×134, func_8017A4AC 536, func_8015A3C8 493 …); jr substantial = 191 fams / 5.53M templatable ins; the 45 small jr families I first eyed = only 129K ins (a light-tail trap Drew caught). The endgame plan = heaviest-byte-weight-first via crack-core → template ×134; §8 is its key enabler.

▶ THE CORRECTED NEXT PLAY (fresh session) — small-jr-first as a DE-RISK PREAMBLE, then the heavy 191 (Drew's sequencing, 2026-07-12; agreed)

Stage 1 — the 45 small jr families (mid/tiny, 129K ins) FIRST, as a pipeline-hardening preamble (NOT for byte-weight — it's ~+1% instr; the point is de-risk). The decisive technical reason: jtbl_carve today only does the single-jtbl-per-overlay carve — but func_8012ACE0 is now matched in all 133 siblings, so the next jr family makes those overlays multi-jtbl → BUILD the ld_interleave --order (address-ordered) multi-jtbl carve HERE, on cheap targets, before a Fable5 giant depends on it. Also validates variety (IMM vs PURE, per-overlay jtbl positions, stubborn exemplars) where a bug costs a 30-ins fn, not a 890-ins core; and needs NO Fable5 (progress now, no session-limit burn). Pipeline: match each small exemplar (m2c + reconcile; func_8012ACE0 had a ready §41b draft — check which of the other 44 m2c cleanly) → jtbl_family_bank.py ×~120 → parallel R22. Do NOT let "45 done, feels productive" become grinding more light tail — pivot to Stage 2 once the pipeline is hardened + multi-jtbl proven. Stage 2 — the heavy 191 jr family cores (5.53M ins — THE byte-weight objective). func_80178D40 (890×134 = 477K ins), func_8017BEBC (952×113), func_8015AE2C (562×134)… Needs Task 7 (Fable5) UN-PAUSED (§8 makes it worth it now — a jtbl core crack banks fleet-wide, not unbankable fuel). Per core: Fable5 crack (whole-binary-gate-verified, NOT plain rtu_match — jr false-match risk, §8a) → jtbl_carve (now multi-jtbl-capable from Stage 1) + jtbl_family_bank.py ×134 → parallel R22. Cheap-Opus applies banked §31/§45 idioms between Fable5 cracks. Non-jr substantial families (7.3M ins) — heavy too; mechanical ones via family_sweep, hard cores via Fable5.

Effort/model (fresh session): plan-mode Max for the Phase-Start re-plan; then prompt Drew to re-approve + /effort for Fable5 (Agent model:fable, parallel-isolated, whole-binary gate) on the heavy cores; Ultracode for the mechanical ×134 template waves; Max for synthesis. Ghidra MCP NOT needed (matching uses cached asm).


SESSION-2 CHECKPOINT (2026-07-12) — superseded by SESSION-3 above

Phase 26 status: Tasks 0–6 DONE + committed (the h_seq engine + triage). Task 7 (Fable5) + Task 8 (mechanical harvest) IN PROGRESS. Committed baseline = commit:0528, R22 136/136 confirmed. Commits this phase: commit:0518→commit:0528.

What's banked (all committed + R22-verified through commit:0528)

  • 729 reconcile-class member-matches: 463 (commit:0527) + 266 (commit:0528), from 6 no-jtbl reconcile-clean triage cracks templated ×~133 via --reconcile-raw. Metrics ≈ 31%+ distinct-code / 59%+ instr-weighted (was 30.3/58.2 at phase start).
  • MID-BAND HARVEST DONE + committed + R22 136/136: plain --hseq --band mid banked 6,853 member-matches = 320 distinct functions (3,098 type-heavy tail / 1,743 pinned / 873 no-ov077-body — all expected). Metrics: distinct-code 31.1→35.2%, instr-weighted 58.9→60.9%.
  • TINY-BAND HARVEST DONE + R22 136/136 (session 3, 2026-07-12): plain --hseq --band tiny banked 17,975 member-matches / 5,617 gate-rejected (h_seq-collision false-templates, correctly refused by the whole-binary gate — the collision-prone <16-ins band) across 266 overlay .c files (180 tiny families; skips 1,184 no-matched-unit / 148 unresolved-imm-Tier2 / 117 STRUCT / 39 pinned). R22 clean-fleet 136/136. Metrics: distinct-code 35.2→39.1%, instr-weighted 60.9→62.9%. Phase-26 total so far: distinct 30.3→39.1%, instr 58.2→62.9%. NEXT: the two harvest gaps — §8 jtbl-rodata tooling (Drew-approved) + the reconcile fn-ptr-extern fix — then re-measure/milestone.

The h_seq family engine (all committed, the durable tooling)

  • tools/family_hseq.py — the survey → .run/family_hseq.json (regen after every bank) + docs/family-hseq.md. Bands: substantial(≥80)/mid(16–79)/tiny(<16).
  • tools/family_remap.py — extended reloc tracker (addu-hi §40b), classify_member (PURE/IMM/STRUCT), imm_map_tier1 (T2a), remap_hseq (plain template), remap_hseq_body (remap a RAW crack body — for reconcile-raw), gather_externs.
  • tools/family_sweep.py --hseq — templates matched-exemplar families; --reconcile-raw RAWDIR = §41c per-sibling reconcile (remap the RAW crack per sibling + canon_sig_reconcile vs that sibling's TU); static pin guard (__asm__("$N" → skip). --band/--only/--stage-only/--min-members.
  • Cracked-seed dirs: .run/crack_raw/ (raw seeds, func_.c) + .run/phase26-seeds/ (triage m2c seeds) + .run/phase26-cracks/ (Fable5 crack bodies).

The TWO harvest gaps (the levers for the remaining crack families) — see decision-log 2026-07-12

  1. §8 jtbl-rodata (Drew APPROVED building it, 2026-07-12). Overlay jtbls live in the monolithic data tail (asm/ov_*/data/tail.data.s, e.g. dlabel jtbl_801D8E24 ~line 92735 in ov077). Replacing a jr-function with C emits a duplicate jtbl → broken layout. FIX = carve each jtbl out of the data tail into a dotted .rodata subseg co-located with its function's code object (ov077 config section_order:[.rodata,.text,.data,.bss]; ld_interleave.py already wired, Makefile:397). Per-overlay ×134 (each sibling's data tail). STAGE: prove on ONE jtbl fn in ov077 → automate ×134. Blocks all jr cracks incl. the top Fable5 cores (890/562/536…) and the 2 Fable5 crack bodies (.run/phase26-cracks/func_80159C84.c+func_8015444C.c — code-matched, jtbl-rodata unconfirmed; they bank once §8 exists).
  2. reconcile fn-ptr-extern gap. canon_sig_reconcile._reconcile_data skips any extern containing ( (fn-ptr syntax), AND visible_above/tu_ambient don't parse void (*D_x[])(…) → fn-ptr dispatch tables (D_801891B8) conflict → blocks ~15 of the 21 no-jtbl triage cracks. FIX = parse fn-ptr-array externs in all 3 fns (additive, but touches proven paths → regression-test the h_norm sweep). Smaller lever than mid/tiny.

Fable5 findings (decision-log 2026-07-12) — Drew: NO more Fable5 agents until re-approval

  • rtu_match ≠ whole-binary gate for jr-functions: rtu_match neutralizes INCLUDE_ASM (excludes the §8 jtbl rodata) + masks relocs → it MATCHES while the whole-binary gate FAILS. Batch-1 (func_80159C84/8015444C/80176218) = 0 whole-binary-confirmed cracks (2 jtbl false-MATCH, 1 limit-cut). Fable5 crack prompts must require the whole-binary gate (or a jtbl-aware check), not plain rtu_match. New idioms found (distill to cookbook when banked): postincrement pending-queue (*(s32*)(p+K))++ >= N (func_80159C84); split-temps inverse-§45-A for cross-jumped tails (func_8015444C).
  • Usage/session limits repeatedly interrupt Fable5 (weekly + session limits, ~3:40am/5am resets).

Triage (Task 6, committed) — docs/phase26-triage.md + .run/phase26_triage.json

119 draftable substantial families: cheap 29 (23 closeness-0 isolation-cracks) / permuter 29 / fable5 61. Whole-binary reality: 6 cheap no-jtbl-reconcile-clean bank; ~15 no-jtbl need the fn-ptr fix; 2 jtbl + the fable5 cores need §8. .run/_rr_todo.txt = the 19 not-yet-banked cracks.

NEXT STEPS (priority order, all MECHANICAL — no Fable5)

  1. mid-harvest → R22 → commit DONE (commit:0530). 2. tiny band --hseq --band tiny DONE (session 3, 17,975 banks, R22 136/136, distinct→39.1%). 3. §8 jtbl tooling (Drew-approved; ov077 PoC → automate) — the mechanical bands are now exhausted, so this + #4 are the remaining harvest levers. 4. reconcile fn-ptr fix → re-harvest the ~15 no-jtbl cracks (.run/_rr19.sh pattern). 5. Task-5 type-families (9 zero-bank substantial) via reconcile. 6. re-measure metrics; when gaps closed → milestone check.

Effort/model: Max set (this session). Fresh session: re-apply /effort max for §8 design (Tier-1 tooling) or /effort xhigh for mechanical harvest runs. Ghidra MCP NOT needed (matching uses cached asm; run /mcp only if RE work resumes — R29).

Milestone (gate 2 — structural completion, per Drew)

  1. h_seq engine committed, V0–V3 validated with a measured template success rate.
  2. Every substantial (nins≥80) family banked ×members OR classified with evidence.
  3. Fable5-necessary cores attempted before window close; every crack distilled (R30/R31).
  4. Carried 5-target queue resolved (cracked or wall-documented).
  5. 136/136 byte-identical from clean fleet rebuild (R22); 0 NON_MATCHING (G4).
  6. All three metrics reported + Step-D residue map.

Projections (targets, NOT gates): Task 5 ≈ +5.7% distinct-code; campaign → distinct 30.3%→~55–70%, instr-weighted 58.2%→~75–88%.

Effort/model toggle map (R26/R27 — prompt + WAIT at every boundary)

On approval → /model opus + /effort xHigh (Tasks 0–4; ALL Fable5 via Agent(model:fable)). Task 5/6/8 waves → /effort ultracode. Task 7 launches + between-wave synthesis → /effort max. Task 12 → Max. Non-obvious debugging → Max.

Blockers

(none)

Log

  • 2026-07-15 (session 13, A11 — DISTILL + CLOSE THE AUDIT (26-A COMPLETE); Max): Closed the inserted tool-hygiene half-phase. Distillation (most captured live per R30/R21 during A1–A10; A11 synthesised the close): docs/tooling-audit.md transformed DIAGNOSIS → AUDIT-CLOSE LEDGER (A1–A10 outcomes table + the payoff 66.5→68.6% instr + the remaining/handoff) and its "two rules" → R32/R33/R34 written crisp for P10 ratification at the Phase-26 PhaseEnd. docs/decision-log.md +A10 verdict (R31 — the broken tools WERE the walls; the payoff was banked by the fixes; the closeness-0 residual is genuine; the real deliverable is the 3 rules + the derived-oracle pattern). docs/SETUP.md +the A9d–A10 tool changes (R21). Cookbook §51 (silent-skip class + over-approximating-detector method + LAWS 1–8) verified complete; LAW 3 tagged R34. OBSERVABLES GREEN (P9): final R22 clean-fleet make clean+extract-all+check-all = 136 passed, 0 failed of 136 BYTE-IDENTICAL; make report EXIT 0 — dedup 1840 validated / 0 failed, C1 coverage 227211/227211 signed, lint_symbol_refs (A9c, now wired) passing; make audit-corpus 0 slices, make audit-cdecl green. This session (13) made ZERO src/config changes — all tool/doc hygiene; the A10 re-gate banked 0 (confirmed-real wall). Tree clean (only db.gbf R23 noise). 🔬 PHASE 26-A COMPLETE. NEXT: resume Phase 26 at Task 7 (family-core crack waves) — a fresh session (Tier-1 R27 boundary: Max/Ultracode + re-plan the Fable5 tier per the window). The handed-forward audit items (un-migrated cdecl consumers; the #4 type-heavy harvest → Task 8) are recorded in the audit-close ledger.
  • 2026-07-14 (session 13, A10 COMPLETE — the wall re-test verdict on all 5 walls; Ultracode/Max): A10 SYNTHESIS — the audit's thesis is CONFIRMED, and the payoff was banked by the FIXES, not by re-gating the residual. Verdict per wall: ① fuel/closeness-0 — CONFIRMED REAL. Fleet-scale re-gate of all 958 closeness-0 backlog drafts = 0/958 bank (135 binaries). Genuine whole-binary near-misses; match_one closeness==0 overstates. The wall is real. ② arity/narrow-param (Phase-15 "dead-end") — WAS TOOLING, dissolved. A3c proved the () rule order-dependent (cc1 doesn't enforce it); 13/18 Phase-15 arity fns already banked; 5 residual. ③ def-side loose-typing (§20/§41, "triple-confirmed") — WAS PARTLY TOOLING, dissolved. reconcile_tu (A3d, retired the fleet-majority oracle) + A9a; A9b banked func_8017A4AC ×134 — a many-phase "wall" that was our oracle. Residual def-side conflicts are genuine (Phase-16 byte-proved real contradictions exist). ④ type-heavy tail (3,098 + 9 zero-bank families) — the BLOCKING TOOL was broken (tooling); now UNBLOCKED, harvest → Task 8. A7 fixed build_engine_types (was dead on 81% of its corpus); confirmed it now RUNS + is idempotent. But the 9 families' types (Work8016/E4/…) aren't lifted, and family_sweep doesn't wire the type-lift (remap_hseq refuses STRUCT members) — so banking them (~1,200 members) is Task-8 INTEGRATION, not a pure re-test. The wall's cause (tool couldn't run) is dissolved; the harvest is handed forward. ⑤ 780 h_seq callee-oracle rejections — WAS TOOLING, dissolved. A3h's post-fix --hseq --band all banked +2,675 members; the 16,049 residual failures are h_seq-collision false-templates the byte-gate correctly refuses (real). BOTTOM LINE (P9): the audit fixes dissolved the tooling-walls and BANKED the payoff (A3f/g/h + A9a/b: 66.5→68.6% instr); the re-tests CONFIRM the residual walls (closeness-0, genuine type contradictions) are real; #4's type-harvest is unblocked and sequenced to Task 8. "A re-confirmed wall is as valuable as a dissolved one" — A10 delivered both. NEXT: A11 — distill + close the audit (R32/R33/R34 for ratification, cookbook, decision-log, ledger, final R22), then resume Phase 26 at Task 7.
  • 2026-07-14 (session 13, A10 fan-out wave 1 — closeness-0 backlog re-test = WALL CONFIRMED REAL; Ultracode): Drew toggled /effort ultracode. Definitive fleet-scale re-test of the "fuel exhausted"/closeness-0 wall (#1): re-gated all 958 open-stub backlog drafts with closeness==0 (match_one says byte-exact, gate-rejected before the A3e all-TU fix) through the FIXED gate_stage --no-propagate across 135 binaries in parallel (Pool-16, 63 min). RESULT: banked=0, near=957, failed=71, 0 errors, tree clean (0 banks). CONCLUSION (P9 — a re-confirmed wall is as valuable as a dissolved one): the closeness-0 backlog is genuine whole-binary near-misses, NOT tooling misses. match_one closeness==0 (isolated, reloc-masked) systematically OVERSTATES whole-binary bankability (A3h's Bucket-X finding, now confirmed fleet-wide at scale), and the repaired gate recovers none of them. The audit's "some walls were tooling" thesis was ALREADY validated by the fixes' banking (A3f/g/h + A9b, 66.5→68.6% instr); this residual is real codegen. Ran as a deterministic parallel job (not agents — agents would be wasted running a shell gate); backlog.py gained an env-gated BACKLOG_NO_RENDER (workers skip the render race; append is atomic) — a small backward-compatible parallel-safety change. NEXT: pivot the reasoning Workflow to the one UNTESTED wall — #4 the type-heavy tail (9 zero-bank type-using families + ~73 type-suspect stubs + the 3,098 tail), which A7 fixed build_engine_types to run but nothing has re-harvested since (A3h's h_seq sweep skipped STRUCT). Sample-test first, then scale.
  • 2026-07-14 (session 13, A10 Max-phase — wall re-test SCOPING + arity measurement; Max, fan-out pending R27): Began A10 (Drew: "begin at Max, prompt before the ultracode fan-out"). Measured before claiming (R14). KEY FRAMING: the audit FIXES already dissolved the easy wall — A3f/g/h banked +2,675 h_seq re-harvest + the byte-exact-stub backlog wins, A9b re-banked func_8017A4AC ×134 (a def-side wall) — driving 66.5→68.6% instr. So A10 is the systematic re-test of what remains. Scoping: backlog = 2,218 open-stub drafts (deduped by fn) — 958 at closeness 0 (match_one says byte-exact, gate-rejected before the A3e all-TU fix), 429 @1-5, 543 @6-20, 288 @20+; fleet = 9,543 open stubs; h_seq = 16,049 residual failures (mostly collision false-templates the byte-gate correctly refused — A3h already re-ran that sweep). #2 arity (targeted, measured): of the 18 Phase-15 arity/narrow-param drafts, 13 are ALREADY banked — the "documented dead-end" wall largely fell (fix_arity_callers Phase-19 + later banking), 5 remain (func_80156600/8015C030/80161208/8016191C/8016BF50); A3c already proved its premise false (the () rule is order-dependent, cc1 doesn't enforce it). #4 type-heavy: build_engine_types now runs (A7) — the tail is re-testable. CONFIRMED the remaining re-test is BREADTH: a serial gate_stage sample timed out (ov_SC01_077 rebuilds per draft; the reason the audit banked via ×134 family sweeps, not per-fn gating). Tree reverted clean (HEAD commit:0618). → R27 BOUNDARY: prompting Drew for /effort ultracode before the fan-out = re-gate the 2,218 open-stub drafts (958 closeness-0 first — the definitive "fuel exhausted" test) + the type-heavy tail through the fixed oracles (reconcile_tu/cdecl-compat/build_engine_types), parallel-isolated, bank the byte-gated wins, R22 before commit; a re-confirmed wall is as valuable as a dissolved one (P9).
  • 2026-07-14 (session 13, A9g — jr_inventory: retire the ephemeral roster, derive banked from the image; Max): R33 applied to "the purest R33 case in the group" (audit). jr_inventory's banked set was filtered by an EPHEMERAL, gitignored .run/banked_func_*.json roster — rm -rf .run/a fresh clone would blind ALL banked jr at once, cross-address siblings (roster named after the exemplar) were structurally invisible, and non-leader banked jr were missed. FIX (audit's exact prescription): deleted the roster glob + cand filter; banked is now DERIVED FROM THE IMAGE — a real-C def/define fn is a banked jr iff family_remap.reloc_targets shows it references a committed .rodata carve offset (config + image, both durable). R32 assertion added: every committed carve must resolve to EXACTLY ONE owner or the run aborts (a stranded/duplicated carve = the §8b func_801734BC incident, never silent). Also fixed the adjacent MEDIUM/LOW finding — the asm_jr scan's func_-fullmatch dropped the curated-name listCdBuffer jr; now resolved via oss.addr_of() (the --only path's own fullmatch is left — it parses user input, not the corpus). Perf: the image is read ONCE and passed to reloc_targets(…, data=) (new backward-compatible param on family_remap; regression-verified 0/80 mismatch vs the re-read path). VERIFIED: (1) reloc_targets data-param behavior-identical; (2) the R33 win — ov_SC02_000 now finds the cross-address sibling func_8017FCB0 the roster missed; the 3 non-leader overlays (ov_SC01_077/04_008/05_009) resolve; (3) full-fleet parallel run = 134/134 OK, 0 false aborts, 1336 banked jr == 1336 carves → 1:1 ownership holds fleet-wide (the R32 assertion is safe). BYTE-SAFE: jr_isolate_all is NOT in the make build/extract path (R22-neutral); the change makes future isolations strictly MORE correct (finds carve owners the roster missed → fewer stranded carves). tooling-audit ledger marked FIXED (3 jr_isolate_all findings). NEXT: A10 — re-test the walls (the audit payoff).
  • 2026-07-14 (session 13, A9f — overlay_src_split force_decl latch fixed + a coverage oracle; Max): The parser jr_isolate_all rewrites source from swallowed 2 real function definitions on physical lines of the form extern A; extern B; void f(){...}: scan_construct's force_decl latched from the FIRST token and returned at the first depth-0 ;, so the def after it was never anchored — absorbed into the next anchor's preamble. The selftest was structurally blind (round-trip = "\n".join(item_texts) stays exact by construction when a miss lands in a preamble — a serialisation check, not coverage). FIX (byte-safe, R14-chosen): rejected the audit's "split the line into 3 constructs" — round-trip joins whole-line chunks with \n, so sub-line splitting would insert a newline where a space was and BREAK round-trip. Instead: force_decl no longer survives a same-line ; with trailing code (re-classify
    • keep scanning → the def anchors, its leading externs staying in its whole-line text); def_name now names the LAST top-level header before { (the definition, not the first same-line extern; byte-identical on every single-def construct). R32 coverage oracle hidden_definitions() wired into selftest (independent detector of func_XXXX(...){ bodies not anchored) — the selftest is now a coverage check. Audit line refs were STALE (src rewritten by banking): real cases are ov_SC01_077_after.c:2020 (func_8014FDF4) + ov_SC01_077_jr_8015444C.c:1495 (func_80155FF8), found via the new oracle (matched the audit's count of 2). VERIFIED: 2 swallowed→0; full-fleet regression over 1738 overlay .c = 0 round-trip fails, 0 non-monotonic, 0 non-additive changes (nothing removed/reordered, def_name unchanged everywhere), exactly +2 anchored defs. BYTE-SAFE: overlay_src_split is NOT in the make build/extract path (R22 byte-neutral); ov_SC01_077 rebuilds d19c9580 [OK]; neither def straddles a committed subseg boundary (no cut in (def, next]) ⇒ a future re-isolation stays byte-identical. tooling-audit ledger marked FIXED. NEXT: A9g — jr_inventory ephemeral gitignored-scratch read (R33).
  • 2026-07-14 (session 13, A9e — reconcile_tu already wired into bank_exemplar; NULL RESULT; Max): P9/R14 — the session-12 handoff item "wire reconcile_tu into bank_exemplar" is a stale carryover; it was already done by A3d. Traced the call chain rather than trusting the note: bank_exemplar's recovered stage (l.53) = fb.recover(base, OV, cf, FUNC) = jtbl_family_bank.recover(), which runs cast_call_sites then reconcile_tu (l.94/96) — the reconcile_tu wiring entered there in A3d (commit:0601, "retire the fleet-majority oracle … on BOTH banking paths"). So reconcile_tu reaches bank_exemplar transitively via recovered, needing no bank_exemplar-specific rung. Already PROVEN: A9b banked func_8017A4AC "at the recovered stage (reconcile_tu resolves its D_80126B58 struct
    • D_801DA75C fn-ptr conflicts)". Also confirmed no live tool references the RETIRED reconcile_decls (only docstring mentions + the make audit-cdecl differential harness, which legitimately measures cdecl against the old regexes). NO code change warranted — adding a redundant explicit reconcile_tu rung would duplicate recovered and diverge from jtbl_family_bank's pattern for zero benefit. One byte-neutral hardening: documented the recovery-ladder composition inline in bank_exemplar (scoped/recovered/reconciled) so a future session doesn't re-run this exact "is it wired?" investigation — the precise waste the audit exists to eliminate. NEXT: A9f — overlay_src_split force_decl latch.
  • 2026-07-14 (session 13, A9d — retire the dead Phase-17 canonical-sig chain; Max): R33 applied to a two-tool dead chain. DELETED tools/census_conflict_callees.py + tools/derive_canonical_sigs.py. census was audit-CONFIRMED MARKED-FOR-DELETION (commit:0593; decision-log 836 "in its entirety gets deleted"): it re-derives from C text the question — "for this TU, which callees are defined/declared/ stubbed/external?" — that reconcile_tu (Phase 26) answers FROM THE BUILD; a parse hole in reconcile_tu is conservative, a parse hole here is WRONG in the unsafe direction (unknown→"conflict-free"). Its output fed only derive_canonical_sigs, which the audit DOWNGRADED but which is genuinely dead: last touched Phase-17 (commit:0140), output .run/canonical_sigs.json read by nothing (no Makefile target, no workflow, no module import — all verified by grep), no-ops on the 2-byte [] input, and its asm-arity heuristic is 36% wrong vs byte-exact banked C (audit skeptic, ground-truth run). Its canonical-sig purpose was itself retired in A3d (fleet-majority oracle → reconcile_tu's per-TU oracle). So the whole dead chain ceases to exist rather than leaving an orphaned, non-runnable, wrong-by-36% scanner (R33: "the best outcome is a DELETED SCANNER"). Byte-neutral BY CONSTRUCTION — neither tool is in any build/report path; verification = module-import smoke over the 13 importable harvest/bank/report/reconcile tools (corpus/cdecl/reconcile_tu/canon_sig_reconcile/gen_harvest_targets/gate_stage/ jtbl_family_bank/family_sweep/dedup_propagate/sig_unify/cast_call_sites/scope_data_externs/ family_remap) = all clean (bank_exemplar is a run-only script that indexes sys.argv at module scope → not importable by design, imports neither deleted module — a test-method false alarm, not breakage). No src//config/ change ⇒ no byte can move ⇒ full R22 not warranted (P9-honest: a 136-binary rebuild would prove only what the absence of a build-path edit already guarantees). Doc-pointer hygiene (so nothing points at a nonexistent tool): docs/hand-matching-process.md §8a, docs/matching-cookbook.md (canonical-sig-layer entry), and docs/tooling-audit.md (ledger row + the derive_canonical_sigs entry) all annotated DELETED/historical. NEXT: A9e — wire reconcile_tu into bank_exemplar (its stage ladder is raw→scoped→recovered→reconciled; recovered=fb.recover, reconciled=canon_sig_reconcile — add a reconcile_tu stage, byte-gate); then A9f (overlay_src_split force_decl latch), A9g (jr_inventory ephemeral read), A10 (wall re-test), A11 (distill + close → resume Phase 26 Task 7).
  • 2026-07-14 (session 12, A9a+A9b — canon_sig_reconcile fn-ptr fix + wall re-test; Max): Continued the tool-hygiene audit. A9a — canon_sig_reconcile sees fn-ptr dispatch tables (commit:0609). The def-side-wall recovery tool, live on the ×134 economic-engine paths (jtbl_family_bank, family_sweep --reconcile-raw, bank_exemplar, t7_bank, scope_data_externs, family_remap). Its tu_ambient/visible_above classifiers + _reconcile_data's _DATA_EXTERN_RE have a type class [\w \*] that can't hold a ( → 100% blind to extern void (*D_x[])(void);, the per-overlay jump-table DISPATCH arrays. A fn-ptr symbol landed in NO bucket, fell out of visible, and the tool block-moved the draft's extern into a guaranteed conflicting types. Fix = SUPPLEMENT, not wholesale-replace (R14-driven): a full cdecl delegation of the funcs classification was tried and REJECTED — cdecl normalizes funcs formatting (void*→void *, param names, a real return-ptr hazard u8*→u8) → 728k value-changes rippling into the callee string-compare; the audit flagged fn-ptr DATA, not funcs. So keep the proven regex byte-identical and supplement from cdecl.tu_scope ONLY the fn-ptr symbols it drops (_fnptr_data); _reconcile_data gains a fn-ptr pre-pass (visible→STRIP, no cast — a call-through D_x[i]() is decl-independent indirect codegen; casting would mangle it, the trap reconcile_tu documents; not-visible→block-move) + the F2 comment-paren-bail fix + an R32 coverage assertion. PROVEN ADDITIVE: classifier snapshot over 1683 TUs = REGRESSIONS 0, ADDITIONS 69,798 (all data fn-ptr); reconcile() OLD-vs-NEW over 434 real drafts = 0 regressions; src/ UNTOUCHED (a tool change moves no bytes). Harness in .run/audit/a9a_*. A9a's immediate banking impact = NULL (correctness fix, like A3c/A3d/A3e — the historical tail fails on codegen/heterogeneous blockers, not the one fix); its value is protecting all future dispatch-table banking. A9b — re-test the walls (commit:0610, commit:0611). Re-ran 7 fn-ptr-referencing "blocked" cracks through bank_exemplar. HONEST (R14): A9a did NOT independently unblock any — the reconciled stage failed on func_8015B950's func-conflicts; the rest hit K&R / scalar-typedef / non-ov077 / non-contiguous-carve blockers. BUT the audit's recover path (A3d reconcile_tu / A3e gate) dissolved func_8017A4AC (536 ins × 134) — a giant "blocked on plumbing" since session 8. It banked at the recovered stage (reconcile_tu resolves its D_80126B58 struct + D_801DA75C fn-ptr conflicts). Isolated + carved + jtbl_family_bank --raw swept 133/133 siblings, 0 failed. R22 clean-fleet 136/136 after each; instr-weighted 68.1→68.6% (+0.5%), distinct 48.0→49.2% (+1.2%); dedup 1840/0. The audit thesis demonstrated: a many-phase "wall" was our TOOLING, not an intrinsic residual — fixed the oracle, the wall banked ×134. A9c — lint_symbol_refs green + wired (commit:0613). The ONLY detector for the R22 rename-drift failure mode (a symbols rename leaves a func_ ref dangling; a clean build fails, an incremental masks it — undetected Phase 21→23). Was RED (262 FPs) + UNWIRED. All 3 audit blind spots fixed: #1 now scans src/shared/*.h (engine_core.h's 10k+ tokens; negative-control-proven a shared-header ref IS flagged); #2 reads every REAL stacked symbol file, excludes R13 proto; #3 all 262 FPs were the __asm__("memcpy") class (engine_core.h binds func_8005C324→memcpy via an asm label → resolves at link) — now modeled. Negative control (inject a bare func_800d1bd8→DsMix in a shared header) FLAGS it then clears — detection intact. Wired fail-closed into make report; no src/build change (report-time gate). SESSION-12 net: instr 68.1→68.6%, distinct 48.0→49.2%, 5 commits (commit:0609, commit:0610, commit:0611, commit:0612, commit:0613) + this log. NEXT (tool-hygiene tail): A9d (DELETE census_conflict_callees after confirming derive_canonical_sigs is dead; wire reconcile_tu into bank_exemplar; overlay_src_split force_decl latch + jr_inventory ephemeral read); then A10 (full wall re-test — the def-side loose-typing wall, 159 arity conflicts, 780 h_seq rejections, the permuter-fuel "exhausted" claim) + A11 (distill + close, then resume Phase 26 Task 7).
  • 2026-07-14 (session 11, A3h — the standing-lead harvest, measured then banked; xHigh): Resumed the A3f/A3g "310 byte-exact stubs" lead. Measured it precisely first (R14) — and the "310 same class, same fix" framing was optimistic. Deduped the backlog's closeness==0 rows (recovering addr from name for the 1,492 null-addr LLM-tier rows — dropping them would have been the exact R32 silent-skip), cross-referenced against corpus.stubs() over all 136 binaries: 1,024 still-open byte-exact functions, in 3 buckets — G (66 open in ov077), P (61 matched in ov077 + open in siblings), X (897 absent from ov077). Bucket G — re-gated the 66 through the A3e-fixed gate_stage --no-propagate → 0 banked, 64 near, 3 failed. HONEST: A3f already took the bankable 33; the residual is the known hard classes (jtbl-rodata / register-pins / struct-collision) + stale backlog rows whose LATEST state is a WAVE mismatch (the closeness==0 was an old row). Correct G3/P9. Bucket P — dedup_propagate --addr (A3g primitive). It skipped 70 of 83 as h_exact reach<2 (their sibling bytes DIFFER — per-location variants → family_sweep territory, not plain propagation) and propagated the 14 genuine PURE fleet families ×~133 (2 stragglers all-or-nothing-dropped). R22 clean-fleet 136/136; instr-weighted 66.8→67.4% (distinct flat — propagation adds MEMBERS); dedup 1826→1840/0. Committed commit:0606. Variant families — regenerated family_hseq.json (the 14 banks refreshed it), then family_sweep --hseq --band all (the audit fixes A3–A8 + new exemplars unblocked it): staged 12,373 member drafts / 223 families, BANKED 2,675 / 9,698 failed (22% — the hard residual; easy bands long-harvested), skipped 3,319 pinned + 212 imm + 137 STRUCT. R22 clean-fleet 136/136; instr 67.4→68.1%, distinct 46.8→48.0% (+1,435 unique fns — templated members are distinct byte-variants, so this moved BOTH). Committed commit:0607. Bucket X (897 absent-from-ov077) — DE-RISKED, then STOPPED (P9). Gated ov_SC04_018's 35 X-drafts → 0 banked, 35 near. THE FINDING (R14): match_one closeness==0 (isolated, relocation-masked) ≠ whole-binary byte-exact. In the real TU their fresh gate logs read residual: 13/19/55… mismatch — they were never whole-binary-bankable; the backlog's isolated-proxy overstated them. Only ~5/35 are true match_one-MATCH "declaration/TU plumbing" — and reconcile_tu is already wired into gate_stage (A3d, l.208), so those need the un-migrated cdecl recovery consumers (sig_unify done A3e, but canon_sig_reconcile's classifier still on the old parser — session-9 handoff item 1), i.e. the next tool-fixing task / A10, not the current pipeline. So the mechanically-clean lead is banked; the residual is gated behind hard classes + recovery holes. NET SESSION: instr-weighted 66.8→68.1% (+1.3%), distinct-code 46.8→48.0% (+1.2%, +1,435 fns). 2 commits, R22 136/136 after each. Tree clean (only db.gbf restart-noise, R23). Measurement + buckets in .run/audit/a3h_*. NEXT: the recovery-tooling task at Max (migrate canon_sig_reconcile + the remaining cdecl consumers → re-test Bucket-G/X plumbing-class + the A10 walls). — R27: prompt Drew, wait for /effort max.
  • 2026-07-14 (session 10, A3f+A3g — the payoff of A3e, banked & propagated; Max): With gate_stage no longer pinned to the main .c, ran the 63 ov_SC01_077 closeness==0 backlog drafts (byte-exact bodies the ledger called unrecoverable) through the fixed gate --no-propagate: 33 banked, 30 near, 0 failed. Every one landed in a SPLIT TU (7 of 8 files are _jr_/_a/_after; none in the main .c) — the diagnosis proven by placement: not hard, unreachable. A3f committed the 33 (commit:0603), R22 clean-fleet 136/136, +1,531 ins, ×1 (fn-count 82.79→82.80%). Then A3g (commit:0604): the 3 of the 33 that are fleet-wide, propagated ×134 — done RIGHT this time (targeted --addr, dry-run-sized to 3×134 first, on a clean tree), after the earlier --auto-from disaster. +399 members (3×133), instr-weighted 66.7→66.8%, dedup 1823→1826/0 failed, R22 clean-fleet 136/136. The other 30 are overlay-unique (×1, no propagation). ⚠ THE STANDING LEAD: the backlog holds 373 still-open stubs with byte-exact bodies; 63 were in ov_SC01_077 → 33 banked (52%). 310 remain across the OTHER overlays — same class, same fix, untouched — the single largest lead the audit produced. Disk hygiene (Drew, mid-session): retired the dead 7B LLM tier (.venv-train 7.9G + HF Qwen cache 5.3G + pip CUDA-wheel cache 5.9G + ghidra installer zip) → ext4 33G→14G; the 112G .vhdx needs a Windows-side wsl --shutdown + compact to actually shrink (deferred to Drew). 7B tier re-creatable from docs/gen2-mips-matching-model.md; nothing irreplaceable removed.
  • 2026-07-14 (session 10, A3e — 🔴 THE BIG ONE: gate_stage pinned the byte-gate back to 4.9% — of A3's own fix; Max): Migrating sig_unify exposed a chain, and the last link is the worst defect in the whole audit. (1) sig_unify silently DROPPED 190 of 196 drafts (97%). cur_stubs was read from the main .c (13 stubs of 263), so any draft whose stub lives in a _jr_* carve hit if fn not in cur_stubs: continue — dropped before the write: never copied to --out, never gated, never logged. The summary printed "drafts unified: 6" and read like success. This is gate_stage's STAGE-2 RECOVERY — the pass whose entire job is to rescue stage-1 failures — and it has been a no-op for almost every draft it was meant to save. Fixed by deriving the TU per draft (corpus.stubs) + the canon from cdecl.tu_scope (cpp, so macro-injected decls are visible) + _keep() (only rewrite when cc1 would actually reject — the §19 "sig_unify regresses canonical drafts" failure mode). Reach: 6 → 196 drafts; callee-externs rewritten 2 → 90; own def-sig 2 → 86. With all three consumers migrated the 196-draft failure profile went near 5 → 116, failed 190 → 17: 173 of 190 "failures" were PLUMBING, not codegen — now compiling and scored instead of invisible. (2) 🔴 gate_stage.py:315: src=a.src or f"src/{b}/{b}.c". src RESTRICTS the byte-gate to ONE TU, and _gate1 does if src: cmd += ["--src", src] — always truthy. A3 had just taught harvest_verify to derive each draft's home TU when --src is OMITTED (reach 4.9% → 100%), and gate_stage never omits it. The fix was neutralised by its own caller's default, leaving the primary banking path (every wave, the grinder, the orchestrator, bulk_harvest) structurally unable to bank 250 of 263 stubs. WHY IT SURVIVED 26 PHASES: a draft whose stub isn't in the pointed-at TU simply never verifies → it is logged near/failed, i.e. as a MATCHING problem → the wave reports a poor close-rate → the function goes to the backlog as a compiler residual. A tool that CANNOT bank a function is indistinguishable, in every log this project keeps, from a function that CANNOT BE banked. Proof, same draft/gate/second: gate_stage rejected func_80129C40; harvest_verify run directly (no --src) VERIFIED it byte-identical and banked it. (3) And a counting bug hid the hiding (gate_stage:261): match_one MATCH + gate-reject logs status="near" and never increments the counter — a 63-draft run printed banked 0, near 0, failed 0, three zeros that do not sum to 63, for phases, and nobody added them up. THE PRIZE, MEASURED: the backlog holds 1,588 closeness==0 entries (body byte-exact per match_one, gate rejected). 1,215 were banked since by other paths; 373 are STILL OPEN STUBS whose bodies are already byte-exact, sitting in a ledger that calls them unrecoverable. ⚠ THE HARVEST ATTEMPT FAILED AND WAS REVERTED (P9). Gating the 63 ov_SC01_077 ones dragged dedup_propagate --auto-from --recover behind it, which ran >1 h and hit its timeout — its first-ever run over the FULL corpus (A6/A7 unblocked the 407 files it could never see). It mutates the tree BEFORE it gates, so the kill left 859 files + engine_core.h (+544 lines) written and UN-GATED, and the registry never updated. R22 clean-fleet on it: 44 passed / 92 FAILED → git checkout -- src/ config/, fleet restored. Nothing was lost (H4: the tree was clean, so the revert was one command) — but two real lessons: dedup_propagate is NOT crash-safe and must never run under a timeout it can hit; and a 63-draft experiment must not drag an unbounded fleet-wide propagation behind it (gate with propagate=False, then propagate as its own bounded job). Committed: the TOOL FIXES only. The harvest is NOT banked and is not claimed. Knowledge captured live (R30/R31): cookbook §51g LAW 11 (a fix is not landed until its caller stops overriding it — after fixing a scanner, grep every call site and ask whether a caller's default re-disables it), tooling-audit A3e. NEXT: re-run the 373-stub harvest with propagate=False, then propagate separately.
  • 2026-07-14 (session 10, A3d — RETIRE the fleet-majority oracle; both banking paths; Max): R33 applied to the audit's worst finding: not fixed, RETIRED. reconcile_decls asks "what does the FLEET call this symbol?"; C asks "what does THIS TU declare?". The engine is loosely typed, so one fleet-wide answer is wrong for some TU by construction — and worse than a skip, because it writes an actively wrong decl into the draft that then collides with the very TU it was meant to conform to. Measured across ov_SC01_077's 12 TUs vs what cpp says each TU really declares: agrees 2,883 · CONFLICTS 548 (16%, cc1 rejects the result) · absent 357. And it was live on both banking paths — gate_stage (rewrote 60 of 196 drafts) and jtbl_family_bank, i.e. EVERY SIBLING of the ×134 family sweep, the project's economic engine. A poisoned decl means that sibling silently doesn't bank and the sweep just reports a smaller number. The irony is exact: that function's own docstring already knew the conflicting symbols are per-overlay — which is precisely why a FLEET oracle could never have been right. reconcile_tu.py (written in Phase 26, never wired) now supersedes it, rebuilt on cdecl: cpp for what the TU declares (macro-injected externs included), cc1 for whether the draft's decl can coexist, TU-derived from corpus.stubs(), and it handles the fn-ptr kind NATIVELY — which is why it supersedes rather than patches: teaching the old parser to see extern void (*D_x[])(void); would have ARMED its fn-ptr-blind data_access_subs to rewrite a call-through D_x[i]() into ((u8 *)D_x)[i](). Fixing the regex would have detonated a dormant bug. NULL RESULT AGAIN (P9/R14): on the 196 never-banked drafts the new oracle banks exactly as many as the old — zero; the two disagree on 45 of 196 and the outcome does not move. That tail fails on codegen, not plumbing. This is a correctness fix (548 wrong decls removed from two live pipelines, protecting all future drafts and every future family sweep), not a banking win, and it is not being sold as one. Three nulls in one session. reconcile_decls.py kept as EVIDENCE, marked RETIRED, no live caller. R22 clean-fleet 136/136; src/ untouched; reconcile_tu 0 coverage defects over 196 drafts. ⚠ The family-sweep path gets its real exercise at Task 8 — watch the per-sibling bank rate.
  • 2026-07-14 (session 10, A3c — first consumer migration: cast_call_sites onto the cdecl oracle; Max): Added cdecl.compatible() — "will cc1 accept these two declarations of one name?", the predicate four tools each half-implement and get wrong (norm_sig/_norm_type collapse the int family, so a signedness change reads as "already compatible" and gets no rewrite — while cc1 rejects that redeclaration). Wrote the rules from the C standard; then let a compiler judge. It contradicted me — and then the RIGHT compiler contradicted the first one. Modern mipsel-linux-gnu-gcc, gcc-2.7.2 cc1, and the standard give three different answers (typedef redefinition: C89 error / C11 accepts / cc1 ERRORS; qualifier mismatch: modern gcc errors / cc1 ACCEPTS; no-proto + narrow param: both error / cc1 accepts in ONE direction). So --compat now adjudicates with tools/bin/gcc-2.7.2-psx/cc1, the front end that actually arbitrates the build → 1,485/1,485 live corpus pairs agree, 0 disagree, 0 skipped. 🏆 THE PRIZE: the Phase-15 narrow-param wall rests on a false premise. The () rule is ORDER-DEPENDENT: void X(s16); void X(); compiles; only the reverse fails. "The 159 arity/narrow-param conflicts — no clean deterministic fix" was closed on a rule cc1 does not enforce. Four three-line probes, 90 s, zero tokens → A10 re-test target. THEN the migration itself: cast_call_sites was canonicalizing 95.1% of drafts against the WRONG TU. --src-file is an optional hand-passed flag defaulting to src/<ov>/<ov>.c, and no caller knows about the Phase-26 _jr_<ADDR> carves — ov_SC01_077 has 263 open stubs across 12 TUs, only 13 in the main .c — while harvest_verify (A3) correctly splices into the real one. Now DERIVED from corpus.stubs() (the INCLUDE_ASM line is self-describing) + the canonical map derived from cdecl.tu_scope() (cpp — so macro-injected decls are finally visible). Repair reach 8 → 58 of 196 drafts (7×). THE NULL RESULT, REPORTED AS SUCH (P9/R14): those 58 banked ZERO. The historical tail fails on codegen, not plumbing — func_801387B8, which the audit blames on one unparsed [4], is really 67/100 instructions off with a $s0/$s1 swap (that claim does not reproduce). The real gain is narrower and still worth having: 52 drafts moved from "won't compile" to "compiles, N instructions off" — from an invisible failure that reads as a compiler wall into a scored near-miss the permuter can act on. Three times in one session a confirmed mechanism produced a null consequence. I also mis-diagnosed the callee oracle as "returning nothing" (my probe was buggy — it did have the sig) — corrected. And my own new audit printed "ALL ORACLES GREEN" while silently skipping 100% of its corpus (a missing -Isrc): the exact bug class, in the tool written to hunt it. Fixed — an unadjudicable check is not a passed check. R22 clean-fleet 136/136 BYTE-IDENTICAL; src/ untouched (0 changes); make audit-cdecl green. Knowledge captured live (R30/R31): cookbook §51g LAWS 9–10, decision-log, tooling-audit A3c. NEXT: sig_unify + reconcile_decls have the SAME wrong-TU bug (same --src-file flag) — migrate them, then lint_symbol_refs, then A10 (re-test the walls) with the narrow-param finding as the first target.
  • 2026-07-14 (session 10, A3b — tools/cdecl.py, THE C-declaration oracle; Max): Built the one parser that lets fifteen die. Rejected the audit's own prescription (a shape-aware alternation per tool, ~15 coordinated regex edits) on R33 grounds: fifteen hand-maintained models are precisely what diverged, and an alternation only ever covers the shapes someone remembered. The thing being scanned has a grammar — C's declarator grammar is small, closed and total, and describes fn-ptr arrays / sized+2-D arrays / multi-declarators / fn-ptr params / K&R identifier-lists without being told they exist. ~250 lines of recursive descent, less code than the regexes it deletes. Two statement paths because the inputs genuinely differ: a TU's file scope is derived from cpp (a decl inside a DEFINE_func_* macro body declares nothing until invoked — §8c; 54 ms/TU, so the "too slow" objection is dead), while a draft gets a span-preserving raw split (drafts are rewritten). Proof, not belief — three oracles over the whole corpus: coverage 2,952,246 depth-0 statements → 2,731,521 declarators, 0 parser defects; the real cross-gcc compiled each of 50,405 distinct declarations beside my reconstruction of it → 0 rejected; differential vs the incumbents → 0 file-scope symbols they see that I miss, 26 in engine_core.h they cannot see, 6 they wrongly promote from block scope. Two ideas worth keeping (cookbook §51g, LAWS 4–8): the candidate set is DERIVED (at file scope C admits nothing but declarations → R32's detector is every depth-0 statement, no second model to rot), and gcc adjudicates my own coverage gap (a statement gcc also rejects is not C; one it accepts and I don't is my defect — grading my own homework is the habit that wrote the fifteen bugs). All 33 residual: NOT-C, all dead .run/drafts* scratch, none in src/. Bugs the oracles caught in ME, that I would otherwise have shipped: extern s32 (*D_801274D0)(s32); parsed the base type as the name; a K&R declaration-list flushes as SEVERAL spans, so the body attached to the wrong one and leaked a0/arr into file scope as fake globals. New findings → docs/tooling-audit.md: reconcile_decls.DATA_DECL_LINE_RE finds ZERO decls in engine_core.h (its authoritative tier has always been empty); two scanners treat block-scope externs as file-scope canonicals (the §8d wall); tu_ambient drops any callee with a fn-ptr param. An honest R14 near-miss: the 33 corrupt drafts were written by a recovery tool prepending extern to an if — but the source bug was already fixed in Phase 19 (0 garbage / 300 sigs today), so mechanism confirmed, consequence nil. NO consumer changed (deliberate — the audit warns the fix ARMS dormant transforms), so it cannot move a byte: R22 clean-fleet 136/136 BYTE-IDENTICAL, make audit-corpus still 0 unmatchable slices. New gate make audit-cdecl — standing, because a loud failure nobody counts is as invisible as a silent one. Knowledge captured during the session (R30/R31): cookbook §51g, decision-log, SETUP inventory (+ the corpus.py row, an R21 gap from session 9). NEXT: migrate the consumers, one at a time, byte-gated — starting with reconcile_decls/reconcile_tu (3,717 actively-wrong canonicals).
  • 2026-07-13 (session 8, the ×133 sweep blocker FIXED — func_8015AE2C banked ×134; Max): Reproduced ONE sibling by hand instead of trusting the session-7 handoff (R14) and the picture sharpened: the isolated region builds [ OK ] WITHOUT the body (so §8b isolation was never implicated), and conflicting types for D_801812A4 was the only hard error in the whole build — all 27 carried function externs were fine raw, so cast_call_sites was doing nothing. Root cause: family_remap.gather_externs prepends carried decls at FILE scope; D_801812A4 is a fn-ptr dispatch table the sibling declares four incompatible ways at BLOCK scope inside its own later functions, so the carried file-scope decl establishes a global the TU never had and every later block-scope extern must now agree with it. Byte-proven asymmetry: BLOCK(int) → BLOCK(struct*) → FILE(void*) builds; FILE(void*) → BLOCK(int) errors. Rejected the session-7 plan (teach reconcile_decls a TU-visible oracle — a big, risky change to a proven path) for a smaller one: don't disturb the TU's decl environment at all. New tools/scope_data_externs.py demotes a carried D_ extern to block scope inside the function body when the TU has no file-scope decl of it above the insertion point — byte-neutral (an extern emits no code), never worse than raw, so it needs no oracle, no type comparator, and no fn-ptr parser (reconcile_decls' regex cannot parse extern void (*D_x[])(void *); and was silently skipping exactly the failing symbols — the third silent-skip bug of the phase). Wired as the scoped stage (raw → scoped → recovered → reconciled). First sibling byte-identical first try; full sweep 133/133 BANKED, 0 failures; R22 clean-fleet 136/136 (534 changed src files); dedup 1813/0; instr-weighted 63.0→63.6%, distinct-code 39.1→40.5% (+256 unique fns / +79,957 ins) — one core, ~0 agent tokens. The R17 triage rule (committed commit:0550 at Drew's ask) paid off on its first test: conflicting types = the compiler refused to compile = a C front-end diagnostic = our Python; a gcc-source read would have taught nothing. Knowledge captured DURING the session (R30/R31): cookbook §8d, decision-log, SETUP tool row. NEXT: func_80178D40 (890×134, close=39, all in case 0x5C — cheap-Opus/permuter, no Fable5), then the R27 prompt for Fable5 on func_8017BEBC (952×113, close=2 — the allocno-tie R17 target).
  • 2026-07-13 (session 6, the §8b scoping wall BROKEN — heavy-jr harvest unblocked; Max): Built the declaration-environment reconstruction on the proven overlay_src_split.py parser. R14 correction: session-5's "gcc-2.7.2 block-scope-extern TU-persistence" root cause was wrong — there is no gcc quirk; DEFINE_func_* macros expand at FILE scope, so their leading externs are genuine file-scope decls that simply live in engine_core.h and are invisible to a col-0 .c scan (census: 1,377 macros / 3,929 extern lines / 1,462 symbols). Rejected the approved "global symbol→type map + shadow set" design (X1/P5d — same goal, better mechanism): the engine is loosely typed (func_80173544 is defined s32 f(void*) yet declared extern void f(void); inside func_801734BC's body), so "declare every used symbol" hoists that block-scope shadow to file scope and CREATES the conflict a shadow-set heuristic then has to dodge. Instead: reconstruct the original TU's file-scope decl environment and carry it strictly forward — conflict-free by construction (every carried decl already coexisted with every definition in the one original TU; decl compatibility is order-symmetric; shadows stay in bodies). The byte-gate then found two more lost decl sources I had not predicted: a definition is itself a declaration for everything below it in its TU (func_8012B2CC undeclared) and file-local typedefs used by a carried prototype (parse error before '*', Vec3s). Full 54-jr isolate-all on ov_SC01_077 → d19c9580 BYTE-IDENTICAL; R22 clean-fleet 136/136 (the exact configuration session 5 could not build). Parser selftest still 404/404. Then wired LAZY per-core isolation into jtbl_family_bank (Drew's call — upfront-×134 would add ~7,200 region files): jtbl_carve NON-CONTIGUOUS fail-loud → jr_isolate_all --only <core> → re-extract → re-carve; proven on func_80178D40 (890×134, the heaviest core) — carve blocked → isolated (byte-neutral) → carve lands in its own subseg. Two latent bugs found + fixed (both would have corrupted the heavy sweeps): func_subseg read the owning subseg from the asm tree, which make extract never prunes, so after an isolation it returned the STALE owner and silently re-created the collision (→ derive from the config); and the sweep's revert() deleted the shared overlays.mk carve var unconditionally, which would have destroyed a committed carve (all 134 overlays have one) on any failed sibling (→ restore to its committed value). Added a dirty-tree preflight to jtbl_family_bank (an uncommitted prior family would be silently reverted). Knowledge captured DURING the session (R30/R31): cookbook §8b RESOLVED + new §8c ("splitting a TU means rebuilding its declaration environment, not moving text") + decision-log. Committed. NEXT: Task 7 heavy-jr crack waves — R27 boundary, prompt Drew for effort/Fable5 before launching.
  • 2026-07-13 (session 5, Stage-2 isolation build — parser DONE, single-cut PROVEN, full isolate-all hit the gcc-2.7.2 scoping wall; Max): Drew re-approved Stage 2 + set /effort max; steer = scalable isolate-ALL-jr-per-sibling upfront resegment, then Fable5 on the heavy cores. Built + fleet-validated tools/overlay_src_split.py (overlay-.c-aware partition; 404/404 overlay .c, 341,902 items, round-trip exact / 0 unresolved / 0 non-monotonic — fixed 5 parser edge cases: definition-vs-declaration by brace/; scan, K&R defs, comment/#-directive peeling, def_name from the signature not preamble, SETTER/RETCONST macro anchors, trailing-footer preservation). Built tools/jr_isolate_all.py (multi-cut resegment: config split at jr boundaries, source repartition + INCLUDE_ASM path repoint, banked-jr carve repoint, -O0 skip, ambient file-scope decl carry). SINGLE-cut isolation BYTE-IDENTICAL (func_8013FFD8 in main, clean make build d19c9580, R22) — Task 2 proven. Full 54-jr isolation FAILED on the dense _after object after fixing 7 C-scoping edge cases in sequence, on the deep one (byte-verified): gcc-2.7.2 block-scope-extern TU-persistence — func_801734BC uses D_80126B3E declared extern s16 only inside engine_core.h DEFINE_func macros; splitting the TU separates the core from the earlier macro expansion that declares it → undeclared. Root: mechanical TU-splitting rebuilds the decl ENVIRONMENT (semantics), not just text — col-0 ambient-carry can't reach macro-injected decls. Candidate fix (not built): declaration-completion from a global engine_core.h+overlay symbol→type map (decision-log 2026-07-13, R31). Baseline reverted + intact; 2 tools untracked, NOT committed — surfaced the fork to Drew (P5a) before sinking more time. NEXT: Drew's steer (build declaration-completion vs pivot Stage-2), then commit the tools.
  • 2026-07-12 (session 4, multi-jtbl --order + family-1 — the Stage-1 de-risk build; Max): Recon proved the Stage-1 core risk is precise: jtbl_family_bank.revert() restores each sibling to committed = has func_8012ACE0's carve, so banking ANY 2nd jr family makes the overlay multi-jtbl. Built ld_interleave.py --order (address-ordered N-piece sandwich; legacy --front/--tail byte-untouched → main + 133 single-carve siblings unaffected) + rewrote jtbl_carve.py additive/regenerate-from-config (parses tail data-region + existing carves, splits the containing data piece for the new jtbl, re-emits --order; same-subseg → fails loud). Fixed jtbl_family_bank.bank() to make extract BEFORE the carve (asm must match the reverted config; the old error-string retry was fragile — 3 early siblings carve-failed). Family-1 func_801734BC (34-ins PURE jr, ov_SC01_077_after) matched in ov077 (shared-tail switch idiom case N: t=-N; break; default: goto after;) + banked 133/133 siblings — CROSS-subseg multi-jtbl (func_8012ACE0 _a + func_801734BC _after), the primary Stage-1 goal. R22 clean-fleet 136/136 byte-identical (~52s). Wrote tools/jr_isolate.py (same-subseg non-contiguous case — split a fn into its own subseg via split_src_region, jtbl_carve re-derives the carve subseg; ascending-order discipline avoids carve-renames) — not yet integrated/committed. §8a finding: the still-INCLUDE_ASM carve build fails (migrated rodata not emitted through INCLUDE_ASM) — OFF the banking critical path (bank swaps C before building). Committed (Drew pushes — R6). NEXT: prove isolation on a higher fn → integrate → pivot to Stage 2.
  • 2026-07-12 (session 3, R22 parallelization — Drew asked "why so slow, profile+optimize"): Profiled the clean-fleet R22 (make clean + extract-all + check-all). Phase split: extract-all ~6m11s (the BIGGER phase, 136 serial splat split runs) + check-all ~2m58s (136 serial builds; most overlays ~1.3s, ov077 slow — maspsx on its 15K-line .c) = ~9 min. Both phases were serial on a 32-core box (the old Makefile comment called cross-binary -j "racy"). Verified the only shared write is the 4 generated include/*.inc macros at extract time (identical content); everything else is per-binary-disjoint and include/ is read-only during a build → parallel is safe. Added JOBS ?= 16 + make extract-all (seed main serial for the macros, then parallel-extract the rest) + parallel make check-all (xargs -P$(JOBS)), correctness-gated. Result: make clean && make extract-all && make check-all = ~50s, 136/136 byte-identical — a ~10.5× speedup (9m→~50s). Committed the Makefile change (own commit). This compounds across the endgame (R22 runs every commit).
  • 2026-07-12 (session 3, §8 ×134 AUTOMATION — func_8012ACE0 banked fleet-wide): Built the jr-function ×134 harvest pipeline and proved it end-to-end. tools/jtbl_carve.py = per-overlay §8 carve generator (finding: the same fn is at the same vram across siblings but its jtbl is at a DIFFERENT address in each — ov_SC01_000 jtbl_801A331C / ov_SC02_000 jtbl_801E587C — so the carve is recomputed per sibling; rewrites the config data-tail subsegs + sets <ov>_JTBL_INTERLEAVE). tools/jtbl_family_bank.py = the sibling sweep driver (per sibling: revert→carve→extract→remap_hseq+reconcile→whole-binary gate; idempotent, revert-on-fail). Propagation-cap fix: family_remap.extract_unit now carries single-line typedefs (jr-function bodies define local typedef struct{} Foo_<addr>; that must template with the body, else the sibling sees Foo undeclared — additive, byte-gate-protected). func_8012ACE0 family: 133/133 siblings BANKED, 0 failures; R22 clean-fleet 136/136 byte-identical. Metrics: distinct-code 39.1% (50,698 unique fns), instr 63.0%. Opportunity map (has_mid_jr families): 237 total (5,805 members / 5.68M ins) — 46 small mid/tiny (771 members, mechanical, same pipeline) + 191 substantial = the Fable5 cores (Task 7, paused). Committed. NEXT: (a) R22 profiling/parallelization (Drew asked — check-all builds 136 binaries serially on a 32-core box); (b) the other 45 small jr families.
  • 2026-07-12 (session 3, §8 jtbl-rodata tooling — PoC PROVEN): Built the overlay jtbl-rodata mechanism (cookbook §8a, the durable writeup). Findings: overlay jtbls form a contiguous .rodata island at the TAIL (between .data globals and a tiny remnant), not the EXE's central island. Mechanism (byte-identical, 2-step): (Step 1) carve a matched jr-function's jtbl out of [data,tail] into a dotted [.rodata, <code-subseg>] subseg (spimdisasm migrates it into the fn's .s) + [data, tail2]; parameterized tools/ld_interleave.py --section .<binary> (derives the symbol prefix; default .main = EXE byte-identical, backward-compat proven) places text→data(pre)→rodata→data(post)→bss — build byte-identical with the fn still INCLUDE_ASM. (Step 2) swap in the C body → byte-identical — the C-emitted jtbl lands exact. Wired into make extract via <bin>_JTBL_INTERLEAVE in config/overlays.mk + a $(strip)-guarded ifneq branch (gotcha caught: a trailing #comment on the := left whitespace → the branch misfired on resident). PoC target: func_8012ACE0 (25-ins single-jtbl jr-function, ov_SC01_077) — reconciled via canon_sig_reconcile (raw draft had conflicting types), banked byte-identical d19c9580, ov077 clean-rebuilds. Key P9 finding: func_80159C84/func_8015444C (the 2 carried Fable5 bodies) are rtu_match FALSE-MATCHes — their C jtbls are incomplete (52B vs 56B), so they never bank; rtu_match excludes the §8 rodata (confirms the checkpoint's "2 jtbl false-MATCH"). The maspsx "hang" scare was a truncated-experimental-file artifact — the real pipeline builds in ~1s. NEXT: full R22 (in flight) → commit the PoC + tooling; then the ×134 automation (generate the per-overlay carve + template the reconciled body per sibling).
  • 2026-07-12 (session 3, TINY-BAND mechanical harvest — Task 8 continuation): Regenerated the h_seq manifest from the mid-band-banked src (fleet 77.2% fn / 60.9% instr / 35.2% distinct — matches commit:0530), then ran family_sweep.py --hseq --band tiny. Dry-run sized it: 180 tiny matched-exemplar families / 23,592 staged member-drafts across 267 (overlay,split) groups (skips 1,184 no-matched-unit / 148 unresolved-imm / 117 STRUCT / 39 pinned). Gated run: 17,975 member-matches BANKED / 5,617 gate-rejected (h_seq-collision false-templates — the whole-binary byte-gate G3/P9 refused every one; ~76% bank rate; per-group ~17/21 main + ~110–129/150 _after). 266 overlay .c files touched. R22 clean-fleet (make clean + extract-all-136 + check-all) → 136/136, 0 failed. dedup-check 1813 validated / 0 failed (registry untouched — hseq banks are per-overlay src, not ×134 registry). Metrics: distinct-code 35.2→39.1% (50,571/84,996 unique fns), instr-weighted 60.9→62.9%. The mechanical size-bands (substantial/mid/tiny) are now harvested; the remaining levers are the two harvest gaps (§8 jtbl-rodata, reconcile fn-ptr-extern). Committed (Drew pushes — R6).
  • 2026-07-12 (session 2b, Fable5 batch-1 whole-binary reality): P9 correction: the 2 Fable5 "cracks" (func_80159C84/func_8015444C) rtu_match-MATCH but FAIL the whole-binary gate — both are jr-functions and rtu_match doesn't verify the §8 jtbl rodata (decision-log). Two harvest gaps found: §8 jtbl-rodata (blocks all jr cracks incl. the Fable5 cores) + reconcile data-extern (D_801891B8-class, blocks ~15/21 no-jtbl triage cracks). Real whole-binary result: 6 no-jtbl reconcile-clean cracks bank = 729 members (463 committed + 266 this commit, func_8015CD20/8015C128 ×133). func_80176218 (no-jtbl) still cracking = the clean non-jtbl Fable5 test. R22 deferred until it releases asm/.
  • 2026-07-12 (session 2, resume after usage-limit interruption): R22 confirmed the committed 463 reconcile-class banks 136/136. Fable5 batch-1 results: func_80159C84 MATCHED (337-ins 2-jtbl giant, pin-free; new cookbook idiom = gcc-2.7.2 postincrement pending-queue (*(s32*)(p+0x238))++ >= K); func_8015444C (reached 29-mismatch) + func_80176218 (early) were cut off by the weekly limit → re-launched (444C resumes from its 29-mismatch partial). The interrupted reconcile-raw-19 harvest (260/1565, partial src) was reverted (R22 discipline) and re-running clean folding in func_80159C84. All background; process on completion → R22 → commit. Fable5 batch-1 = the last batch until Drew re-approves (Drew, 2026-07-12).
  • 2026-07-11 (session 1, Task-8 validation slice — Drew-approved pre-window de-risk): Ran reconcile→gate→template on the triage cracks. Reconcile→bank WORKS: raw 0/23 (§41 wall) → via canon_sig_reconcile v3.2 4/15 banked into ov077 (func_801506A4/8016A73C/80167540/80155800, byte-identical). Templating a reconciled body ×133 FAILS 0/4 — reconciled bodies are ov077-TU-specific (casts + collision-renames), need per-sibling re-reconcile (§41c). → Task-8 prerequisite: port the h_norm --reconcile M2 path into hseq_sweep. Kept the 4 ov077 banks. decision-log 2026-07-11 (R31). Paused before building the wiring per Drew.
  • 2026-07-11 (session 1, Task 6 — DONE + paused): Ultracode triage Workflow (119 agents, 0 err, 5.3M tok): m2c draft (+§8 jtbl) → match_one closeness → class + lever + seed. cheap 29 (23 closeness-0) / permuter 29 / fable5 61. Curriculum → docs/phase26-triage.md. Attempted to bank the 23 cracked "wins" into ov077 (Drew: "bank the wins but pause before task8") → 0/23 gate — the isolation-MATCHes carry standalone types/Ghidra-sigs that hit the §41 def-side wall in the real TU (redefinition of struct Obj / conflicting types); banking needs the Task-8 --reconcile pass. Seeds preserved in .run/phase26-seeds/ + .run/ov077_bank/. PAUSED before Task 8 per Drew. src pristine, ov077 green.
  • 2026-07-11 (session 1, Task 5 — GO/NO-GO = GO): Ran the real byte-gate on the substantial matched-exemplar band. Discovered + fixed the decl class (remap_hseq.gather_externs carries file-scope externs). 532 members banked, R22 136/136 green, distinct 30.3→30.9% / instr 58.2→58.5%. Per-family: 3 tracker-miss PURE families bank 100% ×133 (399 byte-perfect free win); 9 type-using families → §41 reconcile (Task 8); 16 pinned → Task 7. Machinery byte-proven 100% correct on clean families → GO to scale. decision-log 2026-07-11 (R31). Committed.
  • 2026-07-11 (session 1, Task 4): Added family_sweep --hseq (additive hseq_sweep(), h_norm path byte-untouched): manifest-driven templating via remap_hseq, unique per-group verified-out, static __asm__("$N" pin guard (skips the ×1-only pinned cracks → Task 7). Dry-run (--stage-only): substantial band stages 1507 clean / 267 groups, 1643 correctly skipped as pinned (16/29 families pinned, incl. the decision-log-named ones). Drafts byte-correct by construction; whole-TU gate = Task 5. Committed.
  • 2026-07-11 (session 1, Task 3): Built the T2a imm engine (imm_value/imm_map_tier1/remap_hseq) — diff-driven Tier-1 literal swap with an asm-side ambiguity guard — + wired T2b cross-address self-rename. Tier-2 probe deferred (build-if-needed; byte-gate arbitrates). V3 (.run/v3_imm.py): 0 DIFF on all compilable pairs (5 MATCH), ambiguity guard + cross-address self-rename confirmed; comprehensive whole-TU gate validation is Task 5. Committed.
  • 2026-07-11 (session 1, Task 2): Built tools/family_hseq.py (full-frontier h_seq survey) + the shared word-diff classifier in family_remap.py (PURE/IMM/STRUCT, register-drift aware). Reproduces the fleet metrics + the 663/186/1.85M tail cross-check exactly; classification matches the Plan-agent table; 581 substantial target families / 3.22M templatable ins, 345 matched-sibling families / 1.14M ins = the zero-crack corpus. docs/family-hseq.md committed. Committed.
  • 2026-07-11 (session 1, Task 1): Extended reloc_targets for the add/addu indexed-global idiom (the "reach-1 tail" was largely this tracker blind spot, not unique code — decision-log + cookbook §40b). symbol_map/remap gained backward-compatible to_addr (cross-address T2b) + imm_map hook; sequential→single-pass substitution (fixes the latent chained-rename bug). norm_stream/h_norm untouched. V0 (22/22 regression, 15/15 fix vs splat .s) + V1 (160 pairs, 0 regressions) both green. Committed.
  • 2026-07-11 (session 1, planning): Phase Start (Tier 1, Fable5+Max+plan-mode). Megaplan analyzed; survey reproduced from sigs+src (R14) — fleet metrics match PhaseEnd_25 exactly; 186-not-986 substantial-family correction; #2 family is cross-address; 93 matched-sibling families found (0.51M ins). Tooling recon (Explore) + design pressure-test (Plan agent) → the reloc-tracker blind-spot discovery (addu-preserves-hi; 890×121 family is PURE reloc; 63-fam zero-crack corpus; sequential-substitution latent bug). Drew's gate-1 decisions: carried queue → end of phase; structural milestone. Plan approved; task list built (R28). Task 0 complete with this commit.