Files
BFM-decomp/tools
Drew T 6bc8bb6894 feat(phase-31): wave Q — 40 main functions banked; + fragment_check.py and bisect_slate.py
Wave Q (90 cards / 6,249 ins) was stopped mid-flight, then a repair-only pass recovered 12 of its
39 unfinished drafts (+579 ins, 51 -> 64 verified MATCH). Banking took two new tools, because the
gate failed three times on causes no C-level check can see.

tools/fragment_check.py (NEW) — the enclosing-function trap, both halves, in milliseconds:
  * a draft whose [addr, addr+4*nins) SUBSUMES another symbol. splat labels addresses that are not
    functions (a bare epilogue tail, a jump-only branch target, an interior label); convert the
    enclosing function and a neighbour's `jal .L80050F24` loses its target.
  * a draft that DEFINES another stub's symbol in inline asm. MoveImage's repair agent correctly
    worked out that a trailing epilogue belongs to MoveImage (splat attributes those bytes to the
    NEXT symbol) and emitted `.globl SYS_OBJ_8F4` itself -- right bytes, match_one passes, and the
    assembler then sees the symbol defined twice because the fragment still has its own .s.
  My first hypothesis was the subsumption case; the tool REFUTED it (clean) and the redefinition
  case was the real one. Recording that order, because I built the tool before verifying the
  premise -- R35 inverted.

tools/bisect_slate.py (NEW) — gate_main's built-in bisect ran THREE HOURS on a 45-draft slate and
printed nothing. Three defects: it never checked whether the BASELINE builds (if no subset can
pass, the search is doomed from step 1), it logs only when it isolates a single draft, and its
re-split rule degrades toward one rebuild per draft. The replacement runs the NULL CONTROL first
(the same control that refuted this phase's "main is link-blocked" misdiagnosis), logs every step,
and does a true binary search: it found the culprit (SYS_OBJ_1790) in 7 steps and 176 SECONDS.

Result: 40 banked, main stubs 1,803 -> 1,763.
2026-08-16 17:21:10 -06:00
..