mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 14:02:04 -04:00
8a1a1a0d79
The 26-A audit named difficulty's 136-entry BINARIES dict as the exact root cause corpus.py:9 describes (a hand-maintained allowlist over a filesystem that already answers the question), and exemplar_miner's registered_addrs() as a ~60%-wrong proxy for "is this still work?". T7 onboards new overlays, so these are migrated FIRST or the new binaries silently miss make report. - exemplar_miner.py: "still a residual?" now = corpus.stubs(source) membership (the INCLUDE_ASM invariant, R33), not dp.registered_addrs() (config/dedup.us.yaml — a matched-but-unregistered fn, e.g. banked inline or matched-but-local, stayed wrongly in the residual pool). - difficulty.py: the 136-entry hand-dict -> cfg_for(alias), deriving the mechanical layout (src/<a> + asm/<a>/nonmatchings; main/resident the two specials). Validated against the tree (src/<a> must exist -> a typo is a clean error, R32), not a hand-list. A newly-onboarded overlay now needs zero difficulty registration. - new_overlay.sh: DROPPED difficulty from the sentinel-insertion set (T6 made it obsolete; leaving it would insert a dead dict entry into a file that no longer has a dict). The other 3 tools (diff_settings/progress/dup_report) keep their hand-lists — migrated one-at-a-time, byte-gated, per the audit's cadence; NOT dup_report.BINARIES, which corpus depends on as the binary list. VERIFIED: - difficulty derivation is BYTE-EXACT vs the old dict for all 136 aliases (0 mismatches), and old-tool vs new-tool output is byte-identical (.md AND .csv) on the same tree — the diff vs the committed docs was pure staleness (committed 2026-06-20, tree at 2026-07-15), NOT my change (R14). - unknown alias -> clean error, not silent-empty output. - exemplar_miner runs -> 223 residual stubs (corrected; it's a manual tool, not in make report). - new_overlay.sh: bash + embedded-python both parse; difficulty absent from the insertion set.
160 lines
9.1 KiB
Python
160 lines
9.1 KiB
Python
#!/usr/bin/env python3
|
||
"""exemplar_miner.py — route every residual stub to its lever, rank the reach-134 work (Phase 20 T2).
|
||
|
||
Consumes the residual census (tools/wall_taxonomy.json: per-stub bucket/nins/mismatch for the canonical
|
||
source overlay ov_SC01_077) and computes each stub's FLEET REACH (how many of the 134 overlays carry a
|
||
byte-identical copy, the ×N propagation leverage) the same way dedup_propagate does (h_exact over the
|
||
per-overlay sigs). It then:
|
||
- routes each bucket to a lever (WAVE / PINS / STRUCT / STUB) — ADVISORY; the byte-gate is the sole
|
||
arbiter (G3/P9), this is a worklist, not a correctness claim,
|
||
- emits the reach-134 WAVE-target list (T6 fuel; ranked reach desc, nins asc),
|
||
- surfaces the PINS/permuter-class reach-134 near-misses (the class-crack candidate pool for T3),
|
||
- tallies the honest STUB residual (ARITY_WALL loose-typing + NONFAITHFUL) and the STRUCT-context band.
|
||
|
||
Note: wall_taxonomy's `mismatch` is the M2C-DRAFT mismatch (scaffold quality), NOT the hand-match floor —
|
||
a STRUCTURAL_MISS with a high mismatch can still hand-match to a 1-instruction residual (e.g. the loop-guard
|
||
exemplar func_8012C2D0). So the buckets size the POOLS; the cleanest class-isolating exemplar is a Max pick.
|
||
|
||
Outputs: docs/exemplar_curriculum.md (human worklist) + .run/exemplar_routing.json (machine, for the wave gen).
|
||
Usage: tools/exemplar_miner.py [--source ov_SC01_077] [--census .run/wall_taxonomy.json] [--top 60]
|
||
"""
|
||
import argparse, json, os, sys, pathlib, datetime
|
||
|
||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(ROOT / "tools"))
|
||
import dedup_propagate as dp # load_sig, onboarded_overlays (reach == its h_exact computation)
|
||
import corpus # the derived stub oracle (Phase 26-A) — "still work?" = "still INCLUDE_ASM"
|
||
|
||
# bucket -> lever. ADVISORY routing (the byte-gate decides truth). Grouped by how we'd actually attack it:
|
||
# WAVE = m2c/hand-draftable by the Ultracode swarm + §17 toolkit + canon-first gate + fix_arity_callers
|
||
# STRUCT = m2c can't draft until a type is known (fn-ptr tables, jump tables, struct derefs)
|
||
# PINS = regalloc/schedule near-miss -> register-pins hand-match (permuter grind skipped this phase);
|
||
# the small-mismatch reach-134 ones are the class-crack candidate pool (T3)
|
||
# STUB = fundamental (loose-typing ARITY_WALL) or non-faithful (GTE/handwritten) -> honest INCLUDE_ASM
|
||
LEVER = {
|
||
"STRUCTURAL_MISS": "WAVE", "SIG_FIXABLE_KR": "WAVE", "LEAF_READY": "WAVE",
|
||
"MCOMPILE_arg-arity": "WAVE", "MCOMPILE_undeclared-other": "WAVE", "MCOMPILE_stack-var": "WAVE",
|
||
"MCOMPILE_m2c-incomplete-arg": "WAVE", "DATA_CONFLICT": "WAVE", "OTHER_CONFLICT": "WAVE",
|
||
"MCOMPILE_other": "WAVE", "VOID_VALUE_MISUSE": "WAVE",
|
||
"MCOMPILE_fnptr-call": "STRUCT", "M2C_DECOMP_FAIL": "STRUCT", "MCOMPILE_bad-deref": "STRUCT",
|
||
"MCOMPILE_bad-switch": "STRUCT",
|
||
"PERMUTER_CLASS": "PINS",
|
||
"ARITY_WALL": "STUB", "NONFAITHFUL_DEFER": "STUB", "NO_ASM": "STUB", "M2C_EMPTY": "STUB",
|
||
}
|
||
|
||
# the named class-crack exemplars carried from Phase 18/19 (T3 targets); the m2c bucket understates them.
|
||
CLASS_EXEMPLARS = {
|
||
"8012C2D0": "loop-guard operand-order (loop.c get_condition) — T3b",
|
||
"8014F2E0": "store-vs-load scheduling tie-break (sched.c) — T3c",
|
||
"8013C360": "-O0 %lo-folding cluster (address-mode split) — T3a",
|
||
}
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--source", default="ov_SC01_077")
|
||
ap.add_argument("--census", default=".run/wall_taxonomy.json")
|
||
ap.add_argument("--top", type=int, default=60)
|
||
a = ap.parse_args()
|
||
|
||
recs = json.load(open(ROOT / a.census))
|
||
pool = dp.onboarded_overlays()
|
||
sigs = {ov: dp.load_sig(ov) for ov in pool}
|
||
# "Is this census entry still work?" — answered by the INVARIANT (R33), not the dedup registry.
|
||
# registered_addrs() (config/dedup.us.yaml) is a PROXY: a function matched-but-not-registered
|
||
# (banked inline, or matched-but-local) is absent from it, so the old filter kept matched
|
||
# functions in the residual pool (the audit measured ~60% wrong). A function is unmatched iff it
|
||
# is still an INCLUDE_ASM stub in the source — which corpus.stubs derives from the tree.
|
||
live_stub_addrs = set(corpus.stubs(a.source)) # {addr_int: Stub} -> the set of addr ints
|
||
|
||
def reach(addr_int):
|
||
h = sigs[a.source].get(addr_int, {}).get("h_exact")
|
||
if not h:
|
||
return 0
|
||
return sum(1 for ov in pool if sigs[ov].get(addr_int, {}).get("h_exact") == h)
|
||
|
||
rows = []
|
||
for r in recs:
|
||
ai = int(r["addr"], 16)
|
||
if ai not in live_stub_addrs: # not a live INCLUDE_ASM stub -> already matched, not a residual
|
||
continue
|
||
lever = LEVER.get(r["bucket"], "WAVE")
|
||
rows.append({"addr": r["addr"].upper(), "nins": r["nins"], "mismatch": r.get("mismatch"),
|
||
"bucket": r["bucket"], "lever": lever, "reach": reach(ai)})
|
||
|
||
# machine output for the wave generator
|
||
os.makedirs(ROOT / ".run", exist_ok=True)
|
||
json.dump({x["addr"]: x for x in rows}, open(ROOT / ".run/exemplar_routing.json", "w"), indent=1)
|
||
|
||
# ---- human worklist ----
|
||
by_lever = {}
|
||
for x in rows:
|
||
by_lever.setdefault(x["lever"], []).append(x)
|
||
snap = datetime.datetime.fromtimestamp((ROOT / a.census).stat().st_mtime).strftime("%Y-%m-%d")
|
||
|
||
L = ["# BFM residual curriculum / router (generated by tools/exemplar_miner.py)",
|
||
f"# census: {a.census} (snapshot {snap}, {len(rows)} unregistered residual stubs in {a.source})",
|
||
"# reach = #overlays sharing the byte-identical copy (×N propagation leverage), computed from sigs.",
|
||
"# ROUTING IS ADVISORY — the per-overlay byte-gate is the sole arbiter (G3/P9). `mismatch` is the",
|
||
"# M2C-DRAFT mismatch (scaffold quality), NOT the hand-match floor.", "",
|
||
"## Routing summary (by lever, reach-weighted)", "",
|
||
"| lever | fns | reach-134 | Σ reach | what it is |", "|---|---|---|---|---|"]
|
||
desc = {"WAVE": "Ultracode swarm + §17 toolkit + canon-first gate (the yield engine, T6)",
|
||
"STRUCT": "m2c blocked on a type (fn-ptr/jump tables, struct deref) — needs type context",
|
||
"PINS": "regalloc/schedule near-miss — register-pin hand-match; class-crack pool (T3)",
|
||
"STUB": "fundamental loose-typing (ARITY_WALL) / non-faithful — honest INCLUDE_ASM (P9)"}
|
||
for lev in ["WAVE", "STRUCT", "PINS", "STUB"]:
|
||
g = by_lever.get(lev, [])
|
||
r134 = sum(1 for x in g if x["reach"] >= 134)
|
||
sreach = sum(x["reach"] for x in g)
|
||
L.append(f"| {lev} | {len(g)} | {r134} | {sreach} | {desc[lev]} |")
|
||
L.append("")
|
||
|
||
# WAVE pool, reach-134, ranked (T6 fuel)
|
||
wave134 = sorted([x for x in by_lever.get("WAVE", []) if x["reach"] >= 134],
|
||
key=lambda x: (-(x["reach"]), x["nins"] or 9999))
|
||
L += ["", f"## WAVE pool — reach-134, ranked (T6 fuel; {len(wave134)} fns, smallest first)", "",
|
||
"| addr | nins | bucket | reach |", "|---|---|---|---|"]
|
||
for x in wave134[:a.top]:
|
||
L.append(f"| {x['addr']} | {x['nins']} | {x['bucket']} | {x['reach']} |")
|
||
if len(wave134) > a.top:
|
||
L.append(f"| …+{len(wave134)-a.top} more | | | |")
|
||
|
||
# PINS / class-crack candidate pool, reach-134, smallest m2c-mismatch first
|
||
pins134 = sorted([x for x in by_lever.get("PINS", []) if x["reach"] >= 134],
|
||
key=lambda x: (x["mismatch"] if x["mismatch"] is not None else 999, x["nins"] or 9999))
|
||
L += ["", f"## PINS / permuter-class — reach-134 near-misses ({len(pins134)} fns; class-crack candidate pool)",
|
||
"", "| addr | nins | m2c-mismatch | reach |", "|---|---|---|---|"]
|
||
for x in pins134[:a.top]:
|
||
L.append(f"| {x['addr']} | {x['nins']} | {x['mismatch']} | {x['reach']} |")
|
||
if len(pins134) > a.top:
|
||
L.append(f"| …+{len(pins134)-a.top} more | | | |")
|
||
|
||
# the named T3 class exemplars + their census row + reach
|
||
rowmap = {x["addr"]: x for x in rows}
|
||
L += ["", "## Named class-crack exemplars (T3 — the Max spikes)", "",
|
||
"| addr | class | census bucket | nins | reach |", "|---|---|---|---|---|"]
|
||
for addr, note in CLASS_EXEMPLARS.items():
|
||
x = rowmap.get(addr)
|
||
if x:
|
||
L.append(f"| {addr} | {note} | {x['bucket']} | {x['nins']} | {x['reach']} |")
|
||
else:
|
||
L.append(f"| {addr} | {note} | (not a current stub — matched or split) | | |")
|
||
|
||
# STUB honesty tally
|
||
stub = by_lever.get("STUB", [])
|
||
from collections import Counter
|
||
sc = Counter(x["bucket"] for x in stub)
|
||
L += ["", "## STUB residual (honest, no lever — P9)", "",
|
||
f"{len(stub)} fns: " + ", ".join(f"{b}={n}" for b, n in sc.most_common()), ""]
|
||
|
||
open(ROOT / "docs/exemplar_curriculum.md", "w").write("\n".join(L) + "\n")
|
||
print(f"wrote docs/exemplar_curriculum.md + .run/exemplar_routing.json")
|
||
print(f" {len(rows)} residual stubs; WAVE {len(by_lever.get('WAVE',[]))} "
|
||
f"(reach-134 {len(wave134)}) | STRUCT {len(by_lever.get('STRUCT',[]))} | "
|
||
f"PINS {len(by_lever.get('PINS',[]))} (reach-134 {len(pins134)}) | STUB {len(stub)}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|