The largest target on the T14 census: 138 members x 289 ins = 39,882 templated instructions at stake.
conform_decls --check showed 418 sites in 3 forms, pointer-type-only with NO narrowing warning and no
return-type change — the func_80179B74 shape that banked 137/137. Applied (418 sites / 414 files),
gated (jtbl carve succeeded first try), R22 clean-fleet 140 passed / 0 failed of 140.
Committed BEFORE the family bank because jtbl_family_bank refuses to run on a dirty tree — its
per-sibling revert restores from HEAD, so an uncommitted axis would be destroyed. The tool enforcing
H4 in code, correctly.
Also recorded: family_sweep REFUSED this family rather than returning 0/137 — func_80135EB0 is
has_mid_jr, and §53's carve law says a carve-less sweep there yields "a 0% that is a TOOL artifact,
not a wall". That refusal is the good version of today's pattern: every false wall untangled this
session (func_8016B6BC 0/137, the 4 fabricated CC1-FAILs, Phase-28's B2 0/8) came from a tool that
ANSWERED instead of refusing.
BANKED 273 member-matches / 0 failed across 137 overlays (func_8014CF04 136 + func_8015D1B8 137).
R22 clean-fleet 140 passed / 0 failed of 140; report fail-closed green (dedup 1886/0, C1 coverage
239604/239604, 0 NON_MATCHING).
MEASURED from the committed digests, not projected: fn-count 317,898 -> 318,171 (+273);
instr-weighted 83.2 -> 83.4% (+26,770 ins); distinct-code 72.3 -> 72.5% (+129 unique fns).
A USEFUL NEGATIVE RESULT: both families swept cleanly across ov_SC07_006/007/010/011 — the same four
overlays that refused func_80176218's sweep earlier today. So that set is not broken; the 4/137
refusal is family-specific (the _jr_8016AE5C.c carve), which is the per-sibling INTEGRATION signal
§59 describes rather than a codegen or overlay-level wall. Carried, still not concluded.
SESSION-22 total: 3 exemplars + 406 members = 409 functions.
THE BANK: the T14 PLUMBING census showed func_8014CF04 blocking THREE drafts at once. Conforming its
decl axis banked func_8014CF04 + func_8015D1B8 (func_80135260 is a genuine DIFF, agreeing with its
independent SESSION-21 diagnosis). R22 clean-fleet 140/140; report fail-closed green (dedup 1886/0,
0 NON_MATCHING). fn-count 317,896 -> 317,898; distinct 66,110 -> 66,111.
BUT THE AXIS WAS A 1,748-FILE T2 WRITE SET (the --check per-form counts read "1"), and R22 came back
139/140 -- TWICE -- on a change the per-binary gate called BYTE-IDENTICAL. Three defects (§98):
1. THE REGEX CROSSED NEWLINES. `[^;]*` matches '\n', so a match starting at a DEFINITION line ran
past the `{` to the first `;`, swallowing `s32 func_8014CF04(...) {` PLUS the register pin on the
next line and replacing both with a prototype -> undefined reference. Fixed to `[^;{\n]*`: a
definition is now unmatchable by construction.
2. IT REWROTE INSIDE COMMENTS (H5, 3 lines). Now scans cdecl._mask() and rewrites by SPAN (R33 --
that length-preserving primitive already existed for exactly this).
3. THE REAL CAUSE -- IT ASSUMED ONE SIGNATURE FITS THE FLEET. ov_SC07_006 carries its own banked
definition with a DIFFERENT byte-true signature ((s32,s32,void*) vs (s32,void*,void*)), under a
decl marked "per-overlay-local decl (byte-true sig); do NOT re-macroize". That is the Phase-16
loose-typing wall inside a tool that structurally assumes it away. NEW RULE: a TU that DEFINES the
function owns its own declarations; a fleet axis is meaningful only for CONSUMING TUs. This grows
more common as banking proceeds -- every overlay that banks a function becomes an exception.
Then the R32 completion assertion cried wolf on its own by-design skip ("HALF-AXIS -- DO NOT BUILD"
for a complete rewrite): an assertion must be exact about its DOMAIN, not just its condition. Scoped
to consuming TUs -> 1,747 sites, 1 excluded by design. Also hardened to PLAN -> VALIDATE -> WRITE;
the refusal path had aborted mid-write while claiming nothing was modified, creating the very
half-axis §85 calls a guaranteed break.
META (R22's premise, re-earned): after fixing defect 1 I EXPECTED R22 to pass; it failed again for an
unrelated reason, and an individual `make build` of the failing binary SUCCEEDED by reusing objects
the clean run rebuilds. An incremental pass does not refute a clean-tree failure.
+134 functions banked total for this exemplar (1 + 133 members). Measured from the committed
progress.fleet.md, not projected: fn-count 317,762 -> 317,896; instr-weighted 82.9 -> 83.2%
(+43,818 ins); distinct-code 71.5 -> 72.3% (+126 unique fns — these members are genuine byte-
VARIANTS that each count distinctly, not free dedup).
VERIFY: R22 clean-fleet (make clean && extract-all && check-all) -> 140 passed, 0 failed of 140.
make report fail-closed green: dedup-check 1886 validated / 0 failed, C1 coverage 239604/239604,
0 NON_MATCHING in any default build (G4).
THE 4 FAILURES ARE CARRIED, NOT CONCLUDED. All four are ov_SC07_006/007/010/011 and all four differ
from the other 133 in exactly one way: their sibling TU is _jr_8016AE5C.c, not _jr_801734BC.c —
carved under func_8016AE5C, which was banked and swept in SESSION-21. That is the SAME four overlays
and the SAME carve the SESSION-21 checkpoint flagged as "worth checking first" for func_8016B6BC's
0/137, which turned out to be a transitive type-carry (§94) rather than a wall. Each sibling reverted
its byte-neutral self-decl edit cleanly, so no dead diff is left behind. Per §59 a sweep failure is a
per-sibling INTEGRATION signal, not a codegen verdict — read one sibling's real gate result
(COMPILE-fail vs byte-DIFF) before concluding.
The family that failed its sweep twice (once in the 274-member batch, once after the §91 guard) and
looked like the §86 bimodal 'some families just don't template' case. It was not.
DIAGNOSIS (§59 + §93): spliced ONE sibling and read cc1 directly. It reported `c`, `v`, `off`
undeclared — ordinary locals that ARE declared in the remapped body. cc1 says 'undeclared' because it
aborted the declaration block at an unknown TYPE and every later declaration fell out with it. Read
the FIRST error, not the loudest: a visibly-declared variable reported undeclared means suspect its
type.
THE LIFT MUST BE TRANSITIVE. Lifting the type the body names directly (M8_8016B6BC) changed nothing —
still 0/137. The real set was four, found by following each definition's own references:
M8_8016B6BC -> Prim_8016B6BC -> Vtx_8016B6BC (named only inside Prim's body) -> DVec_8016B6BC.
lift_types.py --apply, byte-gated ALONE first (neutral, d19c9580 unchanged), then swept.
RESULT 0/137 -> 137/137, zero failures. R22 clean-fleet 140/140. cookbook §94.
Cost of not diagnosing: this family sat recorded as 'doesn't template' across two sessions. Pointed
at one sibling's real stderr it took under an hour and was worth 137 members.
134/134 BANKED on the remainder after 3/3 on the probe — the FOURTH full-family sweep this session,
all four unblocked by the --like role guard, three of them 100%.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
1,600 decl sites across 523 files, in THREE different forms (s16 *a0 / short * / short *p),
conformed to the byte-true 'void func_80179B74(u16 *p)'. conform_decls ALLOWED this one: the arity
is unchanged, so no 0-arg call site can break, and the return is unchanged, so §85's precondition
does not apply. Gated BYTE-IDENTICAL; R22 clean-fleet 140/140.
The tool has now refused one axis (func_8015B950, correctly — it would have broken 138 binaries)
and cleared another (this one, correctly). Both verdicts held under R22.
133/134 BANKED on the remainder after 3/3 on the probe. ONE sibling refused (ov_SC03_108,
gate-fail) and is left as a stub rather than forced — a 136/137 recorded honestly beats a 137/137
that needed a shortcut. Third full-family sweep this session, all three unblocked by the --like
role guard.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
134/134 BANKED on the remainder after 3/3 on the probe — the second clean full-family sweep this
session, both unblocked by the --like role guard. func_8015B950 is stubbed in NO overlay.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
At 271 ins x 137 members this is the session's largest single family by instruction weight.
The same axis that broke 138 of 140 binaries an hour ago now lands clean, because conform_decls'
NEW arity guard located the actual obstruction instead of leaving me to absorb it by hand.
THE OBSTRUCTION WAS ONE LINE. Conforming `extern s32 func_8015B950(void)` -> `(s32 arg0)` turns
every 0-arg CALL SITE into `too few arguments`. My hand attempt assumed those were spread across the
926 TUs and would need 926 casts (the func_8012AAAC precedent, where it really was 137 separate
sites). They are not: there is exactly ONE call, in `src/shared/engine_core.h`'s
`DEFINE_func_8015BEE4()` macro body — expanded into all 926 TUs by the preprocessor.
func_8015BEE4 is a THUNK: `return func_8015B950();` with $a0 passing straight through from its own
caller. So the 0-arg call shape is byte-CORRECT and must be preserved, not fixed —
`return ((s32 (*)(void))func_8015B950)();` keeps it exactly (§17a-1; gcc folds the cast of a known
symbol to a direct jal, and the s32 return is unchanged so the thunk's value still flows).
Sequence: 1 cast -> conform_decls --apply (925 sites, R32 completion assertion: 0 remaining) ->
gate BANKED byte-identical -> R22 clean-fleet extract-all 139/139, check-all 140 passed / 0 failed.
The draft's 2 callee-decl conflicts (func_801725A4, func_80147078) dissolved with the axis.
Worth 37,398 templatable ins; the ×137 family sweep is next.
- 134/134 BANKED on the remainder (after 3/3 on the probe) => the family is 137/137, ZERO failures.
func_8012AAAC is now stubbed in NO overlay. R22 clean-fleet: extract-all 139/139, check-all
140 passed / 0 failed.
- FLEET 81.9 -> 82.0% instr · 89.60 -> 89.64% fn-count · distinct-code 69.3 -> 69.5%.
- THE METRIC POINT, reproduced twice in one session and in BOTH directions: this jtbl family is
byte-VARIANT (each overlay's table holds its own addresses), so every member is a genuinely new
unique function and distinct-code MOVED. The h_seq PURE families swept earlier added 274 members
and moved distinct-code by +0.0, because those members were already counted via their shared
exemplar. SESSION-20's routing rule, now byte-demonstrated: target byte-VARIANT families to move
RE-completeness; high-reach h_exact families move only the display number.
- cookbook §91 — "a structure-TRANSFER is only valid where the structure corresponds": the --like
role trap, plus the three-hypothesis trail (two wrong, and instructive: the sibling call-site casts
were a real defect that fixed nothing, and my own carve-alone test was a false lead that departed
from the tool's real sequence). The law: any "same family => same structure" transfer must state
which structural fact it assumes and CHECK it on both sides — and a tool that drops an error class
it cannot act on should still SURFACE it, because a bare `gate-fail` repeated 137 times cost far
more than printing one line would have.
The ×137 member sweep returned 0/3, and §53/§86 say a 0% is a diagnosis task, not a verdict.
Diagnosed: every sibling TU has the IDENTICAL shape to ov_SC01_077 — the INCLUDE_ASM stub, then
'extern void func_8012AAAC();', then a 0-arg call LATER in the same file. Splicing the definition
in puts a prototype in scope, so gcc rejects the call with 'too few arguments' — the exact failure
the exemplar hit, reproduced 137 times.
Measured, not assumed: 137 sibling TUs hold BOTH the stub and a 0-arg call — exactly the member
count. Cast one call site per TU to ((void (*)(void))func_8012AAAC)() (§17a-1; gcc folds the cast
of a known symbol to a direct jal). The 137 'extern void func_8012AAAC();' DECLARATIONS were
deliberately left alone — an early count of '274 sites' was the calls AND the externs, and casting
an extern would have been meaningless churn.
Byte-neutrality PROVEN before committing, not asserted: R22 clean-fleet extract-all 139/139,
check-all 140 passed / 0 failed. This must be committed BEFORE the sweep because
jtbl_family_bank reverts each sibling from HEAD — an uncommitted fix would be reverted by the very
tool that needs it.
The first jtbl-routed bank of the session, and it validates the whole chain end-to-end:
1. jtbl_carve SPLIT-TABLE repair (this session): jtbl_801D7FB0 28 -> 50 words (112 -> 200 B),
authorized by func_8012AAAC's own `sltiu 0x32`.
2. NEW FIX — SINGLE-TABLE PREDECESSOR: adding a second table to a subseg whose existing carve was
single-table lost the FIRST table's start entirely (new_offs has only the new one;
overlay_jtbl_addrs cannot see the old one because its owner is banked and extract PRUNED the
stub .s; and single-table carves persist no tables= to rebase). The span then failed its own
validator with "first must equal the span start" — the invariant naming the missing entry.
A single-table carve spans exactly its one table, so ITS SPAN START *IS* THAT TABLE'S START:
inference, not persistence, so it also works for spans carved before tables= existed. This is
the RECOVERABLE half of the documented func_8013F350 lesson (that one was a pre-§8e merged
DOUBLE — two tables, no record, genuinely unrecoverable).
Result: ov_SC01_077_a JTBL_PADS := 0,0 tables=+0x0,+0x14. Carve alone byte-gated BYTE-IDENTICAL
BEFORE the bank was attempted (§81 step 2).
3. ARITY axis, all-or-nothing: 1,244 decl sites / 1,240 files `(void)` -> `()` + an R32 completion
assertion (old-form remaining: 0).
4. ONE call-site cast: the definition lands at line 811 and a 0-arg call sits at 822, so gcc sees
the prototype and rejects it — `((void (*)(void))func_8012AAAC)()` (§17a-1; gcc folds the cast
of a known symbol to a direct jal). Only 1 of the 1,386 fleet-wide 0-arg call sites needed it:
the others see only the `extern ()` decl, which permits a 0-arg call.
DIAGNOSIS NOTE: the failure read CC1-FAIL with only a warning visible under make. Running the
pipeline stage-by-stage (cpp | cc1 | maspsx | jtbl_rodata_pads | as) put it on cc1 rc=33, and cc1's
own stderr named it exactly: "too few arguments to function func_8012AAAC" at line 994. Isolating
the stage was what turned an opaque Error 33 into a one-line fix.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
family_sweep correctly REFUSED this exemplar (§53: a jr-family must route through
jtbl_family_bank.py; "a 0% from this path would be a TOOL artifact, not a wall") — the ×137 member
sweep is the next step and needs a clean tree, which this commit provides.
- family_sweep --hseq over the 3 newly-banked exemplars: BANKED 274 member-matches / 137 failed
across 137 overlays, for ~0 agent tokens. Session total: 3 exemplars + 274 members = 277 fns.
- THE STALE-MAP STEP, hit and handled: the first sweep returned "0 matched-exemplar families"
because .run/family_hseq.json still listed the fresh cracks as draft-ov077. Regenerated
(matched-sib families 60 -> 63) and the sweep found them — the documented bank-x1 -> regen ->
sweep path (memory crack-wave-sweep-map-regen).
- §86 REPRODUCED CLEANLY: 2 of 3 families templated ~137/137; the third failed ~137/137. Not a
rate — a BIMODALITY. One probe per family, then sweep or skip; never a blended pool average.
- R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed (second clean-tree
verification this session). dedup 1886/0, C1 coverage 239,604/239,604, 0 NON_MATCHING (G4).
- FLEET 81.7 -> 81.9% instr · 89.52 -> 89.60% fn-count · distinct-code 69.3% UNCHANGED — correct
and expected: these are h_seq PURE propagation-class families, and SESSION-20's routing rule says
propagation moves only the DISPLAY metric (members were already counted once via their exemplar).
To move RE-completeness, target byte-VARIANT families. Stated plainly so the next session picks
targets by the metric it means to move.
- drive-by: family_sweep --help crashed (argparse %-expands help text; a literal "0%" needed "0%%").
BANKED (whole-binary byte-gate, the sole arbiter): func_8014D2A0 (80 ins ×138) · func_80158638
(87 ×138) · func_8016B6BC (94 ×138). Stubs in ov_SC01_077: 150 -> 147, 0 new stubs.
R22 CLEAN-FLEET: extract-all 139/139, check-all 140 passed / 0 failed. dedup 1886/0,
0 NON_MATCHING (G4). Fleet 81.7% instr / 69.3% distinct-code / 89.52% fn-count.
- WAVE STOPPED at Drew's request with 15/24 agents returned, ALL 15 status=match. Only the
completed drafts were gated; in-flight ones are still being written (§90d).
- PRE-GATE, both oracles, all 15: match_one MATCH + reloc_verify ALL RESOLVED. Routed 7 plain /
8 to the §81 jtbl carve chain.
- THE BLOCKER, MEASURED: 7 of 7 plain drafts failed PLUMBING, 0 DIFF, 0 compiler walls — the same
shape as SESSION-20's T0.2. §58b applies: the draft sig is byte-TRUTH (it MATCHed), the header
decl is the stale stub-era guess, so conform the DECLS.
- §85 RETURN-AXIS WIDEN, all-or-nothing: 3,471 decl sites / 1,736 files, precondition verified
(ZERO callers consume the return => byte-neutral by construction) + an R32 completion assertion
(old-form decls remaining: 0). func_8014D820's s32 return is load-bearing — forcing `void` costs
2 instructions (302 vs 304), so the decls had to move, not the draft.
TWO HONESTY ITEMS:
1. I REPORTED "0 of 7 banked"; the true number was already 2. My diagnostic pass printed only
lines starting with "- func_" (the failures) and hid its own successes while I read it for
error text. A script that prints only failures cannot tell you it succeeded — the R32
silent-skip shape aimed at my own instrumentation. Ground truth is the stub count (§55b(3)).
2. A REAL FINDING fell out of that mistake: same drafts, same tree, minutes apart — gate_stage's
full ladder banked 0/7 while bare harvest_verify banked 2/7. The LADDER REGRESSED two drafts
the bare gate accepts (§19's "sig_unify regresses already-canonical drafts", one level up, and
the exact mirror of SESSION-20's missing-ladder false 33%). Neither "always ladder" nor "never
ladder" is right — run both, let the byte-gate arbitrate. One build per draft.
OPEN: func_8014D820 still a stub — after the widen its error moved from `conflicting types` to an
assembler-stage failure, not finished diagnosing. Recorded as open, NOT as a wall.
T0.7 — the §86 one-member probe applied to the remaining FREE families: 9 LIVE / 6 DEAD / 5 unstaged.
The three highest-value families by raw size (18,084 / 11,234 / 10,880 ins) all probed DEAD — the
probe skipped them instead of burning ~400 gate cycles rediscovering it. Swept the 9 live: 104 banked,
8 of 9 families fully cleared (func_8017BEF8 has 8 stragglers).
BEHEMOTH 2 of 3: func_8017D2DC (1,586 ins, ov_SC01_001) MATCHED and BANKED — closed in ONE agent
round, pin-free. Verified independently (R14): match_one MATCH (1586 ins).
§81 carve chain: the agent predicted step 1 unnecessary; jtbl_carve REFUSED (the subseg already
hosts a .rodata carve and the new table's start != span start). The refusal was RIGHT and is the
instruction to run step 1 — jr_isolate_all --only (2 fns/1 object) -> BYTE-IDENTICAL, then
jtbl_carve -> BYTE-IDENTICAL, then the ladder banked it.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.5 -> 81.6% · distinct-code 69.0 -> 69.1% · fn-count 89.49 -> 89.52%.
The §42e pin guard refuses any family whose exemplar carries `register __asm__` pins: 680 of the top
8 FREE families' 1,083 members (63%) were skipped BEFORE any gate ran. Re-run with --allow-pins,
letting the byte-gate arbitrate (G3/P9): 268 banked, and ZERO cc1 crashes across hundreds of pinned
compiles — confirming the SIGABRT the guard was written against was Phase 27's extract_unit
macro-drop, NOT a compiler limit. The guard is protecting against a bug that no longer exists.
THE LAW (§86): templatability is a PER-FAMILY property, not a per-member rate.
func_801749C8 137/137 = 100% func_80133AB0 4/136
func_8014C6F4 137/137 = 100% func_8014CF04 0/137
func_80143D28 0/136
Two families at 100%, three at ~1%. MY REPORTED "37%" WAS AN ARTEFACT: a 19-member sample that
straddled families reported their AVERAGE and hid the bimodality. Sample PER-FAMILY, never per-pool.
=> PROCEDURE, now the default: probe ONE member per pinned family; bank -> sweep the family; fail ->
skip entirely. The blanket sweep spent ~412 futile gate cycles (60% of the run) on three families
that were never going to bank; the 1-member probe reduces that to 5 probes + 2 sweeps.
Left explicitly UNDIAGNOSED (do not guess): why two families template and three do not. Likely axis
is caller-saved pins spanning a `jal` (§74's corrupting form) vs pins fixing only a local allocno.
Diagnose BEFORE extending --allow-pins fleet-wide — the byte-gate makes a wrong guess free, but a
wrong PROCEDURE costs a sweep.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.3 -> 81.5% · distinct-code 69.0% · fn-count 89.41 -> 89.49%.
Re-derived the T0.1 decomposition post-harvest (it was stale by 395 banked members):
zero-crack pool 76 fams / 347,892 ins -> 73 fams / 290,850 ins (the harvest came out of it)
FREE (sweepable, non-jr, non-O0) -> 58 fams / 167,368 ins
Swept the top FREE families through the gate_stage ladder (sample 8/8 first, then the rest):
389 banked; func_801463A0 / func_8017B490 / func_80156670 now stubbed in ZERO overlays.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.0 -> 81.3% (10,645,711 -> 10,683,424) · distinct-code 68.8 -> 69.0% ·
fn-count 89.30 -> 89.41%.
THE BLOCKER HAS MOVED — it is now OUR OWN PIN GUARD, not gcc and not declarations. Of the 1,083
candidate members in the top 8 FREE families, 680 (63%) were refused by the §42e pinned-exemplar
guard BEFORE any gate ran; only 403 reached staging. Two pieces of evidence say the guard may now be
over-conservative: SESSION-19 banked func_8017A4AC x134 WITH pins once the byte-gate arbitrated, and
Phase 27 dissolved the cc1 SIGABRT that motivated it (it was the extract_unit macro-drop, not a
compiler limit). Next probe: --allow-pins on a sample of 8, byte-gated.
MY OWN SCRIPT BUG, fixed + negative-controlled: the sweep loop globbed `.run/sweep/*/`, which also
matches gate_stage's INTERMEDIATE ladder dirs (-cn, -cn-cast, -cn-cast-rc, -s2in, -s2in-uni). Those
were called as if they were binaries -> 24 phantom "PARTIAL 0/1" lines inflating notbanked to 56 when
the true failure count was ZERO (stub counts 0/0/0 are the ground truth). Fixed by requiring
config/splat.<ov>.yaml to exist; negative control confirms phantoms are skipped and real binaries kept.
The derived-offset recompute swept the whole func_8013D53C family: 119 banked / 0 failed on top of
the 4 earlier; func_8013D53C is now stubbed in ZERO overlays. R22 clean-fleet 140/140 BYTE-IDENTICAL;
tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
RECIPE (and it is NOT the return-axis recipe — sampling caught this):
§84 derived-offset -> per-member literal recompute AND the gate_stage ladder.
With the recompute alone the sample was 0/8; through the ladder it was 3/3, then 119/119.
Had I reused the return-axis recipe (plain harvest_verify, which banked 272/272 there) I would
have swept 123 members to zero banks and mis-concluded the fix was wrong. Probe-before-scale.
METRIC FINDING worth carrying: this harvest moved instr +29,280 AND distinct-code +27,840, while the
return-axis harvest moved instr +26,928 and distinct-code +0. §84-class members are byte-VARIANTS so
each is a new unique function; propagation-class members were already counted once via their shared
exemplar. => §84-class work moves the RE-COMPLETENESS number; propagation moves only the DISPLAY one.
Session fleet: 80.6 -> 81.0% instr · 68.2 -> 68.8% distinct-code · 89.18 -> 89.30% fn-count.
THE §85 WIDEN PAID OFF AS PREDICTED. It is a ONE-TIME fleet edit, so once committed the
`conflicting types` blocker was gone for EVERY member of both families at once:
- sample 8 first (probe-before-scale): 8/8 banked with PLAIN harvest_verify, no ladder needed
- full sweep: 264 banked / 0 failed across 132 overlays; 10 skipped as not-stub
- total 272 members ~= 27k ins, ZERO agent tokens
R22 clean-fleet 140/140 BYTE-IDENTICAL; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 80.6 -> 80.8% (10,589,503 -> 10,616,431, +26,928) · fn-count 89.19 -> 89.26%.
distinct-code UNCHANGED at 68.3% — propagation moves the DISPLAY metric, not the RE-completeness
one (the SESSION-19 split, reconfirmed).
§84 RECOMPUTE now implemented in tools/family_remap.py (fix_derived_offsets), wired into all three
apply_remap call sites as a PRE-pass on the exemplar body (the literal is ambiguous as a substitution
token, so it cannot be a table entry):
correct_literal = mapped(aliased_sym) - mapped(base_sym)
Verified by negative control: the hand-solved case recomputes 0x20 -> 0x18 exactly, and a site whose
target endpoint is NOT a mapped symbol is left byte-for-byte alone AND REPORTED in info
["derived_offsets"] (R32 — a silent skip is a defect, and a silent skip is how this bug survived).
Continues the "see why and try again" chain. Diagnosed all 4 T0.2 failures to 4 DISTINCT causes:
func_8013D53C 240x123 §84 derived-offset remap bug -> BANKED (previous commit)
func_8012CC88 105x137 §73/§30#2 RETURN-axis conflict -> BANKED here
func_8014D12C 93x137 §73/§30#2 RETURN-axis conflict -> BANKED here
func_80144090 154x136 LENGTH-DRIFT (+13 B, ~3 ins long) -> genuine codegen, real work
THE FAILURE THAT TAUGHT THE FIX: widening only src/shared/engine_core.h banked the member in the
TARGET overlay and BROKE ov_SC01_077 (R22 139/140) — the source overlay carries its OWN local
`extern void func_X(...)` decls, so a shared-header-only widen puts them in direct conflict. The
per-binary gate passed while breaking a binary it never built (§63/§61: a T2 write set is only
provable by R22). A half-done axis is a guaranteed break, not a smaller win.
THE FIX: do the WHOLE axis — 3,668 `extern void` decl sites across 2,688 files widened to `s32`,
0 remaining (R32 completion assertion). Precondition verified first: 0 callers consume the return
value, so the widen is byte-neutral by construction. R22 clean-fleet 140/140 BYTE-IDENTICAL;
tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
MY OWN ERROR, recorded (§85 trap): I first spot-checked ov_SC01_077 with
`make build | grep | head; echo rc=$?` and read rc=0 as success — that is the exit status of `head`,
not make, and the output had no BYTE-IDENTICAL line. I reported a false BYTE-IDENTICAL in the
interim. Assert on the SUCCESS STRING, never on $? after a pipe.
Fleet: instr 80.6% (10,589,503) · distinct-code 68.3% (3,846,656) · fn-count 89.19%.
- BANKED (each whole-binary byte-gated; make check-all -> 140 passed, 0 failed of 140):
func_80130D48 ×4 (1,064) · func_8018F3E4 (478) · func_8018B3D0 (478) · 13 × 223-ins siblings
of func_8017E6D8 (2,899). distinct-code 3,833,224 -> 3,838,143 = 68.0% -> 68.1%.
- TWO OF MY OWN COUNTS COLLAPSED UNDER SCRUTINY BEFORE I ACTED ON EITHER (R14/R35):
"func_8017CA80's family = 102 unmatched" was really 13 -- my count tallied family members whose
NAME appears as a stub anywhere in the fleet, not instances actually unmatched (a semantics
error, not arithmetic). "56,267 ins remappable" was really 8,114 -- 86% was the known -O0 /
deferred set (the func_80144B9C whale, the func_8013C414 cluster). I nearly recommended a
target on the first number.
- THE §77 CARRY GAP IS THE DOMINANT COST OF MECHANICAL REMAP: 23 of 27 first-pass CC1-FAILs.
NEW .run/giants/s19_remap_tu.py sources the preamble from the exemplar's OVERLAY TU (the block
between the previous top-level `}` and the def), applies family_remap's own substitution map,
and adds the two includes match_one never adds -> 21 drafts went 0 MATCH -> 14 MATCH. The 13
223-ins siblings share ONE exemplar, so a single preamble fix cleared all 13.
- USEFUL ASYMMETRY: func_8018F3E4/func_8018B3D0 FAILED match_one but BANKED in the whole-binary
gate -- the real TU supplies decls the standalone compile lacks. A match_one CC1 FAIL is not a
reason to skip the real gate on a remapped sibling.
- RESIDUAL 3,195 ins, causes NAMED not guessed: func_8017D5C0 (952) matches standalone, gate
reports `conflicting types for memcpy` = the §58 red-herring (a warning from an unrelated TU
position; SESSION-14 hit the same label and the true cause needed a hand-splice + real cc1
stderr). func_80166994 ×3 + func_8016A290 ×4 still CC1-FAIL after the TU carry.
- FULL R22 DEFERRED DELIBERATELY: make clean wipes asm/, which the concurrently-running BF14
agent reads on every probe. This batch changed only src/*.c (no config), so check-all is sound;
the clean R22 must still run once the agent finishes.
- dedup_extend banked 132 / 179 planned across 135 binaries: func_80174CB0 VERIFIED in 132,
FAILED in exactly 3. 123 ins × 132 = 16,236 ins.
- THE PREDICTION HELD TO THE OVERLAY. The blocker breakdown across the original 134-binary sweep
was 131 class-B (func_8012F14C arity split) / 3 class-A (func_80012ABC, census 73 s32 vs 7
s16). Fixing class B alone banked 132 and left 3 -- precisely the class-A set. A diagnosis that
predicts WHICH members will still fail, and is right, is much stronger evidence than one that
explains failures after the fact; same shape as §75b predicting that the 3 stuck members would
be exactly the 3 files carrying the __volatile__ spelling of SHB.
- FLEET: instr-weighted 80.2% -> 80.3% (10,539,723 -> 10,555,959); fn-count 89.14% -> 89.18%;
distinct-code 67.7% UNCHANGED (propagation moves coverage, not distinct-RE -- fresh cracks are
the only lever there). dedup 1886 validated / 0 failed, C1 coverage 239,604/239,604.
0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- cookbook §75c committed with this batch. The 3 residual overlays need the 7 `s16` func_80012ABC
decls normalized -- worth 3 overlays only, so do it only if trivially cheap.
- dedup_extend banked 157 / 478 planned across 135 binaries: func_80165CA0 (99 ins) ×135
(~+0.10pp) + 22 other functions ×1 picked up in the 3 overlays the first sweep excluded.
- FLEET: instr-weighted 80.1% -> 80.2% (10,525,534 -> 10,539,723, +14,189 ins); fn-count
89.09% -> 89.14%; distinct-code 67.7% (unchanged — propagation moves coverage, not distinct-RE).
dedup 1886 validated / 0 failed, C1 coverage 239,472/239,472. 0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- §75b — extraction lifts `extern`s but NOT file-scope `#define`s, so a body matched with a macro
in its preamble compiles only where that overlay's define is in scope ABOVE the splice point.
Signature is a LINK error (`undefined reference`), never `conflicting types`: an unexpanded
SHB(x) parses as a call to an undeclared function. The diagnosis PREDICTED the membership —
the 3 stuck members are exactly the 3 files carrying the __volatile__ spelling of SHB, i.e. the
function's own preamble still sitting above its own instantiation.
- R14/R35 IN ACTION: the full-sweep census REVERSED the ranking I had just committed. I put the
class-A normalization first at "~+0.13pp if it reaches ×138"; measured across all 134 it is
worth 3 overlays (func_80012ABC 3, func_8012F14C 131). The cheap win was the one I ranked
third. §75a's "collect across the whole sweep before scoping" earned itself immediately.
- NEXT (specified, not guessed): func_80174CB0 is class B on func_8012F14C (1944 `(s32)` vs 968
`(s32,s32,s32)`). The macro carries the 3-param prototype; the failing TU declares the 1-param
one FIRST (ov_SC01_001: TU@328 vs instantiation@2616) -> two prototypes, different arity ->
reject. Per cdecl.compatible's MEASURED rule a K&R `extern void func_8012F14C();` is accepted
BOTH ways round here (prototype-first + `()`-second always; `()`-first + prototype-second when
no param default-promotes, and s32 does not) -> it should satisfy both populations in either
order. One-line probe on the carried decl, byte-gate the 3 members, then extend.
- THE NORMALIZATION PAID: one `dedup_extend --binaries <the 134 excluded>` banked 134/400
planned -- func_8014F3E8 VERIFIED in ALL 134 -> ×138 total (+4,288 ins), no drafting at all.
A 4-overlay island became full fleet reach because the carried extern finally agreed.
- FLEET: instr-weighted 80.0% -> 80.1% (10,509,526 -> 10,525,534 = +16,008 ins, exactly the
projected 84×138 + 32×138); fn-count 89.02% -> 89.09%; distinct-code 67.7% (unchanged, as
expected -- propagation moves coverage, not distinct-RE). dedup 1884 -> 1886 validated / 0
failed, C1 coverage 239,315/239,315. 0 NON_MATCHING (G4).
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- §75a — "PROPAGATION-CAPPED" IS AT LEAST THREE CLASSES, and the classifier names which:
A minority spelling `conflicting types` + a lopsided census (1710 vs 4) -> normalize, cheap
B genuine arity split same message, TWO real populations (func_8012F14C: 1944 `(s32)` vs
968 `(s32,s32,s32)`) -> the §29 loose-typing wall; a K&R `()` MAY satisfy
both but is order-dependent -> PROBE, do not normalize on a guess
C missing extern `undefined reference to 'SHB'` -- a LINK error, unrelated to types
The discriminator is one grep (census the symbol cc1 named) and it decides the remedy.
- R14 self-correction recorded: I predicted func_80174CB0 was "the identical class". It is class
A in KIND but on DIFFERENT symbols, and different ones per overlay (func_80012ABC at
ov_SC01_000 where the minority is on the TARGET side; func_8012F14C at ov_SC01_001 = class B).
One member's error names one blocker, not the blocker set -- collect the classifier's line
across the whole sweep before scoping a fix.
- func_80174CB0 (×3) and func_80165CA0 stay capped, each now with a named cause and a named next
probe -- not a wall verdict.
- func_8014D4C0 (84 ins) PROPAGATED ×138: all 138 overlays rebuilt byte-identical, group
E_func_8014D4C0 registered. 84×138 = 11,592 ins.
- func_8014F3E8 (32 ins) propagated ×4 only (the ov_SC07_{006,007,010,011} island), 134
overlays excluded one at a time.
- TWO FINDINGS, both measured:
(1) THE FIRST RUN'S "drop" WAS A FLAG OMISSION, NOT A WALL. Without --recover,
dedup_propagate takes the historical all-or-nothing path on the first culprit overlay,
so ONE divergent member cost the whole group (×0). With --recover, Part A excludes just
the culprit -> ×4 instead of dropped. Always pass --recover on a targeted --addr run.
(2) THE EXCLUSION CAUSE IS A MINORITY-SPELLING SOURCE OVERLAY, not byte divergence. The
sigs prove all 138 members share ONE h_exact (2ccf344d), so nothing diverges in bytes.
The macro carries the source overlay's `extern void func_8014F468(void);` while the
fleet census is 1,710 `extern s32` + 134 `s32` definitions vs 20 `extern void` + 4
`void` definitions -- and all 4 `void` definitions are ov_SC07_{006,007,010,011}, i.e.
the source overlay I banked from is the OUTLIER. The macro inherited the minority
spelling and silently capped its own reach at that island.
- => the fix is NORMALIZATION (24 occurrences), not a reconciliation engine; the follow-up
commit flips the SC07 minority to the fleet-canonical s32 and re-propagates.
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
- FLEET WIDEN (T2, one edit): extern void -> extern s32 for func_8014F3E8 + func_8014D4C0
across src/** (16 decls in engine_core.h + 5,079 in 3,459 overlay .c; 0 `extern void`
left, 0 pre-existing `extern s32`). Scope re-verified against the tree first (R14/R35):
the SESSION-18 counts reproduce exactly and no decl exists outside the `extern void <name>`
shape in any .c/.h under src/.
- BYTE-NEUTRALITY OF THE WIDEN ISOLATED FIRST: ov_SC07_006 7ca772be + ov_SC01_000 9052dc0e
BYTE-IDENTICAL before splicing any draft (ov_SC01_000 chosen because it instantiates the two
return-CASTING macros — the only sites a decl's return type could touch codegen).
- BANKED into ov_SC07_006 (both ×1, both reach ×138 by sig: single h_exact across 138/138):
func_8014F3E8 (32 ins) on gate 1; func_8014D4C0 (84 ins) on gate 2.
- FINDING -> cookbook §73: the widen fixed only HALF the conflict. A def-side self-decl
conflict has TWO independent axes — RETURN (fleet macro-widen, T2, R22-mandatory) and
PARAMS (canonical param types + casts at each USE, T0, no fleet edit). func_8014D4C0
failed the first gate on the PARAM axis (canon `void*` vs draft `u16*`); the §17a-1 move
applied to the def's own signature banked it with nothing outside the draft touched.
Diagnose the axis before reaching for the expensive fix.
- R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed of 140.
make report: dedup 1884 validated / 0 failed, C1 coverage 239039/239039, 0 NON_MATCHING (G4).
Fleet 80.0% instr / 67.7% distinct / 89.02% fn-count (the ×138 propagation is the value).
- MATCH (59 ins), real-TU verified by the agent before handing back (cc1 rc=0, 59/59, 0 diffs).
- Propagated x138 with ZERO exclusions -> confirms the ×3 cap on func_80174CB0 was purely the
carried-extern collision: a body with no externs propagates clean.
- R22 clean-fleet 140/140, 0 failed. dedup-check 1884 validated / 0 failed, C1 coverage complete.
- FLEET CROSSES 80.0% instr-weighted (10,509,526 / 13,141,652); fn-count 89.02%; distinct 67.7%.
- THE LEVER (cookbook §70): residual was ONE instruction, addiu $t0,$t1,0xC vs $t0,$a0,0xC -- a giv
based on a copy of the param. Reading gcc-2.7.2 loop.c/cse.c proved the natural form can never
emit the target: cse.c:make_regs_eqv makes the copy canonical (it out-lives a0) and
loop.c:update_reg_last_use won't extend a0's last-use (giv-init UID >= max_uid_for_loop). Fix:
walk the PARAMETER itself, so record_initial sees the biv init as hard reg (reg:SI 4),
valid_initial_value_p accepts it (precondition: no calls), and emit_iv_add_mult bases the giv on
$a0 -- yielding both required instructions free.
- META: this compiler-source reasoning was done by an ORDINARY Opus 5 drafting agent, unprompted --
the tier Phase 23 reserved for Fable5. One data point, recorded as such; the cheap action is to
give routine drafting agents the gcc source path.
- func_8012B4B8 (84) + func_80169228 (105) propagated via targeted --addr (--check-only first,
never --auto-from): 138 overlays byte-identical, 2 new dedup groups, ~+26,082 ins.
- R22 clean-fleet: check-all 140 passed, 0 failed of 140. Fleet fn-count 88.90 -> 88.98%.
- The 3 non-banks are diagnosed, not guessed (blocker_probe, both oracles agree): func_801463A0 is
a real-cc1 MATCH in its own TU that the gate still rejects (§65c rtu-vs-gate divergence, link-level);
func_80156670/func_80174CB0 carry "drop when banking" typedefs textually identical to the canonical
ones. Blockers STACK — stripping the cc1-named typedef exposed the next (S8->B8; MATRIX->a callee
conflict). Remedy named: strip ALL shared-provided typedefs, then run the DRIVER's ladder.
- Targeted `dedup_propagate --addr 0x80177940 --recover` (NEVER --auto-from; --check-only first
confirmed the plan held exactly this one address, so the de-macroize hazard could not apply).
- 138 overlays rebuilt byte-identical; 0 stubs remain for the address; 1 new dedup group registered.
- R22 clean-fleet: check-all 140 passed, 0 failed of 140.
- Fleet: fn-count 88.86 -> 88.90%, instr-weighted 79.6 -> 79.7% (+13,938 ins = 101 x 138),
distinct-code 64,874 -> 64,875 unique fns.
- 5 of the 6 s15 fresh cores propagated ×138 (func_801483E8/8014680C/8017129C/80177AD4/801759D8;
func_8014A51C §20-capped). R22 clean-fleet 140/140. fn-count 88.66->88.86%, instr 79.4->79.6%,
distinct-code count 64854->64860, dedup 1879/0.
- EFFICIENCY AUDIT (decision-log): the 2 LLM waves ran 92% match_one MATCH but only ~27% whole-binary
bank; 6 spot-checked non-banks are ALL match_one MATCH (byte-correct bodies). NOT a missing idiom —
an INTEGRATION wall (def-side sig / data-extern / unshared struct). We strand ~16 paid-for correct
functions per wave; a fleet-safe integration-recovery pass would ~3.7× yield for 0 new drafting
tokens. Next investment = integration tooling, not more drafting. Waves held per Drew.
Of the 6 s14 fresh cracks, func_80136F3C propagated ×138 (func_801749C8 dropped as an
ov_SC07_006 straggler; 4 are §20 local-type-capped — future uniquify fodder, low-value
de-duplication per the SESSION-14 finding).
THE POINT (byte-verified): distinct-code UNIQUE count rose 64832 -> 64837 (+5) — the FIRST
distinct-code movement all session. Every other lever today (154-type lift, 6 uniquify cores,
MATRIX rename) was de-duplication and moved distinct-code by 0. These 6 are genuinely UNMATCHED
cores, so banking them ×1 is real new coverage. Percentage is flat (67.6%, denom 87459) but the
direction and the count are the validation the pivot to fresh cracks was correct.
Fleet: instr 79.3 -> 79.4% | fn-count 88.61 -> 88.65% | distinct-code count +5 | dedup 1873/0.
R22 clean-fleet 140/140 BYTE-IDENTICAL.
Oracle (G1, Ghidra types get, /LIBGTE.H, psyq400.gdt): MATRIX = 32 bytes (short m[3][3] @+0x00,
long t[3] @+0x14, 2B pad after m); SVECTOR = 8B; VECTOR = 16B (long vx,vy,vz,pad).
- HAZARD CONFIRMED: the fleet-wide name `MATRIX` held {s32 m[3][3]; s32 t[3]} = 48 BYTES, not a PsyQ
type at all, while THREE other names (MATRIX_c1, MATRIX_c2, MATRIX2) held the true 32B layout. The
canonical name carried an invention; any future reader assuming PsyQ semantics would be misled.
- FIXED by pure consistent rename across 205 files: 48B invention -> MATRIX_L48 (KEPT — ~131 files are
byte-correct against it, and "fixing" it to 32B would change sizeof/array stride hence codegen);
the true 32B layout now owns MATRIX. R22 clean-fleet 140/140 BYTE-IDENTICAL.
- VECTOR left UNCHANGED on purpose: ours is 12B vs PsyQ 16B (missing trailing pad). I first called it
dead — wrong, it has 1 live use (engine_core.h gte_ldlv0((VECTOR*)sp)). Offsets already agree so a
fix is likely byte-neutral, but it is a LAYOUT change and bundling it with a rename would make an
R22 failure ambiguous. Own R22-gated commit, later.
- engine_types.h gains an oracle-sourced GROUND-TRUTH block: real PsyQ layouts, which of our names are
true vs invented, why MATRIX_L48 must not be "corrected", and the draft-time rule (address-suffix
anything you invent — 7 of the 8 collisions this fleet accumulated were bare generic names).
- SCOPE HELD: renamed + documented; did NOT force existing code onto real PsyQ definitions.
- Propagated 0x8012A464 / 0x8014FFDC / 0x801502EC -> 138/138 byte-identical, 0 stragglers, 3 new
groups. R22 clean-fleet 140/140; dedup 1872/0; C1 237654/237654.
- FINDING (R14/R31): the §20 propagation cap was gating source-level DE-DUPLICATION, not coverage.
The whole uniquify campaign (Buf+MATRIX+Vec8, 223 files renamed, ~1559 copies stripped, 5
propagations, 4 R22 cycles) moved the fleet by +6 functions / +558 ins / -6 stubs and 0.00pp on
all three headline metrics. The freed cores' members were ALREADY matched individually;
propagation just consolidated them into shared macros. "Unblocked" != "unmatched".
- SESSION ATTRIBUTION: of -837 stubs / +0.3pp instr / +0.23pp fn-count, -831 stubs came from the
FIRST batch (broad lift -> 13 cores). The uniquify campaign contributed -6.
- Roadmap B4 re-labelled: a maintainability item, not a coverage lever. Remaining camps
(Handler/Blk8/V8/Prim/Prim_8016E7C8) are small and now known low-yield — opportunistic only.
- distinct-code sat at EXACTLY 3811442/5634875 = 67.6% at open and close. Fresh cracks are the sole
mover of that number; point the next session there.
Two units, committed together because they touch overlapping overlay .c files.
- PROPAGATE: 0x8012E778 (freed by the MATRIX uniquify) -> 138/138 byte-identical, 0 stragglers,
1 new dedup group.
- Vec8 UNIQUIFY+LIFT: camp1 {s16 unk0,unk2,unk4,unk6} (8 bytes) x139 files -> Vec8_c1; the 180-file
{s32 w[8]} (32 bytes) majority keeps the name. These are two genuinely DIFFERENT types that had
been sharing one identifier across TUs of the same overlay — the case §64a exists for; reconciling
them to one layout would have merged a 32-byte and an 8-byte struct. Both camps lifted, 319 local
copies stripped.
- Pre-filtered on an overlay from each camp, then R22 clean-fleet 140/140 BYTE-IDENTICAL.
- Blocked core queue 10 -> 7 (0x8012a464, 0x8014ffdc, 0x801502ec freed). Session arc 13 -> 7 via
uniquify (Buf -> MATRIX -> Vec8). Remaining camps all small: Handler, Blk8, V8, Prim, Prim_8016E7C8.
- uniquify_type --type MATRIX: camp1 {short m[3][3]; long t[3]} x71 jr-split TUs -> MATRIX_c1;
camp2 {short m[3][3]; short pad; s32 t[3]} x6 -> MATRIX_c2; the 578-file {s32 m[3][3]; s32 t[3]}
majority keeps the name. All three camps then single-def -> LIFTABLE -> lifted (655 local copies
stripped).
- Pre-filtered on one overlay from EACH camp (ov_SC01_000 / ov_SC01_077 / ov_SC06_033) — a
pre-filter is evidence only about what it filtered — then R22 clean-fleet 140/140 BYTE-IDENTICAL.
- Blocked core queue 11 -> 10 (0x8012e778 freed; propagation follows separately per §55b).
- NB: the R22 job was reported by the harness as exit -1 (session fork killed the shell wrapper
after make completed). Verdict taken from the log's terminal "check-all: 140 passed, 0 failed of
140", which is only emitted on completion. Read the gate's output, not the wrapper's exit code.
MEASUREMENT CORRECTED THE PLAN. The checkpoint called for a "per-camp field-access reconcile";
measuring the camps refutes that: Vec8 = {s32 w[8]} (32B) in 180 files AND {s16 unk0..} (8B) in
139 files; MATRIX 48B/32B/32B; Buf 16B / 0x20+ / DrawEnv. These are DIFFERENT types sharing an
identifier across TUs of the same overlay — reconciling to a canonical layout MERGES them, the
same failure that broke 103 binaries on Prim. The right op is UNIQUIFY: rename the non-majority
camp (byte-neutral — a type name emits no code; TU-local by construction), which makes every camp
single-def and liftable by the existing lift_types rules.
- NEW tools/uniquify_type.py: deterministic camp ordering (file-count desc, then normalized text,
so re-runs assign the same suffixes); majority keeps the name, camp n -> <T>_c<n>; rewrites ONLY
files that DEFINE that camp (a file that merely USES the name gets it elsewhere and is untouched);
\bT\b word boundaries so `Buf` never matches `Buf80153978`.
- VALIDATED on Buf (578/6/1 files): 11 identifiers across 7 files -> 3 camps LIFTABLE -> lifted
(585 local copies stripped) -> R22 140/140 -> blocked core queue 13 -> 11 (0x8012ea90, 0x801749c8
freed). Propagated 0x8012EA90 ×138; 0x801749C8 dropped (straggler in ov_SC07_006).
- YIELD, HONESTLY (P9): ZERO new matched functions. fn-count 88.61% / instr 79.3% / stubs 40281 all
UNCHANGED; dedup 1867->1868, C1 +138. 0x8012EA90's members were ALREADY matched in all 138
overlays — the propagation consolidated duplication into one shared macro (DRY), not coverage.
The value is the PROVEN RECIPE + the queue moving 13->11, not the numbers.
- dedup_propagate (R32): the skip line printed a COUNT and no names, and aggregated three unrelated
causes into n_local — a body skipped merely for a `//` comment (macro-unsafe, 1-line fix) read
identically to one genuinely using an overlay-local type. Now named and split by cause.
- cookbook §64a (uniquify-vs-reconcile + the validated recipe + remaining camps by cost).
With the §20 local-type cap lifted (commit:0863), dedup_propagate --auto-from planned 17
self-contained cores that were previously skipped "not self-contained (local types)".
- BANKED: 12 cores via --auto-from + func_80175308 (propagated separately, 138/138) = 13 ×138.
func_80175308 is the PROVABLE unblock — the core the SESSION-13 checkpoint named as
local-type-blocked. No claim is made that all 17 were unblocked by the lift; measuring that
needs a pre-lift re-scan I did not run (P9).
- 5 correctly DROPPED as cross-overlay stragglers (0x8012A018, 0x80172C50, 0x80173A60,
0x80144090 in ov_SC01_000; 0x801495C4 in ov_SC07_006): h_exact sharing is all-or-nothing and
those overlays' bytes diverge. --recover NOT used (the documented quadratic thrash hazard).
- GATES: R22 clean-fleet 140/140 BYTE-IDENTICAL (2nd full cycle this session); make report green;
dedup-check 1854 -> 1867 validated / 0 failed, C1 236964/236964; 0 NON_MATCHING (G4).
- FLEET: instr 79.0 -> 79.3% | fn-count 88.38 -> 88.61% | INCLUDE_ASM stubs 41112 -> 40281 (-831).
distinct-code stays 67.6% — correct: propagation replicates already-distinct-matched code;
only a fresh crack moves that number.
- STILL BLOCKED: 13 cores "not self-contained (local types)", blocked by exactly the 8 deferred
VARIANT entities (MATRIX 3-def, Buf 3-def, Vec8, Prim, Handler, Blk8, V8, Prim_8016E7C8).
Next lever = a per-camp FIELD-ACCESS RECONCILE, not a lift (lifting them blindly is what broke
103 overlays earlier this session).
The 3-session-carried blocker ("needs collision-vetting + -O0 strip precision") was
misdiagnosed on all three counts; fixing the instruments first (R35) changed every answer.
- (1) the "case-variant collision" is a TAGGED TYPEDEF counted twice with OVERLAPPING spans:
the inner span starts at `struct` so it emits a VARIABLE definition, the alias is redeclared,
and the highest-first strip leaves the outer end offset STALE -> over-deletes past the span.
MEASURED 13 pairs / 6142 occurrences / 0 standalone tags. build_engine_types.resolve_type_defs()
is now the ONE shared model (R33) + assert_disjoint() enforces span disjointness (R32).
The case-insensitive exclude was a heuristic over a structural fact — it would also have
wrongly dropped the legitimate Obj/obj + Vec/vec pairs. Key by (kind, name) — the C namespace.
- (2) the "-O0 strip precision" bug is a VISIBILITY bug: ov_SC01_077_o0.c is the 1 TU of 3226
that deliberately omits engine_core.h, so the strip DELETED its types; `multiple definition of
D_801DAA08` was 3 steps downstream (undeclared -> parse error -> implicit int -> tentative def
-> link collision) and named a symbol no diff touched. bet.type_visible() derives the visible-
header set from the include graph and keeps such defs local, named.
- (3) a third blocker, introduced this session and caught by R22: --candidates classifies per
ENTITY but emits per NAME, so passing `Prim` dragged in the deferred VARIANT `typedef Prim` and
repointed 103 overlays at the header's different layout. Compiled clean, per-binary pre-filter
green, R22 37/140 — the 103 failures were EXACTLY the 103 Prim-stripped overlays (set equality).
Fixed by the strip invariant "remove a local def only if what becomes visible is TEXTUALLY
IDENTICAL", placed at the MUTATION so a selector bug cannot reach the source.
- lift_types.py: (kind,name) entity keying, --candidates derived selector (retires the ad-hoc
102-type pipeline), divergence + visibility reports, whole-line strip (411 whitespace-churn
lines -> 0), complement-based single-pass rewrite (no mid-loop offset mutation).
- RESULT: 154 types lifted, 2958 files stripped, engine_types.h +510 lines. R22 clean-fleet
140/140 BYTE-IDENTICAL; tools-health OK (corpus 0/0, cdecl ALL GREEN, audit-binaries 140,
dedup-check 1854/0, C1 235170/235170); 0 NON_MATCHING (G4). Metrics unchanged 79.0/67.6/88.38
— honest: a type-lift banks no functions, it unblocks the NEXT propagate.
- DEFERRED + NAMED: 8 VARIANT entities (MATRIX 3-def, Buf 3-def, Vec8, Prim, Handler, Blk8, V8,
Prim_8016E7C8) = the per-camp field-access reconcile, the remaining hard part of roadmap B4;
14 carried tags; 5 types kept local in the -O0 TU.
- cookbook §64 (the three laws + the pre-filter lesson: a pre-filter is evidence ONLY about what
it filtered — ov_SC01_077 passed the Prim-broken run too; pre-filter on a binary that FAILED),
decision-log (R31), CURRENT_PHASE.md log.
Builds tools/lift_types.py: targeted fleet-wide lift of a SPECIFIC type list into
src/shared/engine_types.h, using build_engine_types' brace-aware parser (NOT the
[^;]* regex that under-counted multi-field structs — R35, caught twice). It picks
each type's CANONICAL (majority) def across the fleet, writes it once to the header,
strips every local copy, and reports variant overlays. The whole-binary gate (R22)
is the byte-arbiter.
Applied to the 2 CLEAN types the §20-blocked fresh-138 cores need:
- Mat32 (138 canonical + 1 copy-only draft variant in ov_SC07_006) + Cam8012E138
(unique) -> lifted, 139 local copies stripped. R22 clean-fleet 140/140.
- Unblocks func_8012B4B8 + func_8012E138 for x138 propagation (next).
DEFERRED (variant-heavy, high-risk — fleet genuinely split): MATRIX (3 defs), Vec8
(2 near-even 180/139), Buf (3), M8 (2) — lifting them would force ~929 files to one
camp's field layout and break variant field-access. Needs a per-camp reconcile pass.
func_80175308/func_8012A1BC still blocked (need those or other types).
Propagation of the 7 fresh-138 cores banked in commit:0855. Only the 2 self-contained
ones cleared all three integration walls:
- func_80130C08 x138, func_80137178 x138 (+274 stubs). R22 clean-fleet 140/140.
The other 5 are blocked, each by a different integration wall (drafting was solved —
all 7 bodies byte-matched; INTEGRATION is the bottleneck):
- func_8012B4B8/func_80175308/func_8012E138/func_8012A1BC: §20 local-type
propagation cap ("not self-contained") — need build_engine_types type-lift to
propagate x138 (roadmap B4; ~+552 stubs when unblocked). Stay x1.
- func_80169228: per-member divergence (whole SC03 cluster byte-diverges) — a
genuine partial family; --recover thrashes it (killed + reverted). Stays x1.
decision-log: fix_header_decl fragility correction (shared multi-caller decls break;
gate_stage's call-site-cast is the integration spine, not header-decl rewriting).
Bounded probe (SESSION-13, token-free) that REVERSED the same-session "fresh
reach-138 well is spent" verdict. Target func_8014CD80: 138 live, 0 matched, NO
DEFINE macro, a universal body (only universal callees + param offsets, zero
overlay-local D_* refs), clean MATCH draft from batch-1.
- Blocker was a def-side header decl: engine_core.h DEFINE_func_8014CD0C()
forward-declares it `void func_8014CD80(s32,void*,void*)` while the byte-true
def is `int func_8014CD80(s32,u16*,u16*)`. gate_stage's arity pre-pass is
param-COUNT-only (misses return/ptr-type); §54 reconcile_def_sig fixes the
wrong direction.
- One byte-neutral header edit (void->int, void*->u16*; call site passes u16[3]
arrays + ignores the return -> codegen unchanged) -> harvest_verify banked x1
BYTE-IDENTICAL -> dedup_propagate --addr propagated 138/138 overlays
byte-identical (live 138->0) -> R22 clean-fleet 140/140.
- Fleet 78.7->78.8% instr, 88.22->88.26% fn-count from this one family;
tools-health green (dedup 1851/0).
Quantified market (decision-log 2026-07-23): of the 75 fresh (>=100-live)
families, 46 carry an engine_core.h caller forward-decl, 38 SIMPLIFIED = the
func_8014CD80 pattern -> each a candidate x138 (~+1.5-2.8pp instr). NEXT: build
tools/fix_header_decl.py + a fresh-family wave. func_80165CA0's 0/135 was a
non-universal BODY (different failure mode), not this blocker.
- batch-1 wave: 25 non-jtbl ov_SC06_018 targets (reach 3-14 modal), 13/25 match_one
MATCH; whole-binary banked 8 (4 plain + 4 via gate_stage --src-file), swept 4 families
-> 30 members across 13 overlays. 5 matches deferred (missing-sym/s58/deeper plumbing);
12 nears are permuter fuel (several close=2/3/4).
- KEY: non-jtbl fns in a jr-split file need gate_stage --src-file <the jr TU> (the default-
TU reconcile misses them -- same class as the jtbl --src-file fix).
- BUG FIX (R33): gate_stage's arity-undo snapshotted src/<bin>/*.c BEFORE _gate1 splices
the banks there, so an unbanked draft in the batch triggered a snapshot-restore that
SILENTLY REVERTED the banks (measured: a 9-draft run banked 4, the 5 unbanked reverted
all 4 to INCLUDE_ASM). Fix: restore ONLY src/shared/ (the fleet hazard the snapshot
exists for); the binary's own TU arity edits are local + byte-neutral. GATE_NO_ARITY=1
was the interim workaround. s61's law from below (undo scope must not EXCEED write scope).
- incremental check-all 140/140 (concurrent with batch-2 drafting; full R22 after batch 2).
fleet distinct 67.5->67.6% (+38 unique fns), instr 78.6% steady.
- func_8017D648 (102, reach 82, MODAL/cross-address family): cracked fresh in ov_SC06_018
(+ a byte-neutral s17a-1 callee-cast reconcile of func_8012F14C to the TU's 1-arg
convention), banked x1, then swept 70/82 siblings (85%) via family_sweep --hseq
--source ov_SC06_018 --allow-pins (+3 gate-fail, +8 remap-refused unresolved-immediates).
- 3rd data point for the family-specific finding: across 3 thesis families the fresh-
exemplar sweep rate is 100% (func_801365B8) / 0% (func_80133AB0) / 85% (func_8017D648).
Refined model (-> decision-log): the sweep works for the MAJORITY of families at high
per-family rates, with a genuine per-member-wall minority; cracking is the generator.
- R22 clean-fleet 140/140 byte-identical; tools-health green; 0 NON_MATCHING.
fleet 78.5->78.6% instr / 88.18->88.20% fn (session net 78.4->78.6 instr / 67.1->67.5 distinct).
- binary-aware crack wave (new tools/workflows/wave_binary.js): 8-target calibration
over ov_SC06_018 substantial stubs, 7/8 match_one MATCH
- func_801365B8 (155, reach 133): cracked FRESH in ov_SC06_018, swept 132/132 siblings
via family_sweep --hseq --source ov_SC06_018 --allow-pins -- SESSION-10 refused this
family 0/133 from an ov077 exemplar. THESIS CONFIRMED (fresh exemplar unlocks it).
- func_80133AB0 (137, reach 137): cracked fresh + banked x1 (+ a byte-neutral s17a-1
cast reconcile of banked caller func_801343C4), but the family sweep FAILED 0/136 even
from the fresh exemplar (reverted clean) -- THESIS REFUTED for this family.
- FINDING (R14/R31 -> decision-log): the fresh-exemplar sweep is FAMILY-SPECIFIC, not a
blanket mechanical x137. A fresh crack is necessary but not sufficient; the byte-gate
arbitrates each family (~50% on this 2-family sample -> discount the ~1.5pp estimate).
- tooling (R33): family_sweep --source override now searches matched_members (a fresh
member leaves 'members' after a sig-regen); cdecl._depth0_spans consumes backslash
line-continuations so a raw-draft #define macro no longer trips audit-cdecl.
- R22 clean-fleet 140/140 byte-identical; tools-health green (dedup 1849/0, C1 234615);
0 NON_MATCHING. fleet 78.4->78.5% instr / 67.1->67.5% distinct / 88.14->88.18% fn-count.