Files
BFM-decomp/tools/bank_exemplar.py
T
Drew T 4f7c3b64a3 docs(phase-33): commit-map + citations resolved to the rewritten history (C4–C7 — the tip commit)
- docs/commit-map.tsv: 4,032 rows (ordinal of the ORIGINAL main -> rewritten hash, author/committer dates, subject);
  1 pruned row of zeros (ordinal 1712, "session archive update"); 0 old hashes asserted; ordinal 1 unchanged by the
  rewrite (byte-identical)
- resolve_tokens: 1,238 commit:NNNN tokens -> shortest-unique new hashes in 98 files (docs, phase-ends, logs, tool
  docstrings, 2 C comments, the A5 evidence logs); residue left as tokens: commit:1712 x4 (the pruned commit),
  commit:orphan-24 x2, commit:orphan-26, commit:orphan-35 (cited commits that exist in no lineage)
- the rewrite (C4): filter-repo 2.47.0 on a bare clone of the C2 tip, 311 s, exactly 1 pruned, main 4,032 -> 4,031;
  the pre-rewrite history is mirrored in the private archive repo and in the local bundle
- the proof (C5): verify_rewrite 4,031 pairs / 0 failures; absent_scan 0 offenders; gate_scan 0 offenders on the clone
- adoption (C6): 100 text files differ at the tip, 0 purge paths, 0 added/deleted; leftover refs dropped; no gc yet
- resolver skips tools/public_rewrite/ (its self-test fixtures are the token grammar, not citations); repo-local
  identity is the GitHub noreply address from here on; CURRENT_PHASE: C4–C7 logged, checkpoint -> NEXT = C8
2026-09-06 23:28:39 -06:00

84 lines
3.5 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
"""Bank a cracked EXEMPLAR ×1 (carve → splice → staged gate), mirroring jtbl_family_bank.bank()'s
stage ladder (raw → scoped → recovered → reconciled) but for the exemplar's own overlay (no remap).
Usage: bank_exemplar.py <ov> <func> <crack.c>
"""
import os, sys, subprocess, re
sys.path.insert(0, 'tools')
os.chdir('/home/musashi/bfm-decomp')
import jtbl_family_bank as fb
from scope_data_externs import fix as scope_data_fix
from canon_sig_reconcile import reconcile
OV, FUNC, CRACK = sys.argv[1], sys.argv[2], sys.argv[3]
def sh(c):
return subprocess.run(c, shell=True, capture_output=True, text=True)
dirty = sh("git status --porcelain -- config/ src/").stdout.strip()
assert not dirty, "tree dirty:\n" + dirty[:300]
keep = fb.region_files(OV)
print("== extract (committed config)")
assert sh(f"make --no-print-directory extract BINARY={OV}").returncode == 0
print("== carve")
r = sh(f"python3 tools/jtbl_carve.py {OV} --func {FUNC}")
if r.returncode and "NON-CONTIGUOUS" in (r.stdout + r.stderr):
print(" [lazy isolation]")
assert fb.isolate(OV, FUNC).returncode == 0, "isolate failed"
assert sh(f"make --no-print-directory extract BINARY={OV}").returncode == 0
r = sh(f"python3 tools/jtbl_carve.py {OV} --func {FUNC}")
assert r.returncode == 0, (r.stdout + r.stderr)[-300:]
print("== re-extract post-carve")
assert sh(f"make --no-print-directory extract BINARY={OV}").returncode == 0
cf = fb.stub_file(OV, FUNC)
print("== stub:", cf)
assert cf
orig = open(cf).read()
m = re.search(rf'INCLUDE_ASM\("[^"]*",\s*{FUNC}\);', orig)
assert m
body = open(CRACK).read()
scoped, moved = scope_data_fix(body, orig, m.start(), FUNC)
base = scoped if moved else body
# Fallback ladder (§19 — gate the RAW draft first, each rung more aggressive, bank on the first
# byte-match). What each recovery rung actually runs:
# scoped — §8d block-scope demotion of carried D_ externs (only when scope_data_fix moved any).
# recovered — fb.recover = cast_call_sites (callee-sig casts) THEN reconcile_tu (the DATA-symbol
# per-TU oracle, wired A3d/4aae5e758): conform each D_ decl to what THIS TU can SEE and
# cast at use. This is how reconcile_tu reaches bank_exemplar — no separate rung needed.
# reconciled — canon_sig_reconcile (the def-side loose-typing sig wall; A9a made it fn-ptr-aware).
stages = [("raw", lambda: body)]
if moved:
stages.append(("scoped", lambda: scoped))
stages += [("recovered", lambda: fb.recover(base, OV, cf, FUNC) or base),
("reconciled", lambda: reconcile(FUNC, base, tu_path=cf))]
last_err = ""
for name, make in stages:
try:
cand = make()
except Exception as e:
last_err = f"{name}: {repr(e)[:90]}"
continue
open(cf, 'w').write(orig[:m.start()] + cand + orig[m.end():])
b = sh(f"make --no-print-directory build BINARY={OV}")
if b.returncode == 0 and "[ OK ]" in b.stdout:
print(f"== BANKED ×1 [{name}] — BYTE-IDENTICAL (tree left banked for R22 + commit)")
sys.exit(0)
errs = [l.strip()[:120] for l in (b.stdout + b.stderr).split('\n')
if re.search(r'conflicting|undeclared|parse error|redefinition', l) and 'built-in' not in l]
last_err = f"{name}: " + (errs[0] if errs else "byte-diff")
print(f" [{name}] FAIL — {last_err}" + (f" (+{len(errs)-1} more)" if len(errs) > 1 else ""))
open(cf, 'w').write(orig)
fb.revert(OV, cf, keep_regions=keep)
print("== reverted; last:", last_err)
sys.exit(1)