diff --git a/docs/family-cousins.md b/docs/family-cousins.md index 19029f02d..fed9645f3 100644 --- a/docs/family-cousins.md +++ b/docs/family-cousins.md @@ -1,6 +1,6 @@ # P30 S49 — cousin-unit survey (similarity tier over the open frontier) -> Generated by `tools/family_cousins.py` at HEAD `commit:1775` from `.run/family_hseq.json` + the binary sigs (R32-checked against an independent stub recount — a stale map fails loud). Compare digests only at the same HEAD. +> Generated by `tools/family_cousins.py` at HEAD `commit:1784` from `.run/family_hseq.json` + the binary sigs (R32-checked against an independent stub recount — a stale map fails loud). Compare digests only at the same HEAD. > > **A unit is a CANDIDATE grouping** — same-source cousins split by skeleton drift (insertion/deletion; the li-expansion class). Cousins need a per-member seeded CRACK, never a `family_sweep` remap. The whole-binary byte-gate stays the sole arbiter. @@ -8,11 +8,11 @@ | category | units | open fns | open ins | lever | |---|--:|--:|--:|---| -| A-prop | 197 | 1286 | 68729 | family_sweep (propagation) | +| A-prop | 186 | 1238 | 62254 | family_sweep (propagation) | | seeded | 418 | 1652 | 50422 | seeded crack — edit a >=0.85-similar MATCHED body | | cousin-multi | 1552 | 5449 | 249799 | 1 crack seeds the unit's other members | | cold | 3240 | 3240 | 215498 | full-price crack (the honest unique tail) | -| **total** | **5407** | **11627** | **584448** | | +| **total** | **5396** | **11579** | **577973** | | ## Top units by open instructions (whole-cluster weight) @@ -24,37 +24,37 @@ | 4 | A-prop | 2204 | 4 | 1 | 1.0 | `ov_SC04_018:0x80189540` | `ov_SC04_018:0x80189540` (551 ins ×4 jr) | | 5 | seeded | 2166 | 3 | 3 | 0.987 | `ov_SC06_010:0x8017cad4` | `ov_SC06_010:0x8017bebc` (753 ins ×1) | | 6 | A-prop | 1825 | 17 | 7 | 1.0 | `ov_SC01_000:0x8017d290` | `ov_SC01_000:0x8017d290` (102 ins ×10) | -| 7 | A-prop | 1671 | 3 | 1 | 1.0 | `ov_SC03_001:0x80189030` | `ov_SC03_001:0x80189030` (557 ins ×3 jr) | -| 8 | cousin-multi | 1628 | 3 | 3 | · | · | `md_MAIN_011:0x800d1254` (555 ins ×1) | -| 9 | A-prop | 1628 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017c3bc` | `ov_MAIN_012:0x8017c3bc` (407 ins ×4 jr) | -| 10 | A-prop | 1554 | 59 | 3 | 1.0 | `md_SC05_023:0x801edc18` | `md_SC05_023:0x801edc18` (26 ins ×56) | -| 11 | seeded | 1466 | 6 | 3 | 0.93 | `ov_SC07_006:0x801802ec` | `ov_SC07_006:0x8017f9ac` (275 ins ×2) | -| 12 | cousin-multi | 1395 | 5 | 1 | · | · | `ov_SC01_005:0x8017f2d4` (279 ins ×5 jr) | -| 13 | A-prop | 1316 | 7 | 1 | 1.0 | `ov_SC01_000:0x8016ab6c` | `ov_SC01_077:0x8016ab6c` (188 ins ×7 jr) | -| 14 | A-prop | 1309 | 7 | 1 | 1.0 | `ov_SC01_000:0x8013dd68` | `ov_SC01_077:0x8013dd68` (187 ins ×7) | -| 15 | A-prop | 1304 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d730` | `ov_MAIN_012:0x8017d730` (326 ins ×4 jr) | -| 16 | A-prop | 1195 | 30 | 6 | 1.0 | `ov_SC01_077:0x8017d840` | `ov_SC01_077:0x8017d840` (39 ins ×21) | -| 17 | A-prop | 1164 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d2a4` | `ov_MAIN_012:0x8017d2a4` (291 ins ×4 jr) | -| 18 | A-prop | 1089 | 3 | 1 | 1.0 | `ov_SC01_000:0x8015444c` | `ov_SC01_077:0x8015444c` (363 ins ×3 jr) | -| 19 | A-prop | 1049 | 4 | 3 | 1.0 | `ov_SC03_028:0x80183e04` | `ov_SC03_028:0x80183e04` (265 ins ×2) | -| 20 | A-prop | 1035 | 11 | 5 | 1.0 | `ov_SC06_018:0x80187320` | `ov_SC02_011:0x80188250` (94 ins ×4) | -| 21 | seeded | 1034 | 19 | 11 | 0.976 | `ov_SC01_000:0x8017d1f0` | `ov_SC06_022:0x8017d6e0` (59 ins ×4) | -| 22 | A-prop | 996 | 46 | 10 | 1.0 | `ov_SC03_001:0x8017f0ec` | `ov_SC03_001:0x8017f0ec` (20 ins ×30) | -| 23 | A-prop | 987 | 3 | 1 | 1.0 | `ov_SC01_000:0x8013c414` | `ov_SC01_077:0x8013c414` (329 ins ×3 jr) | -| 24 | A-prop | 964 | 12 | 3 | 1.0 | `md_SC03_073:0x801efc94` | `md_SC03_079:0x801efb94` (87 ins ×4) | -| 25 | A-prop | 936 | 3 | 1 | 1.0 | `ov_SC01_000:0x8013faf8` | `ov_SC01_077:0x8013faf8` (312 ins ×3) | -| 26 | A-prop | 924 | 21 | 1 | 1.0 | `ov_SC02_026:0x80185634` | `ov_SC02_026:0x80185634` (44 ins ×21) | -| 27 | A-prop | 914 | 8 | 4 | 1.0 | `ov_SC02_017:0x8017f92c` | `ov_SC02_017:0x8017f92c` (115 ins ×4) | -| 28 | A-prop | 908 | 19 | 6 | 1.0 | `ov_SC02_041:0x80182ccc` | `ov_SC02_041:0x80182ccc` (45 ins ×9) | -| 29 | A-prop | 889 | 7 | 1 | 1.0 | `ov_SC01_000:0x80145cec` | `ov_SC01_077:0x80145cec` (127 ins ×7) | -| 30 | A-prop | 872 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017cf3c` | `ov_MAIN_012:0x8017cf3c` (218 ins ×4 jr) | -| 31 | A-prop | 855 | 57 | 1 | 1.0 | `md_SC03_073:0x801ef5f4` | `ov_SC01_077:0x80130d0c` (15 ins ×57) | -| 32 | A-prop | 816 | 3 | 1 | 1.0 | `ov_SC01_000:0x8013b83c` | `ov_SC01_077:0x8013b83c` (272 ins ×3 jr) | -| 33 | cousin-multi | 810 | 10 | 2 | · | · | `ov_SC01_005:0x8017e108` (84 ins ×5) | -| 34 | A-prop | 798 | 3 | 1 | 1.0 | `ov_SC01_000:0x80130d48` | `ov_SC01_077:0x80130d48` (266 ins ×3) | -| 35 | cousin-multi | 784 | 4 | 1 | · | · | `ov_SC03_108:0x8017ffd0` (196 ins ×4 jr) | -| 36 | cousin-multi | 781 | 8 | 4 | · | · | `ov_SC02_011:0x8018c5a4` (100 ins ×3) | -| 37 | cousin-multi | 778 | 7 | 5 | · | · | `ov_SC03_118:0x80181e58` (113 ins ×2) | -| 38 | cousin-multi | 767 | 15 | 7 | 0.735 | · | `ov_SC03_028:0x8018966c` (63 ins ×4) | -| 39 | A-prop | 753 | 3 | 1 | 1.0 | `ov_SC06_020:0x80180b04` | `ov_SC06_020:0x80180b04` (251 ins ×3 jr) | -| 40 | seeded | 752 | 14 | 10 | 0.889 | `ov_SC02_026:0x8017d6d0` | `ov_SC02_027:0x8017f858` (48 ins ×3) | +| 7 | cousin-multi | 1628 | 3 | 3 | · | · | `md_MAIN_011:0x800d1254` (555 ins ×1) | +| 8 | A-prop | 1628 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017c3bc` | `ov_MAIN_012:0x8017c3bc` (407 ins ×4 jr) | +| 9 | A-prop | 1554 | 59 | 3 | 1.0 | `md_SC05_023:0x801edc18` | `md_SC05_023:0x801edc18` (26 ins ×56) | +| 10 | seeded | 1466 | 6 | 3 | 0.93 | `ov_SC07_006:0x801802ec` | `ov_SC07_006:0x8017f9ac` (275 ins ×2) | +| 11 | cousin-multi | 1395 | 5 | 1 | · | · | `ov_SC01_005:0x8017f2d4` (279 ins ×5 jr) | +| 12 | A-prop | 1316 | 7 | 1 | 1.0 | `ov_SC01_000:0x8016ab6c` | `ov_SC01_077:0x8016ab6c` (188 ins ×7 jr) | +| 13 | A-prop | 1309 | 7 | 1 | 1.0 | `ov_SC01_000:0x8013dd68` | `ov_SC01_077:0x8013dd68` (187 ins ×7) | +| 14 | A-prop | 1304 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d730` | `ov_MAIN_012:0x8017d730` (326 ins ×4 jr) | +| 15 | A-prop | 1195 | 30 | 6 | 1.0 | `ov_SC01_077:0x8017d840` | `ov_SC01_077:0x8017d840` (39 ins ×21) | +| 16 | A-prop | 1164 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d2a4` | `ov_MAIN_012:0x8017d2a4` (291 ins ×4 jr) | +| 17 | A-prop | 1089 | 3 | 1 | 1.0 | `ov_SC01_000:0x8015444c` | `ov_SC01_077:0x8015444c` (363 ins ×3 jr) | +| 18 | A-prop | 1049 | 4 | 3 | 1.0 | `ov_SC03_028:0x80183e04` | `ov_SC03_028:0x80183e04` (265 ins ×2) | +| 19 | A-prop | 1035 | 11 | 5 | 1.0 | `ov_SC06_018:0x80187320` | `ov_SC02_011:0x80188250` (94 ins ×4) | +| 20 | seeded | 1034 | 19 | 11 | 0.976 | `ov_SC01_000:0x8017d1f0` | `ov_SC06_022:0x8017d6e0` (59 ins ×4) | +| 21 | A-prop | 996 | 46 | 10 | 1.0 | `ov_SC03_001:0x8017f0ec` | `ov_SC03_001:0x8017f0ec` (20 ins ×30) | +| 22 | A-prop | 987 | 3 | 1 | 1.0 | `ov_SC01_000:0x8013c414` | `ov_SC01_077:0x8013c414` (329 ins ×3 jr) | +| 23 | A-prop | 964 | 12 | 3 | 1.0 | `md_SC03_073:0x801efc94` | `md_SC03_079:0x801efb94` (87 ins ×4) | +| 24 | A-prop | 924 | 21 | 1 | 1.0 | `ov_SC02_026:0x80185634` | `ov_SC02_026:0x80185634` (44 ins ×21) | +| 25 | A-prop | 914 | 8 | 4 | 1.0 | `ov_SC02_017:0x8017f92c` | `ov_SC02_017:0x8017f92c` (115 ins ×4) | +| 26 | A-prop | 908 | 19 | 6 | 1.0 | `ov_SC02_041:0x80182ccc` | `ov_SC02_041:0x80182ccc` (45 ins ×9) | +| 27 | A-prop | 889 | 7 | 1 | 1.0 | `ov_SC01_000:0x80145cec` | `ov_SC01_077:0x80145cec` (127 ins ×7) | +| 28 | A-prop | 872 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017cf3c` | `ov_MAIN_012:0x8017cf3c` (218 ins ×4 jr) | +| 29 | A-prop | 816 | 3 | 1 | 1.0 | `ov_SC01_000:0x8013b83c` | `ov_SC01_077:0x8013b83c` (272 ins ×3 jr) | +| 30 | A-prop | 810 | 54 | 1 | 1.0 | `md_SC03_073:0x801ef5f4` | `ov_SC01_077:0x80130d0c` (15 ins ×54) | +| 31 | cousin-multi | 810 | 10 | 2 | · | · | `ov_SC01_005:0x8017e108` (84 ins ×5) | +| 32 | A-prop | 798 | 3 | 1 | 1.0 | `ov_SC01_000:0x80130d48` | `ov_SC01_077:0x80130d48` (266 ins ×3) | +| 33 | cousin-multi | 784 | 4 | 1 | · | · | `ov_SC03_108:0x8017ffd0` (196 ins ×4 jr) | +| 34 | cousin-multi | 781 | 8 | 4 | · | · | `ov_SC02_011:0x8018c5a4` (100 ins ×3) | +| 35 | cousin-multi | 778 | 7 | 5 | · | · | `ov_SC03_118:0x80181e58` (113 ins ×2) | +| 36 | cousin-multi | 767 | 15 | 7 | 0.735 | · | `ov_SC03_028:0x8018966c` (63 ins ×4) | +| 37 | seeded | 752 | 14 | 10 | 0.889 | `ov_SC02_026:0x8017d6d0` | `ov_SC02_027:0x8017f858` (48 ins ×3) | +| 38 | seeded | 749 | 32 | 10 | 0.923 | `ov_SC03_001:0x80180b5c` | `ov_SC04_018:0x80183248` (24 ins ×7) | +| 39 | A-prop | 726 | 33 | 4 | 1.0 | `md_SC05_023:0x801ed99c` | `md_SC05_023:0x801ed99c` (22 ins ×27) | +| 40 | cousin-multi | 702 | 7 | 3 | · | · | `ov_SC03_014:0x80188828` (99 ins ×3) | diff --git a/docs/family-hseq.md b/docs/family-hseq.md index 0e891e08b..873224e63 100644 --- a/docs/family-hseq.md +++ b/docs/family-hseq.md @@ -5,13 +5,13 @@ > Generated by `tools/family_hseq.py` from the 212 binary sigs + per-binary src stubs. Ranked by TEMPLATABLE byte-weight (PURE+IMM members × nins × 4). The byte-gate is the arbiter. > -> **Scope (212 binaries): 141 location overlays + 70 md_* modules + the resident · MAIN IS EXCLUDED** · generated at HEAD `commit:1775` · stub set derived from `corpus.stubs` — the same oracle `progress.py` counts, so same-tree overlay totals agree by construction; compare digests only at the same HEAD. +> **Scope (212 binaries): 141 location overlays + 70 md_* modules + the resident · MAIN IS EXCLUDED** · generated at HEAD `commit:1784` · stub set derived from `corpus.stubs` — the same oracle `progress.py` counts, so same-tree overlay totals agree by construction; compare digests only at the same HEAD. -**Fleet (overlays):** 96.8% fn / 95.7% instr / 90.9% distinct-code matched. Unmatched: 11,627 instances / 584,448 ins (10,001 distinct classes). +**Fleet (overlays):** 96.8% fn / 95.7% instr / 91.0% distinct-code matched. Unmatched: 11,579 instances / 577,973 ins (9,960 distinct classes). -**Tail cross-check (Phase-25 close):** 7,645 tail fns / 380,112 ins → 446 h_seq families ≥2, **77 substantial (nins≥80) / 34,893 ins**. +**Tail cross-check (Phase-25 close):** 7,612 tail fns / 375,257 ins → 439 h_seq families ≥2, **72 substantial (nins≥80) / 30,992 ins**. -**Full frontier (all unmatched by h_seq):** 2027 target families (≥2 members or a matched sibling) + 4513 singletons (Step-D residue). Substantial: **274 families / 100,941 templatable ins**, 62 with a matched sibling (zero-crack). Substantial member classes: 701 PURE · 29 IMM · 6 STRUCT-excluded. +**Full frontier (all unmatched by h_seq):** 2015 target families (≥2 members or a matched sibling) + 4513 singletons (Step-D residue). Substantial: **267 families / 95,614 templatable ins**, 55 with a matched sibling (zero-crack). Substantial member classes: 677 PURE · 29 IMM · 6 STRUCT-excluded. ## Top substantial families (by templatable byte-weight) @@ -22,49 +22,49 @@ | 2 | 611 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x80186e24 matched | 2,444 | | 3 | 770 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x80144b9c matched-ov077 | 2,310 | | 4 | 551 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x80189540 matched | 2,204 | -| 5 | 557 | 3 (3/0/0) | 3/3 | cross-address | PURE | 2 | Y | 0x80189030 matched | 1,671 | -| 6 | 407 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017c3bc matched | 1,628 | -| 7 | 279 | 5 (5/0/0) | 4/5 | cross-address | PURE | 0 | Y | 0x8017f2d4 modal | 1,395 | -| 8 | 188 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | Y | 0x8016ab6c matched-ov077 | 1,316 | -| 9 | 187 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | · | 0x8013dd68 matched-ov077 | 1,309 | -| 10 | 326 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017d730 matched | 1,304 | -| 11 | 291 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017d2a4 matched | 1,164 | -| 12 | 363 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8015444c matched-ov077 | 1,089 | -| 13 | 102 | 10 (0/10/0) | 9/10 | scattered | IMM | 78 | · | 0x8017d290 matched | 1,020 | -| 14 | 329 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013c414 matched-ov077 | 987 | -| 15 | 312 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x8013faf8 matched-ov077 | 936 | -| 16 | 127 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | · | 0x80145cec matched-ov077 | 889 | -| 17 | 218 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017cf3c matched | 872 | -| 18 | 272 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013b83c matched-ov077 | 816 | -| 19 | 266 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x80130d48 matched-ov077 | 798 | -| 20 | 196 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | Y | 0x8017ffd0 modal | 784 | -| 21 | 251 | 3 (3/0/0) | 3/3 | cross-address | PURE | 3 | Y | 0x80180b04 matched | 753 | -| 22 | 108 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | Y | 0x8017eeec modal | 648 | -| 23 | 203 | 3 (0/3/0) | 2/3 | cross-address | IMM | 7 | · | 0x8017e804 matched | 609 | -| 24 | 201 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x8015d738 matched-ov077 | 603 | -| 25 | 198 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013bd74 matched-ov077 | 594 | -| 26 | 144 | 4 (4/0/0) | 3/4 | cross-address | PURE | 1 | · | 0x8018b238 matched | 576 | -| 27 | 94 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | · | 0x801874e4 modal | 564 | -| 28 | 110 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x80188b9c matched | 550 | -| 29 | 275 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f9ac modal | 550 | -| 30 | 91 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | · | 0x80189e60 modal | 546 | -| 31 | 265 | 2 (0/2/0) | 2/2 | cross-address | IMM | 5 | · | 0x80183e04 matched | 530 | -| 32 | 127 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x8017eb70 matched | 508 | -| 33 | 101 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x8017f278 matched | 505 | -| 34 | 124 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x8017dd28 matched | 496 | -| 35 | 99 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x80187960 matched | 495 | -| 36 | 246 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f5d4 modal | 492 | -| 37 | 154 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013c0f8 matched-ov077 | 462 | -| 38 | 148 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x8016d1d8 matched-ov077 | 444 | -| 39 | 111 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801832f8 modal | 444 | -| 40 | 108 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017ca18 matched | 432 | -| 41 | 107 | 4 (4/0/0) | 2/4 | cross-address | PURE | 2 | · | 0x8018a438 matched | 428 | -| 42 | 85 | 5 (5/0/0) | 5/5 | cross-address | PURE | 0 | · | 0x8017d3f4 modal | 425 | -| 43 | 212 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x801805d4 modal | 424 | -| 44 | 84 | 5 (5/0/0) | 4/5 | cross-address | PURE | 0 | · | 0x8017e108 modal | 420 | -| 45 | 209 | 2 (2/0/0) | 2/2 | cross-address | PURE | 0 | Y | 0x801810c8 modal | 418 | -| 46 | 201 | 2 (2/0/0) | 1/2 | per-location | PURE | 1 | Y | 0x80181be4 matched-ov077 | 402 | -| 47 | 397 | 1 (1/0/0) | 1/1 | per-location | PURE | 2 | · | 0x8018f060 matched | 397 | -| 48 | 197 | 2 (2/0/0) | 1/2 | per-location | PURE | 0 | · | 0x80184a08 modal | 394 | -| 49 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8017dab8 modal | 388 | -| 50 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x80181edc modal | 388 | +| 5 | 407 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017c3bc matched | 1,628 | +| 6 | 279 | 5 (5/0/0) | 4/5 | cross-address | PURE | 0 | Y | 0x8017f2d4 modal | 1,395 | +| 7 | 188 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | Y | 0x8016ab6c matched-ov077 | 1,316 | +| 8 | 187 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | · | 0x8013dd68 matched-ov077 | 1,309 | +| 9 | 326 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017d730 matched | 1,304 | +| 10 | 291 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017d2a4 matched | 1,164 | +| 11 | 363 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8015444c matched-ov077 | 1,089 | +| 12 | 102 | 10 (0/10/0) | 9/10 | scattered | IMM | 78 | · | 0x8017d290 matched | 1,020 | +| 13 | 329 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013c414 matched-ov077 | 987 | +| 14 | 127 | 7 (7/0/0) | 1/7 | per-location | PURE | 134 | · | 0x80145cec matched-ov077 | 889 | +| 15 | 218 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017cf3c matched | 872 | +| 16 | 272 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013b83c matched-ov077 | 816 | +| 17 | 266 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x80130d48 matched-ov077 | 798 | +| 18 | 196 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | Y | 0x8017ffd0 modal | 784 | +| 19 | 108 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | Y | 0x8017eeec modal | 648 | +| 20 | 203 | 3 (0/3/0) | 2/3 | cross-address | IMM | 7 | · | 0x8017e804 matched | 609 | +| 21 | 198 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013bd74 matched-ov077 | 594 | +| 22 | 94 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | · | 0x801874e4 modal | 564 | +| 23 | 110 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x80188b9c matched | 550 | +| 24 | 275 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f9ac modal | 550 | +| 25 | 91 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | · | 0x80189e60 modal | 546 | +| 26 | 265 | 2 (0/2/0) | 2/2 | cross-address | IMM | 5 | · | 0x80183e04 matched | 530 | +| 27 | 127 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x8017eb70 matched | 508 | +| 28 | 101 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x8017f278 matched | 505 | +| 29 | 124 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x8017dd28 matched | 496 | +| 30 | 246 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f5d4 modal | 492 | +| 31 | 154 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013c0f8 matched-ov077 | 462 | +| 32 | 148 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x8016d1d8 matched-ov077 | 444 | +| 33 | 111 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801832f8 modal | 444 | +| 34 | 108 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017ca18 matched | 432 | +| 35 | 107 | 4 (4/0/0) | 2/4 | cross-address | PURE | 2 | · | 0x8018a438 matched | 428 | +| 36 | 85 | 5 (5/0/0) | 5/5 | cross-address | PURE | 0 | · | 0x8017d3f4 modal | 425 | +| 37 | 212 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x801805d4 modal | 424 | +| 38 | 84 | 5 (5/0/0) | 4/5 | cross-address | PURE | 0 | · | 0x8017e108 modal | 420 | +| 39 | 209 | 2 (2/0/0) | 2/2 | cross-address | PURE | 0 | Y | 0x801810c8 modal | 418 | +| 40 | 201 | 2 (2/0/0) | 1/2 | per-location | PURE | 1 | Y | 0x80181be4 matched-ov077 | 402 | +| 41 | 397 | 1 (1/0/0) | 1/1 | per-location | PURE | 2 | · | 0x8018f060 matched | 397 | +| 42 | 197 | 2 (2/0/0) | 1/2 | per-location | PURE | 0 | · | 0x80184a08 modal | 394 | +| 43 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8017dab8 modal | 388 | +| 44 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x80181edc modal | 388 | +| 45 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801822c0 modal | 388 | +| 46 | 96 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8017fd64 modal | 384 | +| 47 | 192 | 2 (2/0/0) | 2/2 | cross-address | PURE | 1 | · | 0x80191020 matched | 384 | +| 48 | 191 | 2 (2/0/0) | 1/2 | per-location | PURE | 0 | · | 0x80188990 modal | 382 | +| 49 | 127 | 3 (3/0/0) | 3/3 | cross-address | PURE | 0 | · | 0x80181d44 modal | 381 | +| 50 | 95 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8018317c modal | 380 | diff --git a/tools/family_cousins.py b/tools/family_cousins.py index 8b64505b9..e49f236f8 100644 --- a/tools/family_cousins.py +++ b/tools/family_cousins.py @@ -354,14 +354,118 @@ def emit_targets(n, wave): f"unresolved seed paths) · {sum(1 for t in targets if t['prior'])} prior-noted", file=sys.stderr) +# ---------------------------------------------------------------- micro-adapt cards (S49) +ADAPT_JSON = ".run/adapt_cards.json" +LI_TOKS = {(0x0F,), (0x0D,), (0x09,), (0x08,)} # lui, ori, addiu, addi +SMALL_BLOCKS, SMALL_TOKENS = 3, 6 + + +def _disasm(words, base_vram): + """Best-effort disassembly for card readability (falls back to hex-only).""" + try: + import rabbitizer as R + out = [] + for k, w in enumerate(words): + try: + out.append(str(R.Instruction(w, base_vram + 4 * k))) + except Exception: + out.append("?") + return out + except Exception: + return ["?"] * len(words) + + +def emit_adapt_cards(): + """Per open member of a SEEDED unit, classify drift vs the seed's token stream and emit a + micro-adapt card for the LI-ONLY / SMALL-EDIT classes (the ~1,100-member wave-7a pool). + The card carries everything a cheap-tier agent needs: the seed's C body location, the aligned + diff blocks with the MEMBER's raw words + disassembly at each site (the new constant is + readable right there), and the member's own .s home for symbol ground truth. MIXED members are + skipped (standard seeded-crack wave work). Cousin-multi units are skipped — their siblings + become adaptable only after wave 7 cracks the unit head.""" + out = json.load(open(OUT_JSON)) + sigs, _stubs = load_corpus() + + def rec(b, a): + return sigs.get(b, {}).get(a) + + cards, counts = [], collections.Counter() + for u in out["units"]: + if u["cat"] != "seeded" or not u["seed_ref"]: + continue + sb, sa, snins = u["seed_ref"] + sws = FR.stream_words(sb, sa, snins) + if sws is None: + continue + ss = tuple(tok(w) for w in sws) + sname = (rec(sb, sa) or {}).get("name", f"func_{sa:08X}") + sbody = seed_body_ref(sb, sa) + for sk in u["skels"]: + b, a = sk["b"], int(sk["a"], 16) + r = rec(b, a) + if r is None: + continue + mws = FR.stream_words(b, a, r["nins"]) + if mws is None: + continue + ms = tuple(tok(w) for w in mws) + sm = SequenceMatcher(None, ms, ss, autojunk=False) + blocks = [(t, i1, i2, j1, j2) for t, i1, i2, j1, j2 in sm.get_opcodes() if t != "equal"] + if not blocks: + counts["IDENTICAL"] += 1 + continue + li_ok = all(t in ("insert", "delete") + and all(x in LI_TOKS for x in (ms[i1:i2] or ss[j1:j2])) + for t, i1, i2, j1, j2 in blocks) + ntok = sum(max(i2 - i1, j2 - j1) for _, i1, i2, j1, j2 in blocks) + klass = ("LI-ONLY" if li_ok + else "SMALL-EDIT" if len(blocks) <= SMALL_BLOCKS and ntok <= SMALL_TOKENS + else "MIXED") + counts[klass] += 1 + if klass == "MIXED": + continue + name = r.get("name", f"func_{a:08X}") + try: + sub = os.path.dirname(corpus.asm_path(b, name)) + except Exception: + sub = None + vram = a + diff = [] + for t, i1, i2, j1, j2 in blocks: + mw = mws[i1:i2] + diff.append(dict( + kind=t, + member_at=i1, member_words=[f"0x{w:08x}" for w in mw], + member_disasm=_disasm(mw, vram + 4 * i1), + seed_at=j1, seed_words=[f"0x{w:08x}" for w in sws[j1:j2]], + seed_disasm=_disasm(sws[j1:j2], sa + 4 * j1), + )) + cards.append(dict( + name=name, binary=b, addr=f"0x{a:08x}", nins=r["nins"], sub=sub, + reach=sk["mem"], jr=bool(sk["jr"]), klass=klass, sim=sk["seed_sim"], + seed=dict(name=sname, binary=sb, addr=f"0x{sa:08x}", nins=snins, + kind=sbody["kind"], path=sbody["path"]), + diff=diff, n_blocks=len(blocks), n_tokens=ntok, + )) + cards.sort(key=lambda c: (-c["reach"] * c["nins"], c["n_tokens"])) + json.dump(cards, open(ADAPT_JSON, "w"), indent=1) + print(f"adapt cards: {len(cards)} " + f"(classes over seeded members: {dict(counts)}) -> {ADAPT_JSON}") + + if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("--targets", type=int, metavar="N", help="emit top-N wave targets (crack_wave.js shape) to stdout") ap.add_argument("--wave", default="wave7", help="wave tag stamped on targets (output dir routing)") + ap.add_argument("--adapt-cards", action="store_true", help="emit micro-adapt cards for seeded LI-ONLY/SMALL-EDIT members") args = ap.parse_args() if args.targets: if not os.path.exists(OUT_JSON): sys.exit(f"{OUT_JSON} missing — run the survey first") emit_targets(args.targets, args.wave) + elif args.adapt_cards: + if not os.path.exists(OUT_JSON): + sys.exit(f"{OUT_JSON} missing — run the survey first") + emit_adapt_cards() else: survey() diff --git a/tools/wave/README.md b/tools/wave/README.md index ad18714ce..6f2d14985 100644 --- a/tools/wave/README.md +++ b/tools/wave/README.md @@ -21,3 +21,15 @@ where each arg is `{name, binary, sub, nins, reach, jr, model, prior}`. all-Opus wave 1 on bank rate. **Bank rate to date:** 67% → 79% → 69% → 68% of cracks survive the whole-binary gate. + +## adapt_wave.js — the micro-adapt lane (P30 S49) + +Cheap-tier companion to `crack_wave.js` for the COUSIN tier (cookbook §168): members that differ +from an already-matched body by ≤3 blocks / ≤6 tokens (`family_cousins.py --adapt-cards` → +`.run/adapt_cards.json`). The prompt is an EDIT contract, not a crack: copy the seed body, take the +symbol surface from the TARGET .s, apply the minimal semantic edit the measured diff implies +(usually a constant whose value is readable in the card's member words), iterate under match_one. +Same paid-for contracts as crack_wave: per-agent dirs, sha1-last verify, UNVERIFIED ≠ refuted, +never touch outside your own dir. Routing: haiku ≤60 ins / sonnet above (the edit is small even +when the body is not). Invoke: `Workflow({scriptPath:"tools/wave/adapt_wave.js", +args:[{wave:"wave7a"}, ...cards]})`. Pilot-first (R37): ~30 cards, measure the bank rate, then batch. diff --git a/tools/wave/adapt_wave.js b/tools/wave/adapt_wave.js new file mode 100644 index 000000000..34d98681e --- /dev/null +++ b/tools/wave/adapt_wave.js @@ -0,0 +1,152 @@ +export const meta = { + name: 'p30-s49-adapt-wave', + description: 'Micro-adapt wave: edit a proven matched body onto a near-identical cousin (LI-ONLY / SMALL-EDIT cards)', + phases: [ + { title: 'Adapt', detail: 'cheap-tier seeded edits — the diff names the <=6 tokens that change' }, + { title: 'Verify', detail: 'independent re-gate + existence proof, sha1 re-checked LAST' }, + ], +} + +// ⚠ TEMPLATE-LITERAL MINEFIELD (same as crack_wave.js): prompts are backtick strings — a markdown +// backtick or a literal dollar-brace in prose TERMINATES the string and the workflow dies at parse +// time. Use "double quotes" for inline code in prose, escape any literal \` and \${. +// +// Cards come from: tools/family_cousins.py --adapt-cards -> .run/adapt_cards.json +// Each card: {name, binary, addr, nins, sub, reach, jr, klass, sim, +// seed:{name,binary,addr,nins,kind,path}, diff:[{kind,member_at,member_words, +// member_disasm,seed_at,seed_words,seed_disasm}], n_blocks, n_tokens} +// Invoke: Workflow({scriptPath:"tools/wave/adapt_wave.js", args:[{wave:"wave7a"}, ...cards]}) +const REPO = '/home/musashi/bfm-decomp' +const ALL = typeof args === 'string' ? JSON.parse(args) : args +const CARDS = ALL.filter(c => c.name && c.seed) +const WAVE = (ALL.find(c => c.wave) || {}).wave || 'wave7a' +// The edit is small regardless of body size, but a long body copy still wants the mid tier. +const modelFor = c => (c.nins <= 60 ? 'haiku' : 'sonnet') + +const ADAPT_SCHEMA = { + type: 'object', + required: ['fn', 'verdict', 'draft_path', 'notes'], + properties: { + fn: {type: 'string'}, + verdict: {type: 'string', enum: ['MATCH', 'NEAR', 'FAIL']}, + draft_path: {type: 'string'}, + diff_ins: {type: 'integer'}, + sha1: {type: 'string', description: 'sha1sum of the draft you are leaving on disk'}, + notes: {type: 'string', description: 'the exact edit you made (constant/statement), the symbol renames, integration surface; exact residual if NEAR'}, + }, +} +const VERDICT_SCHEMA = { + type: 'object', + required: ['fn', 'confirmed', 'evidence'], + properties: { + fn: {type: 'string'}, + confirmed: {type: 'boolean'}, + file_exists: {type: 'boolean'}, + sha1_after: {type: 'string', description: 'sha1sum re-run LAST, after the gate'}, + evidence: {type: 'string'}, + }, +} + +function diffBlock(c) { + return c.diff.map((d, i) => + ` block ${i + 1} [${d.kind}] at member instr ${d.member_at} / seed instr ${d.seed_at}: + member words: ${d.member_words.join(' ')} (${d.member_disasm.join(' | ')}) + seed words : ${d.seed_words.join(' ')} (${d.seed_disasm.join(' | ')})`).join('\n') +} + +function adaptPrompt(c) { + const seedWhere = c.seed.kind === 'macro' + ? `${c.seed.path} — the body is the DEFINE_${c.seed.name}() macro (read it there; it is the byte-proven body)` + : `${c.seed.path} — the plain definition of ${c.seed.name}` + return `You are ADAPTING a proven byte-matched body onto a near-identical sibling function — Brave Fencer Musashi matching decomp (PS1, gcc-2.7.2 + maspsx). Repo: ${REPO}. This is an EDIT task, not a from-scratch crack. + +TARGET: ${c.name} in ${c.binary} — ${c.nins} instructions${c.jr ? ' (mid-function jr / jump table)' : ''}. Asm truth: ${c.sub}/${c.name}.s +SEED (${(c.sim * 100).toFixed(1)}% skeleton-similar, already byte-matched as ${c.seed.name} in ${c.seed.binary}): ${seedWhere} + +THE MEASURED DIFF — the target differs from the seed at exactly ${c.n_blocks} site(s), ${c.n_tokens} instruction(s) total (class ${c.klass}): +${diffBlock(c)} + +METHOD: +1. Read the seed body. Copy it to ${REPO}/.run/${WAVE}/${c.name}/${c.name}.c as the definition of ${c.name}. +2. RENAME the symbol surface to the TARGET's own: every callee "func_XXXXXXXX", data "D_XXXXXXXX", + and jump-table label must come FROM THE TARGET .s (${c.sub}/${c.name}.s) — the .s is the symbol + ground truth. Do not keep the seed binary's symbols. Positional correspondence: the bodies align + 1:1 outside the diff blocks above. +3. Apply the minimal SEMANTIC edit the diff implies. An insert/delete of lui/ori/addiu is almost + always a changed CONSTANT — decode the target's words above (lui hi16 + ori/addiu lo16) and use + the TARGET's value in the C literal. An inserted load/store or branch token usually means one + added or removed statement/condition. Make the smallest edit that explains the diff. +4. Iterate: cd ${REPO} && python3 tools/match_one.py ${c.name} --c .run/${WAVE}/${c.name}/${c.name}.c --asm-subdir ${c.sub} +5. On MATCH: sha1sum the draft, report it, STOP. If two honest attempts leave a residual, report + NEAR with the exact remaining diff — NEAR is a resumable state, not a failure. + +OUTPUT DISCIPLINE (a previous wave lost 21 verified drafts to a shared directory): +- Your ONLY output dir is ${REPO}/.run/${WAVE}/${c.name}/ — create it, work there. +- Final draft MUST be exactly ${REPO}/.run/${WAVE}/${c.name}/${c.name}.c +- NEVER delete, move or modify ANY file outside your own directory. Sibling agents are working in + adjacent directories RIGHT NOW. Do NOT clean up at the end. +- Never write to src/, config/, include/ or any tracked file. No make. No git. + +RULES: gcc-2.7.2 emits no "error:" prefix — read the raw compiler text. match_one MATCH is a +CANDIDATE (cookbook §52b) — state your integration surface in notes: which callees/globals you +declare and whether the host TU declares them differently (§161c). Match register allocation +exactly (G3). If the seed body is a DEFINE_ macro, expand it to a plain function definition in your +draft — do not emit a macro.` +} + +function verifyPrompt(r, c) { + return `ADVERSARIAL verification, Brave Fencer Musashi decomp. Repo: ${REPO}. + +${r.fn} (${c.binary}, ${c.nins} ins, ADAPTED from ${c.seed.name}) claimed MATCH at ${r.draft_path}; claimed sha1 ${r.sha1 || '(none)'}. + +REFUTE it. Default confirmed=false. In this order: +1. "ls -l" and "sha1sum" the draft — it must EXIST. +2. Gate it yourself: cd ${REPO} && python3 tools/match_one.py ${r.fn} --c ${r.draft_path} --asm-subdir ${c.sub} + ("FAIL: no C file" means the draft is MISSING — that is a refutation, never a pass.) +3. The draft must DEFINE ${r.fn} as a plain function (no INCLUDE_ASM, no macro emission, no inline + asm standing in for compiled code), and its symbol surface must come from the TARGET .s, not the + seed binary (spot-check two callee/data names against ${c.sub}/${r.fn}.s). +4. Re-run "sha1sum" LAST, after everything else, and report it as sha1_after. If the file vanished + or changed since step 1, say so loudly. +Do NOT edit or delete anything. Report failures; do not fix them.` +} + +phase('Adapt') +const results = await pipeline( + CARDS, + c => agent(adaptPrompt(c), {label: `adapt:${c.name}`, phase: 'Adapt', model: modelFor(c), schema: ADAPT_SCHEMA}) + .then(r => (r ? {...r, c} : null)), + r => { + if (!r || r.verdict !== 'MATCH' || !r.draft_path) return r + return agent(verifyPrompt(r, r.c), {label: `verify:${r.fn}`, phase: 'Verify', schema: VERDICT_SCHEMA}) + .then(v => ({...r, verdict_check: v})) + } +) + +const rows = results.filter(Boolean) +// A DEAD ORACLE IS NOT A NEGATIVE VERDICT (P30 S48): UNVERIFIED is its own outcome — re-verify, +// never bank, never discard. +const claimed = rows.filter(r => r.verdict === 'MATCH') +const unverified = claimed.filter(r => !r.verdict_check) +const confirmed = claimed.filter(r => r.verdict_check && r.verdict_check.confirmed + && r.verdict_check.file_exists !== false) +const refuted = claimed.filter(r => r.verdict_check && !confirmed.includes(r)) +const near = rows.filter(r => r.verdict === 'NEAR') +const fail = rows.filter(r => r.verdict === 'FAIL') +log(`adapt wave: confirmed ${confirmed.length} / refuted ${refuted.length} / UNVERIFIED ${unverified.length}` + + ` / near ${near.length} / fail ${fail.length} of ${rows.length}`) +return { + counts: {cards: CARDS.length, returned: rows.length, confirmed: confirmed.length, + refuted: refuted.length, unverified: unverified.length, + near: near.length, fail: fail.length}, + unverified: unverified.map(r => ({fn: r.fn, binary: r.c.binary, draft_path: r.draft_path, + sha1: r.sha1, note: 'VERIFIER NEVER RAN — re-verify, do not discard'})), + confirmed: confirmed.map(r => ({fn: r.fn, binary: r.c.binary, reach: r.c.reach, nins: r.c.nins, + klass: r.c.klass, seed: r.c.seed.name, model: modelFor(r.c), + draft_path: r.draft_path, sha1: r.sha1, notes: r.notes})), + refuted: refuted.map(r => ({fn: r.fn, draft_path: r.draft_path, + evidence: r.verdict_check && r.verdict_check.evidence})), + near: near.map(r => ({fn: r.fn, binary: r.c.binary, diff_ins: r.diff_ins, notes: r.notes, + draft_path: r.draft_path})), + fail: fail.map(r => ({fn: r.fn, binary: r.c.binary, notes: r.notes})), +}