4.0 KiB
§181 — WHAT A WAVE'S GATE ACTUALLY REJECTS (P31 S53, measured on wave R's 45-draft main pile)
Only ONE of 27 blocked drafts was wrong. The other 26 were correct and unbankable.
Wave R drafted 92/110 MATCH and its main pile carried 45 byte-verified drafts. 18 banked in the
first slate. Every rejection was catalogued; five distinct classes, four of which match_one,
reloc_identity, fragment_check and pregate_check are ALL structurally blind to:
| class | n | how it announces itself | who could have caught it |
|---|---|---|---|
| MIRROR-FRAGMENT | 2 | undefined reference to '.L80050D5C' at LINK |
nothing we had |
| duplicate typedef, TU's copy BELOW | 7 | C89 duplicate-typedef at COMPILE | pregate_check did |
| draft-vs-draft data-symbol type clash | 1 | previous declaration of D_80072784 |
nothing we had |
| draft-vs-file signature conflict | 4 | previous declaration of func_80032A74 |
nothing we had |
| genuinely byte-wrong | 1 | whole-binary SHA differs | only the build |
1. THE MIRROR FRAGMENT — the reverse of §176i, and a new refusal we do not yet compute.
fragment_check asks "does another symbol live INSIDE my range?". The opposite is equally fatal:
another stub's .s branches into a label inside YOUR range, and converting you to C deletes that
label. Two instances, both silent until the linker spoke:
gfx2D_BG0_OBJ_4D8—gfx2D_BG0_OBJ_1B4.sbranches to.L80050D5C/.L80050D70inside it;SYS_OBJ_26EC—SYS_OBJ_25C8.sbranches to.L8005B9B8inside it. The test is as cheap as the forward one: for each still-stubbed sibling.s, collect the.Ltargets it references and refuse any draft whose[addr, addr+4*nins)contains one. Shipped asfragment_check.branched_into(), negative-controlled on both known-bad drafts (it names them and the exact referring sibling) — and then measured across the corpus, which is the number that matters:
| binary | stubs owning a branched-into label |
|---|---|
main |
99 of 1,745 (5.7%) — incl. SaveLoadRoutine, GsSortFastBg, the gfx2D_BG0_* cluster |
ov_SC04_011 |
0 of 229 |
ov_SC03_028 |
0 of 194 |
So it is a main-specific hazard at ~1 in 18, and effectively absent from the overlay fleet. That
asymmetry is itself informative: main is where splat's symbol table names the most non-function
addresses. Two notes on the semantics — the check excludes labels a function defines itself, and it
excludes siblings that are IN THE SAME SLATE (converting both at once removes the referencing .s,
so the hazard evaporates). A population scan that puts every stub in one slate therefore correctly
reports zero, which is a right answer to a different question.
2. THE TYPEDEF-BELOW CASE. gate_main.strip_dup_typedefs reuses a definition only when it is
visible ABOVE the insertion point — correct, since stripping a below-survivor leaves the name
undefined (that trap is documented in its own docstring). But it then KEEPS the draft's copy, and
two definitions of one typedef name is a C89 error wherever they sit. Both horns are wrong; the
missing third option is to rename the draft's private copy, or hoist the file's definition (typedefs
emit no code, so hoisting is byte-neutral). 7 correct drafts are parked on this.
3. THE BISECT ECONOMICS ARE SETTLED. bisect_slate.py — null control first, then true binary
search — isolated the single byte-wrong draft (SYS_OBJ_1DC0) in 5 steps / 90 seconds, at ~13 s
per incremental build. gate_main's built-in bisect on a comparable slate ran 3 hours and named
nothing (§176i). Never use the built-in one; always pass --no-bisect and drive bisect_slate.
4. THE STRATEGIC READING. 26 of 27 blocked drafts are byte-correct work that only the plumbing rejects, which is the §180b ratio again from a third independent direction. The lesson is not "draft better" — the drafting is done. It is that every hour spent making the integration layer compute a refusal is worth more than an hour of drafting, because drafting is already at 84–93%.