- each draft compiles STANDALONE so it carries its own 'typedef struct {...} SVECTOR;'. Once
one such function banks, that typedef lives in src/800.c forever and every later draft
defining its own collides — a C89 duplicate-typedef error, not a byte miss. harvest_verify
already handles this; gate_main did not, and wave L lost a verified-correct draft to it.
- strip_dup_typedefs() drops typedefs the destination file (or an earlier body in the same
batch) already defines, and keeps novel ones.
- R39 NC: drops the duplicate, keeps the novel one, leaves the function body untouched, and is
a strict no-op when there are no duplicates.
- clean_build() ran 'make build' and then sha()'d build/us/SLUS_007.26 off disk. If the build
FAILED (compile error), the PREVIOUS successful binary was still there, so sha() returned the
good hash and the tool reported BYTE-IDENTICAL for a build that never ran.
- that is exactly how it claimed '43 banked' for wave K on a TU that did not compile; the
clean-fleet R22 caught it ([FAIL] main). A verifier that can pass without building is worse
than no verifier.
- fix: rm the output before building, and treat a non-zero make return as no-hash/never-pass.
- also: unbuffered print (a 16-min run looked hung with an empty log) and read the compile
error to name the culprit instead of bisecting at a full clean rebuild per step.
- gate_main reported wave K BYTE-IDENTICAL; the clean fleet R22 then failed [FAIL] main.
Three drafts declared D_80078D98 inconsistently (1 scalar, 2 array) and my conflict checker
could not see the difference: typesig() split on the symbol and kept only the prefix.
- fixed to retain the declarator suffix ('' vs '[]'); NC'd both directions — the wave-K
conflict is now caught, wave J's known answer (34/5) is unchanged.
- BOTH failure modes now documented in the tool: v1 too STRICT (compared parameter names,
discarded 2 good drafts), v2 too COARSE (ignored [], passed a real conflict). R22 caught
what the tool missed, which is exactly why the clean-rebuild rule exists.
- phase log: recorded the night's methodological lesson — every serious stall was an
instrument trusted without a control, never the compiler.
- main CANNOT be gated incrementally: its extract runs the EXE-only psyq_integrate +
ld_interleave steps that REWRITE the .ld, so gate_lane/gate_stage's incremental build
re-runs that on an already-rewritten script and yields a FALSE diff (R22's own rationale).
That cost a night: 4 byte-correct drafts gated 0/4 and I wrote up a nonexistent linker
defect before the null-draft control refuted it.
- gate_main substitutes the whole batch -> make extract BINARY=main -> make build -> compare
SHA. ONE clean build verifies the WHOLE batch (34 banked in one rebuild); bisects on failure
so a single bad draft can't sink the rest.
- handles both main-specific hazards: (1) in-TU cross-draft decl conflicts, resolved greedily
on TYPE SIGNATURES ONLY (comparing parameter names wrongly discards good drafts, R39 — my
own first version did exactly that); (2) stale .s after a revert (extract before resolving).
- NC'd against wave J's known answer: 34 compatible / 5 dropped, matching the hand result.
- deltas[int(new[-8:],16) - int(old[-8:],16)] assumed every symbol is func_XXXXXXXX/D_XXXXXXXX.
A draft whose target calls a PsyQ symbol by name (Square0, RotMatrixY) raised
ValueError and aborted the WHOLE audit — in wave F one such callee took down a 55-draft
batch, and the rename itself (re.subn) had already succeeded.
- a curated pair is a plain 1:1 rename with no address delta: count it as 'named-1:1' and
carry on (R32 — one unparseable pair must never answer for the batch). Delta print made
mixed-key safe.
- verified on the literal incident values: old aborts on 'Square0'; new completes, counting
2 named renames + still bucketing the hex pair by its 0x484c delta
- wave D (48 adapt cards): 47/48 standalone, 45 BANKED across two gates (40 + 5 late-repair).
Operational lesson: build the gate slate AFTER the repair stage lands — the first slate was
built early and 5 rescued drafts needed a second gate.
- wave C (35: 11 tell + 24 weak): 32 banked, 91% gate. Weak lane proven 24/24 on haiku.
- MAIN BLOCKER DIAGNOSED (the night's most valuable finding): main drafts are byte-correct yet
gate 0/4. Byte-diff of the built EXE = exactly 2 bytes in 413,696, NOT in the drafted fn: a
jal at 0x80060E74 retargets func_80061FA8 (game code, 800c2.o) -> firstfile (PsyQ libapi
A66.o). Adding one C fn perturbs symbol resolution between game code and the LINKED PsyQ
archives. main is an INTEGRATION wall, not a matching wall -> its own lane; excluded from
build_wave_atlas by default.
- build_wave_atlas.py now packs by (binary, TU) — the REAL gate-group key, since each group is
one whole-binary rebuild (wave D: 42 drafts / 23 groups = the throughput ceiling).
- 3 NEARs enqueued as grinder fuel incl. func_80183578 at close=1 DELAY-SLOT (§60a precedent).
- the old glob('src/<binary>/*.c') found nothing for main (sources live at src/*.c), so every
main draft grouped under src=None: an R36 consumer-blindness, latent because main has never
been wave-gated (main is 0.5% matched, the largest coherent mass left)
- derive from corpus.stubs()[..].path instead; NC'd 3 ways: still-open wave-C drafts 3/3 agree,
overlay sample 96/96 agree (no regression), main now resolves None -> src/800.c
- tools/build_wave_atlas.py: wave selection off the frontier atlas, optimized for GATE
THROUGHPUT (gate cost scales with (binary,TU) groups, not drafts: wave C was 1.3 drafts per
rebuild; atlas selection concentrates to ~96) and weighted toward instruction mass
- wave C: 35 cards (11 tell + 24 weak) -> 35/35 standalone (re-verified independently, R14)
-> 32 banked / 3 near, 91% gate, 0 symbol failures (Law 4 prevention worked)
- weak lane proven for the first time: 24/24 on haiku; 890 candidates remain
- reach measured: 32 exemplars, 8 with sharers, x2 each => ~1.25x effective (the x134
era ended in P25/29/30) -> throughput, not leverage, is now the lever
- tools/build_wave.py (pool=adapt|weak, corpus-derived open-stub filter, R35 gate guard)
- 3 self-inflicted instrument defects found+fixed+NC'd (P9, recorded not buried):
pgrep self-match via shell=True; corpus.stubs() is addr->Stub not names (nearly
declared both card pools spent); a wave fired on hand-typed placeholder cards (stopped)
- STRATEGIC: card lanes are ~0.23% of open ins/wave; the Atlas's head-crack bucket is
1,276 groups / 186k ins with high-reach groups up to 265 instances -> retarget waves
at atlas groups next
- tools/warmstart.py: --from-banked walks a banked exemplar's family's open
members, builds remapped proven-body drafts (symbol_map + build_draft), and
STREAM-classifies member-vs-seed with zero compiles; enqueues ONLY permuter-
shaped work (bucket==permuter or LENGTH-DRIFT |delta|<=2) as backlog records;
--lenmiss ingests T8's 49-route. Armed live: 59 enqueued, 120 refused by the
stream filter (the anti-92%-wasted-CPU discipline)
- grinder patch NOT needed: candidates() deliberately keeps unclassified
records ('unknown is not a reason to skip'), so pre-filtered enqueues flow
as-is — documented in the feeder docstring (YAGNI honored)
- family_cousins --weak-cards: 954 seeded-crack cards from the never-consumed
0.70-0.85 band, ins-ranked, §168 laws embedded, model-routed haiku 804 /
v3 43 / sonnet 86 / opus 21 (cheap tiers dominate), 0 unresolved .s
- match_one --emit-streams (additive; stdout-identity NC'd): word streams for the
len lane
- family_align.addr_true_rel: reloc-vs-constant range discriminator — FULL
conservative set kept for pair semantics (NC-1 157/157 regression), address-
true subset for indel eligibility only (a constant li-cluster must not read as
reloc-in-indel); synthetic probes green both directions
- tools/len_tells.py: aligned classification + §172b tell tagging (EXTPAIR/
SELECT/NOP) on target-side indels; detectors imported from atlas_features
(R33); cookbook text embedded in cards
- tools/lenmiss_route.py: pool-parallel (A8) — 587 audit LEN rows re-verified
live + routed in 24s: redraft 345 (frac>0.35, APPEND-ONLY backlog
reclassification — near-miss metrics stop lying) / permuter-length 49 (grinder
fuel) / cards 192 incl 14 tell-tagged (the audit's own detectors had emitted
ZERO) / mechanical 0 — an HONEST NULL: stored drafts rarely get constants
wrong; LEN drift is shape, family_align's value here is classifier/detector
- R32 accounting 587/587
- tools/family_align.py (NEW module — classify_member's return contract untouched,
the remap_hseq silent-pass trap avoided by design): SequenceMatcher alignment
over FC.tok streams; li-cluster reconstructor (lui/lui+addiu/lui+ori/li-from-$0
chains, split-cluster absorb for the rs-changed addiu partner); verdicts
LEN-LI/LEN-NOP/LEN-JTBL/LEN-STRUCT/STRUCT-ALIGNED/PURE/IMM; aligned imm engine
mirroring imm_map_tier1 (ordinal deliberately out in v1)
- NC-1 verdict-equivalence 157/157 banked pairs — the NC caught two real gaps:
R-type non-shift sa diffs are STRUCT; registers tested BEFORE the reloc skip
(a reloc-slot word with a different register is STRUCT). NC-2 parity 21/21
- R37 PROBE REFUTED the planned mechanical driver before it was built: 0/26
LI-ONLY cards classify mechanically (regfields x19) — cousins are 0.85-similar
DIFFERENT functions; §168 law 1 re-derived by measurement; no driver written
- family_align re-scoped: its consumer is T8's LEN+N near-miss pile (draft vs
its OWN target = same function); reloc-vs-constant range discriminator parked
for T8. decision-log entry (R31)
- tools/plumbing_groups.py: derives the honest still-open pool from the classified
ledgers (R38) — '1,217 PLUMBING' collapsed to 237 (SELF 109 / CALLEE 48 / OTHER
48 / DATA 32)
- recover_integration: PER-GROUP ISOLATION (git-checkout binary TUs between groups
— one TU-stage edit was poisoning every other group's whole-binary gate with a
phantom shared error; per-group banked_from_source capture) + new stages
'macro-externs' (§121 draft-tier, via family_sweep.macro_def_sig_map, R33) and
'tu-scope' (§103 STU binary-tier, the sweep-only lever)
- the probe (ov_SC03_107): raw 0/14 -> root-caused (poisoning + stale seed
symbols; rtu_match MATCHes them — blind to reloc names, R34) -> symfix-first
-> 9/14 BANKED (64%)
- sweep finding (Law 3): the no-draft majority (ov_SC02_037 44/44, most of
ov_MAIN_012) had verdicts from transient sweep remaps never persisted — family-
lane fuel, not recovery fuel; the stored-draft class is consumed
- cookbook §173 (symfix-first / per-group isolation / verdicts-without-drafts);
index 518 green; R22 clean fleet 213/213; phase total 17 banked @ 0 agent tokens
- decision-log: the P31 re-charter entry (organize-before-grind; R37/R38/R39
ratified at gate-1) per R31
- harvest_verify.py: import guard — a bare import now RAISES loud instead of
running a full gate (CLI unchanged, verified both directions)
- sig-resident: bootstrap boundary artifacts fixed (fused +0 data word with
func_800CEDFC; func_800D33E0 dropped past a glued tail) -> ELF-seeded per the
S45 pattern, exactly 145 fns; true denominator confirmed 145 (progress was
right); audit-corpus 0 PHANTOM + 0 TRUNCATED; all three oracles agree
- family maps regenerated at HEAD commit:2161: 11,025 open non-main members
reconciles EXACTLY with 12,059 - main 1,034 (102 stale phantoms cleared);
adapt cards 704, aprop cards 204 (full emission)
- main fuel-gap finding: 2,001/2,002 main stubs already have cached Ghidra-C
(only func_80049600 missing) — the roadmap '0/2,096' note was stale
- tools-health OK (dedup 2,063/0; C1 254,521/254,521; audit-digest green)
- tools/cc1_dumps.sh: run the pinned cc1 with -dr/-ds/-dj/-dc/-dl/-dg and count standalone
(use (reg)) insns in the .combine dump — each is one 8-byte never-referenced reload slot.
The NEAR-2 residual = 12 such orphans vs the target's 16, now ENUMERATED pseudo-by-pseudo
instead of inferred from ablations.
- The orphan mechanism, exact (combine.c:10835 + mips.md force_not_mem): every short-mem read
is a movhi+shift-pair triple; the fold orphans its ashift temp iff the HI reg carries an
extra HImode use and the death-note walk hits a label/jump. Single-use loads never orphan;
the head can never orphan (walk reaches insn 0).
- The zero-code +4 factory is byte-refuted: 18 new probe families x 3 placements (s16
respellings, cast truncations, <<16>>16 factories, placement sweeps, loop t/n, dossier-file
re-sweep) all land at vars=224-same-bytes or drift. p_optr reproduces the target's exact
16-orphan frame at +7 insns — opacity that defeats cse equally blinds num_sign_bit_copies;
fold-const closes the tree level. Three canonicalizers, one wall.
- Dossier NOTES.md updated with the S50 ledger; backlog klass -> WALL(P32) for both names.
- NOT banked: no 0-closeness draft exists. The dial draft (NEAR 2) remains the floor.
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.
Final S50 state: 307 instances banked, stubs 12,468 -> 12,161, fleet 95.3% instr / 90.0%
distinct / 96.65% fn-count. R22 clean rebuild 4x, check-all 213/213 every time.
- tools/aprop_autodraft.py + tools/draft_prechecks.py: seed body + symbol_map + a MINIMAL
synthesized preamble. The seed's decl layer never travels — that layer is family_sweep's
dominant failure (331 of 458 S49 verdicts). 256 banked at zero agent tokens, against the
~20M the same work would have cost as a wave.
- Macro seeds (567 of 1196 members, all 3737 de-macroize) take the DEFINITION only; the block
stays the decl source. Pasting it whole measured 28% vs inline's 68% — func_8016AB6C's macro
is 1,891 lines of which 108 are the function.
- IMM is a second engine, not a wall: T2a's imm_map_tier1 resolves a per-location LITERAL like
symbol_map resolves a per-location SYMBOL. 131 of 275 IMM members resolve.
- draft_prechecks negative-controlled against ALL 205 banked drafts: zero false positives,
catches 39 of 67 known failures. That control found two bugs in the checks themselves —
C89 `f()` declares UNSPECIFIED parameters (not zero), and a member's own definition read as
a call to itself. Conservative by design: a pre-check that discards good drafts is worse
than one that lets a few builds fail.
- The A-prop pool is now priced exactly: PURE 437/37,376 ins, IMM 275/8,849, STRUCT 238/4,259.
- Cookbook §171a; SETUP rows; CURRENT_PHASE S50 FINAL checkpoint.
Measured on the 489-draft run: inline seeds banked 145/213 (68%), macro seeds 77/276 (28%),
and the macro failures were decl collisions to a fault — 'parse error before *', 'too few
arguments to function'. Cause: a de-macroized DEFINE_ block is decls + definition, and
pasting it wholesale reintroduces the exact decl-agreement failure the minimal-preamble
design exists to avoid. func_8016AB6C's block is 1,891 lines of which 108 are the function.
- seed_body() now returns the DEFINITION as the body and keeps the macro block as the DECL
SOURCE, so only the externs the definition actually references are emitted.
- aprop_symfix.body_in_text(): text-based sibling of body_text (R33 — one implementation).
Indented-definition fallback for macro blocks, confirmed by a '{' with no ';' before it,
so an indented CALL can never be mistaken for a definition.
A lane-A member's body IS its matched sibling's body with the per-location symbols rebased.
Two mechanisms had each left that population on the table: family_sweep remaps but CARRIES
the seed's decl layer (decl-agreement = 331 of 458 S49 failures), and the agent wave
re-derives the same body at ~80k tok/fn. This does neither — seed body + symbol_map
(positional reloc zip) + a minimal synthesized preamble.
- Byte-proven before the tool existed: a hand-written draft of exactly this shape banked
func_801E2858@md_SC03_132, in a family the mechanical sweep had refused. Then 9/9 banked
on the first generated batch.
- THE SELECTOR, measured: only PURE members (every diff site a RELOC) are drafted. An IMM
(per-location literal) or STRUCT (register/opcode drift) site cannot be reached by a
rename — the first 5 gated 0/5, all IMM or STRUCT. classify_member now decides that
BEFORE a build is spent, not after. --allow-impure to override.
- Macro seeds supported: DEFINE_<fn>() de-macroizes back to a plain self-contained block
(3737/3737 extract). They were the largest skip bucket — 567 of 1196 members.
- 592 drafts emitted (304 macro / 288 inline) from 1196 members; every skip is named (R32).
- aprop_symfix: asm_syms_ordered() so an n:m card hands over the target's reference ORDER.
- Two defects caught by probes before they scaled: a typedef regex that stopped at the `;`
INSIDE the struct braces, and a continuation walk that started on the empty pre-newline
element and returned an empty body.
- The blocker was carried as "one missing file-scope extern gates 83 PURE members". Both
halves were wrong (R14): corpus.stubs says 4 open members, and D_801ED98C is a DEFINED
const Blk8 whose rodata lives inside the member's own nonmatchings .s — replacing the stub
deletes the data with it. gather_externs can carry an extern DECL, never a DEFINITION,
which is why it reported "no file-scope decl" for a symbol md_SC05_023 defines on line 114.
- Fix: paste typedef + const definition + body per sibling (data bytes verified identical
across md_SC05_024/025/028/029). 4/4 banked.
- aprop_symfix: new `local-only` class — draft-DEFINED identifiers that merely carry a
vram-looking suffix (Blk8_…, S8_…, L_call_…) are not stale symbols. Measured: that is every
non-clean case in the whole wave-7a/7b stored-draft residue, which holds ZERO stale-symbol
recoveries (a clean negative result — the defect was A-prop-specific).
- cookbook index regenerated (tools-health fails closed on a stale index — it caught §171).
- R22 clean rebuild: check-all 213 passed, 0 failed of 213. Stubs 12,445 -> 12,441.
- REFUTES §170's open hypothesis (batched cards concentrate members into one TU ⇒ §169
collision): 5-draft groups banked 5/5; 11 of 35 unbanked drafts were already one-per-TU;
and the two "concentrated" groups banked 12/12 and 10/10 once the real defect was fixed.
- The cause: a per-location data symbol carried out of the seed body unrebased. match_one
compares instruction ENCODINGS and is blind to a relocation's target NAME, so it scores
MATCH standalone and dies at link in the host TU. 24 of 24 concentrated failures, all 1:1
rewritable at one constant vram delta (0x4128).
- tools/aprop_symfix.py: audit + --fix, emits a gate_lane-shaped slate; deterministic and
build-free, so it runs BEFORE the gate. The R34 second oracle for the class match_one
cannot see.
- family_cousins.py --aprop-cards: members now carry sym_map, the explicit {seed -> member}
renames, read from the seed's C BODY (a matched seed has no .s of its own) vs the member's
.s. Two case-mismatch defects fixed while wiring it (sig lowercase vs splat uppercase).
- 23/24 banked. Stubs 12,468 -> 12,445. Fleet 95.2% instr / 89.9% distinct / 96.57% fn.
R22 clean rebuild: check-all 213 passed, 0 failed of 213. dedup 2,043/0.
- A-prop's true conversion is 87% (79/91); the 320 batched members are unblocked.
- Cookbook §171 + §170 struck in place; SETUP row; decision-log (R31).
- checkpoint block refreshed for a fresh session (R30/checkpoint-before-pause): the TU-spread
test is the named FIRST action, the regen chain and gate contract are spelled out, and the
>=16 head's three open items are listed with their evidence.
- promote gate_lane.py into tools/ (it was scratchpad-only): accepts slate OR confirmed shapes,
explicit draft paths, R32 coverage assertion (refuses to report 0 as a result), dirty-tree
abort, no outer timeout, per-function propagation after.
- session: 179 commits, 177 instances banked, 5 R22 clean-fleet gates all 213/213.
- NEW family_cousins.py --aprop-cards + tools/wave/aprop_wave.js: lane A (1,700 open fns /
76,419 ins) had NO card type — cousin diffs are empty for h_seq-identical members, so the card
is a positional WORD diff vs the matched sibling, grouped BY FAMILY (one agent, N drafts).
Head cards: 13 families / 433 members, median TWO differing words each.
- calibration 9 batches / 108 members: 98 agent-MATCH (91%, best of any wave) -> 56 BANKED (57%),
~80k tok/banked fn vs 157k (cousin card) vs 400k+ (crack wave). R22 213/213 BYTE-IDENTICAL.
- HONEST GAP (R14): 91% agent -> 57% gate is the worst conversion measured; 14 groups banked 0.
Hypothesis TESTABLE not proven — family batching concentrates members per destination TU, the
§169 collision. Re-gate unbanked ONE PER TU before scaling the remaining 320.
- >=16 head diagnosed: 3 of 4 blockers are plumbing — the --band substantial default hid 5 of 13
families from every prior sweep; one missing file-scope extern (D_801ED98C) gates 56 PURE
members; dedup_extend is macro-only. Only func_8017C294 is a genuine crack.
- fleet 96.56% fn / 95.2% instr / 89.9% distinct; stubs 12,535 -> 12,468; dedup 2,043/0.
- cookbook §170.
- thresholds relaxed to <=6 blocks/<=16 tokens UNION edit-fraction <=0.20: cards 518 -> 721,
MIXED 310 -> 50 skeletons; the 753-ins func_8017BEBC (0.987 sim) became reachable.
- 59 cards -> 48 agent-MATCH (81%) -> 44 BANKED (92% MATCH->bank, 75% end-to-end), 6.9M tok.
- FINDING (the actionable one): 7b's bank rate crushed 7a's because it SPREAD 48 drafts over 35
destination TUs; 7a's failures were per-TU declaration collisions between sibling drafts.
Cookbook §169 updated with the spread law.
- R22 213/213 BYTE-IDENTICAL from clean; fleet 96.55% fn / 95.2% instr / 89.9% distinct;
stubs 12,584 -> 12,535; dedup 2,035/0.
- incidents 3 & 4 recorded: an agent wrote a TRACKED header (guard caught it, prose is not
enforcement); my own gate_lane filtered on the wrong key and printed 'gating 0 drafts' as a
result (R32 silent skip) — fixed with a coverage assertion that refuses to report 0.
sig_image emits lowercase func_<hex> names; splat writes the .s with UPPERCASE hex, so
corpus.asm_path returned None for every address containing a hex letter — 60% of the cards
would have shipped agents a 'null/func_x.s' path. resolve_asm() tries the sig spelling
(authoritative for CURATED names), then both hex cases; a card with no resolvable .s is
now DROPPED, not emitted (R32). 518 cards, 0 bad paths.
- 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.
- FINDING (Drew's smell, byte-verified): the '4,513 unique singletons' picture is substantially
an h_seq exact-hash artifact — 86/120 near-pairs in the 0.85-0.99 band differ by PURE
insertion/deletion (li-expansion tell in 25). Specimen: ov_SC06_010:0x8017bebc (753 ins,
'singleton') is 0.987-similar to a MATCHED fn in the same binary.
- NEW tools/family_cousins.py: distinct open skeletons -> shingle index -> >=0.85 union-find ->
matched-seed attachment -> .run/family_cousins.json + docs/family-cousins.md. R32 BOTH ways
(independent stub recount fails loud on a stale map — negative-control-proven; partition
assert). Reproduced the probe within +-1%; totals EXACT (11,627 inst / 584,448 ins).
- Unit table: A-prop 197u/68,729ins · seeded 418u/50,422 · cousin-multi 1,552u/249,799 ·
cold 3,240u/215,498 — the genuinely-unique tail is 37% of the remainder, not 90%.
Main's 'structurally barren' HOLDS at the similarity tier (94% mass <0.70).
- --targets wave slate: .run/wave7_targets.json = 40 targets / 33,304 unit ins (+33% vs
family-ranked), 9 resolved seed C paths, size-routed 2 haiku/20 sonnet/18 opus.
- LAWS (§168): a cousin is a SEEDED CRACK never a remap; rank waves by UNIT weight; discount
short-fn similarity. Byte-gate stays the sole arbiter (G3/P9).
- docs/family-hseq.md: this session's frontier regen (post-S48 propagations) rides along.
- cookbook §168 + SETUP inventory row (R16/R21/R30); CURRENT_PHASE S49 entry.
gate_stage labelled every "standalone MATCH / whole-binary DIFF" with
"(declaration/TU plumbing)". The tool never checked for a declaration conflict —
that was a GUESS printed as a diagnosis, and func_8017F2D4 carried it through
SEVEN attempts across five waves while every agent hunted codegen. The body was
byte-correct from the first attempt; the notes had simply named the wrong
destination TU (a file holding only a caller + prototype), and splicing there is
a no-op that leaves the INCLUDE_ASM bytes in place.
- gate_stage now says only what is true (the two oracles disagree) and hands
over the check that resolves it, instead of naming a cause it did not measure.
- §166a banks the oracle: asm/<ov>/nonmatchings/<TU_stem>/<fn>.s => the
INCLUDE_ASM is in src/<ov>/<TU_stem>.c. The third path component IS the TU
stem, derived from the split config, and it beats any prose citation — a grep
for the function name also hits callers and prototypes in OTHER TUs and reads
exactly like a destination hit.
- Plus the two probe gotchas that cost wave-5/6 agents real time: the wrong
--aspsx-version fakes ~32 ori-vs-addiu mismatches, and a collateral-drift
check must filter to sized symbols (nm -S) or the zero-size .NON_MATCHING
aliases all report false drift.
cookbook_index.py: 506 sections.
A markdown backtick inside the prompt's template literal terminates the string
and the workflow dies at parse time. Cheap (0 agents, 0 tokens) but it has now
cost two launches, so the file carries a warning at the top: use double quotes
for inline code in prompt prose.
Wave 5 wrote its drafts into .run/wave4/ because the script was built by
sed-ing the wave-3 copy. Harmless (per-function dirs) but wrong. Pass
{wave:'waveN'} on any target; defaults to '.run/wave/' so a missing field can
never silently reuse a prior wave's directory. Meta name/description are now
generic too — this is THE crack-wave script, not wave 4's copy.
A prior attempt that was GATE-REFUSED (standalone MATCH, whole-binary DIFF) has
an in-TU residual, so the standalone gate cannot see it. A wave-5 agent closed
exactly this by splicing into the real destination TU, running the pinned
triple end-to-end, and masked-diffing the function out of the WHOLE-TU object
(0 mismatched, 279/279). That is now instruction, not luck.
Carries both gotchas it paid for: the wrong --aspsx-version produces ~32
spurious mismatches all of the ori-vs-addiu li-form shape (the fingerprint of a
version mismatch, not a codegen residual), and engine_core.h resolves relative
to the including file so the spliced TU needs a directory + shared symlink.
Also warns that two wave-5 agents found their predecessor's file/line citations
wrong while its idioms were right — re-derive the TU, don't trust quoted lines.
crack_wave.js classified anything without verdict_check.confirmed as 'refuted'.
When the usage limit killed 22 verifiers mid-wave, the result read
'refuted: 22' — 22 good drafts reported as rejected, with evidence 'verifier
died' as the only tell. Acting on that would have discarded the wave.
Same disease as the no-diagnostic classifier (S47) and this session's
poisoned-tree 0/17: a tool stating a conclusion it never reached. UNVERIFIED is
now its own outcome, returned with the draft path and sha1 and the instruction
'VERIFIER NEVER RAN — re-verify, do not discard'.
A workflow script is plain JS; the cookbook citation I added used markdown
backticks inside a template string, which terminated it. Caught at launch
(parse error, 0 agents, 0 tokens) rather than mid-wave.
The wave script encodes contracts paid for in real failures (per-agent output
dirs after wave 2 lost 21 verified drafts; sha1-last verification; prior-notes
seeding at 7/9 then 10/12; size routing). It was living only in the workflow
scratch dir, so each wave rebuilt it from memory — which is how the cookbook
citation list went stale: it still named only §162 while §163 and §164 (87 more
entries) had been banked in between.
Updated the citation list to §160-§164 and pointed agents at §164z, the 28
REFUTED claims, so they do not re-derive a disproved mechanism.
Wave 4 (wf_05895a19-121, 75 agents, 7.9M tok): 40 targets -> 35 agent-MATCH,
0 refuted, 5 NEAR, 0 FAIL, 0 drafts lost. 18 banked so far on the whole-binary
gate across 9 binaries; the other 17 are re-gating on a clean tree (see below).
PRIOR-NOTES SEEDING HELD AT SCALE: 10 of 12 seeded targets confirmed (wave 3
was 7 of 9). func_8017C294 — the x16 family, the largest single item on the
board — is now NEAR at **2 ins** (18 -> 11 -> 2 across three seeded attempts).
THE 4th COMMENT-BLINDNESS DEFECT OF THE SESSION, and the first with blast
radius. A crack agent annotated a decl in its draft:
extern void func_801842DC(s32 a0); /* TU:4023 INCLUDE_ASM (no decl) */
`corpus._INCLUDE_ASM_CAND` only skips lines that BEGIN with a comment marker,
so it read `INCLUDE_ASM (` out of the trailing PROSE, found no quoted path, and
refused the whole binary's stub oracle — correctly, by its own R32 contract.
That then failed gate_stage for every LATER binary in the run, because they all
walk the corpus: 12 binaries banked, then 5 were blocked by one comment in a
13th. Fixed the same way as the other three today: decide candidacy on
cdecl._mask'ed text, PARSE FROM THE ORIGINAL (the mask blanks string content
and would erase the asm path). Verified on 4 binaries incl. main (2,002 stubs).
AND THE DAMAGE IT LEFT: gate_stage raised out of the CorpusError BEFORE its
revert, stranding failed drafts spliced in src/. The 17 solo re-gates that
followed all read 0/17 — they were building a POISONED TREE, not judging their
own drafts. Residue reverted here; the 17 re-gate clean next.
The pattern is now sharp enough to state: any scanner that greps C source for a
token must mask comments and strings FIRST — and agent-authored drafts make it
far likelier, because their prose mentions the exact tokens our tools hunt for.
Today a 28-agent wave lost 21 adversarially-verified drafts to a shared output
directory, and I wrote them off before Drew asked whether the workflow results
could just be analysed. They were all recoverable, for zero agent tokens.
Encodes the method that worked 21/21, including the two shortcuts that do NOT:
- taking each Write's content recovers only single-write drafts (8/21 — agents
refine);
- taking an Edit's new_string as a file yields a FRAGMENT, not a file.
So it replays the mutation history per (agent, file_path), snapshots after
every mutation, emits newest-first, and also scans Bash heredocs (the 21st
draft never used Write/Edit at all). --gate runs match_one newest-first and
keeps the first MATCH.
Self-test on wf_d804f25a-f6f: 3/3 including the heredoc case.
`jtbl_family_bank` fed every module jr member to `jtbl_carve`, which died with
`jtbl_… not found in the raw data asm`; `harvest_verify` turned that into
CARVE-REFUSED and never built. So the verdict named the TOOL, and 12 slots in
the wave-1 propagation read as a carve bug. Probing one member to the byte
level shows it is a LAYOUT the carve model does not cover:
A module binds `.rodata` at 0x0 to the SAME subseg as its code (§154-A), so
the object's rodata order IS the C file's include chain — INCLUDE_RODATA
pieces, then each INCLUDE_ASM'd function's MIGRATED table, in address order.
That reproduces the island exactly while the function is a stub. Matching it
PRUNES its .s, its table leaves the chain, and cc1 re-emits it at the END of
the object's .rodata: build 43,768 vs 43,760 bytes, first diff at 0x144
inside the island's own pointer table.
`JTBL_PADS` does not reach it either — `jtbl_rodata_pads` refuses the object
outright ("unexpected rodata content .include ... D_801EF468.s"): the carve
model covers jump tables, not an island of mixed included data.
- `migrated_tables()` detects the layout by EVIDENCE (table absent from the
data asm, present as a dlabel in the function's own .s), refuses loud with
the measurement and the design that would work (isolate the jr function into
its own subseg so its .rodata is a separate OBJECT, then ld_interleave — the
§8 machinery re-aimed at a LEADING island instead of a data tail), and
refuses a mixed carve set rather than half-carving (R32).
- Regression-checked both ways: overlay stubs classify [], modules classify
migrated.
SIZED (R37): 70 module binaries, 42 with this layout; 1,345 open module
member-slots in sibling families, of which only 44 are jr. The island work is
worth 44 slots — it is NOT the module lane's main gate.
R22 clean-fleet: 213 passed / 0 failed of 213.
`_body_open_brace` ran BOTH its scans on unmasked text. A crack agent's draft
opens with a header comment that names the function and quotes C at it:
/* func_801EE8E0 (ov_MAIN_012 / jr_801789AC) — 188 ins, byte-exact vs …
* 3) The `do { } while (0)` around the loop-1 call is a REGISTER-ALLOCATION
so `sig` matched the COMMENT's first line and `find('{')` found the comment's
`do {`. Every carried `extern` was spliced into the comment — silently
commented out — and the gate reported `'D_8011511A' undeclared`.
The sweep classified that CC1-FAIL, so it read as a property of the SIBLING
(all 4 members failed identically) when it was a property of the EXEMPLAR'S
PROSE. It had nothing to do with module binaries, which is where I had filed
it. Every richly-commented agent draft is a carrier; the trigger is any brace
inside the header comment — so this would have grown with the campaign.
- both scans now run on `cdecl._mask`ed text and index the original by the
masked offsets (§134 / R33: one masking oracle);
- refuse outright if the length invariant is broken, rather than mis-place a
declaration into live code (R32).
Measured: family func_8017CBC8 -> its 4 md_ siblings went 0/4 -> 4/4 banked.
The test was `^\s*typedef\b[^\n]*\bNAME\b` — the name must sit on the SAME LINE
as the keyword. True of `typedef unsigned char u8;`, never true of the
multi-line form the preamble backscan actually carries:
typedef struct Foo { … } Foo;
so every multi-line typedef already in the unit was carried a SECOND time and
the unit reached the gate with two definitions of one tag. canon_sig_reconcile
uniquifies draft tags, so the duplicate is exact: `redefinition of struct
Foo_8013C0F8`.
- Extract the block capture as `_typedef_blocks(lines)` and derive the
already-carried set from it (R33: one parser, two callers), so the
multi-line form is recognised exactly as the single-line form always was.
Measured on the 0b population: func_8013C0F8 (3 slots) carried Foo+Bar twice;
func_8013B83C dropped a redundant file-scope copy of a typedef its body
declares at block scope. 6 other families byte-identical output.
`_proto_from_lines` starts `_strip` with in_block=False, but item boundaries
are `;`-terminated — so a declaration whose TRAILING comment wraps hands the
comment's continuation to the NEXT item, and the implied prototype came out as
extern * a prototyped (s32) decl is `conflicting types` … */ void func_80151664(void);
It compiled only because the hoist emits the opening `/*` line immediately
above it, so the garbage lands back inside a comment — but `_file_scope_decls`
then meets a col-0 `extern …;` whose base type is `extern` and REFUSES (R32).
That is the isolate-fail class: 23 of the 111 open 0b member-slots.
- overlay_src_split._proto_from_lines: apply family_remap's D1 backstop — a
`*/` with no `/*` before it means the chunk opened inside a comment; drop
that residue before parsing the header.
- Repaired the 16 already-emitted region files (the garbage line's live
payload was a redundant `extern void func_80151664(void);`).
Byte-gate after the repair: ov_MAIN_012 / ov_SC02_037 / ov_SC03_107 all
BYTE-IDENTICAL.
The 0b blocker was not "the pads line is left behind" alone — it fails two
different ways, and the second one is silent:
* bare isolate + `make build`: the stale line arms the pads filter on the
RESIDUAL object, which emits no jump table ->
`jtbl_rodata_pads: consumed 0 rodata .align(s) but 4 pad spec(s) given` (S47).
* isolate -> jtbl_carve (the jtbl_family_bank path): `set_pads_vars`
regenerates the block keyed by the CURRENT subseg names, finds no prior spec
under the new `_jr_<addr>` name, and DROPS the line. cc1's natural `.align 3`
then pads the span's non-8-aligned interior tables and the image shifts —
reported only as `built, bytes differ`.
- jr_isolate_all.repoint_overlays_mk: repoint the `build/src/<ov>/<sub>.o:
JTBL_PADS` target with the `--order` leaf whenever a carve moves; refuse
loud if the old object still hosts a .rodata piece (R32).
- jtbl_carve.set_pads_vars: second, disagreeing oracle (R34) — refuse when a
spec would vanish for a subseg no longer in the carve set (rename/merge
drift), instead of silently emitting a padless object.
R37 probe: func_801789AC -> ov_SC02_037 went `built, bytes differ` -> BANKED
on the whole-binary byte gate. ov_SC02_037's spec is 0,0,0,0 over tables
+0x0,+0x14,+0x34,+0x4c — load-bearing (span start is 4 mod 8).
Stage 0a's first defect, and the largest single zero-token bank of the session.
family_remap's kind test asked ONE question — is this address a function in the SIBLING'S OWN sig?
— and defaulted to `D_` on "no". But a body calls outside its image constantly: an overlay calls
resident helpers, an md_* module calls the overlay-range engine. Those addresses are absent from
the sibling's sig, so the test fell through and emitted a DATA NAME FOR A FUNCTION —
`D_800183E0`, `D_800D1EBC`, `D_80171A1C`. None exist anywhere in src/ or config/symbols.us.txt,
while `func_80171A1C` alone has 1,061 references. Measured: 611 member-rows across 45 symbols,
the largest named residue class. "Not in MY sig" means "not mine", not "is data".
Fix — three oracles, strongest first, never a blanket fallback:
1. the sibling's own sig (authoritative for its image; this is what preserves the Phase-29 T82
case where a slot is a function in the exemplar and DATA in the member — unioning every sig
would have re-broken the 251 members T82 fixed),
2. the always-linked images via extern_fn_addrs() — resident + main, 2,146 addresses whose ranges
cannot collide with an overlay's,
3. the exemplar reached it by `jal` — a call target is a function BY DEFINITION, which covers an
external address neither sig claims (0x80171A1C from an md_* module, 112 rows).
Only a non-call reloc no oracle claims still falls to `D_`.
Result: BANKED 205 member-matches, failures 670 -> 575, derived net = report = 205.
R22 clean-fleet 213 passed / 0 failed of 213.
Fleet 94.4% instr / 88.3% distinct / 96.27 -> 96.33% fn-count; stubs 13,563 -> 13,345.
THE RESIDUE HAS CROSSED OVER: DIFF is now the LARGEST class at 143 of 575 — real byte divergence
outranks plumbing for the first time this session (undefined-ref 611 -> ~8, PLUMBING-other
231 -> 81). The ~5:1 plumbing:DIFF ratio that justified "tooling beats volume" has inverted in this
queue, exactly as the frontier analysis predicted: the declaration-axis vein was one-time.
The new head class is `conflicting types for func_80175414` (27) — the same addresses this fix
started naming correctly, now surfacing the NEXT layer (the symbol resolves; its declared signature
disagrees). That is the conform axis, not the remap axis.
Note for anyone auditing this class: rtu_match MASKS HI16/LO16, so a wrong %hi/%lo symbol still
reports MATCH (the T82 comment records `MATCH (10 ins)` on a member the fleet gate refused). This
defect is invisible to the per-function tool by construction — only the whole-binary gate sees it.
The targeting oracle stamped its scope as "the N OVERLAYS only (no main, no resident)" while
load() has scanned the md_* modules and the resident since S44. Measured at this HEAD: 141 location
overlays + 70 md_* modules + the resident = 212 binaries. That is the §159 coverage law broken by
the file that documents coverage, on the repo's most load-bearing targeting instrument — and it is
how "main is structurally barren" survived two phases unexamined.
The COUNT beside it was already derived, with a comment saying "report the scope we ACTUALLY
scanned, never a hardcoded count". The PROSE describing what the count meant was hardcoded and
rotted. Both are derived now.
Caught while fixing it: my first cut read glob(".run/sig.main.jsonl") and stamped "main INCLUDED"
the moment that file existed — while load() still did not glob it. Same defect one layer down: a
stamp describing the filesystem instead of the run. Now derived from the loaded instances.
Also added a glossary line: "zero-crack" means n_matched == 0 (needs its FIRST crack) in this map,
and the OPPOSITE (a matched exemplar awaiting propagation) in roadmap §3 T3 — a ~30x mis-scope risk
for any session reading one against the other.
NOT DONE — main inclusion (0c) is still blocked on settling the attribution. Confirmed the
mechanism: main has 49 LINKED PsyQ subsegs, corpus.stubs('main') returns 2,002 INCLUDING them,
progress.py correctly excludes them and reports 1,034 game-code stubs. progress.linked_subsegs'
own docstring records this exact trap ("an importer then classifies ~1,300 already-byte-identical
LINKED library stubs as outstanding game-code work") — and my sig-main seeded from corpus.stubs,
so it inherited the LINKED rows, which is why G2's 207-family finding was inflated.
My partition probe is NOT trustworthy: 954 of 2,002 stubs returned no asm path from
corpus.asm_path, so 199 LINKED / 849 game / 954 unresolved does not reconcile with 1,034. Fix the
probe before trusting any main-scope number.
func_801EDC18 (md_SC05_023) is the largest multiplier remaining — 57 members. The wave agent
abandoned it at "closeness 6" with class SIZE-MISMATCH [redraft]. It was two lines from correct.
THE CODE (cookbook §160a): the target copies 8 bytes with lwl/lwr + swl/swr — gcc-2.7.2's
emit_block_move for a type with ALIGNMENT 1. The draft used a u32 copy (aligned lw/sw), which is
wrong by construction. `typedef struct { char c[8]; } Blk8; buffer = D_801ED98C;` reproduces it.
Six spellings were tried in parallel; two independent agents converged on the same one.
THE INSTRUMENT (§160b) — this is the part worth more than the function. The target .s bundles a
leading `.section .rodata` block (D_801ED98C as two .word) ahead of .text. Those lines carry the
same `/* off vaddr HEX */` shape as instructions, so masked_diff.insns_from_s counted them as TARGET
instructions, while insns_from_object (objdump -j .text) can never emit them. A byte-perfect draft
therefore read `mine=26, target=28, 26 mismatched` — every position shifted by a constant +2 — and
got classified as needing a redraft. 116 of 12,583 .s files in the corpus have this shape, one at
-29 instructions. Every one of them would report a false wall to any agent that tried it.
Fixed: insns_from_s tracks .section and counts only .text. Full-corpus control: 12,467 unchanged,
116 corrected, 0 regressions. Same artifact class as §129a (post-carve jtbl inflation).
THE OWNERSHIP LAW (§160c) — my own error, corrected by the gate. Four sites declare
`extern short D_801ED98C;` and nothing in src/ defines it, so I shipped an extern-only draft. The
gate refuted it: `undefined reference`. The .s block the draft REPLACED was the definition. The
variant emitting `const Blk8 D_801ED98C = {{...}}` banks clean. Never infer ownership from externs.
R22 clean-fleet: check-all 213 passed / 0 failed of 213.
ALSO BANKED — the wave's idiom harvest, which had been sitting unwritten in workflow transcripts
(R16/R30 debt): §160d the ASYMMETRIC INDEX RELOAD (a just-stored narrow field read twice emits
reuse-then-reload; the C is deliberately asymmetric), §160e a stack-layout scheduling rule now
byte-proven on a SECOND independent function (promoting it from coincidence to rule), §160f the
address-only global store via array decl, §160g sibling-search keyed on the CALLEE SET as step 0 of
every wave prompt (one grep turned a 126-instruction crack into a copy-edit).
Cookbook index regenerated: 468 sections.
scope_data_fix detected conflicts by SCANNING TU TEXT, which cannot see a MACRO-INJECTED
declaration — and that is where these conflicts live: `extern Vec8 D_80114F24;` sits inside a
DEFINE_func_* macro body in engine_core.h while the overlay .c holds only `DEFINE_func_XXXX()`.
That declaration is a genuine file-scope decl of every TU invoking the macro, and it is what the
draft collides with.
Fix: family_sweep builds a per-TU map from cdecl.tu_scope (cpp-derived, cached — 467 data decls in
ov_MAIN_012 vs 0 findable by text scan) and passes it to scope_data_fix, which now takes an
optional tu_types. tu_scope is the repo's stated oracle for "what does this TU declare", and its
own docstring warns that the `above` form answers VISIBILITY, not CONFLICT — C requires
compatibility regardless of order. scope_data_externs was built on that wrong question.
Banked 5 -> 31; failures 730 -> 699. The whole data-symbol class is gone: D_80114F24 (12),
D_800AE620 (10), D_80126B58, D_80078EB4, D_800183E0 all cleared.
R22 clean-fleet: check-all 213 passed / 0 failed of 213.
REGRESSION I CAUSED, AND FIXED: the reclassification showed 9 fresh `conflicting types for
aD800B9A02` — my own alias colliding. The group-level path suffixes the alias per function
precisely to prevent this; scope_data_externs did not, so two drafts aliasing one symbol declared
`aD800B9A02` twice with different types — re-creating the collision one level down. Both paths now
use _alias_name(sym, func). Verified: distinct names, each keeps its own type, both bind the real
symbol via the asm label.
family_sweep stages every member of an (overlay, split) group into ONE TU before gating, so two
templated bodies routinely carry different views of one address — D_80114F24 is `s32` in one body
and `Vec8` in another. scope_data_fix is handed one draft plus the pre-splice TU and structurally
cannot see the others, so that collision was invisible to it.
_alias_group_data_conflicts(): after staging, any data symbol a group's drafts declare with >=2
distinct types gets a PER-DRAFT §37 asm-label alias. The alias is suffixed with the function name
(aD80114F24_8017B880) — aliasing both drafts to a shared name would re-create the same collision at
one remove, which the unit test exists to catch. Each body keeps its own type: for data the declared
type drives the load (lh vs lhu), so canonicalising would silently change codegen for every other
view. Codegen unchanged — the asm label pins the emitted symbol.
83 conflicts aliased across 172 groups; banked 2 -> 5. R22 clean-fleet 213 passed / 0 failed of 213.
WHY THE HEADLINE SYMBOLS DID NOT MOVE — root cause now CONFIRMED, not inferred. D_80114F24 (12)
and D_800AE620 (10) are unchanged because the conflicting declaration is MACRO-INJECTED:
`extern Vec8 D_80114F24;` lives inside a DEFINE_ macro in engine_core.h (D_800AE620 has 6 such),
while the overlay .c holds only DEFINE_func_XXXX() invocations. Neither a scan of the staged drafts
nor a scan of the TU text can see it — that needs the preprocessed TU. The sweep already does
exactly this for CALLEES (cast_call_sites' canonical map is cpp-derived "so it sees macro-injected
declarations"); the data path never got it.
This collapses F2's remainder and F4 into one fix: memcpy's 26 failures are the same shape — task B
found nine `extern void *memcpy(...)` spellings inside those same DEFINE_ macros. A cpp-derived
declaration map feeds both, and the alias mechanism is already built and control-tested; only the
detection SOURCE is wrong. For memcpy the alias is the documented house solution, not a workaround
(engine_core.h:24480 hand-writes `extern void func_8005C324(...) __asm__("memcpy")`).
Four attempts on this class for 5 members: three mechanisms proposed before reading what the
compiler actually complained about. The mechanisms are correct; they targeted the wrong collision.