mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
feat(phase-25): T2 — structural-family target manifest (tools/family_manifest.py)
Regroups the unmatched frontier by h_norm: 44,101 families; 2,764 multi-member / 11.1 MB. Per family: the ov_SC01_077 drafting exemplar + INSTANCE leverage (not h_exact-reach). The endgame decomposes into 3 levers by ov_SC01_077 membership: draftable 127/6.7MB (unmatched member -> draft exemplar, T5/T7) matched-free 156/2.3MB (matched member + unmatched siblings -> free --tier h_norm, T3) absent 2481/2.1MB (no member -> draft from another overlay) -> docs/family-manifest.md (digest) + .run/family_manifest.json.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Phase 25 — Structural-family target manifest (T2)
|
||||
|
||||
> Generated by `tools/family_manifest.py` from the 134 overlay sigs + ov_SC01_077 matched set + `config/dedup.us.yaml`. One exemplar per h_norm family, drafted from its **ov_SC01_077 member** where present. h_norm families are reloc-only by construction; whether one C body matches all members is confirmed per-family by the byte-gate (T3 magnitude / T7 banks). **Leverage = `inst` (fleet-wide member count), NOT `hexR` (h_exact-reach, ~1-3 for byte-shattered families).**
|
||||
|
||||
**Families:** 44101 · **multi-member (hidden leverage):** 2764 / 11.1 MB · **with an ov_SC01_077 draftable exemplar:** 127.
|
||||
|
||||
**Endgame levers** (multi-member families × ov_SC01_077 membership): **draftable** 127 / 6.7 MB (unmatched ov077 member → draft the exemplar, T5/T7) · **matched-free** 156 / 2.3 MB (matched ov077 member + unmatched siblings → free `--tier h_norm` propagation, T3) · **absent** 2481 / 2.1 MB (no ov077 member → draft from another overlay).
|
||||
|
||||
| # | rep addr | ov077 exemplar | nins | hexcls | inst | hexR | byteMB | bucket |
|
||||
|--:|----------|----------------|-----:|-------:|-----:|-----:|-------:|--------|
|
||||
| 1 | 0x80133cd4 | 0x80133cd4 | 399 | 127 | 134 | 3 | 0.21 | GIANT |
|
||||
| 2 | 0x80166994 | 0x80166994 | 369 | 127 | 134 | 3 | 0.20 | GIANT |
|
||||
| 3 | 0x8013c414 | 0x8013c414 | 329 | 127 | 134 | 3 | 0.18 | GIANT |
|
||||
| 4 | 0x80150480 | 0x8014d3e0 | 22 | 1892 | 1997 | 3 | 0.18 | small |
|
||||
| 5 | 0x8014d820 | 0x8014d820 | 304 | 126 | 134 | 3 | 0.16 | GIANT |
|
||||
| 6 | 0x8016a290 | 0x8016a290 | 284 | 126 | 134 | 3 | 0.15 | GIANT |
|
||||
| 7 | 0x801670e4 | 0x801670e4 | 279 | 126 | 134 | 3 | 0.15 | GIANT |
|
||||
| 8 | 0x80130d48 | 0x80130d48 | 266 | 126 | 134 | 3 | 0.14 | GIANT |
|
||||
| 9 | 0x80135480 | 0x80135480 | 258 | 127 | 134 | 3 | 0.14 | GIANT |
|
||||
| 10 | 0x80163ec8 | 0x80163ec8 | 234 | 127 | 134 | 3 | 0.13 | GIANT |
|
||||
| 11 | 0x80134c20 | 0x80134c20 | 230 | 127 | 134 | 3 | 0.12 | GIANT |
|
||||
| 12 | 0x8014e284 | 0x8014dd8c | 108 | 2 | 268 | 134 | 0.12 | large |
|
||||
| 13 | 0x8016cbc0 | 0x8016cbc0 | 209 | 127 | 134 | 3 | 0.11 | GIANT |
|
||||
| 14 | 0x80133784 | 0x80133784 | 203 | 127 | 134 | 3 | 0.11 | GIANT |
|
||||
| 15 | 0x8013bd74 | 0x8013bd74 | 198 | 127 | 134 | 3 | 0.11 | GIANT |
|
||||
| 16 | 0x80166690 | 0x80166690 | 193 | 127 | 134 | 3 | 0.10 | GIANT |
|
||||
| 17 | 0x80150170 | 0x8014fe60 | 95 | 2 | 268 | 134 | 0.10 | med |
|
||||
| 18 | 0x8013b274 | 0x8013b274 | 189 | 126 | 134 | 3 | 0.10 | GIANT |
|
||||
| 19 | 0x8013dd68 | 0x8013dd68 | 187 | 127 | 134 | 3 | 0.10 | GIANT |
|
||||
| 20 | 0x80167ae0 | 0x80167ae0 | 183 | 126 | 134 | 3 | 0.10 | GIANT |
|
||||
| 21 | 0x80161e08 | 0x80161e08 | 175 | 127 | 134 | 3 | 0.09 | GIANT |
|
||||
| 22 | 0x80167dbc | 0x80167dbc | 173 | 126 | 134 | 3 | 0.09 | GIANT |
|
||||
| 23 | 0x8016c49c | 0x8016c49c | 172 | 127 | 134 | 3 | 0.09 | GIANT |
|
||||
| 24 | 0x8016cf04 | 0x8016cf04 | 166 | 127 | 134 | 3 | 0.09 | GIANT |
|
||||
| 25 | 0x801347a0 | 0x801347a0 | 162 | 126 | 134 | 3 | 0.09 | GIANT |
|
||||
| 26 | 0x80136334 | 0x80136334 | 161 | 126 | 134 | 3 | 0.09 | GIANT |
|
||||
| 27 | 0x8016dc20 | 0x8016dc20 | 161 | 127 | 134 | 3 | 0.09 | GIANT |
|
||||
| 28 | 0x80162438 | 0x80162438 | 160 | 127 | 134 | 3 | 0.09 | GIANT |
|
||||
| 29 | 0x80136824 | 0x80136824 | 156 | 126 | 134 | 3 | 0.08 | GIANT |
|
||||
| 30 | 0x8016df5c | 0x8016df5c | 156 | 127 | 134 | 3 | 0.08 | GIANT |
|
||||
| 31 | 0x801365b8 | 0x801365b8 | 155 | 126 | 134 | 3 | 0.08 | GIANT |
|
||||
| 32 | 0x8013c0f8 | 0x8013c0f8 | 154 | 127 | 134 | 3 | 0.08 | GIANT |
|
||||
| 33 | 0x8012c890 | 0x8012c890 | 149 | 126 | 134 | 3 | 0.08 | large |
|
||||
| 34 | 0x8016d1d8 | 0x8016d1d8 | 148 | 127 | 134 | 3 | 0.08 | large |
|
||||
| 35 | 0x8013d9b0 | 0x8013d9b0 | 141 | 127 | 134 | 3 | 0.08 | large |
|
||||
| 36 | 0x80133ab0 | 0x80133ab0 | 137 | 127 | 134 | 3 | 0.07 | large |
|
||||
| 37 | 0x80140f00 | 0x80140f00 | 128 | 126 | 134 | 3 | 0.07 | large |
|
||||
| 38 | 0x80145cec | 0x80145cec | 127 | 126 | 134 | 3 | 0.07 | large |
|
||||
| 39 | 0x8017d960 | — | 3338 | 5 | 5 | 1 | 0.07 | GIANT |
|
||||
| 40 | 0x8013eb7c | 0x8013eb7c | 124 | 126 | 134 | 3 | 0.07 | large |
|
||||
| 41 | 0x80145a2c | — | 62 | 250 | 266 | 3 | 0.07 | med |
|
||||
| 42 | 0x8013ad38 | 0x8013ad38 | 122 | 126 | 134 | 3 | 0.07 | large |
|
||||
| 43 | 0x8013d178 | 0x8013d178 | 110 | 127 | 134 | 3 | 0.06 | large |
|
||||
| 44 | 0x8013221c | 0x801320d8 | 27 | 507 | 540 | 3 | 0.06 | small |
|
||||
| 45 | 0x8013ef88 | 0x8013ef88 | 108 | 126 | 134 | 3 | 0.06 | large |
|
||||
| 46 | 0x80168828 | 0x80168828 | 108 | 126 | 134 | 3 | 0.06 | large |
|
||||
| 47 | 0x80134a74 | 0x80134a74 | 107 | 127 | 134 | 3 | 0.06 | large |
|
||||
| 48 | 0x801345f8 | 0x801345f8 | 106 | 127 | 134 | 3 | 0.06 | large |
|
||||
| 49 | 0x80141100 | — | 106 | 125 | 133 | 3 | 0.06 | large |
|
||||
| 50 | 0x8017b614 | 0x8017b614 | 101 | 127 | 134 | 3 | 0.05 | large |
|
||||
| 51 | 0x801596f0 | 0x801596f0 | 97 | 126 | 134 | 3 | 0.05 | med |
|
||||
| 52 | 0x8017b490 | 0x8017b490 | 97 | 127 | 134 | 3 | 0.05 | med |
|
||||
| 53 | 0x8013dbe4 | — | 97 | 126 | 133 | 3 | 0.05 | med |
|
||||
| 54 | 0x8016bfd0 | 0x8016bfd0 | 95 | 126 | 134 | 3 | 0.05 | med |
|
||||
| 55 | 0x8013ee10 | 0x8013ee10 | 94 | 126 | 134 | 3 | 0.05 | med |
|
||||
| 56 | 0x801418f8 | 0x801418f8 | 90 | 127 | 134 | 3 | 0.05 | med |
|
||||
| 57 | 0x8013d3d4 | — | 90 | 126 | 133 | 3 | 0.05 | med |
|
||||
| 58 | 0x8013e6ac | — | 90 | 125 | 133 | 3 | 0.05 | med |
|
||||
| 59 | 0x80131b14 | 0x80131b14 | 89 | 126 | 134 | 3 | 0.05 | med |
|
||||
| 60 | 0x80135004 | 0x80135004 | 89 | 127 | 134 | 3 | 0.05 | med |
|
||||
+25
-11
@@ -25,23 +25,23 @@ idiom curriculum → sweep one exemplar per family (curriculum-ordered), each cr
|
||||
## Tasks
|
||||
- [x] T0 — Refresh + housekeeping
|
||||
- [x] T1 — Integration-recovery pass (split-aware recover_integration fix; caller-arity cohort exhausted 0/16 — residuals backlogged)
|
||||
- [ ] **T2 — Build the exemplar-scoped, class-routed target manifest** (one per h_norm family + reach-1 uniques + reach-134 h_exact tractable) ← CURRENT
|
||||
- [ ] T3 — Swing-question PROOF (match one mid-size reloc-only family exemplar → `--tier h_norm --recover` → measure ×134 bank count)
|
||||
- [x] T2 — Exemplar target manifest (tools/family_manifest.py: 2,764 multi-member families → 127 draftable / 156 matched-free / 2481 absent)
|
||||
- [ ] **T3 — Swing-question PROOF** (draft-free via the 156 matched-free families → `--tier h_norm` propagation → measure) ← CURRENT
|
||||
- [ ] T4 — v4 retrain + A/B gate (cheap drafter prep; may run parallel to T2/T3)
|
||||
- [ ] T5 — Cheap-tier soften + measure wave → the complete class/closeness frontier map (Step A) + pre-advanced seeds *(Ultracode — prompt)*
|
||||
- [ ] T6 — Step B: Fable5Max curriculum authoring from the measured map *(Fable5Max — prompt)*
|
||||
- [ ] T7 — Sweep one exemplar per family, curriculum-ordered (validate top ~3-5 first, then scale) *(Ultracode harvests / Fable5 discovery — prompt)*
|
||||
- [ ] Close — clean-fleet verify · PhaseEnd synthesis · plain-English recap (R25) · Phase-26 backlog
|
||||
|
||||
## Current task — T2: Exemplar-scoped, class-routed target manifest
|
||||
Build the ONE-exemplar-per-family target set from the T0 surveys: extend `.run/probe_hnorm.py` (or a companion)
|
||||
to emit a ranked manifest of the 2,764 multi-member h_norm families — per family: rep addr, exemplar (an
|
||||
ov_SC01_077 member if present, else any overlay+addr to draft), n_instances, n_hexcls, maxIns, reach, byteMB,
|
||||
size bucket. Plus the reach-134 h_exact tractable stubs (warm-up) + reach-1 uniques. NOTE (T0/T3): an h_norm
|
||||
family is **reloc-only by construction** (norm_stream masks only jal/HI16/LO16, keeps true immediates+regs), so
|
||||
h_norm-identical ⟹ intra-family diffs are reloc-only; whether ONE C body matches all 134 additionally needs the
|
||||
reloc targets to be consistently-named-per-overlay symbols — T3 proves that empirically via `--tier h_norm`.
|
||||
Output: `.run/family_manifest.json` + a committed digest. This scaffolds Step A (the curriculum input).
|
||||
## Current task — T3: Swing-question PROOF (decisive, draft-free)
|
||||
The 156 **matched-free** families (2.3 MB) prove the reloc-only ×N economics WITHOUT drafting: ov_SC01_077's
|
||||
member is already MATCHED, and the family has UNMATCHED h_norm-siblings in other overlays (byte-variants Phase-24
|
||||
propagated only via `--tier h_exact`). Steps: (1) pick a mid-size matched-free fn (find an already-matched
|
||||
ov_SC01_077 addr whose h_norm has unmatched siblings); (2) `tools/dedup_propagate.py --addr <it> --tier h_norm
|
||||
--recover`; (3) measure how many siblings bank via the per-overlay byte-gate. A meaningful bank ⟹ the free
|
||||
reloc-only propagation lever is proven (one C body matches h_norm-siblings when reloc targets resolve per-overlay
|
||||
— the consistent-naming condition holds), banking free % now; then sweep the 156 (T3→partial-T7). Clean-fleet
|
||||
verify (R22). If h_norm propagation banks ~0, fall back to draft-then-propagate on a `draftable` exemplar.
|
||||
|
||||
## Blockers
|
||||
None.
|
||||
@@ -83,3 +83,17 @@ None.
|
||||
lhu/sh** (the target uses lhu/sh). T5/T7 drafters: avoid struct-assign for small mem-copies in these overlays.
|
||||
- **Decision (Drew):** conclude T1; closeness-0 residuals stay in the ranked backlog (available later); the 2
|
||||
flagship ×134 leaf-matches NOT hand-banked (per-function ROI < the family lever).
|
||||
|
||||
### T2 — Exemplar-scoped target manifest ✅ (2026-07-08)
|
||||
- **Deliverable:** `tools/family_manifest.py` (committed, reusable) → `docs/family-manifest.md` + gitignored
|
||||
`.run/family_manifest.json`. Regroups the unmatched frontier by h_norm; per family: the ov_SC01_077 drafting
|
||||
exemplar (where present), **instances** (the ×N leverage — NOT h_exact-reach, ~1-3 for byte-shattered families),
|
||||
size, byte-weight. 44,101 families; **2,764 multi-member / 11.1 MB**.
|
||||
- **The endgame's 3 levers (× ov_SC01_077 membership):**
|
||||
- **draftable 127 / 6.7 MB** — unmatched ov077 member → draft the exemplar (primary T5/T7; most byte-weight).
|
||||
- **matched-free 156 / 2.3 MB** — matched ov077 member + unmatched siblings → FREE `--tier h_norm` propagation
|
||||
(draft-free; the T3 proof + a free win).
|
||||
- **absent 2481 / 2.1 MB** — no ov077 member → small fns in other overlays, draft elsewhere (low priority).
|
||||
- **Insight:** h_norm families are reloc-only by construction, so the matched-free bucket is likely bankable NOW by
|
||||
re-propagating already-matched ov077 fns via `--tier h_norm` (byte-gated). T3 tests decisively.
|
||||
- T3 candidates (mid-size draftable): 0x8012c890(149)/0x8016d1d8(148)/0x8013d9b0(141) — all inst=134, ~127 hexcls.
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Phase-25 T2: the ranked STRUCTURAL-FAMILY target manifest for the idiom-curriculum endgame.
|
||||
|
||||
Regroups the UNMATCHED frontier by structure (h_norm) instead of bytes (h_exact) — the Phase-24 reframe —
|
||||
and emits, per family, the ONE exemplar to draft (its ov_SC01_077 member where present: the canonical harvest
|
||||
overlay with cached Ghidra-C) + the family's leverage. An h_norm family is RELOC-ONLY by construction
|
||||
(sig_image.norm_stream masks only jal/HI16/LO16, keeping registers+true immediates), so its members differ
|
||||
ONLY in per-overlay symbol addresses; whether ONE C body matches all members is confirmed per-family by the
|
||||
whole-binary byte-gate (T3 proves the magnitude; T7 banks each). Leverage = the family's INSTANCE count (how
|
||||
many overlays carry it), NOT h_exact-reach (which is ~1-3 for a byte-shattered family).
|
||||
|
||||
.venv/bin/python tools/family_manifest.py
|
||||
-> .run/family_manifest.json (full, ranked by byte-weight) + docs/family-manifest.md (digest, committed)
|
||||
|
||||
Ground truth = the sigs (they sign the ORIGINAL bytes, stable across matching); reproducible via
|
||||
`make sig-overlays`. Companion to the frontier surveys .run/{analyze_frontier,probe_hnorm}.py.
|
||||
"""
|
||||
import json, glob, re, collections
|
||||
|
||||
sig_by_ov = {}
|
||||
for p in sorted(glob.glob(".run/sig.ov_*.jsonl")):
|
||||
ov = p.split("sig.")[1][:-6]
|
||||
recs = {}
|
||||
for line in open(p):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
d = json.loads(line)
|
||||
recs[int(d["addr"], 16)] = (d["nins"], d["h_exact"], d["h_norm"])
|
||||
sig_by_ov[ov] = recs
|
||||
|
||||
# matched set (h_exact): ov_SC01_077's non-stub functions + every dedup-registered hash
|
||||
src077 = "".join(open(cf).read() for cf in glob.glob("src/ov_SC01_077/*.c"))
|
||||
stub077 = set(int(m, 16) for m in re.findall(r'INCLUDE_ASM\([^)]*,\s*func_([0-9A-Fa-f]+)\)', src077))
|
||||
sig077 = sig_by_ov.get("ov_SC01_077", {})
|
||||
matched_hex = {hx for v, (n, hx, hn) in sig077.items() if v not in stub077}
|
||||
matched_hex |= set(re.findall(r'hash:\s*([0-9a-f]{40})', open("config/dedup.us.yaml").read()))
|
||||
|
||||
# per h_exact class: {ovs, nins, rep_vram (ov_SC01_077's if present), h_norm}
|
||||
hexcls = {}
|
||||
for ov, recs in sig_by_ov.items():
|
||||
for v, (n, hx, hn) in recs.items():
|
||||
e = hexcls.setdefault(hx, [set(), n, v, hn])
|
||||
e[0].add(ov)
|
||||
if ov == "ov_SC01_077":
|
||||
e[2] = v
|
||||
|
||||
# UNMATCHED h_exact classes -> group by h_norm (the structural family)
|
||||
fam = collections.defaultdict(lambda: {"hexclasses": 0, "instances": 0, "nins": 0, "bw": 0, "rep": 0, "maxhexreach": 0})
|
||||
for hx, (ovs, n, v, hn) in hexcls.items():
|
||||
if hx in matched_hex:
|
||||
continue
|
||||
r = len(ovs)
|
||||
f = fam[hn]
|
||||
f["hexclasses"] += 1
|
||||
f["instances"] += r # fleet-wide member count = the x-N propagation leverage
|
||||
f["bw"] += r * n * 4
|
||||
f["maxhexreach"] = max(f["maxhexreach"], r)
|
||||
if n >= f["nins"] or f["rep"] == 0:
|
||||
f["rep"] = v
|
||||
f["nins"] = n
|
||||
|
||||
# the draftable exemplar per family: the largest UNMATCHED ov_SC01_077 stub whose h_norm == the family
|
||||
ov077_ex = {} # h_norm -> (vram, nins)
|
||||
for v, (n, hx, hn) in sig077.items():
|
||||
if v not in stub077 or hx in matched_hex:
|
||||
continue
|
||||
if hn not in ov077_ex or n > ov077_ex[hn][1]:
|
||||
ov077_ex[hn] = (v, n)
|
||||
|
||||
|
||||
def bucket(n):
|
||||
return "tiny" if n < 20 else "small" if n < 50 else "med" if n < 100 else "large" if n < 150 else "GIANT"
|
||||
|
||||
|
||||
rows = []
|
||||
for hn, f in fam.items():
|
||||
ex = ov077_ex.get(hn)
|
||||
rows.append({
|
||||
"hnorm": hn,
|
||||
"rep_addr": f"0x{f['rep']:08x}",
|
||||
"ov077_exemplar": (f"0x{ex[0]:08x}" if ex else None),
|
||||
"n_hexcls": f["hexclasses"],
|
||||
"instances": f["instances"],
|
||||
"max_nins": f["nins"],
|
||||
"max_hex_reach": f["maxhexreach"],
|
||||
"byte_weight": f["bw"],
|
||||
"size_bucket": bucket(f["nins"]),
|
||||
"multi_member": f["hexclasses"] >= 2,
|
||||
})
|
||||
rows.sort(key=lambda r: -r["byte_weight"])
|
||||
json.dump(rows, open(".run/family_manifest.json", "w"), indent=0)
|
||||
|
||||
multi = [r for r in rows if r["multi_member"]]
|
||||
with_ex = [r for r in multi if r["ov077_exemplar"]]
|
||||
|
||||
# classify the multi-member families by ov_SC01_077 membership -> the endgame's 3 levers
|
||||
ov077_matched_hn = {hn for v, (n, hx, hn) in sig077.items() if v not in stub077 or hx in matched_hex}
|
||||
lev = {"draftable": [0, 0], "matched_free": [0, 0], "absent": [0, 0]}
|
||||
for r in multi:
|
||||
k = "draftable" if r["ov077_exemplar"] else ("matched_free" if r["hnorm"] in ov077_matched_hn else "absent")
|
||||
lev[k][0] += 1
|
||||
lev[k][1] += r["byte_weight"]
|
||||
|
||||
L = ["# Phase 25 — Structural-family target manifest (T2)\n"]
|
||||
L.append("> Generated by `tools/family_manifest.py` from the 134 overlay sigs + ov_SC01_077 matched set + "
|
||||
"`config/dedup.us.yaml`. One exemplar per h_norm family, drafted from its **ov_SC01_077 member** where "
|
||||
"present. h_norm families are reloc-only by construction; whether one C body matches all members is "
|
||||
"confirmed per-family by the byte-gate (T3 magnitude / T7 banks). **Leverage = `inst` (fleet-wide member "
|
||||
"count), NOT `hexR` (h_exact-reach, ~1-3 for byte-shattered families).**\n")
|
||||
L.append(f"**Families:** {len(rows)} · **multi-member (hidden leverage):** {len(multi)} / "
|
||||
f"{sum(r['byte_weight'] for r in multi)/1e6:.1f} MB · **with an ov_SC01_077 draftable exemplar:** {len(with_ex)}.\n")
|
||||
L.append(f"**Endgame levers** (multi-member families × ov_SC01_077 membership): **draftable** {lev['draftable'][0]} / "
|
||||
f"{lev['draftable'][1]/1e6:.1f} MB (unmatched ov077 member → draft the exemplar, T5/T7) · **matched-free** "
|
||||
f"{lev['matched_free'][0]} / {lev['matched_free'][1]/1e6:.1f} MB (matched ov077 member + unmatched siblings → "
|
||||
f"free `--tier h_norm` propagation, T3) · **absent** {lev['absent'][0]} / {lev['absent'][1]/1e6:.1f} MB "
|
||||
f"(no ov077 member → draft from another overlay).\n")
|
||||
L.append("| # | rep addr | ov077 exemplar | nins | hexcls | inst | hexR | byteMB | bucket |")
|
||||
L.append("|--:|----------|----------------|-----:|-------:|-----:|-----:|-------:|--------|")
|
||||
for i, r in enumerate(multi[:60], 1):
|
||||
L.append(f"| {i} | {r['rep_addr']} | {r['ov077_exemplar'] or '—'} | {r['max_nins']} | {r['n_hexcls']} | "
|
||||
f"{r['instances']} | {r['max_hex_reach']} | {r['byte_weight']/1e6:.2f} | {r['size_bucket']} |")
|
||||
open("docs/family-manifest.md", "w").write("\n".join(L) + "\n")
|
||||
|
||||
print(f"families {len(rows)} | multi-member {len(multi)} ({sum(r['byte_weight'] for r in multi)/1e6:.1f} MB) | "
|
||||
f"ov077-draftable {len(with_ex)}")
|
||||
print(f"levers: draftable {lev['draftable'][0]}/{lev['draftable'][1]/1e6:.1f}MB | "
|
||||
f"matched-free {lev['matched_free'][0]}/{lev['matched_free'][1]/1e6:.1f}MB | "
|
||||
f"absent {lev['absent'][0]}/{lev['absent'][1]/1e6:.1f}MB")
|
||||
print("-> .run/family_manifest.json + docs/family-manifest.md")
|
||||
|
||||
# T3-ready picks: mid-size (100-150 ins) multi-member family, ov077-draftable, spanning most overlays
|
||||
t3 = [r for r in multi if r["ov077_exemplar"] and 100 <= r["max_nins"] <= 150 and r["instances"] >= 100]
|
||||
t3.sort(key=lambda r: (-r["instances"], -r["byte_weight"]))
|
||||
print("\nT3 swing-question candidates (mid-size 100-150 ins, ov077-draftable, inst>=100):")
|
||||
for r in t3[:10]:
|
||||
print(f" ex={r['ov077_exemplar']} nins={r['max_nins']} inst={r['instances']} hexcls={r['n_hexcls']} "
|
||||
f"bw={r['byte_weight']/1e6:.2f}MB")
|
||||
Reference in New Issue
Block a user