feat(phase-30 S50): bank the top-reach families 0x801F1CD8 (41/41) and 0x8017D290 (9/9)

Three carries a mechanical seed-body draft needs beyond the symbol rebase (cookbook §171b),
each found from one compiler verdict:

- DATA DEFINED INSIDE THE MEMBER'S OWN .s must be DEFINED, not externed — it vanishes with
  the stub it lives in. Re-initialised with THIS member's bytes (the 0x801F1CD8 family carries
  4 distinct 8-byte patterns across 42 members). Flat-byte-list initializers only; refuse the
  rest rather than mis-initialise something the gate would reject unexplainably.
- SHARED TYPES the destination cannot see: MATRIX/SVECTOR live in engine_types.h, which md_*
  TUs never include. `parse error before 'm1'` was the only thing between 4 of 9 members and a
  bank. Carried brace-matched, vetoed by the destination.
- A POSITIONAL LITERAL MAP where imm_map_tier1 gives up: it refuses a value that also appears
  at a non-differing slot (0x10 collides with the struct offsets), but the 8 differing slots
  map 1:1 onto the C's call sites in order. Asserted ([C literals] == [seed slot values])
  before substituting — that assert is the whole safety argument. 10/10 refused -> 9/9 banked.

- ROOT-CAUSE FIX: body_text matched `extern void func_X(...);` at column 0 and returned the
  NEXT function's body — silently shipping wrong seed bodies, visible only as "no definition
  after rename" skips. A definition is now confirmed by a `{` with no `;` before it.

R22 clean rebuild: check-all 213 passed, 0 failed of 213. Stubs 12,161 -> 12,111.
Fleet 95.3% instr / 90.0% distinct / 96.66% fn-count.
This commit is contained in:
Drew T
2026-08-14 11:23:26 -06:00
parent 9c16d8a8b8
commit e7d61e823d
8 changed files with 1150 additions and 1019 deletions
+919 -921
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -2,7 +2,7 @@
> **Generated by `tools/cookbook_index.py` — do not hand-edit** (R33). Regenerate after adding a cookbook section.
>
> `docs/matching-cookbook.md` is ~716 KB / 513 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
> `docs/matching-cookbook.md` is ~716 KB / 514 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
**How to use:** name what you SEE in the diff (a stolen delay slot, an extra `la`, a swapped register pair, a `conflicting types` error), find that symptom below, read those sections first. If nothing fits, THEN grind — and add a section when you win.
@@ -309,7 +309,7 @@
- **§127a** — §71 (sibling-first) is the strongest `-O0` lever, and it beats the index <sub>L8377</sub>
- **§132** — The `JR-PAIR-IN-ONE-O0-OBJECT` "wall" was TWO instrument defects: a merged-double span the carve could not see, and a truncated object no rule deleted (P30 S29, `func_8013B83C` + `func_8013BD74`) <sub>L8570</sub>
### family propagation & sweeps (85)
### family propagation & sweeps (86)
- **§8d** — Templating a body INTO a TU must not CHANGE its declaration environment — demote the carried data externs (Phase 26 session 8, byte-proven on `func_8015AE2C` ×133) <sub>L483</sub>
- **§11** — Cross-binary dedup & code-sharing (Phase 11 — "one match unlocks many") <sub>L908</sub>
@@ -396,6 +396,7 @@
- **§3-The** — ADDRESS-CLASS TABLE: which load/store pairs even REACH the `/s` clause (P30 S48 wave 4, `func_80185B44`, ov_SC03_014) <sub>L14339</sub>
- **§168** — THE COUSIN TIER (P30 S49, 2026-08-12): h_seq's exact-hash brittleness, measured — and the similarity map above it <sub>L16208</sub>
- **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop <sub>L16398</sub>
- **§171b** — THREE CARRIES THE MECHANICAL DRAFT NEEDS (P30 S50, banking the top-reach families) <sub>L16448</sub>
### integration / TU plumbing (39)
@@ -1315,3 +1316,4 @@
- **§170** — THE A-PROP WORD-DIFF CARD (P30 S49): the lane that had no owner <sub>L16307</sub>
- **§171** — THE STALE SEED SYMBOL (P30 S50, 2026-08-13): why §170's 91%→57% was never codegen <sub>L16351</sub>
- **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop <sub>L16398</sub>
- **§171b** — THREE CARRIES THE MECHANICAL DRAFT NEEDS (P30 S50, banking the top-reach families) <sub>L16448</sub>
+16 -16
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:1901` 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:2089` 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 | 239 | 1606 | 74209 | family_sweep (propagation) |
| seeded | 364 | 1152 | 33618 | seeded crack — edit a >=0.85-similar MATCHED body |
| cousin-multi | 1542 | 5409 | 248330 | 1 crack seeds the unit's other members |
| cold | 3240 | 3240 | 215454 | full-price crack (the honest unique tail) |
| **total** | **5385** | **11407** | **571611** | |
| A-prop | 187 | 1172 | 59976 | family_sweep (propagation) |
| seeded | 401 | 1311 | 38306 | seeded crack — edit a >=0.85-similar MATCHED body |
| cousin-multi | 1541 | 5406 | 248215 | 1 crack seeds the unit's other members |
| cold | 3238 | 3238 | 215408 | full-price crack (the honest unique tail) |
| **total** | **5367** | **11127** | **561905** | |
## Top units by open instructions (whole-cluster weight)
@@ -20,18 +20,18 @@
|--:|---|--:|--:|--:|--:|---|---|
| 1 | cousin-multi | 3936 | 16 | 1 | · | · | `ov_SC01_077:0x8017c294` (246 ins ×16) |
| 2 | A-prop | 2444 | 4 | 1 | 1.0 | `ov_SC04_018:0x80186e24` | `ov_SC04_018:0x80186e24` (611 ins ×4) |
| 3 | A-prop | 2367 | 22 | 9 | 1.0 | `ov_SC02_011:0x8018a438` | `ov_SC01_000:0x8017d290` (102 ins ×10) |
| 4 | A-prop | 2310 | 3 | 1 | 1.0 | `ov_SC01_000:0x80144b9c` | `ov_SC01_077:0x80144b9c` (770 ins ×3) |
| 5 | A-prop | 2204 | 4 | 1 | 1.0 | `ov_SC04_018:0x80189540` | `ov_SC04_018:0x80189540` (551 ins ×4 jr) |
| 6 | seeded | 2166 | 3 | 3 | 0.987 | `ov_SC06_010:0x8017cad4` | `ov_SC06_010:0x8017bebc` (753 ins ×1) |
| 3 | A-prop | 2310 | 3 | 1 | 1.0 | `ov_SC01_000:0x80144b9c` | `ov_SC01_077:0x80144b9c` (770 ins ×3) |
| 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 | 1939 | 18 | 8 | 1.0 | `ov_SC01_000:0x8017d290` | `ov_SC01_000:0x8017d290` (102 ins ×10) |
| 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 | seeded | 1466 | 6 | 3 | 0.93 | `ov_SC07_006:0x801802ec` | `ov_SC07_006:0x8017f9ac` (275 ins ×2) |
| 10 | cousin-multi | 1395 | 5 | 1 | · | · | `ov_SC01_005:0x8017f2d4` (279 ins ×5 jr) |
| 11 | A-prop | 1320 | 50 | 3 | 1.0 | `md_SC03_076:0x801f1cd8` | `md_SC03_076:0x801f1cd8` (26 ins ×47) |
| 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) |
| 11 | A-prop | 1316 | 7 | 1 | 1.0 | `ov_SC01_000:0x8016ab6c` | `ov_SC01_077:0x8016ab6c` (188 ins ×7 jr) |
| 12 | A-prop | 1309 | 7 | 1 | 1.0 | `ov_SC01_000:0x8013dd68` | `ov_SC01_077:0x8013dd68` (187 ins ×7) |
| 13 | A-prop | 1304 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d730` | `ov_MAIN_012:0x8017d730` (326 ins ×4 jr) |
| 14 | A-prop | 1190 | 45 | 3 | 1.0 | `md_SC03_076:0x801f1cd8` | `md_SC03_076:0x801f1cd8` (26 ins ×42) |
| 15 | A-prop | 1164 | 4 | 1 | 1.0 | `ov_MAIN_012:0x8017d2a4` | `ov_MAIN_012:0x8017d2a4` (291 ins ×4 jr) |
| 16 | A-prop | 1089 | 3 | 1 | 1.0 | `ov_SC01_000:0x8015444c` | `ov_SC01_077:0x8015444c` (363 ins ×3 jr) |
| 17 | A-prop | 1049 | 4 | 3 | 1.0 | `ov_SC03_028:0x80183e04` | `ov_SC03_028:0x80183e04` (265 ins ×2) |
@@ -52,8 +52,8 @@
| 32 | cousin-multi | 767 | 15 | 7 | 0.735 | · | `ov_SC03_028:0x8018966c` (63 ins ×4) |
| 33 | seeded | 752 | 14 | 10 | 0.921 | `ov_SC02_027:0x8017f6d4` | `ov_SC02_027:0x8017f858` (48 ins ×3) |
| 34 | A-prop | 727 | 18 | 6 | 1.0 | `ov_SC01_074:0x8017dba0` | `ov_SC01_077:0x8017d840` (39 ins ×9) |
| 35 | A-prop | 725 | 31 | 10 | 1.0 | `ov_SC04_018:0x80183248` | `ov_SC04_018:0x8017f500` (25 ins ×6) |
| 36 | cousin-multi | 702 | 7 | 3 | · | · | `ov_SC03_014:0x80188828` (99 ins ×3) |
| 35 | cousin-multi | 702 | 7 | 3 | · | · | `ov_SC03_014:0x80188828` (99 ins ×3) |
| 36 | A-prop | 698 | 28 | 10 | 1.0 | `ov_SC03_001:0x80180b5c` | `ov_SC04_018:0x8017f500` (25 ins ×6) |
| 37 | cousin-multi | 696 | 10 | 3 | · | · | `ov_SC02_041:0x80181164` (72 ins ×4) |
| 38 | A-prop | 694 | 17 | 6 | 1.0 | `ov_SC02_005:0x8017df78` | `ov_SC02_005:0x8017df78` (39 ins ×11) |
| 39 | cousin-multi | 680 | 8 | 2 | 0.7 | · | `md_SC03_077:0x801efb58` (87 ins ×4 jr) |
+32 -32
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:1901` · 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:2089` · 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 / 91.1% distinct-code matched. Unmatched: 11,407 instances / 571,611 ins (9,824 distinct classes).
**Fleet (overlays):** 96.9% fn / 95.8% instr / 91.3% distinct-code matched. Unmatched: 11,127 instances / 561,905 ins (9,618 distinct classes).
**Tail cross-check (Phase-25 close):** 7,520 tail fns / 372,305 ins → 440 h_seq families ≥2, **72 substantial (nins≥80) / 30,992 ins**.
**Tail cross-check (Phase-25 close):** 7,424 tail fns / 369,359 ins → 419 h_seq families ≥2, **72 substantial (nins≥80) / 30,992 ins**.
**Full frontier (all unmatched by h_seq):** 2014 target families (≥2 members or a matched sibling) + 4504 singletons (Step-D residue). Substantial: **266 families / 94,856 templatable ins**, 59 with a matched sibling (zero-crack). Substantial member classes: 671 PURE · 29 IMM · 6 STRUCT-excluded.
**Full frontier (all unmatched by h_seq):** 1956 target families (≥2 members or a matched sibling) + 4504 singletons (Step-D residue). Substantial: **257 families / 92,430 templatable ins**, 50 with a matched sibling (zero-crack). Substantial member classes: 649 PURE · 29 IMM · 6 STRUCT-excluded.
## Top substantial families (by templatable byte-weight)
@@ -40,31 +40,31 @@
| 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 | 127 | 3 (3/0/0) | 3/3 | cross-address | PURE | 0 | · | 0x80181d44 modal | 381 |
| 49 | 95 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8018317c modal | 380 |
| 50 | 94 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x80188250 modal | 376 |
| 23 | 275 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f9ac modal | 550 |
| 24 | 91 | 6 (6/0/0) | 6/6 | cross-address | PURE | 0 | · | 0x80189e60 modal | 546 |
| 25 | 265 | 2 (0/2/0) | 2/2 | cross-address | IMM | 5 | · | 0x80183e04 matched | 530 |
| 26 | 101 | 5 (5/0/0) | 5/5 | cross-address | PURE | 1 | · | 0x8017f278 matched | 505 |
| 27 | 124 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | · | 0x8017dd28 matched | 496 |
| 28 | 246 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x8017f5d4 modal | 492 |
| 29 | 154 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | Y | 0x8013c0f8 matched-ov077 | 462 |
| 30 | 148 | 3 (3/0/0) | 1/3 | per-location | PURE | 138 | · | 0x8016d1d8 matched-ov077 | 444 |
| 31 | 111 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801832f8 modal | 444 |
| 32 | 108 | 4 (4/0/0) | 4/4 | cross-address | PURE | 1 | Y | 0x8017ca18 matched | 432 |
| 33 | 85 | 5 (5/0/0) | 5/5 | cross-address | PURE | 0 | · | 0x8017d3f4 modal | 425 |
| 34 | 212 | 2 (2/0/0) | 2/1 | cross-address | PURE | 0 | · | 0x801805d4 modal | 424 |
| 35 | 84 | 5 (5/0/0) | 4/5 | cross-address | PURE | 0 | · | 0x8017e108 modal | 420 |
| 36 | 209 | 2 (2/0/0) | 2/2 | cross-address | PURE | 0 | Y | 0x801810c8 modal | 418 |
| 37 | 201 | 2 (2/0/0) | 1/2 | per-location | PURE | 1 | Y | 0x80181be4 matched-ov077 | 402 |
| 38 | 397 | 1 (1/0/0) | 1/1 | per-location | PURE | 2 | · | 0x8018f060 matched | 397 |
| 39 | 197 | 2 (2/0/0) | 1/2 | per-location | PURE | 0 | · | 0x80184a08 modal | 394 |
| 40 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8017dab8 modal | 388 |
| 41 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x80181edc modal | 388 |
| 42 | 97 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801822c0 modal | 388 |
| 43 | 96 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8017fd64 modal | 384 |
| 44 | 192 | 2 (2/0/0) | 2/2 | cross-address | PURE | 1 | · | 0x80191020 matched | 384 |
| 45 | 127 | 3 (3/0/0) | 3/3 | cross-address | PURE | 0 | · | 0x80181d44 modal | 381 |
| 46 | 95 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x8018317c modal | 380 |
| 47 | 94 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x80188250 modal | 376 |
| 48 | 94 | 4 (4/0/0) | 4/4 | cross-address | PURE | 0 | · | 0x801824dc modal | 376 |
| 49 | 187 | 2 (2/0/0) | 1/2 | per-location | PURE | 0 | · | 0x801800c0 modal | 374 |
| 50 | 187 | 2 (1/1/0) | 2/1 | cross-address | IMM | 0 | · | 0x80183da4 modal | 374 |
+33
View File
@@ -16444,3 +16444,36 @@ silently: **C89 `f()` declares UNSPECIFIED parameters, not zero** — so `decl 0
conflict at all — and a member's own definition read as a call to itself. A pre-check that discards
good drafts is worse than one that lets a few builds fail; when in doubt make it CONSERVATIVE
(flag only `decl > call`, on named parameters).
## §171b — THREE CARRIES THE MECHANICAL DRAFT NEEDS (P30 S50, banking the top-reach families)
Working the frontier's top three families end-to-end exposed three carries a seed-body draft needs
beyond the symbol rebase. Each was found by reading a single compiler verdict, and each generalises.
**1. DATA DEFINED INSIDE THE MEMBER'S OWN `.s` must be DEFINED, not declared.** When a symbol's
bytes live between `dlabel`/`enddlabel` in the very `.s` the draft replaces, an `extern` links to
nothing — the data vanishes with the stub. Emit the seed's definition re-initialised with **this
member's own bytes**: they differ per location (the `0x801F1CD8` family carries four distinct
8-byte patterns across 42 members). Substitute only when the initializer is a flat byte list whose
count matches, and refuse otherwise — a partially-understood initializer silently mis-initialised
is a failure the gate catches but nobody can explain. *Verdict that names it:* `undefined reference
to 'D_…'` on a symbol that visibly exists in the binary's asm.
**2. SHARED TYPES the destination cannot see.** `MATRIX`, `SVECTOR` and friends live in
`engine_types.h`, which `ov_*` TUs reach through `engine_core.h` and **`md_*` TUs do not include at
all**. A draft using one in an md module dies as `parse error before 'm1'` — 4 of the 9
`0x8017D290` members, and the only thing between them and a bank. Carry the brace-matched typedef
block, vetoed by the destination (re-declaring a type it already has is a hard C89 error).
**3. A POSITIONAL LITERAL MAP where the ordinal engine gives up.** `imm_map_tier1` refuses a value
that also appears at a NON-differing asm position (`asm-ambiguous`) — here `0x10`, which collides
with the struct offsets `param_1 + 0x10`. But when the differing slots map 1:1 onto the C's call
sites **in order**, the substitution is exact: assert `[C literals in order] == [seed slot values]`
first, then rewrite positionally. That assert is the whole safety argument — it fails loudly if the
C and the asm ever disagree, and it took a family the generic engine had refused 10/10 to **9/9**.
**AND THE TRAP UNDER ALL THREE:** `body_text` matched `extern void func_X(s32, s16 *);` at column 0
and returned **the next function's body**. Silent, and it had been shipping wrong seed bodies —
visible only as "no definition of the member after rename" skips, which read like a niche edge case
and were actually the symptom. **A definition is confirmed by a `{` with no `;` before it**; a name
match alone never is.
+36 -36
View File
@@ -4,16 +4,16 @@
# cross-binary collapsible-byte leverage: docs/duplicates.cross.md.
# THREE progress metrics (all matter — see the labels):
FLEET fn-count byte-ident: 350995 / 363163 = 96.65% (REAL+LINKED+empties; FUNCTION-count, ×134-inflated — one crack counts per overlay)
FLEET instr-weighted : 12882877 / 13523856 = 95.3% (shipped .text across main + resident + 211 overlays; the decomp.dev-DISPLAY number)
FLEET distinct-code(uniq): 5267836 / 5851963 = 90.0% (81310/90928 unique fns; the DISTINCT-RE number)
FLEET fn-count byte-ident: 351045 / 363163 = 96.66% (REAL+LINKED+empties; FUNCTION-count, ×134-inflated — one crack counts per overlay)
FLEET instr-weighted : 12884861 / 13523856 = 95.3% (shipped .text across main + resident + 211 overlays; the decomp.dev-DISPLAY number)
FLEET distinct-code(uniq): 5268274 / 5851963 = 90.0% (81321/90928 unique fns; the DISTINCT-RE number)
MAIN game-code weighted : 436 / 79510 = 0.5% (INCLUDED in the fleet numbers above since 2026-07-22 — roadmap §1 metrics contract; LINKED-excluding Ghidra sig dated 2026-08-05; caveat is R34: no independent second oracle for a PS-X EXE, NOT drift)
(fleet EXCLUDING main, for continuity with pre-2026-07-22 readings: 12882441 / 13444346 = 95.8%)
(fleet EXCLUDING main, for continuity with pre-2026-07-22 readings: 12884425 / 13444346 = 95.8%)
FLEET REAL substantive : 348825 (of which dedup-shared 254443 via 2061 groups / 254517 instances)
FLEET REAL substantive : 348875 (of which dedup-shared 254445 via 2062 groups / 254519 instances)
FLEET LINKED PsyQ objs : 959
FLEET NON_MATCHING : 7 (0 in any default build — G4)
FLEET INCLUDE_ASM stubs : 12161
FLEET INCLUDE_ASM stubs : 12111
FLEET matchable : 363163
| binary | REAL | shared | LINKED | byte-ident | matchable | byte-ident % |
@@ -60,44 +60,44 @@ FLEET matchable : 363163
| md_MAIN_046 | 10 | 0 | 0 | 11 | 39 | 28.2% |
| md_MAIN_047 | 3 | 0 | 0 | 4 | 12 | 33.3% |
| md_SC02_009 | 5 | 0 | 0 | 5 | 40 | 12.5% |
| md_SC03_073 | 25 | 10 | 0 | 25 | 28 | 89.3% |
| md_SC03_074 | 23 | 10 | 0 | 23 | 29 | 79.3% |
| md_SC03_075 | 27 | 10 | 0 | 27 | 41 | 65.9% |
| md_SC03_073 | 27 | 10 | 0 | 27 | 28 | 96.4% |
| md_SC03_074 | 25 | 10 | 0 | 25 | 29 | 86.2% |
| md_SC03_075 | 29 | 10 | 0 | 29 | 41 | 70.7% |
| md_SC03_076 | 34 | 0 | 0 | 36 | 66 | 54.5% |
| md_SC03_077 | 23 | 0 | 0 | 25 | 35 | 71.4% |
| md_SC03_078 | 24 | 9 | 0 | 24 | 27 | 88.9% |
| md_SC03_079 | 23 | 0 | 0 | 25 | 29 | 86.2% |
| md_SC03_077 | 24 | 0 | 0 | 26 | 35 | 74.3% |
| md_SC03_078 | 25 | 9 | 0 | 25 | 27 | 92.6% |
| md_SC03_079 | 24 | 0 | 0 | 26 | 29 | 89.7% |
| md_SC03_132 | 25 | 0 | 0 | 27 | 28 | 96.4% |
| md_SC03_133 | 21 | 1 | 0 | 23 | 29 | 79.3% |
| md_SC03_134 | 25 | 1 | 0 | 27 | 41 | 65.9% |
| md_SC03_135 | 32 | 0 | 0 | 34 | 66 | 51.5% |
| md_SC03_136 | 21 | 0 | 0 | 23 | 35 | 65.7% |
| md_SC03_137 | 21 | 1 | 0 | 23 | 27 | 85.2% |
| md_SC03_138 | 24 | 1 | 0 | 28 | 37 | 75.7% |
| md_SC03_133 | 23 | 1 | 0 | 25 | 29 | 86.2% |
| md_SC03_134 | 27 | 1 | 0 | 29 | 41 | 70.7% |
| md_SC03_135 | 34 | 0 | 0 | 36 | 66 | 54.5% |
| md_SC03_136 | 24 | 0 | 0 | 26 | 35 | 74.3% |
| md_SC03_137 | 23 | 1 | 0 | 25 | 27 | 92.6% |
| md_SC03_138 | 26 | 1 | 0 | 30 | 37 | 81.1% |
| md_SC04_024 | 24 | 0 | 0 | 26 | 28 | 92.9% |
| md_SC04_025 | 20 | 0 | 0 | 22 | 29 | 75.9% |
| md_SC04_026 | 24 | 0 | 0 | 26 | 41 | 63.4% |
| md_SC04_027 | 31 | 0 | 0 | 33 | 66 | 50.0% |
| md_SC04_028 | 21 | 0 | 0 | 23 | 35 | 65.7% |
| md_SC04_029 | 20 | 0 | 0 | 22 | 27 | 81.5% |
| md_SC04_030 | 22 | 0 | 0 | 26 | 31 | 83.9% |
| md_SC05_023 | 22 | 0 | 0 | 24 | 28 | 85.7% |
| md_SC05_024 | 20 | 0 | 0 | 22 | 29 | 75.9% |
| md_SC05_025 | 24 | 0 | 0 | 26 | 41 | 63.4% |
| md_SC05_026 | 30 | 0 | 0 | 32 | 66 | 48.5% |
| md_SC05_027 | 20 | 0 | 0 | 22 | 35 | 62.9% |
| md_SC05_028 | 20 | 0 | 0 | 22 | 27 | 81.5% |
| md_SC05_029 | 22 | 0 | 0 | 26 | 31 | 83.9% |
| md_SC04_025 | 22 | 0 | 0 | 24 | 29 | 82.8% |
| md_SC04_026 | 26 | 0 | 0 | 28 | 41 | 68.3% |
| md_SC04_027 | 33 | 0 | 0 | 35 | 66 | 53.0% |
| md_SC04_028 | 24 | 0 | 0 | 26 | 35 | 74.3% |
| md_SC04_029 | 22 | 0 | 0 | 24 | 27 | 88.9% |
| md_SC04_030 | 24 | 0 | 0 | 28 | 31 | 90.3% |
| md_SC05_023 | 23 | 0 | 0 | 25 | 28 | 89.3% |
| md_SC05_024 | 21 | 0 | 0 | 23 | 29 | 79.3% |
| md_SC05_025 | 25 | 0 | 0 | 27 | 41 | 65.9% |
| md_SC05_026 | 32 | 0 | 0 | 34 | 66 | 51.5% |
| md_SC05_027 | 23 | 0 | 0 | 25 | 35 | 71.4% |
| md_SC05_028 | 21 | 0 | 0 | 23 | 27 | 85.2% |
| md_SC05_029 | 23 | 0 | 0 | 27 | 31 | 87.1% |
| md_SC07_003 | 18 | 0 | 0 | 18 | 100 | 18.0% |
| md_SC07_004 | 43 | 0 | 0 | 43 | 315 | 13.7% |
| ov_MAIN_012 | 2225 | 1690 | 0 | 2304 | 2403 | 95.9% |
| ov_SC01_000 | 2390 | 1809 | 0 | 2390 | 2403 | 99.5% |
| ov_SC01_001 | 2399 | 1811 | 0 | 2401 | 2466 | 97.4% |
| ov_SC01_004 | 2399 | 1802 | 0 | 2400 | 2414 | 99.4% |
| ov_SC01_005 | 2433 | 1825 | 0 | 2433 | 2503 | 97.2% |
| ov_SC01_006 | 2433 | 1825 | 0 | 2433 | 2503 | 97.2% |
| ov_SC01_008 | 2399 | 1803 | 0 | 2401 | 2426 | 99.0% |
| ov_SC01_009 | 2426 | 1803 | 0 | 2427 | 2507 | 96.8% |
| ov_SC01_005 | 2434 | 1826 | 0 | 2434 | 2503 | 97.2% |
| ov_SC01_006 | 2434 | 1826 | 0 | 2434 | 2503 | 97.2% |
| ov_SC01_008 | 2400 | 1803 | 0 | 2402 | 2426 | 99.0% |
| ov_SC01_009 | 2427 | 1803 | 0 | 2428 | 2507 | 96.8% |
| ov_SC01_074 | 2400 | 1803 | 0 | 2402 | 2426 | 99.0% |
| ov_SC01_077 | 2568 | 1764 | 0 | 2570 | 2586 | 99.4% |
| ov_SC01_080 | 2426 | 1806 | 0 | 2426 | 2512 | 96.6% |
@@ -134,7 +134,7 @@ FLEET matchable : 363163
| ov_SC03_024 | 2530 | 1813 | 0 | 2537 | 2641 | 96.1% |
| ov_SC03_028 | 2466 | 1807 | 0 | 2470 | 2664 | 92.7% |
| ov_SC03_029 | 2504 | 1810 | 0 | 2514 | 2644 | 95.1% |
| ov_SC03_030 | 2423 | 1811 | 0 | 2425 | 2495 | 97.2% |
| ov_SC03_030 | 2424 | 1811 | 0 | 2426 | 2495 | 97.2% |
| ov_SC03_031 | 2449 | 1808 | 0 | 2452 | 2514 | 97.5% |
| ov_SC03_089 | 2524 | 1811 | 0 | 2531 | 2581 | 98.1% |
| ov_SC03_090 | 2542 | 1811 | 0 | 2550 | 2625 | 97.1% |
+91 -3
View File
@@ -84,6 +84,43 @@ def seed_body(sb, seed_name, header="src/shared/engine_core.h"):
return None, "", sb["kind"]
def dlabel_bytes(asm_path, sym):
"""The bytes of `sym` when it is DEFINED INSIDE the member's own nonmatchings .s — data that
vanishes the moment the INCLUDE_ASM stub is replaced. Such a symbol must be DEFINED in the
draft (with the member's OWN bytes, which differ per location), never merely declared extern.
Byte-proven on func_801EDC18 x4, whose four siblings each carry their own const."""
if not asm_path or not os.path.exists(asm_path):
return None
m = re.search(rf'^dlabel {re.escape(sym)}\b(.*?)^enddlabel {re.escape(sym)}\b',
open(asm_path).read(), re.M | re.S)
if not m:
return None
out = b''
for w in re.findall(r'\.word\s+(0x[0-9A-Fa-f]+)', m.group(1)):
out += int(w, 16).to_bytes(4, 'little')
for b in re.findall(r'\.byte\s+([^\n]+)', m.group(1)):
out += bytes(int(x, 0) for x in b.split(',') if x.strip())
return out or None
def data_def_for(sym, new_sym, seed_text, data):
"""The seed's DEFINITION of `sym`, renamed and re-initialised with this member's bytes.
Only the flat-byte-list shape is handled: the substitution must be provably byte-for-byte, and
a partially-understood initializer silently mis-initialised is exactly the failure the gate
would catch but nobody would explain."""
m = re.search(rf'^[ \t]*((?:const\s+|static\s+|volatile\s+)*[A-Za-z_]\w*[\s\*]+)'
rf'{re.escape(sym)}\b(\s*\[[^\]]*\])?\s*=\s*([^;]+);', seed_text, re.M)
if not m:
return None
lits = re.findall(r'0x[0-9A-Fa-f]{1,2}\b', m.group(3))
if len(lits) != len(data):
return None
it = iter(data)
init = re.sub(r'0x[0-9A-Fa-f]{1,2}\b', lambda _: f"0x{next(it):02X}", m.group(3))
return f"{m.group(1).strip()} {new_sym}{m.group(2) or ''} = {init};".replace("static ", "")
def decl_for(sym, seed_text):
"""A file-scope declaration for `sym` taken from the SEED's own TU, normalised to an `extern`.
@@ -102,6 +139,48 @@ def decl_for(sym, seed_text):
return m.group(0).strip() if m else None
TYPES_H = "src/shared/engine_types.h"
def typedef_block(text, name):
"""The FULL (possibly multi-line) `typedef struct {...} NAME;` for NAME, brace-matched.
The shared types (`MATRIX`, `SVECTOR`, …) live in engine_types.h, which the `md_*` TUs do not
include — the `ov_*` ones reach them through engine_core.h. A draft using one in an md module
dies as `parse error before 'm1'`, which is what 4 of the 9 func_8017D290 members did."""
m = re.search(rf'^[ \t]*typedef\s+(?:struct|union|enum)\b[^\n]*?\{{', text, re.M)
for m in re.finditer(r'^[ \t]*typedef\s+(?:struct|union|enum)\b[^;{]*\{', text, re.M):
i = text.index('{', m.start())
depth = 0
for j in range(i, len(text)):
if text[j] == '{':
depth += 1
elif text[j] == '}':
depth -= 1
if depth == 0:
end = text.find(';', j)
blk = text[m.start():end + 1]
if re.search(rf'\}}\s*{re.escape(name)}\s*;\s*$', blk):
return blk
break
return None
def shared_types_for(body, dest_text):
"""Shared typedefs the body NAMES, the destination lacks, and engine_types.h defines."""
if not os.path.exists(TYPES_H):
return []
types = open(TYPES_H).read()
out = []
for m in re.finditer(r'\}\s*([A-Za-z_]\w*)\s*;', types):
n = m.group(1)
if re.search(rf'\b{re.escape(n)}\b', body) and not re.search(rf'\b{re.escape(n)}\b', dest_text):
blk = typedef_block(types, n)
if blk and blk not in out:
out.append(blk)
return out
def typedefs_for(body, seed_text, dest_text):
"""Single-line seed typedefs the body NAMES and the destination TU does not already define.
@@ -122,7 +201,7 @@ def typedefs_for(body, seed_text, dest_text):
def build_draft(body, seed_name, member_name, renames, seed_text, dest_text,
already_self_contained=False):
already_self_contained=False, member_asm=None):
"""-> (draft_text, skipped_reason). Renames are applied SIMULTANEOUSLY (one pass), so a chain
like D_A->D_B, D_B->D_C can never cascade."""
keys = sorted((k for k in renames if k != seed_name), key=len, reverse=True)
@@ -147,6 +226,13 @@ def build_draft(body, seed_name, member_name, renames, seed_text, dest_text,
if s == member_name:
continue
old = next((k for k, v in renames.items() if v == s), s)
data = dlabel_bytes(member_asm, s)
if data is not None:
d = data_def_for(old, s, seed_text, data)
if d is None:
return None, f"data lives in the member's own .s and its initializer is not a flat byte list: {s}"
decls.append(d)
continue
d = decl_for(old, seed_text)
if d is None:
# No decl in the seed — but if the DESTINATION already declares it, none is needed.
@@ -163,7 +249,8 @@ def build_draft(body, seed_name, member_name, renames, seed_text, dest_text,
# R32: a draft missing a declaration is a KNOWN-BAD draft. Don't spend a build on it.
return None, "no seed decl for " + ",".join(missing[:3])
pre = typedefs_for(new_body, seed_text, dest_text) + sorted(set(decls))
pre = (shared_types_for(new_body, dest_text)
+ typedefs_for(new_body, seed_text, dest_text) + sorted(set(decls)))
return "\n".join(pre) + "\n\n" + new_body + "\n", None
@@ -234,7 +321,8 @@ def main():
member_body = FR.apply_remap(member_body, imm_map)
draft, why = build_draft(member_body, sb.get("name") or seed["name"],
m["name"], ren, seed_text, dest,
already_self_contained=False)
already_self_contained=False,
member_asm=corpus.asm_path(m["binary"], m["name"]))
if draft is None:
skip[why.split(" for ")[0]] += 1
continue
+19 -9
View File
@@ -61,16 +61,26 @@ def body_in_text(txt, name):
whole-file scan would drag every OTHER function's symbols into the comparison and drown the
signal, so callers annotating a SEED must pass through here."""
txt = _strip_comments(txt)
# Column-0 first (a file's definitions start there). The INDENTED fallback exists for a
# de-macroized DEFINE_ block, whose lines are all indented — but an indented `func_X(` is far
# more often a CALL, so that form must be confirmed by a `{` with no `;` before it.
m = (re.search(rf'\bDEFINE_{re.escape(name)}\s*\(', txt) or
re.search(rf'^[A-Za-z_][^\n=;]*\b{re.escape(name)}\s*\(', txt, re.M))
# EVERY candidate must be confirmed a DEFINITION by a `{` with no `;` before it. Column-0 first
# (a file's definitions start there), then indented — a de-macroized DEFINE_ block is entirely
# indented, while an indented `func_X(` in a file is far more often a call.
#
# The `;` test is not optional on either. A seed file that opens with
# `extern void func_8017D290(s32, s16 *);`
# followed by the definition of a DIFFERENT function handed back that other function's body —
# silently, and the draft then defined the wrong thing. build_draft's own assert caught it
# ("no definition of the member after rename"), which is why this surfaced as 48 skips rather
# than as a wrong bank; but the skips were the SYMPTOM and this is the defect.
m = re.search(rf'\bDEFINE_{re.escape(name)}\s*\(', txt)
if not m:
for cand in re.finditer(rf'^[ \t]*[A-Za-z_][^\n=;]*\b{re.escape(name)}\s*\(', txt, re.M):
i = txt.find('{', cand.start())
if i >= 0 and ';' not in txt[cand.end():i]:
m = cand
for pat in (rf'^[A-Za-z_][^\n=;]*\b{re.escape(name)}\s*\(',
rf'^[ \t]*[A-Za-z_][^\n=;]*\b{re.escape(name)}\s*\('):
for cand in re.finditer(pat, txt, re.M):
i = txt.find('{', cand.start())
if i >= 0 and ';' not in txt[cand.end():i]:
m = cand
break
if m:
break
if not m:
return None