mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
209 lines
10 KiB
Python
209 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""tools/audit_frontier.py — do the independent "what's left" views agree with the corpus oracle?
|
|
|
|
WHY THIS EXISTS (Drew's MASTER_REMAINING proposal, P30 S39; docs/decision-log.md 2026-08-04)
|
|
============================================================================================
|
|
The project already derives a total / banked / remaining triple, and derives it correctly:
|
|
|
|
total = .run/sig.*.jsonl (sig_image over the ORIGINAL bytes, independent of splat)
|
|
banked = sig - stubs (the proven invariant: INCLUDE_ASM pastes the original asm,
|
|
so a function NOT wrapped in it is byte-exact)
|
|
remaining = corpus.stubs(binary) (derived from the filesystem, coverage-asserted, R32/R33)
|
|
|
|
So a hand-maintained MASTER_REMAINING file would be strictly worse: it can drift, and — as the
|
|
Phase-26 audit proved ~10 times over — it drifts SILENTLY and in the flattering direction (the
|
|
canonical case: `.run/fuel_manifest.json` recorded 130 live stubs when the truth was 30, hiding
|
|
91.6% of remaining gain, and nobody noticed BECAUSE A TARGET THAT IS NEVER NOMINATED PRODUCES
|
|
SILENCE, NOT AN ERROR). R33: derive, don't re-derive; the best outcome is a deleted scanner.
|
|
|
|
WHAT IS ACTUALLY MISSING, and what this file fixes
|
|
--------------------------------------------------
|
|
"What's left" is answered today by SIX artifacts — `corpus.stubs`, `docs/worklist.md`,
|
|
`docs/backlog.md`, `.run/family_hseq.json`, `.run/fuel_manifest.json`, `docs/progress.fleet.md`.
|
|
Each is individually derived. **None of them is ever checked against the others.** That is exactly
|
|
the gap that cost P30's T0 a hand-reconciliation (family_hseq said 29,961 remaining instances while
|
|
progress.py said 28,296 — an unexplained R32 gap that a human had to chase down).
|
|
|
|
This is the R34 move: not a better assertion inside one oracle, but a SECOND view that can DISAGREE,
|
|
with the disagreement made loud. `corpus.stubs` is the reference; every other view is compared to it.
|
|
|
|
WHAT IT DOES *NOT* CLAIM
|
|
------------------------
|
|
Agreement here does NOT mean the denominator is complete. The byte-gate and every view below are
|
|
blind to code that was never onboarded (R34/R36) — the 39 un-onboarded type-1 modules, and main's
|
|
missing independent boundary oracle (`sig_image` cannot sign a PS-X EXE), are tracked in
|
|
`docs/disc-completeness.md` and `docs/second-oracle.md`. This file reconciles the views we HAVE; it
|
|
cannot see what none of them can see. That limitation is printed in the report, deliberately.
|
|
|
|
Usage: tools/audit_frontier.py [--strict]
|
|
--strict : exit 1 on any unexplained disagreement (for wiring into tools-health later)
|
|
"""
|
|
import argparse
|
|
import collections
|
|
import glob
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.insert(0, os.path.join(REPO, "tools"))
|
|
import corpus # noqa: E402
|
|
import progress # noqa: E402
|
|
|
|
|
|
def reference():
|
|
"""THE reference view: open stubs per binary, from the coverage-asserted corpus oracle."""
|
|
per, missing = {}, []
|
|
for p in sorted(glob.glob(os.path.join(REPO, ".run/sig.ov_*.jsonl"))) + \
|
|
sorted(glob.glob(os.path.join(REPO, ".run/sig.md_*.jsonl"))) + \
|
|
[os.path.join(REPO, ".run/sig.resident.jsonl"),
|
|
os.path.join(REPO, ".run/sig.SLUS_007.26.jsonl")]:
|
|
if not os.path.exists(p):
|
|
continue
|
|
b = progress._sig_binary(p)
|
|
try:
|
|
per[b] = set(corpus.stubs(b))
|
|
except Exception as e: # R32: a refusal is DATA, not a shrug
|
|
missing.append((b, str(e).split("\n")[0][:90]))
|
|
return per, missing
|
|
|
|
|
|
def view_family_hseq(ref):
|
|
"""`.run/family_hseq.json` — the family map's own count of remaining instances."""
|
|
p = os.path.join(REPO, ".run/family_hseq.json")
|
|
if not os.path.exists(p):
|
|
return None
|
|
d = json.load(open(p))
|
|
claimed = (d.get("metrics") or {}).get("unmatched_instances")
|
|
# recount the map's members that the corpus still calls open
|
|
seen = 0
|
|
fams = d.get("families") or []
|
|
for f in (fams.values() if isinstance(fams, dict) else fams):
|
|
for m in (f.get("members") or []):
|
|
ov, addr = (m[0], m[1]) if isinstance(m, list) else (m.get("ov"), m.get("addr"))
|
|
if ov in ref and int(str(addr), 16) in ref[ov]:
|
|
seen += 1
|
|
return {"claimed_unmatched_instances": claimed, "members_still_open_per_corpus": seen}
|
|
|
|
|
|
def view_worklist(ref):
|
|
"""`.run/worklist.json` — the ranked decision spine."""
|
|
p = os.path.join(REPO, ".run/worklist.json")
|
|
if not os.path.exists(p):
|
|
return None
|
|
d = json.load(open(p))
|
|
rows = d.get("rows") or []
|
|
stale = sum(1 for r in rows
|
|
if r.get("binary") in ref and r.get("addr")
|
|
and int(str(r["addr"]), 16) not in ref[r["binary"]])
|
|
return {"claimed_n_stubs": d.get("n_stubs"), "rows": len(rows), "rows_already_banked": stale}
|
|
|
|
|
|
def view_backlog(ref):
|
|
"""`.run/backlog.jsonl` — the near-miss ledger (append-only; pruned at read)."""
|
|
p = os.path.join(REPO, ".run/backlog.jsonl")
|
|
if not os.path.exists(p):
|
|
return None
|
|
rows = [json.loads(l) for l in open(p) if l.strip()]
|
|
banked = 0
|
|
for r in rows:
|
|
nm, b = r.get("name"), r.get("binary") or "ov_SC01_077"
|
|
if nm and nm.lower().startswith("func_") and b in ref:
|
|
try:
|
|
if int(nm[5:], 16) not in ref[b]:
|
|
banked += 1
|
|
except ValueError:
|
|
pass
|
|
return {"rows": len(rows), "rows_already_banked": banked}
|
|
|
|
|
|
def view_fuel(ref):
|
|
"""`.run/fuel_manifest.json` — the target pool (the artifact whose decay motivated R33)."""
|
|
p = os.path.join(REPO, ".run/fuel_manifest.json")
|
|
if not os.path.exists(p):
|
|
return None
|
|
d = json.load(open(p))
|
|
tg = d.get("targets") or []
|
|
stale = 0
|
|
for t in tg:
|
|
b, a = t.get("binary") or t.get("source_overlay"), t.get("addr")
|
|
if b in ref and a:
|
|
try:
|
|
if int(str(a), 16) not in ref[b]:
|
|
stale += 1
|
|
except ValueError:
|
|
pass
|
|
return {"targets": len(tg), "targets_already_banked": stale}
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--strict", action="store_true",
|
|
help="exit 1 on any unexplained disagreement (for tools-health)")
|
|
a = ap.parse_args()
|
|
|
|
ref, missing = reference()
|
|
total_open = sum(len(v) for v in ref.values())
|
|
print(f"audit-frontier: reference = corpus.stubs over {len(ref)} binaries -> "
|
|
f"{total_open} open stubs")
|
|
if missing:
|
|
print(" !! the reference oracle REFUSED for some binaries (R32) — every count below is "
|
|
"unreliable until fixed:")
|
|
for b, e in missing:
|
|
print(f" {b}: {e}")
|
|
|
|
problems = []
|
|
for name, fn in (("family_hseq", view_family_hseq), ("worklist", view_worklist),
|
|
("backlog", view_backlog), ("fuel_manifest", view_fuel)):
|
|
v = fn(ref)
|
|
if v is None:
|
|
print(f" {name:14s} (absent — regenerate it, or it is simply not in use)")
|
|
continue
|
|
print(f" {name:14s} {v}")
|
|
for k, n in v.items():
|
|
if k.endswith("already_banked") and n:
|
|
problems.append(f"{name}: {n} row(s)/target(s) name a function the corpus says is "
|
|
f"BANKED — that view is stale; regenerate it")
|
|
# A view that PUBLISHES its own count must still agree with a recount against the corpus.
|
|
# This is the exact shape of the gap P30's T0 had to hand-reconcile (family_hseq 29,961 vs
|
|
# progress.py 28,296): both numbers were individually derived, neither was ever compared.
|
|
claimed = v.get("claimed_unmatched_instances")
|
|
recount = v.get("members_still_open_per_corpus")
|
|
if claimed is not None and recount is not None and claimed != recount:
|
|
problems.append(
|
|
f"{name}: publishes {claimed} unmatched instances but only {recount} of its members "
|
|
f"are still open per the corpus (delta {claimed - recount:+d}) — the map predates "
|
|
f"{abs(claimed - recount)} bank(s); regenerate with tools/family_hseq.py before "
|
|
f"ranking or scoping ANY work off it")
|
|
if missing:
|
|
problems.append(f"corpus.stubs refused for {len(missing)} binaries")
|
|
|
|
print()
|
|
if problems:
|
|
print("DISAGREEMENTS (each one is a view that would mis-scope real work):")
|
|
for p in problems:
|
|
print(f" - {p}")
|
|
else:
|
|
print("All views agree with the corpus oracle.")
|
|
print()
|
|
# The denominator facts, DERIVED (P33 A5, S87): this used to be a typed caveat naming "the 39 un-onboarded
|
|
# modules" and "main's missing oracle" — both resolved in P30/P31 while the text stayed. R51.
|
|
linked = {f"src/{seg}.c" for seg in progress._main_linked_segs_from_makefile()}
|
|
# ref holds ADDRESSES (set(corpus.stubs(b))); the Stub records with their TU path come from the dict itself
|
|
n_linked_stubs = sum(1 for st in corpus.stubs("main").values() if st.path in linked) if "main" in ref else 0
|
|
n_total = total_open
|
|
print(f"DENOMINATOR (R34/R36): agreement above says nothing about code never onboarded, so:")
|
|
print(f" stubs {n_total} total, {n_linked_stubs} inside main's LINKED (Sony-object) regions by design, "
|
|
f"{n_total - n_linked_stubs} game-code stubs open")
|
|
ledger = os.path.join(REPO, "docs/disc-ledger.md")
|
|
m = re.search(r"## Code payloads — (\d+) UNCLAIMED of (\d+)", open(ledger, encoding="utf-8").read()) if os.path.exists(ledger) else None
|
|
print(f" disc: {m.group(1) + ' UNCLAIMED of ' + m.group(2) + ' code payloads (docs/disc-ledger.md, from make audit-disc)' if m else 'docs/disc-ledger.md ABSENT — run make audit-disc'}")
|
|
sig_main = os.path.join(REPO, ".run/sig.main.jsonl")
|
|
print(f" main's build-derived boundary sig: {'present' if os.path.exists(sig_main) else 'ABSENT — run make sig-main'} (.run/sig.main.jsonl; the Ghidra sig is the second, independent oracle)")
|
|
return 1 if (problems and a.strict) else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|