Files
BFM-decomp/tools/sunset/bank_exemplar.py
T
Drew T 827295e241 tools+docs(phase-33.5): task 13.5 — the tools audit + the two dictionaries: tools/tool_census.py (two agreeing enumerations of 327 tool files; docstring/SETUP row/consumers/class derived from the tree; the authored half in config/tool_dictionary.tsv — phase · portability · the NEED each tool answers · what · adapts · verdict — with coverage asserted both ways) → docs/tool-index.md (need-keyed, KEEP-GEN, Reference-index row, wiki + how-to pointers), the kit's tools/MANIFEST.md regenerated (header states live 293 + superseded 28 = 321 rows), and the two verbatim corpora in-tree (Drew, confirmed S91): decomp-architect/corpus/tools/<phase>/ (302 copies + 28 superseded pointers + INDEX) and corpus/cookbook/ (the cookbook, its symptom index, the codegen map, a front page stating what transfers per compiler) — sha1-equal to their sources by tool_census --check in tools-health, regenerated by make kit-corpus; kit_lint exempts the corpus dirs (verbatim evidence) but syntax-checks them; G66 (consult the tool dictionary first) + G67 (translate an inherited idiom through its pass) + two memory seeds (34 at install); SETUP Step 6 installs docs/knowledge-corpus.md and checks the manifest against its own stated total; the ops-setup dictionary rows; the intake's Phase 7 cites G66/G67 and Phase 10 + Part C name the raw-cast → declared-symbol step; templates/layout-contract.md (the five-tool probe, a draft for the split). The review under Drew's criterion: 93 no-consumer tools (one Opus agent's draft, verified: 0 defects, every successor live, 0 live consumers, 0 collisions; four one-off verdicts overturned to STILL-NEEDED) → 34 retired by git mv to tools/sunset/ (28 superseded, 6 one-offs; README review table; SETUP rows moved; Archive-index group). Run 4 (fresh throwaway, the final kit): stopped on my Step-6 check (321 vs the live 293) → both sides derived → resumed → PASS 10/10, manifest 56 == 56, 4 commits, guardrails held (the one foreign path was the timeline regenerated by the detached tools-health). tools-health OK; doc_links --strict rc 0; audit_public OK over 6,842 paths; the purge probe PASSED (Phase 34's gate open). decision-log "P33.5 S91" + accelerators "P33.5 S91" banked; log + checkpoint (NEXT = task 14, xHigh, fresh session)
2026-09-07 22:09:15 -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)