Files
BFM-decomp/tools/exemplar_miner.py
T
Drew T 8a1a1a0d79 feat(phase-27 T6): migrate difficulty + exemplar_miner off hand-lists/proxies (R33) — before T7
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.
2026-07-15 18:20:55 -06:00

160 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()