Files
BFM-decomp/tools/backlog.py
T
Drew T e91859fb4a feat(phase-23): grinder per-binary fix (5-layer) — unlock non-077 near-miss grinding
The grinder/backlog pipeline was ov_SC01_077-hardcoded 5 layers deep (same class as the
T7 lora_grind bug). Fixed all so the permuter grinder can process a non-077 near-miss:
1. gate_stage.append_record stores the source "binary"
2. backlog.FIELDS keeps it (else append_record dropped it)
3. backlog.load_best/_open_stubs is fleet-aware: a fn matched in ov_SC01_077 but
   propagation-stuck stays OPEN in its overlay, so it surfaces via that record instead
   of being dropped as "matched" (the grinder must SEE it to grind it)
4. p16_permute.setup takes the target binary's asm-subdir (was hardcoded 077)
5. grinder resolves per-binary asm + gates grouped by binary + allows unknown nins
Backward-compatible: legacy records (no binary) default ov_SC01_077.

Validated end-to-end: the 3 fresh reach-134 close=1 ov_SC01_000 fns now surface, resolve
to ov_SC01_000's asm, and gate via ov_SC01_000.

TWO byte-evidenced findings (redirect the fuel strategy):
- the reach>=2 close=1 fuel is MODEL semantic-misses, not permuter fuel: func_8012E27C's
  target is "return 1" but the 7B drafted an empty "void f(void){}" (corpus overfit
  empty-leaf); func_8012BF4C/AD64 are trivial sw/sh setters drafted empty. A corrected
  draft banks them (+3 byte-identical via the fixed gate, @commit:0326); the permuter cannot
  add a missing return/store. Lever = corpus-v3 leaf variety, not the permuter.
- x reach is propagation-capped: the 3 are inline-matched in ov_SC01_077_a.c (the stuck-
  local cap) -> dedup_propagate "nothing to propagate" -> banked x1. Lever = dedup-collapse.

check-all 136/136 throughout. docs/gen2-mips-matching-model.md + CURRENT_PHASE updated.
2026-06-30 00:58:13 -06:00

169 lines
8.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
"""backlog.py — Phase 21 near-miss ledger for the automation manager.
The automation's headline DELIVERABLE: every function the worker/grinder got *close* on but
did NOT byte-match is logged here, ranked for Drew's hand-sessions. The byte-gate
(harvest_verify) is the sole arbiter (G3/P9): a byte-MATCH banks and does NOT appear here;
only genuine near-misses/blockers do. Honest by construction.
Two faces:
.run/backlog.jsonl append-only machine log (one record per logged attempt; history kept)
docs/backlog.md ranked human table (best record per addr; rows that are now matched are dropped)
Record: {ts, addr, name, reach, klass, nins, status, closeness, where_stuck, best_draft, source}
status : near | failed | capped | stub (near = match_one near-miss; capped = matched-but-
propagation-blocked; stub = loose-typing/irreducible wall; failed = won't compile/gate)
closeness: int (match_one mismatch count; lower = closer) or None
where_stuck: the residual class / blocker (e.g. "regalloc-order", "store-vs-load sched",
"propagation: undeclared callee func_X", "loose-typing arity")
best_draft : .run/backlog_drafts/<name>.c (saved best draft, so Drew resumes where it stuck)
source : worker | grinder | manual
Ranking (hand-session priority): reach desc (x134 leverage), then closeness asc, then nins asc.
Usage:
tools/backlog.py log --addr 0x.. --name func_.. --reach 134 --class PINS --nins 80 \
--status near --closeness 6 --where regalloc-order --source worker [--draft path.c]
tools/backlog.py render # regenerate docs/backlog.md from .run/backlog.jsonl
tools/backlog.py show [-n 40] # print the top N to stdout
Importable: append_record(dict), render(), save_draft(name, text)->path.
"""
import argparse, glob, json, os, re, shutil, time
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
JSONL = os.path.join(REPO, ".run/backlog.jsonl")
MD = os.path.join(REPO, "docs/backlog.md")
DRAFTS = os.path.join(REPO, ".run/backlog_drafts")
SRC_GLOB = os.path.join(REPO, "src/ov_SC01_077/ov_SC01_077*.c")
STUB_RE = re.compile(r"INCLUDE_ASM\([^,]+,\s*(\w+)\)")
FIELDS = ("ts", "addr", "name", "reach", "klass", "nins", "status",
"closeness", "where_stuck", "best_draft", "binary", "source")
def append_record(rec):
"""Normalize + append one record to .run/backlog.jsonl (adds ts if absent)."""
r = {k: rec.get(k) for k in FIELDS}
if not r.get("ts"):
r["ts"] = time.strftime("%Y-%m-%d %H:%M:%S")
if r.get("addr") and not r.get("name"):
r["name"] = "func_" + r["addr"].lower().replace("0x", "").upper()
os.makedirs(os.path.dirname(JSONL), exist_ok=True)
with open(JSONL, "a") as f:
f.write(json.dumps(r) + "\n")
return r
def save_draft(name, text):
"""Persist a best-draft .c so the human picks up where the machine stuck. Returns rel path."""
os.makedirs(DRAFTS, exist_ok=True)
p = os.path.join(DRAFTS, f"{name}.c")
open(p, "w").write(text)
return os.path.relpath(p, REPO)
_STUB_CACHE = {}
def _open_stubs(binary):
"""INCLUDE_ASM stub names still OPEN in <binary>'s source (main + any _a/_o0 split). Fleet-aware:
a fn matched in ov_SC01_077 but propagation-stuck stays OPEN in the other overlays (the Phase-19/20
cap), so the grinder must judge open-ness against the record's OWN binary, not just 077."""
if binary not in _STUB_CACHE:
s = set()
for p in glob.glob(os.path.join(REPO, f"src/{binary}/{binary}*.c")):
s |= set(STUB_RE.findall(open(p).read()))
_STUB_CACHE[binary] = s
return _STUB_CACHE[binary]
def load_best():
"""Best (lowest closeness, latest ts) record per addr, restricted to fns still OPEN in their OWN
binary (rec['binary']; legacy records default ov_SC01_077). Fleet-aware so a 077-matched-but-
stuck-local fn surfaces via its overlay record — the grinder must SEE it to grind it (P9 honesty:
a fn banked in its own binary since logged is dropped)."""
if not os.path.exists(JSONL):
return []
_STUB_CACHE.clear()
best = {}
for line in open(JSONL):
line = line.strip()
if not line:
continue
r = json.loads(line)
nm = r.get("name")
binary = r.get("binary") or "ov_SC01_077"
if nm and nm not in _open_stubs(binary): # banked in ITS binary since logged -> drop (P9)
continue
key = r.get("addr") or nm
cur = best.get(key)
c = r.get("closeness")
cscore = c if isinstance(c, int) else 10 ** 9
if cur is None or (cscore, r.get("ts", "")) <= (cur[0], cur[1]):
best[key] = (cscore, r.get("ts", ""), r)
return [v[2] for v in best.values()]
def _rank_key(r):
return (-(r.get("reach") or 1),
r.get("closeness") if isinstance(r.get("closeness"), int) else 10 ** 9,
r.get("nins") or 10 ** 9)
def render():
recs = sorted(load_best(), key=_rank_key)
by_status, by_class = {}, {}
for r in recs:
by_status[r.get("status")] = by_status.get(r.get("status"), 0) + 1
by_class[r.get("klass")] = by_class.get(r.get("klass"), 0) + 1
lines = []
lines.append("# Backlog — automation near-misses for hand-finishing\n")
lines.append("> Generated by `tools/backlog.py render` from `.run/backlog.jsonl`. "
"These are functions the Phase-21 automation got **close** on but did NOT "
"byte-match. The whole-binary byte-gate is the sole arbiter (G3/P9): **byte-matches "
"bank and are NOT listed here** — only genuine near-misses/blockers are. Ranked by "
"hand-session priority: **reach** (×N propagation leverage) → **closeness** "
"(match_one mismatch count, lower = closer) → **size**. Each row's `best_draft` is "
"the closest C the machine reached — resume from there.\n")
lines.append(f"**Open near-misses:** {len(recs)} · by status {by_status} · by class {by_class}\n")
lines.append("| # | addr | reach | class | nins | status | closeness | where it stuck | best draft |")
lines.append("|--:|------|------:|-------|-----:|--------|----------:|----------------|------------|")
for i, r in enumerate(recs, 1):
c = r.get("closeness")
lines.append("| {} | {} | {} | {} | {} | {} | {} | {} | {} |".format(
i, r.get("name") or r.get("addr"), r.get("reach"), r.get("klass") or "", r.get("nins"),
r.get("status") or "", c if c is not None else "", (r.get("where_stuck") or "").replace("|", "/"),
f"`{r.get('best_draft')}`" if r.get("best_draft") else ""))
os.makedirs(os.path.dirname(MD), exist_ok=True)
open(MD, "w").write("\n".join(lines) + "\n")
return len(recs)
def main():
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
lg = sub.add_parser("log")
for fl in ("addr", "name", "klass", "status", "where", "draft", "source"):
lg.add_argument(f"--{fl if fl != 'klass' else 'class'}", dest=fl, default=None)
for fl in ("reach", "nins", "closeness"):
lg.add_argument(f"--{fl}", type=int, default=None)
sub.add_parser("render")
sh = sub.add_parser("show"); sh.add_argument("-n", type=int, default=40)
a = ap.parse_args()
if a.cmd == "log":
rec = append_record({"addr": a.addr, "name": a.name, "klass": a.klass, "status": a.status,
"where_stuck": a.where, "best_draft": a.draft, "source": a.source,
"reach": a.reach, "nins": a.nins, "closeness": a.closeness})
n = render()
print(f"logged {rec.get('name') or rec.get('addr')}; backlog open={n}")
elif a.cmd == "render":
print(f"docs/backlog.md: {render()} open near-misses")
elif a.cmd == "show":
for i, r in enumerate(sorted(load_best(), key=_rank_key)[:a.n], 1):
print(f"{i:3} {r.get('name'):16} reach={r.get('reach')} {r.get('klass'):7} "
f"nins={r.get('nins')} {r.get('status')} close={r.get('closeness')} "
f"<{r.get('where_stuck')}>")
if __name__ == "__main__":
main()