Files
BFM-decomp/docs/family-endgame-megaplan.md
T
Drew T 6344a2c424 docs(roadmap): adopt the Road-to-100 endgame roadmap (Phase 27+) — supersedes the family-endgame megaplan
- docs/roadmap-to-100.md: the adopted P27-P32 roadmap to game-code TRUE 100% + public flip +
  Gen2 exit. Contract (Drew 2026-07-15): true 100% (walls re-attacked until they fall), PsyQ
  LINKED = complete (libs-from-source = far-future note), flip AT 100% (standing per-phase
  velocity checkpoint keeps the timing falsifiable), Fable5 window ~7/19 (P27 discovery
  sprint runs FIRST). Measured baseline from committed post-audit artifacts only; residue
  buckets B1-B12 incl. the never-probed 0x8017BEBC IMM family (~106k ins) and the
  main/resident second-oracle gap; foreseen-tooling table (member_adapt, gate farm, fleet
  Ghidra-C prefetch, diff_regions, cc1_probe, assert-partition); authority rules (docs-layer
  advisory; every PhaseEnd carries a 'Roadmap delta' line; constitution + latest PhaseEnd win)
- docs/decision-log.md (R31): the adoption entry — Drew's four contract decisions, the
  flip-timing tension + checkpoint, the 16-defect red-team pass that purged numbers carried
  past their invalidation events (R14 at planning scale)
- docs/family-endgame-megaplan.md: SUPERSEDED banner (content preserved; h_seq reframe
  survives, the templating thesis is byte-proven spent per PhaseEnd_Phase26)
- memory: roadmap-to-100 pointer added; structural-family-mechanical-remap corrected with the
  Phase-26 probe results (outside the repo tree)
- Phase 27 NOT started (fresh session, plan mode, per the constitution)
2026-07-15 15:49:45 -06:00

17 KiB
Raw Blame History

BFM Family-Endgame Megaplan — draft for the Fable5 plan-mode session (2026-07-11)

⚠️ SUPERSEDED (2026-07-15) by docs/roadmap-to-100.md — the Road-to-100 endgame roadmap adopted at the Phase-27 planning gate (see docs/decision-log.md, 2026-07-15). The h_seq reframe in this file survives (it produced Phase 26); its scan-derived numbers and the "crack ~986 exemplars → template ×120" thesis are byte-proven spent (PhaseEnd_Phase26: 3 gate probes, 0%). Kept intact for the record — do not plan from it.

Purpose. Input package for a fresh plan-mode, Fable5Max session to build the "finish the decomp this weekend" plan. This file holds every byte-verified finding from the 2026-07-11 session. The headline: the "87% unique tail" was a measurement artifact — most of the remaining code is templatable per-location families we can crack once and stamp ×~120. Read this end-to-end, then analyze the data yourself and build the finish-plan. Ordering everywhere = most byte-weight value → least.

Effort/model discipline (R26/R27): plan-mode = Max/Fable5 for the plan itself. Execution is mixed: Ultracode for breadth waves, Fable5 ONLY for hard family cores (discovery), cheap-Opus for applying banked idioms, mechanical/permuter for the rest. STOP + prompt Drew for every effort/model toggle — Claude cannot set them itself. Fable5 is GONE after ~2026-07-13 — spend it only on what nothing else can crack.


1. THE HEADLINE FINDING (byte-verified this session) — the tail is families, not unique code

We measure progress three ways (tools/progress.py --weighted): fn-count ~74.6% (×134-inflated), instr-weighted 58.2% (decomp.dev-display, shipped .text), distinct-code 30.3% (each unique fn once — the honest RE truth).

The distinct-code "70% remaining" looked like a 36,190-function unique-shape long grind when grouped by our strict h_norm fingerprint (87.3% of remaining distinct-ins were reach-1). That was an artifact of h_norm being too strict (it keeps normalized immediates, so per-location constant differences fragment one function into 121 "unique" functions).

Re-clustered by the looser h_seq (mnemonic-skeleton only — same instruction sequence, ignoring registers/operands/immediates):

  • 90% of the "unique tail" collapses: 32,432 of 36,190 "unique" fns (2.36M of 2.59M ins) share an h_seq skeleton with siblings → 754 families; 30,117 fns / 2.2M ins (85% of the tail's byte-weight) are per-location-family-shaped (many members, few addresses, ~120 overlays).
  • Substantial families (nins≥80): 986 families / 1.88M ins; the top-20 = 52% of that byte-weight; 20/20 are per-location-shaped (1 distinct address across ~120 overlays).
  • Templatability CONFIRMED (sig spot-check): top families are the SAME function per location — identical nbytes, ncalls, and call sequence across all members — differing only in per-location immediates/data:
    • 0x80178d40 — 890 ins × 121 members, #addr=1, nbytes 3560, ncalls 75 (identical across all).
    • 0x8015ae2c — 562 ins × 121, #addr=1, identical structure.
    • 0x8017ca80 — 952 ins × 100, identical structure but varying address per overlay (cross-address variant).

Conclusion: the endgame is ~986 substantial families → crack ONE exemplar each → template ×~120 (plus the tiny/low-value families and the genuine unique residue). This is the SAME leverage as the reach-134 shared engine we already templated — h_norm just hid it. This is what makes "finish this weekend" credible.

Caveats to carry into the plan (be rigorous — R14)

  1. h_seq families differ in immediates, so they are NOT free dedup (unlike reach-134 h_exact). They are templates: one C body + per-member immediate+symbol substitution + byte-gate. Needs a family_remap extension (see §3, task T2).
  2. The two biggest families by count are tiny (15 ins × 6,385 and × 5,309) — low byte-weight, and a 15-mnemonic skeleton risks coincidental h_seq collisions. Filter by size; the value is in the substantial (nins≥80) families. Fable5 should sanity-check whether the mega-tiny-families are one real pattern or lumped noise (probably the cheap-tier's ≤15-ins domain already — Phase 23 saturated ≤15 for $0).
  3. Per-family crack difficulty is UNKNOWN until drafted. An 890-ins exemplar may be cheap-Opus (clean structure) or a hard Fable5 class. Triage cheap-first; only walls → Fable5.
  4. h_seq still fragments families that differ by a few INSTRUCTIONS (an extra branch, an inlined helper) — those need a fuzzy pass (MinHash/edit-distance) or Ghidra-C clustering (§5). The h_seq pass is the first, biggest, cheapest cut; the fuzzy pass is the follow-on.

Reproduce / extend the survey (committed-reproducible)

  • Sigs: .run/sig.ov_*.jsonl (134) + .run/sig.resident_image.jsonl — regen make sig-overlays. Each record: {addr,name,src,nins,nbytes,ncalls,h_exact,h_norm,h_seq,calls}. h_seq is the family key here.
  • The survey logic (ran this session; fold into a committed tools/family_hseq.py): group tail (h_norm reach-1) by h_seq; rank families by members × nins; per-family #distinct addr/#overlays (per-location test); templatability = identical nbytes/ncalls/call-sequence across members.

2. THE PLAN SHAPE (most-value → least; validate before scaling — Phase 15/16 lesson)

Step A (cheap/mechanical, NO Fable5) — build the real leverageable-family map + Fable5-necessary list. This is the measured foundation the finish-plan is built on. Do FIRST.

  1. tools/family_hseq.py — commit the survey: h_seq re-cluster the whole remaining frontier (not just the h_norm-reach-1 tail — also the reach 2–10 and any h_norm families), rank by byte-weight, tag per-location (same-addr) vs cross-address vs scattered, filter tiny/coincidental. Output the ranked family map + per-family exemplar (prefer an ov_SC01_077 member for drafting; else the lowest-addr member).
  2. Templatability validation on the top ~30 substantial families: confirm members are mechanically substitutable (identical structure, diffs = immediates+symbols). The 3 spot-checked pass; validate the rest.
  3. Cheap-Opus triage of each top family's EXEMPLAR (draft via the existing pipeline + match_one/rtu_match closeness + residual class). Output: which exemplars cheap-Opus/permuter can crack vs which are Fable5- necessary (the hard cores). THIS is the "know immediately what's Fable-necessary" list, over the whole tail.

Step B (Fable5, the 2-day window; parallel-isolated, distill-between) — crack the hard family CORES.

  • Highest leverage = the hardest EXEMPLAR of the biggest families (crack once → template ×~120). The 890/952/562 cores are the real "monsters worth Fable5" — NOT the unique behemoths (§4).
  • Also the carried Fable5 queue from this session (§6): the 2 permanent walls + the 3 walled reach-134 giants.
  • Each crack → distill the idiom into cookbook §31/§45 (R30) → cheap-Opus applies it to sibling family cores.

Step C (cheap-Opus + mechanical, PARALLEL with B and continues AFTER Fable5 is gone) — the harvest.

  • Extend family_remap to substitute immediates (task T2) + a cross-address variant.
  • Per validated family: crack exemplar (Step A/B) → template ×~120 → whole-binary byte-gate → batched R22 → commit.
  • The ≤180-ins draftable frontier + the 5.9 MB matched-free propagation (exemplars already matched, just propagate) run here too. Most of the headline % comes from this mechanical harvest.

Step D (deferred / lowest ROI) — the genuine unique residue + the behemoths. After the families collapse, whatever's left that's truly reach-1-by-h_seq (and the 5 unique ≥1000-ins behemoths, §4) is the real long grind — cheap-Opus/hand/decomp.me, post-Fable5, or never. Do LAST.


3. TOOLING TO BUILD (execution tasks — these are IN the plan, not prerequisites)

  • T1 tools/family_hseq.py — the committed h_seq family survey + ranked map (§2 Step A).
  • T2 family_remap immediate-substitution extension — the current family_remap.py substitutes SYMBOLS positionally (reloc-masked). h_seq families ALSO differ in immediates, so extend it to substitute per-member immediates too (positionally paired from the two disassemblies, like the reloc pairing). Plus a cross-address variant for families at different vram per overlay (0x8017ca80 class). Whole-binary byte-gate is the sole arbiter (a wrong substitution fails+reverts).
  • T3 family-aware family_sweep — feed it the h_seq grouping (not just h_norm) so one exemplar templates the whole h_seq family. Reuse the proven two-phase stage-then-gate structure.
  • Existing tools that carry over: family_remap.py, family_sweep.py (--only, --edit-remap, --reconcile), harvest_verify.py (the gate), canon_sig_reconcile.py, rtu_match.py (real-TU-faithful closeness), masked_diff.py/masked_scorer.py (permuter scorer), permuter_ils.py, dedup_propagate.py.

4. FABLE5 TARGETING — the corrected doctrine (data-backed this session)

Fable5's deliverable is PERMANENT IDIOMS, not matched functions. Everything it discovers (e.g. this session's §45 levers) is banked into the codegen map and cheap-Opus wields it forever, including after Fable5 is gone. So the 2 days should MAXIMIZE hard-class DISCOVERY on high-leverage targets.

RIGHT Fable5 targets (highest leverage):

  • Hard EXEMPLARS of the biggest per-location families (890/952/562… ins cores) — crack once → template ×~120 AND bank the idiom. This is the top of the queue now.
  • The 2 permanent walls (each a class-defining idiom): func_801412A8 (198×134, S11 LUID×alloc permutation), func_80178004 (165×134, currently matched only WITH pins → needs a pin-free lever for ×134).
  • The 3 walled reach-134 giants (recon done this session — pin-free seeds + gdb-oracle specs, §6).

WRONG Fable5 targets (worst ROI — do NOT burn the window here):

  • The 5 unique ≥1000-ins behemoths (0x8017bf14 4763, 0x8017d2dc 1586, 0x8017c954 1194, 0x8017c730 1061 ×2): all reach-1 unique — ×1, non-transferable idiom, enormous token cost, might not even close. Cracking all 5 ≈ 0.3% of distinct-code. Explicitly deferred to Step D / post-Fable5.

HOW to run Fable5 (proven this session): parallel-isolated agents, NEVER one batched agent — a giant crack is a long tool-heavy grind (the flagship: 125 tool-calls / 506k tokens) and context accumulation makes one shared-context agent ~N× more expensive; the §31 map already amortizes the compiler-source read, so isolation is cheaper AND higher-quality. Crack one exemplar per distinct class → distill to §45 → cheap-Opus mops up transfers (the flywheel). Agent(model: fable); ~375k tok/giant.


5. THE FUZZY / GHIDRA-C FOLLOW-ON (for families h_seq still misses)

h_seq requires an EXACT mnemonic sequence, so it fragments families that differ by a few instructions. To catch those (Drew's "convert to C to find groups" idea, done right):

  • Fuzzy asm clustering (cheaper, do first): MinHash / edit-distance / longest-common-subsequence on the normalized instruction stream → cluster near-duplicates that differ by a few instructions. Extends h_seq.
  • Ghidra-C structural clustering (heavier, complementary): batch-decompile remaining fns (headless DecompileFunctions.java — already exists) to READABLE (not compiling) C, cluster by C-AST shape → catch semantic families with divergent asm (different scheduling/inlining). Also good for humans naming/understanding families (Gen2 quality).
  • DO NOT chase a "compiling C draft tool for all remaining funcs" — that's the Phase-16 struct wall (m2c/ Ghidra emit actor->unkXXX that won't compile without per-function struct types), and it is NOT needed: family detection runs on asm, and byte-matching reads access widths off the asm opcode, not a struct def.

6. CARRIED CONTEXT — this session's other findings (fold into the plan, most-value-first)

Committed this session (commit:0515)

  • 🎯 FLAGSHIP func_80133CD4 (399 ins) CRACKED ×134 (Fable5 gdb-on-cc1) — the ~22-phase "unsteerable whole-function register permutation" retired. Fleet instr-weighted 57.7→58.2%, distinct-code 29.3→30.3%. Idiom → cookbook §45 (READ IT — it's the template for the family-core cracks): (a) merged-accumulator variables break a "whole-function permutation" (K8 no-coalescing → one reused C var); (b) the 1-death local-alloc in-out-asm (flow.c:2511, pin-free/×134-safe); (c) offset-0 /s store; (d) goto-shared-return. Draft .run/giants/func_80133CD4.fable.c, report .fable.md, gdb oracle/dumps .run/giants/fable_cd4/.

Deferred → Fable5 queue (recon DONE — pin-free seeds + gdb-oracle escalation specs)

  • func_8014D820 (304×134): flywheel → LCS 226 pin-free; RC-6 sched1 delay-slot (sched.c:3199 param-pin + ratan2-delay-slot tie-break). .run/giants/func_8014D820.opus.{c,md}.
  • func_8016CBC0 (209×134): flywheel → close=153 pin-free (beat the 155-pinned ×1); qty-birth/allocno-priority lock. .run/giants/func_8016CBC0.opus.{c,md}.
  • func_801670E4 (279×134): flywheel → close=23 pin-free (block0 fence-lock 6 + block2 S3-priority 17); permuter re-tried this session, stuck at masked-22 (intrinsic). .run/giants/func_801670E4.opus.{c,md}.
  • 2 permanent walls: func_801412A8 (198×134, S11), func_80178004 (165×134, pin-free needed).
  • Flywheel reusable sub-findings to fold into §45 when these crack: merge-lever has a mirror split-law for scalars; a new input-only-anchor load-early+tie dial; §44-Lever-5 "i=0/p-hoist" DOWNGRADED (cracked 42→0, R14 — was mis-tagged intrinsic). Detail in the .opus.md files.

F-band permuter batch (this session — all WALLED, no banks; → Fable5 or fuzzy tail)

  • func_801670E4 masked-22, func_8012E364 masked-31 (backlog "c=4" is stale — needs a fresh draft), func_801549F8 masked-2 (2 diffs from done, but a jump-table so a .text-0 still needs the rodata jtbl verified via §8 workflow). The free permuter tier is exhausted on these; they're Fable5 or fuzzy-tail work.

Strategic doctrine settled this session (Drew + data)

  • Metrics: report all three; distinct-code (30.3%) is the honest RE truth; instr-weighted (58.2%) is the decomp.dev-display; fn-count (74.6%) is ×134-inflated.
  • Fable5 = permanent idioms; parallel-isolated agents (never batched); distill-between; cheap-Opus mops up.
  • Reach distribution (this session): reach-134 shared engine 2.1% (mostly done), reach 2–133 10.6%, reach-1 87.3% by strict h_norm — but 90% of that reach-1 is h_seq families (the whole point of this plan).

7. FABLE5-EXAMINE-FURTHER LIST (put your compiler-internals + planning edge here, plan-mode session)

  1. Analyze the ranked h_seq family map (build it via T1 first) — pick the crack curriculum: which family cores first/second/third by (byte-weight × crackability). Ground it on Step-A cheap-Opus triage data, NOT predictions (Phase-25 lesson: an ungrounded Fable5 plan is speculation).
  2. Are the two tiny mega-families (15-ins × 6,385 / × 5,309) one real pattern or coincidental h_seq collision? Decide if they're worth a template or just cheap-tier fodder.
  3. The immediate-substitution + cross-address family_remap extension (T2) — design it; it's the mechanical lever that converts every family crack into ×~120.
  4. The hard family cores — the 890/952/562-ins exemplars: are they §45-class (merged-vars/1-death) or new classes? Each new class banked into §45 = cheap-Opus cracks the rest.
  5. The 2 permanent walls (func_801412A8 S11, func_80178004 pin-free) — the flagship proved "permanent" verdicts fall; these are class-defining.
  6. The fuzzy/Ghidra-C follow-on (§5) — scope how much extra byte-weight it recovers beyond h_seq.
  7. Honest finish math: with ~986 families × ~120 templating, what distinct-code % is reachable this weekend, and what's the genuine unique residue (Step D) that is NOT weekend-finishable? Set a realistic target.

8. GATE / DISCIPLINE (unchanged, non-negotiable)

  • Whole-binary byte-gate (harvest_verify) is the SOLE arbiter (G3/P9) — match_one/rtu_match/permuter scores are indicators, never banks. Re-verify every agent "MATCH" against the bytes (R14).
  • R22 clean-fleet every batch before commit: make clean + re-extract ALL 136 + make check-all → 136/136. (make clean wipes asm/, so re-extract all before check-all; don't run R22 while agents need asm/.)
  • ×134 pin-free rule (§42e): register __asm__("$N") pins SIGABRT sibling TUs → ×1 only. Family templating needs pin-free bodies (generic-constraint asm + GTE blocks are fine).
  • Commits: Claude commits locally, Drew pushes (R6); never stage the ghidra/ db.*.gbf churn (R23); no Co-Authored-By (R5). Update CURRENT_PHASE.md + capture findings during the session (R30/R31).