feat(phase-30 S49): the micro-adapt lane — adapt cards + adapt_wave.js (wave 7a)

- family_cousins.py --adapt-cards: per seeded-unit member, drift classified vs the seed
  (LI-ONLY 27 / SMALL-EDIT 491 / MIXED 310 excluded); cards carry the seed C location + the
  aligned diff blocks with the member's raw words + disasm (the new constant is readable in
  the card). 518 cards / 1,101 instances / 23,820 ins; 514 haiku-band.
- tools/wave/adapt_wave.js: the EDIT-contract wave (crack_wave contracts preserved: per-agent
  dirs, sha1-last, UNVERIFIED != refuted); symbol surface from the TARGET .s; haiku<=60/sonnet.
- regen chain absorbed the 48 lane-A banks (A-prop open ins -6,475 == the report's instr
  delta exactly — two independent derivations agree); pilot slate .run/wave7a_pilot.json (30).
- R37: pilot before scaling to the 518-card pool.
This commit is contained in:
Drew T
2026-08-12 22:03:04 -06:00
parent ea94fece6f
commit dcba5d0f4f
5 changed files with 355 additions and 87 deletions
+37 -37
View File
@@ -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) |
+50 -50
View File
@@ -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 |
+104
View File
@@ -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()
+12
View File
@@ -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.
+152
View File
@@ -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})),
}