aprop_symfix deduped its slate by BARE FUNCTION NAME, so a four-row slate for
func_8016AB6C across ov_SC03_107/ov_SC07_007/010/011 reported "1 drafts audited" - and
the three dropped rows each needed a DIFFERENT rebase, because each overlay has its own
target symbols. Same root as reloc_filter's binof and gate_lane's homonym staging: three
tools, one R48/§238 defect.
With all four visible, the structure is two uniform delta clusters of two, identical in
shape across all four overlays - one seed body's two data clusters each moving as a
block. STALE-DELTA only admits ONE cluster, so it refused all four as AMBIGUOUS. §420
records the safe generalisation (runs of constant delta, every run >= 2 members, D_
symbols only) and the verification step.
Rebased by hand under that rule, all four still MATCH at closeness 0, and the gate
banked 4/4 in 57 seconds with no drafting (commit:3629). Frontier 165, 45 banked.
- 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
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.
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).