Commit Graph

475 Commits

Author SHA1 Message Date
Drew T ab3ac83a41 feat(phase-29): func_8012AAAC family swept 137/137 — the --like guard unblocked the whole class
- 134/134 BANKED on the remainder (after 3/3 on the probe) => the family is 137/137, ZERO failures.
  func_8012AAAC is now stubbed in NO overlay. R22 clean-fleet: extract-all 139/139, check-all
  140 passed / 0 failed.
- FLEET 81.9 -> 82.0% instr · 89.60 -> 89.64% fn-count · distinct-code 69.3 -> 69.5%.
- THE METRIC POINT, reproduced twice in one session and in BOTH directions: this jtbl family is
  byte-VARIANT (each overlay's table holds its own addresses), so every member is a genuinely new
  unique function and distinct-code MOVED. The h_seq PURE families swept earlier added 274 members
  and moved distinct-code by +0.0, because those members were already counted via their shared
  exemplar. SESSION-20's routing rule, now byte-demonstrated: target byte-VARIANT families to move
  RE-completeness; high-reach h_exact families move only the display number.
- cookbook §91 — "a structure-TRANSFER is only valid where the structure corresponds": the --like
  role trap, plus the three-hypothesis trail (two wrong, and instructive: the sibling call-site casts
  were a real defect that fixed nothing, and my own carve-alone test was a false lead that departed
  from the tool's real sequence). The law: any "same family => same structure" transfer must state
  which structural fact it assumes and CHECK it on both sides — and a tool that drops an error class
  it cannot act on should still SURFACE it, because a bare `gate-fail` repeated 137 times cost far
  more than printing one line would have.
2026-07-27 11:25:42 -06:00
Drew T 2cc49d0310 feat(phase-29): SESSION-21 — func_8012AAAC banked via the §81 carve chain (R22 140/140)
The first jtbl-routed bank of the session, and it validates the whole chain end-to-end:
 1. jtbl_carve SPLIT-TABLE repair (this session): jtbl_801D7FB0 28 -> 50 words (112 -> 200 B),
    authorized by func_8012AAAC's own `sltiu 0x32`.
 2. NEW FIX — SINGLE-TABLE PREDECESSOR: adding a second table to a subseg whose existing carve was
    single-table lost the FIRST table's start entirely (new_offs has only the new one;
    overlay_jtbl_addrs cannot see the old one because its owner is banked and extract PRUNED the
    stub .s; and single-table carves persist no tables= to rebase). The span then failed its own
    validator with "first must equal the span start" — the invariant naming the missing entry.
    A single-table carve spans exactly its one table, so ITS SPAN START *IS* THAT TABLE'S START:
    inference, not persistence, so it also works for spans carved before tables= existed. This is
    the RECOVERABLE half of the documented func_8013F350 lesson (that one was a pre-§8e merged
    DOUBLE — two tables, no record, genuinely unrecoverable).
    Result: ov_SC01_077_a JTBL_PADS := 0,0 tables=+0x0,+0x14. Carve alone byte-gated BYTE-IDENTICAL
    BEFORE the bank was attempted (§81 step 2).
 3. ARITY axis, all-or-nothing: 1,244 decl sites / 1,240 files `(void)` -> `()` + an R32 completion
    assertion (old-form remaining: 0).
 4. ONE call-site cast: the definition lands at line 811 and a 0-arg call sits at 822, so gcc sees
    the prototype and rejects it — `((void (*)(void))func_8012AAAC)()` (§17a-1; gcc folds the cast
    of a known symbol to a direct jal). Only 1 of the 1,386 fleet-wide 0-arg call sites needed it:
    the others see only the `extern ()` decl, which permits a 0-arg call.

DIAGNOSIS NOTE: the failure read CC1-FAIL with only a warning visible under make. Running the
pipeline stage-by-stage (cpp | cc1 | maspsx | jtbl_rodata_pads | as) put it on cc1 rc=33, and cc1's
own stderr named it exactly: "too few arguments to function func_8012AAAC" at line 994. Isolating
the stage was what turned an opaque Error 33 into a one-line fix.

R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
family_sweep correctly REFUSED this exemplar (§53: a jr-family must route through
jtbl_family_bank.py; "a 0% from this path would be a TOOL artifact, not a wall") — the ×137 member
sweep is the next step and needs a clean tree, which this commit provides.
2026-07-27 10:38:10 -06:00
Drew T 5be4c21480 feat(phase-29): SESSION-21 — the ×138 member sweep: 274 members from 3 exemplar cracks (R22 140/140)
- family_sweep --hseq over the 3 newly-banked exemplars: BANKED 274 member-matches / 137 failed
  across 137 overlays, for ~0 agent tokens. Session total: 3 exemplars + 274 members = 277 fns.
- THE STALE-MAP STEP, hit and handled: the first sweep returned "0 matched-exemplar families"
  because .run/family_hseq.json still listed the fresh cracks as draft-ov077. Regenerated
  (matched-sib families 60 -> 63) and the sweep found them — the documented bank-x1 -> regen ->
  sweep path (memory crack-wave-sweep-map-regen).
- §86 REPRODUCED CLEANLY: 2 of 3 families templated ~137/137; the third failed ~137/137. Not a
  rate — a BIMODALITY. One probe per family, then sweep or skip; never a blended pool average.
- R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed (second clean-tree
  verification this session). dedup 1886/0, C1 coverage 239,604/239,604, 0 NON_MATCHING (G4).
- FLEET 81.7 -> 81.9% instr · 89.52 -> 89.60% fn-count · distinct-code 69.3% UNCHANGED — correct
  and expected: these are h_seq PURE propagation-class families, and SESSION-20's routing rule says
  propagation moves only the DISPLAY metric (members were already counted once via their exemplar).
  To move RE-completeness, target byte-VARIANT families. Stated plainly so the next session picks
  targets by the metric it means to move.
- drive-by: family_sweep --help crashed (argparse %-expands help text; a literal "0%" needed "0%%").
2026-07-27 10:08:19 -06:00
Drew T 2ce6c5fade feat(phase-29): SESSION-21 — 3 family exemplars banked + the §85 return-axis widen (R22 140/140)
BANKED (whole-binary byte-gate, the sole arbiter): func_8014D2A0 (80 ins ×138) · func_80158638
(87 ×138) · func_8016B6BC (94 ×138). Stubs in ov_SC01_077: 150 -> 147, 0 new stubs.
R22 CLEAN-FLEET: extract-all 139/139, check-all 140 passed / 0 failed. dedup 1886/0,
0 NON_MATCHING (G4). Fleet 81.7% instr / 69.3% distinct-code / 89.52% fn-count.

- WAVE STOPPED at Drew's request with 15/24 agents returned, ALL 15 status=match. Only the
  completed drafts were gated; in-flight ones are still being written (§90d).
- PRE-GATE, both oracles, all 15: match_one MATCH + reloc_verify ALL RESOLVED. Routed 7 plain /
  8 to the §81 jtbl carve chain.
- THE BLOCKER, MEASURED: 7 of 7 plain drafts failed PLUMBING, 0 DIFF, 0 compiler walls — the same
  shape as SESSION-20's T0.2. §58b applies: the draft sig is byte-TRUTH (it MATCHed), the header
  decl is the stale stub-era guess, so conform the DECLS.
- §85 RETURN-AXIS WIDEN, all-or-nothing: 3,471 decl sites / 1,736 files, precondition verified
  (ZERO callers consume the return => byte-neutral by construction) + an R32 completion assertion
  (old-form decls remaining: 0). func_8014D820's s32 return is load-bearing — forcing `void` costs
  2 instructions (302 vs 304), so the decls had to move, not the draft.

TWO HONESTY ITEMS:
 1. I REPORTED "0 of 7 banked"; the true number was already 2. My diagnostic pass printed only
    lines starting with "- func_" (the failures) and hid its own successes while I read it for
    error text. A script that prints only failures cannot tell you it succeeded — the R32
    silent-skip shape aimed at my own instrumentation. Ground truth is the stub count (§55b(3)).
 2. A REAL FINDING fell out of that mistake: same drafts, same tree, minutes apart — gate_stage's
    full ladder banked 0/7 while bare harvest_verify banked 2/7. The LADDER REGRESSED two drafts
    the bare gate accepts (§19's "sig_unify regresses already-canonical drafts", one level up, and
    the exact mirror of SESSION-20's missing-ladder false 33%). Neither "always ladder" nor "never
    ladder" is right — run both, let the byte-gate arbitrate. One build per draft.

OPEN: func_8014D820 still a stub — after the widen its error moved from `conflicting types` to an
assembler-stage failure, not finished diagnosing. Recorded as open, NOT as a wall.
2026-07-27 09:58:17 -06:00
Drew T 0be781efdf docs(phase-29): cookbook §90 — five tool-integrity laws from SESSION-21 (R16/R30)
Each law is one this session paid for, and each fix CHANGED the answer its tool gave:
 §90a a comparison tool must share its reference oracle's index space exactly (objdump -dr vs -drz
      elided 6 nops -> 104 vs 110 ins -> phantom mismatches; and the .s word field is little-endian
      hex TEXT, not the instruction integer). Five false alarms preceded the first true one.
 §90b 'byte-neutral' is not 'wanted' — undo on the SUCCESS path (a 0-bank run left 123 files of
      dead diff because the backstop only fired on a NON-neutral edit). §61 applied forward.
 §90c a library-callable function must FAIL CLOSED on unconfigured module state — an empty result
      is indistinguishable from a true negative (R32's silent skip in a different hat).
 §90d do not measure a live wave's drafts — §87 staleness in real time (same function, two verdicts
      two minutes apart; its agent rewrote the file 12s earlier).
 §90e an agent's CONCLUSION and its EVIDENCE fail independently: re-derive the premise, design the
      fix from what you can prove. The agent's jtbl bug was real and confirmed 3 ways; its 'zero
      xrefs' premise was false and its proposed remedy would have deleted a referenced symbol.
      The fix instead gates on the function's own sltiu bound — the program declaring its own table
      length. Negative control (228 combinations, exactly 1 change) is half the deliverable.
2026-07-27 09:29:12 -06:00
Drew T 2b38c68333 feat(phase-29): SESSION-21 T1-T3 — the frontier measured, the family-exemplar wave, 3 tool fixes
- T1 FRONTIER MEASURED (zero-token, R35: family map regenerated on fresh sigs first — it was
  stale by ~657 banked members): 36,020 stubs / 2,345,599 weighted ins remain, and only
  9.0% are h_exact-FREE. PROPAGATION IS TAPPED (238 distinct classes / 3,245 instances);
  22,498 distinct classes / 1,680,097 distinct ins is what is actually left. The mass is FLAT
  across all 139 binaries (~300-550 sub-500 stubs each) -> "pick the best overlay" is not a
  strategy. .run/s21_frontier.py + .run/s21_frontier.json
- T2 THE AXIS IS THE FAMILY, NOT THE LOCATION: 1,342 substantial h_seq families /
  1,298,135 templatable ins = 55% of ALL remaining weighted instructions. Routed by blocker:
  jr/§81 181 fams (33.6%) · DRAFT-with-cached-Ghidra-C 91 (28.6%) · DRAFT-modal 1,023 (27.5%)
  · zero-crack 45 (6.5%) · permanent walls 2 (3.9%). Live+cached+non-wall in ov_SC01_077 = 54
  families / 589,502 ins, value steeply concentrated (top 24 = 96%).
  .run/s21_targets.py + .run/s21_targets.json + .run/s21_draft_pool.json
- T3 WAVE 1 LAUNCHED: tools/workflows/family_core_wave.js (NEW) — 24 xHigh drafters, one per
  family exemplar, stake 575,488 templatable ins (24% of remaining). Supersedes worker_wave.js
  for family work: carries each target's family STAKE, encodes the four §58/§87 integration
  rules at source (splat D_<UPPERHEX> not Ghidra DAT_; never invent a symbol; canonical callee
  sigs; leave decl plumbing to the ladder), and requires symcheck.py on any claimed MATCH.
- T3b LADDER HYGIENE, both SESSION-20 carry items fixed — one defect, two masks: a byte-NEUTRAL
  transform was left in the tree when it banked nothing. family_sweep's --normalize-self-decls
  backstop only fired on MISMATCH (left 123 files of dead diff on a 0/123 run); gate_stage's
  ARITY undo narrowed to src/shared/ and left ~40 TUs. Both now restore the full snapshot when
  NOTHING banked (no banks to preserve => the splice hazard cannot apply). §61 on the success path.
- T3c BACKLOG addr DEFECT fixed (R32/R33): new addr_of() derives the address from `name`,
  assert_addr_coverage() fails loud on an unkeyable row, append_record fills both directions.
  Found a latent bug doing it: load_best() keyed on `addr or name`, splitting one function into
  two "best" records. Keyable rows 128/1,701 (7.5%) -> 1,701/1,701 (100%).
2026-07-26 23:37:35 -06:00
Drew T 5c422e8426 feat(phase-29): tools/sweep_parallel.py + §89 — the parallel gate farm, reachable from the family path (step 2)
bulk_harvest's Phase B has been a ProcessPoolExecutor over DISTINCT binaries (per-binary flock,
per-worker result files, compute_fleet=False) since Phase 23 — but welded to Phase A's LLM drafting.
Family sweeps stage drafts differently (family_sweep --stage-only), so the farm was UNREACHABLE from
that path, and SESSION-20 gated 389 + 268 + 104 members SERIALLY for no architectural reason (~8-16x
throughput loss on a 32-thread box). This is a thin adapter: same gate_stage.run_gate, same
per-binary lock, NO new gate logic.

Also fixes the phantom-dir bug at source: a bare .run/sweep/*/ glob matches gate_stage's own
intermediate ladder dirs (-cn/-cast/-rc/-s2in/-uni) and calls them as binaries — 24 phantom
PARTIAL 0/1 lines that inflated one run's notbanked from 0 to 56. Requires config/splat.<bin>.yaml
to exist (R33/R36: derive the binary set, never glob it). Smoke-tested: the phantom is skipped and
named, real binaries kept.

§89 records both throughput rules the project already had and was not following.
2026-07-26 23:10:06 -06:00
Drew T 6ce2e8963f chore(phase-29): preserve the behemoth close-out artifacts (R20)
39 files from the three behemoth agents: the matched drafts (s21_func_80183814_b2.c,
s21_func_8017D2DC_b1.c, s21_func_8017DC1C_b1.c), their reports with do-not-re-buy tables AND BASES
(§80), and the reusable harnesses — including s21_g21_reloc_verify.py, which resolves every
relocation (incl. the implicit MIPS-REL addend objdump -r does not print) against the target and is
the missing rung between match_one and the binary (§88f). ~735k agent tokens of work; .run/giants is
the curated allowlist.
2026-07-26 23:08:33 -06:00
Drew T 890dd08e6f docs(phase-29): §88 — the behemoth close-out laws (cross_jump/call, slti literal-position + its equality false-positive, banking order, the reloc gate) 2026-07-26 22:52:22 -06:00
Drew T d2a79deff2 feat(phase-29): ALL THREE BEHEMOTHS BANKED — func_80183814 (5,122), func_8017DC1C (1,518), func_8017D2DC (1,586)
Zero functions >1000 ins remain unmatched anywhere in the fleet.

func_80183814 (5,122 ins — the LARGEST function in the game) — round 2 closed it: length 5127->5122
exact, structural residual 36->0, register-sensitive 1201->0, frame -256 -> -0xF8 exact, saves
10 -> .mask 0x807f0000 exact. Verified independently (R14): match_one MATCH (5122 ins).
  ROUND 1's DIAGNOSIS WAS WRONG and the agent refuted it properly: the +5 length was a SYMPTOM, not
  the lever, and the §83d max_reg/cse.c:8340 story does not hold — a 15-line reproducer reproduced the
  case-0/3 CSE exactly (so it cannot be max_reg-gated), max_qty only gates extension ACROSS blocks,
  and the target leaves $s7/$fp unused (no pressure story). Confirmed from a second direction: C01 has
  the identical two groups over the identical symbols with ZERO residual, because a `break` puts a
  CODE_LABEL between them. The two biggest levers were pure DECLARATION SCOPE (§45/§76), not pins.
func_8017DC1C (1,518) — MATCH first round, pin-free, zero __asm__ dials. NOT a jr fn (0 mid-fn jr).
func_8017D2DC (1,586) — MATCH first round (banked in the previous commit).

BANKING ORDER MATTERS — a new failure mode found and worked around: banking func_8017DC1C BEFORE the
carve chain broke the build. Its draft establishes the canon for 39 previously-undeclared externs;
jr_isolate_all's re-partition (overlay_src_split) then DROPPED ALL 39 across the new split boundary
(`D_801C1EB0 undeclared`), leaving them in NEITHER file. The §77 preamble-drop class, in a third tool.
FIX = ordering, not patching: run the §81 carve chain FIRST on a clean tree (gated BYTE-IDENTICAL),
then bank. Reverted, re-sequenced, both banked clean.

R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.6 -> 81.7% · distinct-code 69.1 -> 69.3% · fn-count 89.52%.
2026-07-26 22:16:48 -06:00
Drew T 09b1993059 feat(phase-29): T0.7 sweep (104 members) + BEHEMOTH func_8017D2DC banked (1,586 ins)
T0.7 — the §86 one-member probe applied to the remaining FREE families: 9 LIVE / 6 DEAD / 5 unstaged.
The three highest-value families by raw size (18,084 / 11,234 / 10,880 ins) all probed DEAD — the
probe skipped them instead of burning ~400 gate cycles rediscovering it. Swept the 9 live: 104 banked,
8 of 9 families fully cleared (func_8017BEF8 has 8 stragglers).

BEHEMOTH 2 of 3: func_8017D2DC (1,586 ins, ov_SC01_001) MATCHED and BANKED — closed in ONE agent
round, pin-free. Verified independently (R14): match_one MATCH (1586 ins).
  §81 carve chain: the agent predicted step 1 unnecessary; jtbl_carve REFUSED (the subseg already
  hosts a .rodata carve and the new table's start != span start). The refusal was RIGHT and is the
  instruction to run step 1 — jr_isolate_all --only (2 fns/1 object) -> BYTE-IDENTICAL, then
  jtbl_carve -> BYTE-IDENTICAL, then the ladder banked it.

R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.5 -> 81.6% · distinct-code 69.0 -> 69.1% · fn-count 89.49 -> 89.52%.
2026-07-26 22:02:37 -06:00
Drew T 1eb36504f6 docs(phase-29): §87 — match_one never LINKS, so the 315 'integration' entries are not bankable
T0.6 measured: the autopsy's integration bucket (315 match_one MATCHes, 'blocked only on plumbing')
banked 0/27 through the full gate_stage ladder. Two causes, neither plumbing:
 (1) UNDEFINED DATA SYMBOLS — the gate fails at LINK on symbols defined in NO overlay's symbol file.
     match_one compiles one TU and never links, so an extern resolving nowhere is structurally
     invisible to it. (Refuted the obvious alternative: the drafts WERE authored for the right binary.)
 (2) STALE DRAFTS — 'redefinition of struct S80172C50': the struct was since lifted into
     engine_types.h, so the draft's own copy collides. A stored draft is scored against TODAY's tree.

=> FOUR match_one blindness classes now catalogued: §81 jump tables, §84 masked %lo, §87 link, §87
staleness. A match_one MATCH is 'this TU compiles to the right bytes with relocations masked' —
nothing about linking, nothing about the current tree. A stored MATCH is a CLAIM WITH A TIMESTAMP.

Consequence: with §83's 44%-misfiled finding, docs/backlog.md's headline count is NOT a work queue.
Re-gate a sample before planning against any stored-draft pool. Cheap discriminator added (grep each
D_ symbol against the binary's symbol files; any UNRESOLVABLE will fail at link regardless of ladder).

MY RECOMMENDATION WAS WRONG: I ranked this pool first on 'highest certainty of any pool we have'.
The certainty was an artifact of a tool that cannot see link errors. 0 banked, 0 tokens, tree clean.
2026-07-26 20:48:54 -06:00
Drew T c2566010f2 feat(phase-29): --allow-pins sweep — 268 banked; pin templatability is PER-FAMILY (cookbook §86)
The §42e pin guard refuses any family whose exemplar carries `register __asm__` pins: 680 of the top
8 FREE families' 1,083 members (63%) were skipped BEFORE any gate ran. Re-run with --allow-pins,
letting the byte-gate arbitrate (G3/P9): 268 banked, and ZERO cc1 crashes across hundreds of pinned
compiles — confirming the SIGABRT the guard was written against was Phase 27's extract_unit
macro-drop, NOT a compiler limit. The guard is protecting against a bug that no longer exists.

THE LAW (§86): templatability is a PER-FAMILY property, not a per-member rate.
  func_801749C8  137/137 = 100%      func_80133AB0    4/136
  func_8014C6F4  137/137 = 100%      func_8014CF04    0/137
                                     func_80143D28    0/136
Two families at 100%, three at ~1%. MY REPORTED "37%" WAS AN ARTEFACT: a 19-member sample that
straddled families reported their AVERAGE and hid the bimodality. Sample PER-FAMILY, never per-pool.

=> PROCEDURE, now the default: probe ONE member per pinned family; bank -> sweep the family; fail ->
skip entirely. The blanket sweep spent ~412 futile gate cycles (60% of the run) on three families
that were never going to bank; the 1-member probe reduces that to 5 probes + 2 sweeps.

Left explicitly UNDIAGNOSED (do not guess): why two families template and three do not. Likely axis
is caller-saved pins spanning a `jal` (§74's corrupting form) vs pins fixing only a local allocno.
Diagnose BEFORE extending --allow-pins fleet-wide — the byte-gate makes a wrong guess free, but a
wrong PROCEDURE costs a sweep.

R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.3 -> 81.5% · distinct-code 69.0% · fn-count 89.41 -> 89.49%.
2026-07-26 20:23:28 -06:00
Drew T 52e6522f1b feat(phase-29): T0.5 — FREE-subset sweep, 3 families fully banked (389 members, +37,713 ins)
Re-derived the T0.1 decomposition post-harvest (it was stale by 395 banked members):
  zero-crack pool 76 fams / 347,892 ins  ->  73 fams / 290,850 ins   (the harvest came out of it)
  FREE (sweepable, non-jr, non-O0)       ->  58 fams / 167,368 ins

Swept the top FREE families through the gate_stage ladder (sample 8/8 first, then the rest):
389 banked; func_801463A0 / func_8017B490 / func_80156670 now stubbed in ZERO overlays.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.0 -> 81.3% (10,645,711 -> 10,683,424) · distinct-code 68.8 -> 69.0% ·
fn-count 89.30 -> 89.41%.

THE BLOCKER HAS MOVED — it is now OUR OWN PIN GUARD, not gcc and not declarations. Of the 1,083
candidate members in the top 8 FREE families, 680 (63%) were refused by the §42e pinned-exemplar
guard BEFORE any gate ran; only 403 reached staging. Two pieces of evidence say the guard may now be
over-conservative: SESSION-19 banked func_8017A4AC x134 WITH pins once the byte-gate arbitrated, and
Phase 27 dissolved the cc1 SIGABRT that motivated it (it was the extract_unit macro-drop, not a
compiler limit). Next probe: --allow-pins on a sample of 8, byte-gated.

MY OWN SCRIPT BUG, fixed + negative-controlled: the sweep loop globbed `.run/sweep/*/`, which also
matches gate_stage's INTERMEDIATE ladder dirs (-cn, -cn-cast, -cn-cast-rc, -s2in, -s2in-uni). Those
were called as if they were binaries -> 24 phantom "PARTIAL 0/1" lines inflating notbanked to 56 when
the true failure count was ZERO (stub counts 0/0/0 are the ground truth). Fixed by requiring
config/splat.<ov>.yaml to exist; negative control confirms phantoms are skipped and real binaries kept.
2026-07-26 18:32:09 -06:00
Drew T 015ebff536 feat(phase-29): §84 family fully harvested — 123/123 members, 0 failed (+29,280 ins, +27,840 distinct)
The derived-offset recompute swept the whole func_8013D53C family: 119 banked / 0 failed on top of
the 4 earlier; func_8013D53C is now stubbed in ZERO overlays. R22 clean-fleet 140/140 BYTE-IDENTICAL;
tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).

RECIPE (and it is NOT the return-axis recipe — sampling caught this):
  §84 derived-offset -> per-member literal recompute AND the gate_stage ladder.
  With the recompute alone the sample was 0/8; through the ladder it was 3/3, then 119/119.
  Had I reused the return-axis recipe (plain harvest_verify, which banked 272/272 there) I would
  have swept 123 members to zero banks and mis-concluded the fix was wrong. Probe-before-scale.

METRIC FINDING worth carrying: this harvest moved instr +29,280 AND distinct-code +27,840, while the
return-axis harvest moved instr +26,928 and distinct-code +0. §84-class members are byte-VARIANTS so
each is a new unique function; propagation-class members were already counted once via their shared
exemplar. => §84-class work moves the RE-COMPLETENESS number; propagation moves only the DISPLAY one.

Session fleet: 80.6 -> 81.0% instr · 68.2 -> 68.8% distinct-code · 89.18 -> 89.30% fn-count.
2026-07-26 16:48:56 -06:00
Drew T 9d9cd58028 feat(phase-29): T0.4 harvest — 272 return-axis members banked (+26,928 ins) + the §84 recompute in family_remap
THE §85 WIDEN PAID OFF AS PREDICTED. It is a ONE-TIME fleet edit, so once committed the
`conflicting types` blocker was gone for EVERY member of both families at once:
  - sample 8 first (probe-before-scale): 8/8 banked with PLAIN harvest_verify, no ladder needed
  - full sweep: 264 banked / 0 failed across 132 overlays; 10 skipped as not-stub
  - total 272 members ~= 27k ins, ZERO agent tokens
R22 clean-fleet 140/140 BYTE-IDENTICAL; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 80.6 -> 80.8% (10,589,503 -> 10,616,431, +26,928) · fn-count 89.19 -> 89.26%.
distinct-code UNCHANGED at 68.3% — propagation moves the DISPLAY metric, not the RE-completeness
one (the SESSION-19 split, reconfirmed).

§84 RECOMPUTE now implemented in tools/family_remap.py (fix_derived_offsets), wired into all three
apply_remap call sites as a PRE-pass on the exemplar body (the literal is ambiguous as a substitution
token, so it cannot be a table entry):
    correct_literal = mapped(aliased_sym) - mapped(base_sym)
Verified by negative control: the hand-solved case recomputes 0x20 -> 0x18 exactly, and a site whose
target endpoint is NOT a mapped symbol is left byte-for-byte alone AND REPORTED in info
["derived_offsets"] (R32 — a silent skip is a defect, and a silent skip is how this bug survived).
2026-07-26 15:36:06 -06:00
Drew T 772b5c4e02 feat(phase-29): the RETURN-axis fleet widen — 2 more families unlocked (cookbook §85); 140/140
Continues the "see why and try again" chain. Diagnosed all 4 T0.2 failures to 4 DISTINCT causes:
  func_8013D53C  240x123  §84 derived-offset remap bug        -> BANKED (previous commit)
  func_8012CC88  105x137  §73/§30#2 RETURN-axis conflict      -> BANKED here
  func_8014D12C   93x137  §73/§30#2 RETURN-axis conflict      -> BANKED here
  func_80144090  154x136  LENGTH-DRIFT (+13 B, ~3 ins long)   -> genuine codegen, real work

THE FAILURE THAT TAUGHT THE FIX: widening only src/shared/engine_core.h banked the member in the
TARGET overlay and BROKE ov_SC01_077 (R22 139/140) — the source overlay carries its OWN local
`extern void func_X(...)` decls, so a shared-header-only widen puts them in direct conflict. The
per-binary gate passed while breaking a binary it never built (§63/§61: a T2 write set is only
provable by R22). A half-done axis is a guaranteed break, not a smaller win.

THE FIX: do the WHOLE axis — 3,668 `extern void` decl sites across 2,688 files widened to `s32`,
0 remaining (R32 completion assertion). Precondition verified first: 0 callers consume the return
value, so the widen is byte-neutral by construction. R22 clean-fleet 140/140 BYTE-IDENTICAL;
tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).

MY OWN ERROR, recorded (§85 trap): I first spot-checked ov_SC01_077 with
`make build | grep | head; echo rc=$?` and read rc=0 as success — that is the exit status of `head`,
not make, and the output had no BYTE-IDENTICAL line. I reported a false BYTE-IDENTICAL in the
interim. Assert on the SUCCESS STRING, never on $? after a pipe.

Fleet: instr 80.6% (10,589,503) · distinct-code 68.3% (3,846,656) · fn-count 89.19%.
2026-07-26 15:22:21 -06:00
Drew T b6bb04ecff feat(phase-29): CRACK THE PLUMBING FAILURE — the derived-offset remap bug (cookbook §84); func_8013D53C banked
Drew: "if it fails, see why and try again with new knowledge." It failed twice, then banked.

ROOT CAUSE, byte-proven: a family_remap member reached match_one MATCH (240 ins) and failed the
whole-binary gate by ONE BYTE. The exemplar carries a deliberate matching idiom — reach a symbol via
a DIFFERENT symbol plus a literal offset, so gcc cannot CSE the two %hi/%lo pairs:
    (*(S9*)&D_801DAA78) = *(S9*)(&D_801DA998 + 0x20);  /* same addr as &D_801DA9B8 */
family_remap substitutes the symbol NAMES correctly and leaves the literal 0x20 — but 0x20 is not a
constant of the algorithm, it is the DISTANCE BETWEEN TWO PER-OVERLAY SYMBOLS:
    exemplar 0x801DA998 + 0x20 = 0x801DA9B8  OK
    member   0x801A5778 + 0x20 = 0x801A5798  WRONG (real symbol 0x801A5790)
    member   0x801A5778 + 0x18 = 0x801A5790  correct
match_one MASKS HI16/LO16 so it is STRUCTURALLY BLIND to this — the §81 blindness in its DATA form.

TWO FIXES WERE EACH INDIVIDUALLY INSUFFICIENT: the byte fix alone re-failed as PLUMBING; the ladder
alone re-failed as DIFF. Together -> BANKED, R22 clean-fleet 140/140.

TWO LADDER CORRECTIONS (my own T0.2 error): bare harvest_verify is the LAST RUNG, not the ladder —
gate_stage runs canon_resident_calls -> cast_call_sites -> reconcile_tu -> ARITY -> sig_unify ->
harvest_verify, so T0.2's "8/8 PLUMBING" measured the UN-RECOVERED rate. And reconcile_decls.py is
RETIRED (R33, superseded by reconcile_tu): asking "what does the FLEET call this symbol?" is wrong by
construction in a loosely-typed engine (548 of its answers conflicted, rewriting 60 of 196 drafts) —
so Drew's suggested tool would have made it worse.

SCOPE, MEASURED (not over-generalised, §80): the idiom appears at only 5 sites corpus-wide — BUT one
gates a 123-member family (133 staged drafts all carry the un-recomputed +0x20 with different
per-overlay bases), so the mechanical fix is worth ~240 ins x 123 ~= 29,520 ins. It does NOT explain
the pool generally: func_80144090 / func_8012CC88 / func_8014D12C have ZERO derived-offset sites and
fail for a different, still-undiagnosed cause.

THE FIX IS MECHANICAL: correct_literal = mapped(aliased_sym) - mapped(base_sym). The remap already
holds both mappings, and the exemplar's own comment names the aliased symbol.

Also observed: the ARITY pre-pass left 40 TUs of caller-decl edits after a 0-bank run (same hygiene
bug as --normalize-self-decls, twice in one session) — reverted, both binaries byte-identical.
2026-07-26 13:47:06 -06:00
Drew T adafeb13d6 feat(phase-29): T0.3b autopsy — 44% of the "near-miss backlog" are not near-misses; 315 are plumbing-blocked MATCHes
Recomputed every backlog residual from the bytes (1,699 rows, -j 12, zero agent tokens) through the
validated match_one path, deriving asm-subdir + -O0 from corpus.py. R34 cross-check PASSED (closeness
agreed with masked_diff.structured_diff on all 1,610 built rows, 0 classifier errors); 89 nobuild rows
REPORTED not dropped (R32).

BUCKETS: redraft 707 | structural 528 | integration 315 | permuter 57 | unknown 3.

1. HONESTY CORRECTION: 707 of 1,610 (44%) are class SIZE-MISMATCH — the stored best-draft is a
   PARTIAL, an incomplete attempt logged with a closeness score (the func_80183814 666-of-5,122
   shape). docs/backlog.md has been overstating readiness by ~44%. These route to a FRESH CRACK,
   not to a wall and not to the permuter.
2. ACTIONABLE: 315 entries are match_one MATCH *right now*, blocked only on the reconcile ladder —
   recomputing beat trusting the stored label because the tree moved since they were logged.
   ~108,959 gain-ins; top func_80174CB0 (16,482), func_801463A0 (13,534). §52b still applies: ~half
   of close=0 drafts fail the whole-binary gate, so these are CANDIDATES not banks.
3. The permuter bucket is 57/1,610 = 3.5% (Task-13B measured 7.7% and called targeting the problem).
   Extending the mutation set is CONFIRMED not the big lever — small, real, now bounded.
4. R34 again: 3 of 4 comparable labels DISAGREE with measurement — func_80140D68 / func_8012A328 /
   func_801549F8 recorded "schedule" but measure ADDRESSING -> cse. The grinder was aimed wrong.

CONVERGENCE: T0.2 (8/8 failures PLUMBING, 0 walls) and T0.3b (315 integration) independently point at
the SAME lever — the declaration/integration reconcile ladder, worth the 224,410-ins FREE pool AND
~108,959 backlog gain-ins. Two keys eliminated today; untried: canon_sig_reconcile v3.2 and
reconcile_decls.py (the DATA-symbol analog — one T0.2 failure text was a DATA symbol).
2026-07-26 13:25:29 -06:00
Drew T d3e6d6a702 feat(phase-29): T0.2 gate probe — the FREE pool is PLUMBING-blocked, not wall-blocked (4 banked)
MEASURED, not projected (R14): 12 gate attempts across ov_SC01_000 + ov_SC01_001, one draft per
build for clean attribution.

- 4 BANKED (func_8017B490 x2, func_801463A0 x2); 8 failed; **0 DIFF — zero compiler walls**
- all 8 failures are the §75a/def-side declaration class: `conflicting types for 'D_800A651C'`
  (DATA sym) and `conflicting types for 'func_8013D53C'` (the member's OWN def-side decl)
- => raw conversion 33%, but the ceiling is NOT 33%: the blocker is declaration plumbing, which
  this project has named tools for. Plumbing recovery has out-earned drafting in every phase that
  measured both (P19 fix_arity_callers, P28 dedup_extend 6,174 members / 95.6% from one new mode)

TWO CORRECTIONS TO MY OWN T0.1 POOL MATH, both downward:
- 2 of the 8 top "FREE" families were refused outright by the §42e pinned-exemplar guard (the 270
  skips) => "FREE" does NOT imply sweepable; pins are a third blocker the decomposition missed.
  Recoverable (--allow-pins; SESSION-19 banked pinned families x134), but I mis-labelled them
- n_templatable counts the matched exemplar, so every T0.1 family figure is ~1 member (~0.7%) high

NEGATIVE RESULT (§80, scoped to this base): --fix-def-sig REGRESSES this class — 0 banked and 2
PLUMBING became CC1-FAIL despite targeting the same error text. Do not re-buy without re-testing.

NAMED NEXT LEVER: family_sweep --normalize-self-decls, whose help text cites fixing "the conflicting
types for func_X that blocked 133/137 of func_801670E4" — exactly this failure. Gate-phase transform,
so it cannot run under --stage-only, and --limit caps FAMILIES not MEMBERS => needs a full ~123-member
family run. Highest-value outstanding probe, zero agent tokens.

R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health/dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 80.6% (10,589,065) · distinct-code 68.3% (3,846,416) · fn-count 89.19%.
2026-07-26 13:12:09 -06:00
Drew T 1fe4850136 feat(phase-29): T1.1 func_80183814 round 1 — 99.3% structural, named lever; cookbook §83
- VERIFIED INDEPENDENTLY (R14): match_one reproduces DIFF 5127 vs 5122, LENGTH-DRIFT/+5. Agent did
  not over-claim; tree untouched. Difflib-aligned truth: 36/5122 structural (99.3%), 17/21 cases
  EXACT, args+locals BYTE-EXACT at 216B
- §83a: on a LENGTH-DRIFT class match_one's mismatch count is NOT a progress signal — 4,622 and 36
  describe the same draft (index-wise comparison smears every index after the delta)
- §83b THE LEVER: the handoff's '35x repeated template' (which I passed on flagged UNVERIFIED) is
  TRUE and was the whole game — 2,625 of 5,122 ins (51%) from ONE parameterised 72-ins body. Three
  sub-levers: pointer walk (no strength-reduction under -G0), rand()%(u32) for divu, cast barrier vs combine
- §83c TRAP: the inherited 'dead local' pad[32] is gcc's OWN SPILL AREA — removing it made the locals
  area byte-exact. §83e: two 'pure allocation' residuals were a copy-pointer walk -> zero (§80 again)
- §83d THE STALL, cited: cse.c:8340 sizes the quantity table by WHOLE-FUNCTION pseudo count, so no
  per-case edit can move a function-global CSE fork. Next move = close the +5 (buys length parity AND
  perturbs max_reg), then re-run the do-not-re-buy table on the new base
- no pins in the deliverable (diagnostic-only, table row 15) — agent self-reported unprompted
- DECISION: round 2 QUEUED, not spent now — T0.2 (224,410-ins pool) outranks a ~0.04pp lever
2026-07-26 13:02:58 -06:00
Drew T 57ee715f3c feat(phase-29): T0.1 frontier survey re-run (138 ovs) — the family lever is ALIVE; a 224,410-ins zero-crack FREE pool
- verified the tool BEFORE trusting its scan (R35): load() correctly globs all 138 overlays, but the
  generated header hardcoded '134' -> fixed to derive from the same glob (a doc misreporting its own
  scope is the P28 img_path shape, one severity down)
- stale(07-23,134ov) -> fresh(07-26,138ov): fleet 88.5/79.0/68.4 -> 89.4/80.9/69.0%; families 2721 ->
  2688; substantial 558 -> 544; with-matched-sibling 74 -> 76. Structure STABLE => the P25 family
  reframe is NOT an artifact and P26's ~0% stays unsupported post-fix
- FINDING: 3,419 instances banked but only 85 distinct CLASSES fell -> recent yield was propagation,
  not new classes (SESSION-19's split, now fleet-wide)
- THE POOL: 76 zero-crack families (exemplar already matched) = 347,892 ins = 19.4% of remaining
  distinct code, decomposed by real blocker: FREE(PURE/non-jr/non-O0) 61 fams/224,410 ins = 12.5% of
  remaining; jr 13/57,311 (§81 chain); -O0 2/66,171 (known deferred build-infra, Arm A proved 9/9 bank)
- STILL A PREDICTION (R14/G3): T0.2 re-targeted from this data to measure the GATE conversion rate on
  8 members sampled across the FREE subset before any arithmetic scales
2026-07-26 12:46:34 -06:00
Drew T 661f5aa751 feat(phase-29): bank func_8017C730 x ov_SC03_013 (+1,061 ins) via the §81 carve chain
The SESSION-19 handoff's item 1, closed as specified — no drafting, no agent.

- §77 MINIMAL CLOSURE (519 lines, not the 2,993-line whole-file carry): 18 gte_* macros
  + 5 externs + the bandsetup static-inline helper -> match_one MATCH (1061 ins)
- §81 chain, each step byte-gated before the next: jr_isolate_all --only (2 fns/1 object)
  -> BYTE-IDENTICAL; jtbl_carve --func (single-table, 44-piece interleave) -> BYTE-IDENTICAL;
  harvest_verify --chunk 1 -> verified 1 / failed 0, 7042bc71 BYTE-IDENTICAL
- R22 clean-fleet 140/140 from a genuinely clean tree; tools-health OK; dedup 1886/0;
  0 NON_MATCHING (G4). FLEET distinct-code 3,845,161 -> 3,846,222 = 68.3% (+1,061, all
  distinct — a behemoth-class bank, not a propagation); instr-weighted 80.6%
- No §75a class spoke: the exemplar's ApplyMatrixSV(void*,void*,void*) canon fix was
  already carried, so the declarations were clean and it banked first try
- cookbook §77: the ladder CLOSED with all four rungs measured (-56 -> -34 ->
  MATCH-but-uncommittable -> MATCH+BANKED), plus a NEW subsection — the CANDIDATE gate
  and the REAL gate need DIFFERENT preambles (match_one compiles standalone, so a
  shared-type body's CC1-FAIL is a report about the PROBE, not the draft; the types
  header goes in a throwaway probe copy, never in the banked draft)
- FINDING, flagged not acted on (P5d): that shortcut already leaked an ABSOLUTE include
  path into 21 git-tracked files / 23 lines. All 21 verified semantically no-op (guarded
  engine_types.h via engine_core.h at line 2) => removal is byte-neutral, but cpp must
  still find the literal path, so those TUs cannot preprocess on any clone not at
  /home/musashi/bfm-decomp. Invisible to every byte-gate (R34's null-oracle shape, aimed
  at portability). Proposed as the next task.
2026-07-26 11:57:17 -06:00
Drew T 2199c71192 docs(phase-29): §77 — add the 5th measured variant (static helper) + the minimal-closure corollary
Drew asked whether the cookbook was actually being updated per behemoth. It was (13 commits,
each paired with its bank), but the check found a REAL GAP: the last probe's two lessons went
into CURRENT_PHASE.md and a commit message and were never folded into §77 itself. So the
cookbook PREDICTED the static-helper variant (its closing line named it) without recording that
the prediction had since been CONFIRMED, and lacked the corollary entirely.

- VARIANT 5: a `static inline` helper, dropped by family_remap -> LENGTH-DRIFT/-56 with NO
  compile error at all. The nastiest variant precisely because it produces no diagnostic: the
  draft compiles clean and is simply ~56 instructions short, which reads as a codegen residual
  rather than a missing construct. Rule added: a NEGATIVE length drift with no compile error on a
  mechanically-remapped sibling means look for an uncarried static/inline helper BEFORE touching
  a lever.
- COROLLARY (measured, and it cost a bank): carry the MINIMAL TRANSITIVE CLOSURE of what the body
  references, not the whole file. Carrying the exemplar's entire 2,993-line region file produced
  a clean standalone match_one MATCH and then failed the whole-binary gate on PLUMBING --
  over-carrying trades a match_one failure for an in-TU collision. Measured ladder: -56 (nothing)
  -> -34 (helper + externs) -> MATCH-but-uncommittable (whole file); the minimal set is the only
  bankable point.
- Also recorded: the walk-back-to-previous-brace heuristic breaks on an ISOLATED REGION FILE
  (_jr_<addr>.c from jr_isolate_all), where the construct above the function IS the needed helper
  -- it returns a 1-line preamble. A preamble-carry tool needs a reference-closure rule, not a
  positional one.
2026-07-26 11:19:05 -06:00
Drew T 109ce6a2c3 feat(phase-29): BEHEMOTH #6 func_8017C730 BANKED (1,061 ins) + §82 two source-shape oracles
- CRACKED at xHigh and VERIFIED INDEPENDENTLY: match_one MATCH (1061 ins); agent re-matched 3x
  from clean runs (100% register-masked AND register-kept, all 10 regions, frame 0x270 exact).
  §81 carve chain clean first try: jr_isolate_all --only -> byte-identical cacaf7c2 -> jtbl_carve
  (43-piece set) -> byte-identical -> bank -> R22 clean-fleet 140/140, tools-health OK.
  instr 80.6%; distinct-code 3,844,100 -> 3,845,161.
- WHAT IT IS: the matched base func_8017CA80 + camera height-band cull + distance-driven CLUT
  fade. func_8004974C (TransposeMatrix) sits in a 36-ins prologue deriving a Y band; the
  part-level `lim >= g.otz` cull is GONE; flat arms gain an `sz < lim` near-plane cull. The
  base+one-extra-callee fingerprint predicted this exactly.
- §82 ORACLE 1 -- A DUPLICATED `addiu $aN,$sp,K` ACROSS A `jal` MEANS THE BLOCK WAS INLINED.
  `&X` on any non-first local always creates a pseudo and CSE always merges two of them
  (expr.c:6260 ADDR_EXPR -> force_operand(..., NULL); exception: virtual-stack-vars offset 0).
  So the same stack address re-materialised at two sites separated by a jal means CSE was
  PREVENTED from merging => not the same function body. 17 non-inline spellings failed; a
  `static inline` helper reproduced the prologue BYTE-FOR-BYTE first try. Reusable probe: scan
  the ~1,200 built objects for that signature in NON-INCLUDE_ASM functions.
- §82 ORACLE 2 -- SCALAR vs AGGREGATE DECIDES *WHEN* A STACK SLOT IS ALLOCATED: lazily at first
  `&` for a scalar, AT DECLARATION for an aggregate. Six GTE result words had to be six separate
  longs, not a struct, or they don't land after the inlined helper's temps and the frame isn't
  0x270. Second-order: it also flips MEM_IN_STRUCT_P (§30's /s) -- with one word a fixed-address
  scalar, ((PolyF3*)pkt)->rgbc stops aliasing it, so a store needed respelling to keep the
  target's nop. A scalar-vs-struct choice is simultaneously a frame-layout AND an aliasing
  decision.
- BANKING FOOTNOTE (§75a class A): first bank rejected `conflicting types for ApplyMatrixSV` --
  draft (MATRIX2*, SVECTOR2*, SVECTOR2*) vs the TU/fleet canon (void*, void*, void*), 2,286 of
  2,835 sites. Conforming the decl is byte-neutral and banked first try. On a jr function expect
  BOTH gates to speak: the carve chain answers the jump table, §75a answers the declarations.
- Also reproduced: §78 (reuse a busy variable), §80(i) (a lever went -8 -> exactly neutral as the
  base moved), §72 (a register pin made it worse).
- AGENT'S OWN CAVEAT, recorded not hidden: one zero-byte __asm__ keeps a vestigial `mnc = hmid`
  alive that flow.c would delete (costing 10 ins + the 0x130 spill slot). Emits nothing, compile
  is 1061 exact, but it is a documented stand-in -- 12 natural spellings measured, all DCE'd.
2026-07-25 23:24:11 -06:00
Drew T faf4547345 feat(phase-29): func_8017C954 BANKED — jr carve chain cleared; a shared type was PRESENT but INVISIBLE
- BANKED (1,194 ins, ×1 distinct-code). Chain cleared, each step byte-gated before the next was
  built on it: one-line fix to jr_isolate_all._engine_types() -> jr_isolate_all --only
  func_8017C954 (2 fns / 1 object, NOT the bare 47-fn / 21-object resegment) -> BYTE-IDENTICAL
  b7b0d4ae -> jtbl_carve --func func_8017C954 (44-piece carve set + interleave order) ->
  BYTE-IDENTICAL -> harvest_verify VERIFIED BYTE-IDENTICAL -> R22 clean-fleet 140/140,
  tools-health OK. instr 80.5 -> 80.6%; distinct-code 3,842,906 -> 3,844,100.
- THE DEFECT (tools/jr_isolate_all.py): _engine_types() harvested shared type names with four
  patterns -- `typedef ... X;`, `} X;`, forward-decl `struct X;`, fn-ptr typedef -- and a TAGGED
  DEFINITION WITH A BODY matches NONE of them. So `struct PW8017E6D8 { int w; }
  __attribute__((packed));` at engine_types.h:658 was present in the shared header yet invisible
  to the carried-type check, and `extern struct PW8017E6D8 D_801E1EC4;` could not be placed.
  MEASURED BLAST RADIUS: 77 such tags in engine_types.h were invisible. One added pattern fixes
  all 77.
- WHY THIS COST 20 MINUTES INSTEAD OF A MYSTERY BYTE-DIFF THREE PHASES LATER: the Phase-26 audit
  had already turned this predicate's SILENT DROP into a LOUD REFUSAL. The original bug dropped
  4,040 col-0 decls, 683 of them function PROTOTYPES -- and a dropped prototype is a SILENT
  BYTE-CHANGER (C89 implicit `int f()`; return type drives delay-slot fill in this codebase). The
  refusal named the exact symbols and the exact remedy. A loud "I cannot place this" is worth far
  more than a green build -- the audit paying for itself, live.
- §81: the 3-step jr-carve chain + why match_one CANNOT see the problem (it masks jal/HI16/LO16,
  so a jump-table function reports MATCH while the whole-binary gate reports DIFF, correctly).
  Detect with `grep -cE 'jr \$(v0|v1|a0|t[0-9])'` on the target .s + a jtbl_ in asm/<ov>/data/.
  ALWAYS use --only: bare would have resegmented 47 jr-functions across 21 objects.
2026-07-25 21:58:04 -06:00
Drew T 8b828f1ea6 feat(phase-29): BEHEMOTH func_8017BF14 CLOSED — 45 -> 0 (4,763 ins, the largest match yet)
- 45 -> 37 -> 33 -> 21 -> 11 -> 3 -> 2 -> 0, reproduced 3x from independent work dirs. Verified
  independently before believing it (R14): match_one MATCH (4763 ins), then harvest_verify
  --binary ov_SC03_116 BYTE-IDENTICAL, then R22 clean-fleet 140 passed, 0 failed of 140.
  distinct-code 3,838,143 -> 3,842,906 = 68.1% -> 68.2%. instr 80.5%. Agent was interrupted by a
  weekly API limit and RESUMED FROM ITS TRANSCRIPT -- its round-2 harness survived, nothing was
  re-derived.
- §80 THE PROCESS CORRECTION, worth more than the match: A DO-NOT-RE-BUY ENTRY IS SCOPED TO ITS
  BASE, NOT TO THE FUNCTION. Three of round 1's ~40 measured negatives INVERTED on round 2's
  base -- the same edit (qsingle23) measured 1,040 mismatched on the 45-base and 11 on the
  21-base. Re-testing the round-1 negative list cost ~20s and produced THREE of the seven winning
  levers. Such a table records (edit, base) -> result, NOT edit -> useless; after any lever that
  moves the base materially, RE-RUN THE NEGATIVE LIST. This retroactively qualifies every
  do-not-re-buy table in the cookbook (§45, §60b, §75a, §76, §78, §79). Concrete: round 1 measured
  "removing the va->$t2 pin costs 4% elsewhere" => keep the pin; on a base with c0..c3 at function
  scope, removing those pins is worth 21->13. Same experiment, opposite conclusion.
- MY FLAGGED "#1 MOVE" LOST, and the failure is the finding. I briefed variable REUSE (§45-A /
  RC-14) as the top lever because it took func_8017F510 from 97->10. Swept in full here: EVERY
  merge lost, 43-3294 across 8 merges. Reason: the TRI and QUAD grants did not differ by RANK but
  by IDENTITY -- two independent allocno sets, and re-ranking inside one set cannot fix a two-set
  problem. Diagnose ranking-vs-identity before reaching for a merge. The actual fix (c0..c3 at
  FUNCTION scope, 33->21) was read off the two matched relatives (b5:310, b4:338) and confirmed
  against the target -- the 4th time today that reading a matched relative beat the clever lever.
- PIN'S HIDDEN COST, cited: combine_regs' hard-register branch (local-alloc.c:1795, reached from
  :1295 with already_dead==0) records the pinned reg in qty_phys_sugg UNCONDITIONALLY -- no death
  guard. A pin invites local-alloc to tie producer chains into it. New cure R7: a zero-byte
  __asm__ ref keeping the pinned value live past the temp so find_free_reg can't honour the
  suggestion -- closed the last 2 ins (c1->$a0 is uniquely load-bearing; every alternative pin
  lost 64 ins).
- §78's attribution primitive RUN and REPRODUCED: under -fno-schedule-insns, -fno-schedule-insns2
  and both, order unchanged => the rgb transposition was never a sched.c decision.
- Cold-start economics complete: round 1 = decode + exact length + exact frame + 99.06%; round 2 =
  the last 45, and cheaper. Budget TWO passes at this size. 5th source copy-paste artefact found.
2026-07-25 20:29:10 -06:00
Drew T f5f8dec5ee docs(phase-29): the cold-start experiment — func_8017BF14 to 45/4763; §79; full R22 discharged
- COLD-START RESULT (verified independently): 4763/4763 ins, 45 mismatched = 99.06% byte /
  99.94% structural, exact frame, exact opcode histogram. NOT a match; nothing banked (45 != 0,
  the byte-gate is the sole arbiter). The residual is 3 register-grant ties, 0 structural
  divergence. Named next move: variable REUSE across c0..c3/a0v..a3v, the one §76 lever class
  the pass never reached.
- MY BRIEF'S PREMISE WAS WRONG BY CONSTRUCTION -> §79. I chose this target partly because §71's
  callee-set fingerprint returned 0.00 against every matched giant = "a genuine cold start". But
  the function makes ZERO jal calls, so its callee fingerprint is EMPTY and §71 CANNOT FIRE:
  0.00 meant "cannot answer", not "no relative". Grepping the target's DATA symbol D_800A5E60
  found the matched func_8017BEBC at once -- func_8017BF14 is the 4-light-box member of the same
  renderer family whose 3-box sibling func_8017D960 was matched hours earlier. RULE: when §71
  returns an empty/zero-overlap callee set, fall back to DATA-symbol fingerprinting; an empty
  fingerprint must never become a cold-start brief.
- NEW LEVER (§79): THE FRAME LAYOUT IS A DECLARATION-ORDER ORACLE. gcc-2.7.2 assigns stack slots
  to spilled pseudos in pseudo-number order, and pseudo numbers follow first use ~ declaration
  order -- so the target's frame map reads back its source's declaration order. Moving ONE line
  took 73% -> 84% structural and brought all 127 slots into exact correspondence.
- §76 CONFIRMED AT SCALE: the entire -62 length residual was ONE allocno-class decision (c0..c3
  declared inside the cull blocks -> 1-death local allocnos -> global.c:668-671 removes those
  regs from the global pool -> r1lo spills), 52% -> 93%. An __asm__ ref-dial reached the same
  spill and scored WORSE -- declaration scope beat the ref dial again.
- PIN NUANCE: pins are safe on a 0-jal function (§74's hazard cannot arise), 4 pins took
  94% -> 99%; but §72 held -- pins 5 and 6 made it worse.
- EFFORT ANSWER, HONEST: xHigh from a genuine cold start on a 4,763-ins giant bought the decode,
  the exact length, the exact frame and 99.06%, and did NOT close. Budget a SECOND pass at this
  size: the first buys structure, the last ~1% is register grants.
- FULL R22 DISCHARGED: make clean + extract-all + check-all -> 140 passed, 0 failed of 140 (run
  after the agent finished, per the deferral recorded in the pool commit). tools-health OK.
2026-07-25 17:08:02 -06:00
Drew T a1f1a948af feat(phase-29): h_norm-remappable pool — +4,919 ins banked, no cracking, no agent
- BANKED (each whole-binary byte-gated; make check-all -> 140 passed, 0 failed of 140):
  func_80130D48 ×4 (1,064) · func_8018F3E4 (478) · func_8018B3D0 (478) · 13 × 223-ins siblings
  of func_8017E6D8 (2,899). distinct-code 3,833,224 -> 3,838,143 = 68.0% -> 68.1%.
- TWO OF MY OWN COUNTS COLLAPSED UNDER SCRUTINY BEFORE I ACTED ON EITHER (R14/R35):
  "func_8017CA80's family = 102 unmatched" was really 13 -- my count tallied family members whose
  NAME appears as a stub anywhere in the fleet, not instances actually unmatched (a semantics
  error, not arithmetic). "56,267 ins remappable" was really 8,114 -- 86% was the known -O0 /
  deferred set (the func_80144B9C whale, the func_8013C414 cluster). I nearly recommended a
  target on the first number.
- THE §77 CARRY GAP IS THE DOMINANT COST OF MECHANICAL REMAP: 23 of 27 first-pass CC1-FAILs.
  NEW .run/giants/s19_remap_tu.py sources the preamble from the exemplar's OVERLAY TU (the block
  between the previous top-level `}` and the def), applies family_remap's own substitution map,
  and adds the two includes match_one never adds -> 21 drafts went 0 MATCH -> 14 MATCH. The 13
  223-ins siblings share ONE exemplar, so a single preamble fix cleared all 13.
- USEFUL ASYMMETRY: func_8018F3E4/func_8018B3D0 FAILED match_one but BANKED in the whole-binary
  gate -- the real TU supplies decls the standalone compile lacks. A match_one CC1 FAIL is not a
  reason to skip the real gate on a remapped sibling.
- RESIDUAL 3,195 ins, causes NAMED not guessed: func_8017D5C0 (952) matches standalone, gate
  reports `conflicting types for memcpy` = the §58 red-herring (a warning from an unrelated TU
  position; SESSION-14 hit the same label and the true cause needed a hand-splice + real cc1
  stderr). func_80166994 ×3 + func_8016A290 ×4 still CC1-FAIL after the TU carry.
- FULL R22 DEFERRED DELIBERATELY: make clean wipes asm/, which the concurrently-running BF14
  agent reads on every probe. This batch changed only src/*.c (no config), so check-all is sound;
  the clean R22 must still run once the agent finishes.
2026-07-25 16:11:38 -06:00
Drew T f9a2de3edd feat(phase-29): BEHEMOTH #2 func_8017D960 CRACKED (1806 -> 0) + its 5-member family = 16,690 ins
- CRACKED pin-free at xHigh (Opus 5 agent), then ALL FOUR family siblings banked via §40 remap,
  each MATCHING FIRST TRY: ov_SC03_090 (the crack) · ov_SC03_089 · ov_SC03_104 ·
  func_8017E778 @ ov_SC03_091 · func_8017CD9C @ ov_SC03_102 (the last two cross-address).
  Verified independently before believing the report (R14): match_one MATCH (3338 ins), then
  harvest_verify BYTE-IDENTICAL on all five binaries, then R22 clean-fleet 140/140.
- METRICS: distinct-code 3,816,534 -> 3,833,224 (+16,690) = 67.7% -> 68.0%, the first
  percentage-point movement in that metric all session. instr-weighted 80.3% -> 80.5%.
  Session distinct-code total +19,712 ins, ALL from the three behemoths; propagation gave +0.
- MY BRIEF WAS WRONG IN AN INSTRUCTIVE WAY -> §78. I said a negative length drift means "missing
  instructions". The 4 absent instructions were 4 emit tails × 1 nop -- delay slots the target
  could NOT FILL because the register it wanted was still live. otp at function scope has 4
  deaths -> fails local-alloc.c:472 -> global allocno in $a2 -> via global.c:668-671 pushes tp
  off $a1 -> the 0xFFFFFF mask is free early -> maspsx hoists it into the slot. Declaring otp
  PER EMIT ARM fixed the whole drift in one edit (3334->3338, 1806->333).
  SECOND TIME IN ONE SESSION a "structural"-looking residual was an allocno-class choice (the
  first: F510's "scheduling" transposition, §76). A nop present in the target but absent from the
  draft is usually a register-liveness fact, not missing code.
- TWO MORE REUSABLE FINDINGS (§78): gcc-2.7.2 fold NEVER leaves a literal first in an `|` chain
  (7 parenthesisations, all reassociate) -- so `or acc, var, K` first in the target means K was a
  VARIABLE in the source, an asm->source read that retires a whole sweep family. And "make it a
  variable" has TWO separable effects (fold-opacity vs a new allocno): a fresh short-lived local
  fixes structure and wrecks allocation (690 mismatched, damage ~300 ins away); reuse a busy one.
- ECONOMICS: 9 levers, each necessary by drop-one ablation, and 5 of the 9 were read straight off
  the MATCHED relatives func_8017F510 (cracked earlier today) and func_8017CA80. Crack the
  smaller family member first -- it is a lever library for the larger one.
- NEW TOOL .run/giants/s19_remap_family.py: family_remap + the §77 preamble carry in one step
  (reproduces the exemplar's FULL file-scope preamble with the tool's own substitution map
  applied). Took the 4 siblings from "4 rounds of CC1 FAIL each" to MATCH first try, ×4.
2026-07-25 15:48:54 -06:00
Drew T 130b9407b1 feat(phase-29): func_8017F5B4 (1,511 ins) banked MECHANICALLY off behemoth #3 — ~0 agent tokens
- func_8017F5B4 @ ov_SC02_031 shares behemoth #3's h_norm AND h_seq (96fe0455c344 /
  9a6bd2b91fd4) with a different h_exact = the same instruction stream differing only in masked
  reloc fields. The §40 family_remap case exactly, so NO agent was spent: family_remap
  --addr 0x8017F510 --from ov_SC03_006 --to ov_SC02_031 --to-addr 0x8017F5B4 substituted 52
  per-overlay symbols correctly on the FIRST invocation.
- match_one -> MATCH (1511 ins); harvest_verify --binary ov_SC02_031 -> BYTE-IDENTICAL;
  R22 clean-fleet 140 passed, 0 failed of 140.
- DISTINCT-CODE 3,815,023 -> 3,816,534 (+1,511). With behemoth #3 that is +3,022 distinct-code
  instructions from the two behemoths, versus +0 from every propagation win this session.
- ALL the work was PREAMBLE, none of it the body -> cookbook §77. Four CC1 FAIL rounds, each
  naming one construct the extractor drops: (1) multi-line `typedef struct {...} PolyGT4;` --
  family_remap's backward walk accepts a line only if it STARTS with extern/typedef/comment, and
  a multi-line typedef ENDS with `} PolyGT4;`, so the walk halts there AND LOSES EVERYTHING ABOVE
  IT; (2) hence the file-scope extern block above the #define BOXTEST/ATTEN block; (3) the
  exemplar's own #include lines (PolyFT3/PolyFT4 live in engine_types.h).
- THIRD CONFIRMATION TODAY OF ONE DEFECT CLASS, NOW ACROSS TWO TOOLS. §75b found
  dedup_propagate dropping a file-scope #define and PREDICTED the generalisation; family_remap
  then dropped a typedef, an extern block, and the includes. RULE (§77): after any mechanical
  template/propagate step, diff the exemplar's full file-scope preamble against what the tool
  emitted. A CC1 FAIL on a remapped sibling is a PREAMBLE report until proven otherwise -- it
  says nothing about whether the remap was right.
- Artifact preserved: .run/giants/s19_func_8017F5B4_remap.c
2026-07-25 15:02:37 -06:00
Drew T 3ffff74f5b feat(phase-29): BEHEMOTH #3 func_8017F510 CRACKED — 97 -> MATCH (1,511 ins), pin-free
- BANKED into ov_SC03_006 through the whole-binary byte-gate (G3/P9); R22 clean-fleet
  140 passed, 0 failed of 140. Verified independently before believing the agent's report
  (R14): match_one -> MATCH (1511 ins), then harvest_verify -> BYTE-IDENTICAL.
- DISTINCT-CODE 3,813,512 -> 3,815,023 = +1,511, EXACTLY the function's instruction count and
  the ONLY distinct-code movement of the entire session. Reach is ×1 by sig, no propagation --
  which is precisely why it moves the metric propagation cannot touch. instr 80.3%, fn 89.18%.
- EFFORT EXPERIMENT (Drew): behemoths #1-#3 were worked at High; this is the first at xHigh
  (Opus 5 agent). It closed a residual the lower tier had fully localized but could not move,
  and that 3,663 permuter candidates at base 97 had failed to improve by even 1.
- MECHANISM -> cookbook §76: the allocno CLASS (local vs global) is the dominant regalloc lever
  and C reaches it ONLY through declaration scope and variable reuse -- unreachable by statement
  order, expression shape, pins, or random search, which is exactly why the permuter was spent.
  (1) `otp` per emit ARM: 4 deaths -> four 1-death local pseudos (local-alloc.c:472); its
  second-order effect via global.c:668-671 (local placements re-marked as HARD regs for
  global-alloc) had made the target's otp=$a0 STRUCTURALLY IMPOSSIBLE, visible as hard-reg 4 in
  the `;; N conflicts:` tail of the .greg dump. (2) `cb` reused as the unlit rgbc temp: refs
  27->39 lifts its global.c:594 allocno_compare priority past `tp`, flipping the 3-colouring ->
  97 -> 10. (3) one shared `rgbw` temp -> 10 -> 2. (4) mny-before-my + one zero-byte __asm__ at
  the head of the tri cull block -> MATCH.
- THREE CORRECTIONS TO MY OWN BRIEF, all byte-evidenced: residual B was never a scheduling
  residual (it fell out free with lever 2 -- a register grant seen as a schedule diff); residual
  A is RTL EXPANSION order, proven with -fno-schedule-insns AND -fno-schedule-insns2 (source
  order survives both -- that attribution primitive is the reusable bit); residual C had no
  single c3 seed (c3 has no lever of its own, it moves only when cb out-ranks tp).
- FIXED a latent SHARED-HEADER defect, pre-existing and unrelated to the draft:
  src/shared/engine_types.h closed its include guard at line 1174 of 1259, leaving 11 typedefs /
  85 lines OUTSIDE the guard since the crack-wave lift. A TU including it twice re-declares them
  and gcc-2.7.2 rejects a repeated typedef even when identical -> `conflicting types for
  Blk16_956C`. Guard moved to EOF; byte-neutral.
- ARTIFACTS TRACKED (R20): .run/giants/s19_func_8017F510_b4.c (130-line dossier) +
  s19_f510_report.md, whose ~50-row do-not-re-buy table is arguably worth more than the match,
  + the s19_* analysis tooling.
- STRETCH, MEASURED: func_8017F5B4 (1,511 ins, ov_SC02_031) has a DIFFERENT h_exact -- not a
  dedup sibling, a family_remap TEMPLATE candidate off the b4 source.
2026-07-25 14:41:08 -06:00
Drew T 6e9cbda926 feat(phase-29): func_80174CB0 ×3 -> ×135 (§75c pair); the census predicted the residual exactly
- dedup_extend banked 132 / 179 planned across 135 binaries: func_80174CB0 VERIFIED in 132,
  FAILED in exactly 3. 123 ins × 132 = 16,236 ins.
- THE PREDICTION HELD TO THE OVERLAY. The blocker breakdown across the original 134-binary sweep
  was 131 class-B (func_8012F14C arity split) / 3 class-A (func_80012ABC, census 73 s32 vs 7
  s16). Fixing class B alone banked 132 and left 3 -- precisely the class-A set. A diagnosis that
  predicts WHICH members will still fail, and is right, is much stronger evidence than one that
  explains failures after the fact; same shape as §75b predicting that the 3 stuck members would
  be exactly the 3 files carrying the __volatile__ spelling of SHB.
- FLEET: instr-weighted 80.2% -> 80.3% (10,539,723 -> 10,555,959); fn-count 89.14% -> 89.18%;
  distinct-code 67.7% UNCHANGED (propagation moves coverage, not distinct-RE -- fresh cracks are
  the only lever there). dedup 1886 validated / 0 failed, C1 coverage 239,604/239,604.
  0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- cookbook §75c committed with this batch. The 3 residual overlays need the 7 `s16` func_80012ABC
  decls normalized -- worth 3 overlays only, so do it only if trivially cheap.
2026-07-25 13:44:24 -06:00
Drew T 3bbfe21596 feat(phase-29): func_80165CA0 ×3 -> ×135 via ENGINE_SHB; +22 fns ×1; §75b (the carried-#define gap)
- dedup_extend banked 157 / 478 planned across 135 binaries: func_80165CA0 (99 ins) ×135
  (~+0.10pp) + 22 other functions ×1 picked up in the 3 overlays the first sweep excluded.
- FLEET: instr-weighted 80.1% -> 80.2% (10,525,534 -> 10,539,723, +14,189 ins); fn-count
  89.09% -> 89.14%; distinct-code 67.7% (unchanged — propagation moves coverage, not distinct-RE).
  dedup 1886 validated / 0 failed, C1 coverage 239,472/239,472. 0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- §75b — extraction lifts `extern`s but NOT file-scope `#define`s, so a body matched with a macro
  in its preamble compiles only where that overlay's define is in scope ABOVE the splice point.
  Signature is a LINK error (`undefined reference`), never `conflicting types`: an unexpanded
  SHB(x) parses as a call to an undeclared function. The diagnosis PREDICTED the membership —
  the 3 stuck members are exactly the 3 files carrying the __volatile__ spelling of SHB, i.e. the
  function's own preamble still sitting above its own instantiation.
- R14/R35 IN ACTION: the full-sweep census REVERSED the ranking I had just committed. I put the
  class-A normalization first at "~+0.13pp if it reaches ×138"; measured across all 134 it is
  worth 3 overlays (func_80012ABC 3, func_8012F14C 131). The cheap win was the one I ranked
  third. §75a's "collect across the whole sweep before scoping" earned itself immediately.
- NEXT (specified, not guessed): func_80174CB0 is class B on func_8012F14C (1944 `(s32)` vs 968
  `(s32,s32,s32)`). The macro carries the 3-param prototype; the failing TU declares the 1-param
  one FIRST (ov_SC01_001: TU@328 vs instantiation@2616) -> two prototypes, different arity ->
  reject. Per cdecl.compatible's MEASURED rule a K&R `extern void func_8012F14C();` is accepted
  BOTH ways round here (prototype-first + `()`-second always; `()`-first + prototype-second when
  no param default-promotes, and s32 does not) -> it should satisfy both populations in either
  order. One-line probe on the carried decl, byte-gate the 3 members, then extend.
2026-07-25 13:18:36 -06:00
Drew T 39127535da feat(phase-29): func_8014F3E8 ×4 -> ×138 via dedup_extend; §75a enumerates the exclusion classes
- THE NORMALIZATION PAID: one `dedup_extend --binaries <the 134 excluded>` banked 134/400
  planned -- func_8014F3E8 VERIFIED in ALL 134 -> ×138 total (+4,288 ins), no drafting at all.
  A 4-overlay island became full fleet reach because the carried extern finally agreed.
- FLEET: instr-weighted 80.0% -> 80.1% (10,509,526 -> 10,525,534 = +16,008 ins, exactly the
  projected 84×138 + 32×138); fn-count 89.02% -> 89.09%; distinct-code 67.7% (unchanged, as
  expected -- propagation moves coverage, not distinct-RE). dedup 1884 -> 1886 validated / 0
  failed, C1 coverage 239,315/239,315. 0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- §75a — "PROPAGATION-CAPPED" IS AT LEAST THREE CLASSES, and the classifier names which:
    A minority spelling   `conflicting types` + a lopsided census (1710 vs 4) -> normalize, cheap
    B genuine arity split same message, TWO real populations (func_8012F14C: 1944 `(s32)` vs
                          968 `(s32,s32,s32)`) -> the §29 loose-typing wall; a K&R `()` MAY satisfy
                          both but is order-dependent -> PROBE, do not normalize on a guess
    C missing extern      `undefined reference to 'SHB'` -- a LINK error, unrelated to types
  The discriminator is one grep (census the symbol cc1 named) and it decides the remedy.
- R14 self-correction recorded: I predicted func_80174CB0 was "the identical class". It is class
  A in KIND but on DIFFERENT symbols, and different ones per overlay (func_80012ABC at
  ov_SC01_000 where the minority is on the TARGET side; func_8012F14C at ov_SC01_001 = class B).
  One member's error names one blocker, not the blocker set -- collect the classifier's line
  across the whole sweep before scoping a fix.
- func_80174CB0 (×3) and func_80165CA0 stay capped, each now with a named cause and a named next
  probe -- not a wall verdict.
2026-07-25 13:00:00 -06:00
Drew T 2fcb8de6bf fix(phase-29): normalize func_8014F468 to the fleet-canonical s32 (unblocks the ×138 reach) + §75
- THE PROPAGATION CAP WAS A MINORITY-SPELLING SOURCE OVERLAY, byte-censused:
    extern s32 func_8014F468(void);   1710   |  s32 func_8014F468(void)  134   <- fleet canon
    extern void func_8014F468(void);    20   |  void func_8014F468(void)   4   <- the outlier
  and ALL 4 `void` definitions are ov_SC07_{006,007,010,011} — the overlay the F3E8 body was
  banked from. dedup_propagate carries the source overlay's file-scope externs into the shared
  macro VERBATIM, so the macro inherited `extern void` and the 134 overlays that define the
  symbol `s32` rejected it. Propagation landed on exactly that 4-overlay island.
- The exclusion message ("byte-diverge / irreconcilable") is provably the wrong cause: members
  are selected BY h_exact, so all 138 are byte-identical by construction. It is a COMPILE
  conflict, never a byte one (same defect family as §68's mislabel, same tool).
- NORMALIZED the 24 minority occurrences to s32 (4 definitions + 19 overlay externs + the 1
  line in the freshly-authored macro). func_8014F468 is a pure inline-asm $sp-switch trampoline
  — no C-level value flow — and 134 overlays already PROVED s32 is byte-correct for the
  identical function. Fleet is now uniform: 1730 extern s32 + 138 s32 defs, 0 `void`.
- BYTE-GATED the complete blast radius (the 4 instantiators of DEFINE_func_8014F3E8):
  ov_SC07_006 7ca772be · 007 b3b95547 · 010 d7b5875d · 011 9885af74 — all BYTE-IDENTICAL.
- cookbook §75: census the carried extern before believing an exclusion message; prefer a
  majority-spelling source overlay; always pass --recover; after normalizing use dedup_extend
  (the body is already a macro) not dedup_propagate --addr.
2026-07-25 12:45:11 -06:00
Drew T 6f9860c46a docs(phase-29): §74 — pin-safety audit; the 5-pin behemoth draft is SAFE (open action #3)
- AUDITED .run/giants/s18_func_8017D960_b2.c (pins $25 $17 $19 $20 $21) WITHOUT recompiling:
  the SESSION-18 match_one object survives and cmp proves its t.c is this draft.
- VERDICT SAFE. The corrupting form of the §72 hazard is a CALLER-SAVED pin ($25=$t9) whose
  live range spans a jal — gcc-2.7.2 does not save/restore an explicit-register variable
  across a call. Byte-checked: exactly 3 jal, all at 0x2c-0x50; first pin write at 0x58 =>
  NO call after the pins are established. Corroborated by a token census of the C (every
  call-shaped token after line 270 is a file-local macro: gte_*, BOXTEST, ATTEN, CLAMP80).
- The observed excess writes (2/3/3/5/5 vs 2 assignments each + 1 epilogue lw) are the BENIGN
  §72 mode: gcc using the pinned reg as scratch before the pinned value lands (lui/lw/addiu on
  $20, with addu t9,s4,zero routing r1's value out through it). Nothing live was clobbered.
- cookbook §74: the reusable audit (objdump the surviving object; compare jal addresses against
  the first pin write; expect writes == assignments + 1 epilogue restore) + the standing rule —
  prefer a callee-saved register for any pin outliving a call; a caller-saved pin across a jal
  is a real wall verdict, not a drafting slip.
2026-07-25 12:28:14 -06:00
Drew T 1c0d29d91d feat(phase-29): §30#2 widen batch — func_8014F3E8 + func_8014D4C0 banked; §73 (the two axes)
- FLEET WIDEN (T2, one edit): extern void -> extern s32 for func_8014F3E8 + func_8014D4C0
  across src/** (16 decls in engine_core.h + 5,079 in 3,459 overlay .c; 0 `extern void`
  left, 0 pre-existing `extern s32`). Scope re-verified against the tree first (R14/R35):
  the SESSION-18 counts reproduce exactly and no decl exists outside the `extern void <name>`
  shape in any .c/.h under src/.
- BYTE-NEUTRALITY OF THE WIDEN ISOLATED FIRST: ov_SC07_006 7ca772be + ov_SC01_000 9052dc0e
  BYTE-IDENTICAL before splicing any draft (ov_SC01_000 chosen because it instantiates the two
  return-CASTING macros — the only sites a decl's return type could touch codegen).
- BANKED into ov_SC07_006 (both ×1, both reach ×138 by sig: single h_exact across 138/138):
  func_8014F3E8 (32 ins) on gate 1; func_8014D4C0 (84 ins) on gate 2.
- FINDING -> cookbook §73: the widen fixed only HALF the conflict. A def-side self-decl
  conflict has TWO independent axes — RETURN (fleet macro-widen, T2, R22-mandatory) and
  PARAMS (canonical param types + casts at each USE, T0, no fleet edit). func_8014D4C0
  failed the first gate on the PARAM axis (canon `void*` vs draft `u16*`); the §17a-1 move
  applied to the def's own signature banked it with nothing outside the draft touched.
  Diagnose the axis before reaching for the expensive fix.
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
  make report: dedup 1884 validated / 0 failed, C1 coverage 239039/239039, 0 NON_MATCHING (G4).
  Fleet 80.0% instr / 67.7% distinct / 89.02% fn-count (the ×138 propagation is the value).
2026-07-25 12:24:30 -06:00
Drew T dfa366c7c6 docs(phase-29): cookbook §72 — behemoth #3 to 1511/1511; a register pin is a PREFERENCE, not a reservation
func_8017F510 (1,511 ins): EXACT length, frame 0x258 exact, byte-exact prologue AND epilogue,
identical sp-slot set, 99.5% register-masked structural / 93.3% byte-aligned, SYMS-OK, 97 divergent.
Not a match (G3). §71's callee-set lever delivered ~90% of the C and a first compile at 1528/1511.

- CORRECTNESS FINDING (qualifies §17): a local `register s32 x __asm__("$30")` pin produced a
  seductive 1511 ins / 98.9% and was a MISCOMPILE -- gcc-2.7.2 ALSO allocated $s8 to an unrelated
  live value. A pin is a HINT to the allocator, not a reservation. Never ship one without inspecting
  the pinned register's defs. Banked work is safe by construction (the byte-gate rejects a
  miscompile); the exposure is UN-GATED drafts. ACTION: behemoth-2's draft carries FIVE pins
  ($25 $17 $19 $20 $21) and must be re-checked before anyone builds on it.
- THE HONEST FIX was source-level: the +17 drift was live-range stretching from REUSING w/wz for the
  vertex-word reads (spilling amb, 7 lw+nop pairs). Dedicated temps -> 1528->1511, 88%->99.6%, no pin.
- GIV RECORD ORDER (§70 family): part->prim must be read BEFORE part->nprim -- loop.c:combine_givs
  walks bl->giv in REVERSE record order, so the last-recorded giv becomes the combined base.
- RESIDUAL 97 traced to ONE seed: c3 is $a2 in the target, $a3 in the draft; tp takes the other of
  the pair and renames the whole 4-tail block. Fix c3 -> $a2 and ~93 should fall together.
- SPENT, byte-recorded: decl-order permutations, block-scoping, splitting/inlining tp, reusing f0,
  vertex axis orders, and decomp-permuter (4,724 candidates, base 97, ZERO improvement -- a §3 hard
  tail outside the C-randomisation space).
- b3_align.py / b3_pos.py supersede the b2_* aligners.
2026-07-25 02:05:40 -06:00
Drew T 1de9a1a2e9 docs(phase-29): §71 corrected — fingerprint a giant's family by CALLEE SET, not adjacency or h_seq 2026-07-25 01:26:06 -06:00
Drew T 8e18bb3df6 chore(phase-29): preserve the remaining behemoth-2 analysis tools (b2_cmp/dump/map/marks/side) 2026-07-25 01:22:36 -06:00
Drew T 28d10601ae docs(phase-29): cookbook §71 — behemoth #2 to 3334/3338; the adjacent-sibling lever; §69 partly REFUTED
func_8017D960 (3,338 ins): 3,334 ins drafted, 98.8% register-masked-identical, 88.3% byte-aligned,
byte-exact prologue AND epilogue, exact 0x320 frame, same 10 saved regs, identical ~110 stack slots,
SYMS-OK. NOT a match (G3) -- but an order of magnitude closer than behemoth #1.

- THE LEVER: it is the LIT VARIANT of func_8017CA80, the 952-ins renderer immediately above it in the
  same file (already matched). Diffing the sibling gave ~90% of the C free and a 3334/3338 draft on
  the FIRST compile. GENERALISED: before mapping any giant, grep for an already-matched adjacent
  function that is the same routine. One grep can replace days of analysis.
- §69 PARTLY REFUTED: its law 1 (write whole body coarsely -> correct frame/saved-reg set) CONFIRMED
  and decisive; law 2 (measure region-aligned) confirmed but its TOOL did not transfer (per-switch-
  case); its HEADLINE ("the deliverable is the map, not a match") is refuted for non-dispatchers --
  §69 was derived from a 359-call dispatcher with no sibling.
- TOOL SUPERSESSION: .run/giants/b2_mask.py + b2_full.py = shape-agnostic masked sequence aligner
  (structural AND byte numbers). Replaces s18_regions_comparator.py for all giants.
- FAMILY: func_8017CD9C + func_8017E778 are the same 3,338-ins fn with only 3 light-descriptor
  symbols changed -> one crack templates x3.
- MY OWN PROFILING ERROR, recorded (R14/R35): I briefed "no switch" from a sltiu jump-table grep; the
  switch is a COMPARISON TREE (23 slti). A jtbl grep is not a switch detector.
2026-07-25 01:18:00 -06:00
Drew T ba35785ec4 feat(phase-29): bank func_801777BC (59 ins) x138 — the giv-init base-register lever (§70)
- MATCH (59 ins), real-TU verified by the agent before handing back (cc1 rc=0, 59/59, 0 diffs).
- Propagated x138 with ZERO exclusions -> confirms the ×3 cap on func_80174CB0 was purely the
  carried-extern collision: a body with no externs propagates clean.
- R22 clean-fleet 140/140, 0 failed. dedup-check 1884 validated / 0 failed, C1 coverage complete.
- FLEET CROSSES 80.0% instr-weighted (10,509,526 / 13,141,652); fn-count 89.02%; distinct 67.7%.
- THE LEVER (cookbook §70): residual was ONE instruction, addiu $t0,$t1,0xC vs $t0,$a0,0xC -- a giv
  based on a copy of the param. Reading gcc-2.7.2 loop.c/cse.c proved the natural form can never
  emit the target: cse.c:make_regs_eqv makes the copy canonical (it out-lives a0) and
  loop.c:update_reg_last_use won't extend a0's last-use (giv-init UID >= max_uid_for_loop). Fix:
  walk the PARAMETER itself, so record_initial sees the biv init as hard reg (reg:SI 4),
  valid_initial_value_p accepts it (precondition: no calls), and emit_iv_add_mult bases the giv on
  $a0 -- yielding both required instructions free.
- META: this compiler-source reasoning was done by an ORDINARY Opus 5 drafting agent, unprompted --
  the tier Phase 23 reserved for Fable5. One data point, recorded as such; the cheap action is to
  give routine drafting agents the gcc source path.
2026-07-25 00:49:04 -06:00
Drew T c31b1ad0b3 docs(phase-29): cookbook §70 — the giv-init base register lever (walk the param, not a copy) 2026-07-25 00:39:58 -06:00
Drew T a8872fa169 docs(phase-29): cookbook §69 — behemoth recon (func_80183814, 5122 ins): map it, don't draft it
First attempt on the game's largest unmatched function. No match (never the goal); the deliverable
is the map, and every claim is byte-verified against the target .s.

- STRUCTURE: an actor state machine, not a straight-line giant. 21-case switch via jtbl_801F4CE4
  (sltiu 0x15); 359 jals to only 37 DISTINCT callees (verified); 48-ins preamble + 19-ins shared tail.
- THE FINDING: it decomposes into repeated templates, not 5122 unique instructions —
  35 instances of one "spawn-effect" packet (~1400 ins, crack one -> 34 free),
  7 "wait/countdown" (already reproduced at 0 skeleton diffs), 12 "HUD/text",
  plus twin cases (0≈3, 1≈4). Only 3 cross-jump edges couple anything.
- TWO GENERAL LAWS FOR GIANTS, measured: (1) register pressure is GLOBAL, so a partial draft gets
  10 callee-saved regs instead of 8 and a matching PREFIX is structurally unavailable — write all
  cases coarsely first, then refine; (2) match_one's global number is meaningless on a partial giant
  (666 vs 5122) — measure REGION-ALIGNED instead.
- NEW REUSABLE TOOL: .run/giants/s18_regions_comparator.py (region-aligned skeleton comparator, works
  on any giant). Caveat travels with it: masks register numbers + jal targets, so it proves STRUCTURE,
  never closeness; finish on the whole-binary gate (G3/P9).
- 2 idioms cracked in passing (the D_x[t+K] constant-fold needing a separate index statement; the
  (s16)*(u16*)p + /455 magic-0x90090091 form).
- VERDICT: tractable but a ~2000-line WRITE, not a hard puzzle — no scheduler wall, no unsteerable
  regalloc. Recipe for the next attempt recorded.
- artifacts preserved under the tracked .run/giants/ path (.gitignore now allowlists *.py there).
2026-07-25 00:33:06 -06:00
Drew T e112eff601 fix(phase-29): find_site — a comment-only line halted the extern scan (§68); func_80174CB0 x1 -> x3
TWO mislabels in one tool, both found by making it print what the compiler actually said.

1) compiles_standalone() returned a bare False and the caller filed EVERY failure under
   "overlay-local TYPE (the real cap)". The dominant real cause is undeclared FILE-SCOPE EXTERNS.
   Now returns (ok, stderr) and the skip is classified by actual cc1 output.
2) find_site()'s backward walk over "preceding contiguous externs" skipped BLANK lines but not
   COMMENT-ONLY lines, so a full-line /* ---- */ between two extern groups dropped every extern
   above it. Comment lines are now skipped like blanks and filtered out of the emitted body so
   make_macro never meets a `//`.

RESULT, measured honestly: func_80174CB0 went from "not self-contained" to a 138-member PLAN, but
--recover banked only x3 (ov_SC07_006/007/011); 135 overlays excluded. Those exclusions are NOT
byte divergence (all 138 share h_exact) -- they are the CARRIED EXTERNS colliding with each target
overlay's own decls. The carry is necessary but not sufficient: it must reconcile per-target-TU
(cdecl.compatible(), the shape reconcile_tu already uses). Spec updated in CURRENT_PHASE.md.

- R22 clean-fleet 140/140, 0 failed. dedup-check 1883 validated / 0 failed, C1 coverage complete.
- fleet instr 79.9% (10,501,384 / 13,141,652); +246 ins from the x3.
- WHY THIS MATTERS beyond the numbers: the Phase-21 backlog already prescribed "macro-extern-
  injection frees them x134 (~+0.3%)" and it was never built, because the mislabel told every later
  session these were the known-hard type wall. A wrong diagnostic label cost ~4 phases.
- cookbook §68. NOTE the exclusion message is ALSO mislabelled ("byte-diverge / irreconcilable"
  conflates differing bytes with a non-compiling instantiation) -- logged to fix.
2026-07-25 00:17:08 -06:00
Drew T e6053357b8 docs(phase-29): cookbook §68 — the comment-halted extern scan + the mislabel that hid it for 4 phases 2026-07-25 00:00:41 -06:00
Drew T af59d8a630 feat(phase-29): bank func_80174CB0 (123 ins) — the §65g verdict was a wrong SIGNATURE
SESSION-17 filed this as §65g-class: "not 'run one more tool', but 'needs a transform that does
not exist yet'". Refuted. It needed the correct self-declaration.

- The TU expands DEFINE_func_80174C80() carrying `extern s32 func_80174CB0(s32, s32);`, while all
  ~100 prior drafts defined `void func_80174CB0(s32, s16)` — matches perfectly STANDALONE, dies in
  the real TU with `conflicting types`. Defining it `s32 (s32, s32)` and recovering param_2's
  s16-ness with an explicit (s16) cast at the func_80012558 use site is byte-identical.
- Drafted by an isolated agent (Opus 5 @ High, 65k tok) pointed at the NAMED blocker with the
  canonical callee sigs supplied — not asked to re-derive the C. It self-verified through the real
  cpp->cc1->maspsx->as chain (cc1 rc=0, 123/123 ins, 0 diffs) before reporting, so the bank was
  first-try clean.
- make check BINARY=ov_SC07_006 BYTE-IDENTICAL (7ca772be); R22 clean-fleet 140/140, 0 failed.
- Propagation ×138 follows as a separate targeted step (§55b: bank -> commit -> dedup_propagate --addr).
- FOLLOW-UP LOGGED: the recovery ladder also relaxed `extern s32 func_80174CB0(s32,s32)` -> `()` in
  src/shared/engine_core.h (+2 overlay files), escalating a binary-local bank to FLEET tier. The
  banked def AGREES with the original prototype, so that edit looks unnecessary — to be tested.
2026-07-24 23:43:15 -06:00
Drew T 16089c7659 docs(phase-29): wave batch 1 — sig-targeted drafting works (MATCH in 55s/32k); blocker walked to the §30#2 macro-widen 2026-07-24 23:38:41 -06:00
Drew T c937f494c6 docs(phase-29): qualify §66d-4 — profile diversity is a lottery ticket (0-for-1 on func_80140958) 2026-07-24 22:06:56 -06:00