Commit Graph

1933 Commits

Author SHA1 Message Date
Drew T 862e31df10 fix(phase-29): T75 — reconcile_def_sig no-prototype regression; func_80147364 is the narrow-param wall
Item 3 closes with 0 banks and a real answer: both routes priced, both refused.

  conform_decls (4,021 sites) : ⚠ SCALAR-NARROWING (s32->u16), NOT caller-neutral — argument
                                promotion changes at every call site (byte-proven on func_80175DA8).
                                Trades a plumbing failure for a byte failure.
  §99 no-prototype (9 sites)  : gated 140/140 byte-neutral, but the sweep fails with
                                `conflicting types ... An argument type that has a default promotion`

The second is the PHASE-15 DEAD-END reproduced: gcc-2.7.2 refuses to match a `()` no-prototype decl
against a definition with a default-promotion parameter (s8/s16/u8/u16/float). func_80147364 takes
(u16, u16). The remaining route is §43 — convert the DEFINITION to K&R so its params promote to int —
which is def-side and needs the exemplar re-matched, not a header edit.

A REGRESSION I CAUSED AND FIXED IN THE SAME TASK: the §99 header change broke reconcile_def_sig —
with the canonical now `void func_80147364()`, _merge_sig saw zero canonical params and returned the
canonical verbatim, DELETING the definition's parameters so the body referenced `param_1' undeclared
x137. A no-prototype decl constrains nothing, so it now REFUSES rather than conforms, distinguishing
`()` from `(void)` on the raw text. Verified the def keeps (u16 param_1, u16 param_2).

A §61 JUDGMENT CALL, FLAGGED: the func_80147364 header edit bought 0 banks and §61's undo law says an
edit that bought nothing gets undone. I KEPT it — `()` asserts no wrong type where `(u16, s32)` did,
it is gated byte-neutral, and it is a prerequisite for the §43 route; reverting costs another full
R22 gate for no functional gain. This is a judgment call against a documented law, Drew's to overrule.
2026-07-29 00:51:01 -06:00
Drew T 14bc520c96 fix(phase-29): engine_core.h — §99 no-prototype for func_80147364 (sidesteps a 4,021-site conform)
Item 3, and the cheap route won. conform_decls' dry run priced the direct fix and warned it off:

  byte-true def : void func_80147364(u16 param_1, u16 param_2)
  4,021 decl sites: 2,030 (u16,s32) + 1,983 (u16 a0,s32 a1) + 4 byte-true + 4 (u16,u16)
  ⚠ SCALAR-NARROWING (s32 -> u16) — NOT caller-neutral; argument promotion changes at every call
    site, so callers emit different code (byte-proven on func_80175DA8)

So conforming 4,021 sites would likely trade a PLUMBING failure for a BYTE failure. The §99
no-prototype form on the HEADER is compatible with both the byte-true definition and the existing
(u16, s32) prototypes, and touches 9 sites instead of 4,021:

  extern void func_80147364(u16, s32);  ->  extern void func_80147364();

Verified: header change ALONE, no src change, R22 clean-fleet 140 passed, 0 failed of 140.

This is the T67 failure resolved — that batch failed 2/140 with  because it corrected the header's TYPES while 272 TUs disagreed. Dropping the
prototype instead disagrees with nobody.
2026-07-29 00:47:01 -06:00
Drew T 3e6c364cd8 feat(phase-29): T73 — items 1+2: ARITY class resolved, audit learns §113; DECLS is the last value
ITEM 1 (call-vs-address re-check). My first detector counted the DECLARATIONS as calls, so every
function looked "called". Stripping `extern ...;` first gives the real split: func_80144B14 is
ADDRESS-TAKEN only (full retype — done in T72, 137/137); func_8013BD34 / func_8014358C /
func_8017D808 are genuinely CALLED and need §99.

§99 applied to all three -> R22 clean-fleet 140 passed, 0 failed of 140, byte-neutral.

SWEEP YIELD: ZERO, and recorded as such. func_8013BD34's family swept 0/136 — exactly as predicted
when I switched T72's probe off it (its def lives in ov_SC07_010_o0.c and _o0 families sweep ~1/137).
func_8014358C has no family as exemplar; func_8017D808's family is 1 member with an unbanked
exemplar. The §99 fixes are correct and byte-neutral but unblock nothing today.

ITEM 2: called_in_headers() strips declarations, treats `fn(` as a call and `&fn` as not; arity_ok is
now "arity matches OR the macro never calls it" (§113). Verified against all four.

THE AUDIT AFTER BOTH — 28 findings (from 61):
  DECLS  9 fns  141 stubbed binaries   <- the only class with value left
  SAFE  13 fns   15
  ARITY  3 fns    0                    <- §99 cleared the stub-bearing ones
  §85    3 fns    0
func_80147364 is 137 of those 141, and is item 3.
2026-07-29 00:42:40 -06:00
Drew T 86c315ec08 fix(phase-29): engine_core.h — §99 no-prototype for the three CALLED ARITY functions
Item 1's payoff. §113's call-vs-address re-check found func_80144B14 was the ONLY address-taken one
(already fully retyped, T72); func_8013BD34 / func_8014358C / func_8017D808 are genuinely CALLED, so
their arity IS constrained by the macro's own call site and the full retype is unavailable.

§99 no-prototype is the fix: `extern void func_X();` accepts the macro's fixed-arity call AND the
definition's differing arity, and a no-prototype call passing the same arguments generates the same
code.

  extern void func_8013BD34(void);  ->  extern void func_8013BD34();   (def takes s32 a0)
  extern void func_8014358C(void);  ->  extern void func_8014358C();   (def takes s32 param_1)
  extern void func_8017D808(s32, s32); -> extern void func_8017D808(); (def takes void *a0)

Verified in one step per the T48 discipline: the header change ALONE, no src change, R22 clean-fleet
140 passed, 0 failed of 140. Batched three because the technique was the variable, not the targets —
a bisect over three is cheap if it fails.
2026-07-29 00:39:22 -06:00
Drew T ed95cad428 feat(phase-29): T72 — ARITY probe banks 137/137; most of the class was never an arity problem (§113)
Probe target switched from func_8013BD34 on measured evidence (its def is in ov_SC07_010_o0.c and
_o0 families sweep ~1/137 — a poor test of an unproven technique). func_80144B14: same class, 137
stubs, not -O0, real 34x137 family, tests both axes (void(void) -> int(int)).

THE PROBE FOUND THE PRECONDITION OVER-FIRING. The ARITY blocker exists because the macro's own CALL
SITE passes the header's arity. But DEFINE_func_* does not call func_80144B14 — it takes its ADDRESS:
    *(s32 *)((s32)a0 + 0xDC) = (s32)&func_80144B14;
No call site => no arity constraint => the FULL correction is available, not the §99 no-prototype
workaround. Applied `extern int func_80144B14(int param_1);`.

RESULT: header change ALONE -> R22 clean-fleet 140 passed, 0 failed of 140 (byte-neutral); family
sweep -> 137/137, 0 failed.

METRICS: instr 86.6% (+4,658 ins); fn-count 91.12% -> 91.15% (+137); distinct-code +0 (byte-identical
family — §111 predicted it).

THE REFINEMENT (cookbook §113): the precondition must ask what the macro DOES with the symbol — a
call constrains arity, an address-taken or unused decl does not. Blocking on "both names appear"
over-fires, and it had 137 members behind it. The remaining ARITY findings should each be re-checked
for call-vs-address before assuming §99 is needed.

GATES: R22 140/140 twice; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
2026-07-29 00:34:12 -06:00
Drew T df86fcce50 fix(phase-29): engine_core.h — func_80144B14 declared int(int), not void(void)
The ARITY blocker did not apply: DEFINE_func_* does not CALL func_80144B14, it takes its ADDRESS
(`*(s32 *)((s32)a0 + 0xDC) = (s32)&func_80144B14;`). There is no call site to break, so the FULL
correction is available rather than the §99 no-prototype workaround.

That is a refinement the audit needs: the ARITY precondition asks whether the macro's own call site
would break, but an address-taken use has no call site. Over-fires on that shape.

§85: 0 consumers, so the void->int return widening is byte-neutral.

Verified in two steps (T48 discipline): header change ALONE, no src change, R22 clean-fleet 140
passed, 0 failed of 140. Fleet-shared (§61/§63), R22 mandatory.

Probe target switched from func_8013BD34 on measured evidence: that one's definition lives in
ov_SC07_010_o0.c, and _o0 families sweep ~1/137, making it a poor test of an unproven technique.
func_80144B14 is the same class, 137 stubs, not -O0, with a real 34x137 family.
2026-07-29 00:23:14 -06:00
Drew T 3233ff3f81 docs(phase-29): T71 — the byte-variant stall is an AUDIT GAP, not an immediate-engine limit
The next-list item was "measure the T2a immediate-resolution rate". The log refutes that framing: of
T70's 10 families only 152 members were refused at remap for unresolved immediates — 1,346 failed the
GATE. The immediate engine is not the bottleneck.

Diagnosed the largest failed family (0x80131eec, 15 ins x 289 members, class=IMM, cross-address):
member ov_SC01_000 @ func_80151944, imm_map entries 0 (nothing to resolve), verdict PLUMBING —
`conflicting types for func_80151944`, with the §85 guard correctly refusing to bend the draft.

Same header-vs-byte-truth class as T63/T64/T68 — but audit_header_sigs.py never flagged it:
  func_80151944 definitions in src/ : 0 (a stub in all 138)
  declarations                      : 2,022
  engine_core.h says                : s32 func_80151944(void)
  byte truth (exemplar func_80131EEC): void func_80131EEC(void *a0)

THE GAP: the audit compares a header decl against definitions OF THE SAME NAME and skips a function
that has none. For a CROSS-ADDRESS family member the byte truth is the EXEMPLAR's definition, under a
different name at a different address — so every such member is invisible to the audit while being
blocked by exactly the defect the audit exists to find. That is why item 5 keeps hitting header
conflicts the audit said were not there.

THE EXTENSION: feed .run/family_hseq.json in, so an undefined member inherits its exemplar's
signature as truth. This one would then class as ARITY ((void) vs (void *a0)) and need §99 treatment
— the extension makes the blocker VISIBLE AND NAMED, not automatically fixable.

No src/ or config/ change: no bank, no metric move. Tree clean.
2026-07-29 00:15:06 -06:00
Drew T 2962b01ec1 docs(phase-29): SESSION-24 REVISED-2 checkpoint — 1,771 banked, fleet 86.6% instr
Supersedes both earlier SESSION-24 blocks. Session total reconciled against the metric (fn-count
320524 -> 322295 = +1,771; instr +134,811), the lesson earned nine times, a ranked measured NEXT list
led by the T2a immediate-rate measurement (which decides whether the 26 remaining byte-variant
families are worth sweeping), my errors, carried defects.

Fresh session safe from here: HEAD commit:1175, tree clean but for the R23 db.*.gbf churn, R22
clean-fleet 140/140 (run 17x), tools-health OK, dedup 1886/0, 0 NON_MATCHING.
2026-07-28 23:47:58 -06:00
Drew T 283937ed8e feat(phase-29): T70 — byte-variant families sweep 1 of 10 (138 banked, +130 distinct)
Item 5, first batch. Swept 10 byte-VARIANT non-jr non-O0 families (42,235 ins / 1,552 distinct
projected): 138 BANKED / 1,346 failed — ONE family of ten (func_801627E8 137/137), plus 152 members
skipped as "unresolved immediates (T2a)".

THE FINDING: that is a ~10x worse rate than the byte-IDENTICAL families, which banked 137/137 apiece
all session. It follows from what §111 established — a byte-variant member differs in more than
relocations, so the template must adapt immediates too, and family_remap's T2a engine refuses what it
cannot resolve. The distinct-code lever is real but it is NOT the same cheap sweep, and the projected
"2,962 distinct across 36 families" should be discounted until the immediate-resolution rate is
measured. That measurement is now item 1 of the next list, ahead of sweeping the other 26.

§111 PASSED A SECOND PREDICTIVE TEST: projected +129 distinct for func_801627E8; observed +130 (the
extra from an unrelated 2-member bank).

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK; 0 NON_MATCHING (G4).

METRICS: instr 86.5% -> 86.6% (+2,618 ins); fn-count 91.08% -> 91.12% (+138); distinct-code
68,066 -> 68,196 = +130 — the first real distinct-code movement of the session.
2026-07-28 23:47:22 -06:00
Drew T a6e5abfd39 fix(phase-29): T69 — audit preconditions computed, not discovered; validated against known outcomes
Item 1. audit_header_sigs.py now COMPUTES the safe subset instead of leaving it to a failed gate,
and the two new preconditions took two wrong models to get right (cookbook §112).

PRECONDITION 1 — ARITY: correcting a `(void)` header decl for a 1-param definition breaks the macro's
OWN call site ("too few arguments"). Measured before the batch.

PRECONDITION 2 — VISIBLE COLLISION, and the two wrong models on the way:
  (a) "any disagreeing decl in src/ blocks it" — compares type SPELLINGS, so s32-vs-int and
      u32-vs-unsigned-int count as disagreements. Fixed by comparing type IDENTITY via
      cdecl.compatible. Finding count 61 -> 32 once that noise is gone.
  (b) "any INCOMPATIBLE decl in src/ blocks it" — STILL WRONG. It blocked ALL SIX corrections that
      had just gated 140/140 and banked 685 members. func_80161774 has 1,063 TUs carrying the old
      spelling and correcting it was byte-clean.
  The right model: a macro-body decl is only visible where the MACRO IS INSTANTIATED, so a collision
  needs a TU that BOTH instantiates the macro AND carries an incompatible decl. Measure the
  INTERSECTION, not the population (macro_owners() + per-TU macro-use set).

VALIDATED AGAINST KNOWN OUTCOMES (the control this needed): the six that gated clean -> 0 colliding
TUs each; the one that failed the gate (func_80147364) -> 272. Perfect discrimination.

HONEST RESULT: 32 findings, 13 SAFE — but the safe subset is worth only 15 stubbed binaries. The
high-value targets (func_80147364 at 137, the arity trio at ~410) are all BLOCKED and need
conform_decls or §99 first. The cheap header lever is spent.

No src/ or config/ change: no bank, no metric move.
2026-07-28 23:26:02 -06:00
Drew T f59ae302b8 feat(phase-29): T68 — 6 header corrections sweep 685 members (+33,565 ins); fleet 86.5% instr
The audit was the right precondition: THREE of the six corrected functions were families already
queued for the item-3 sweep, and each would have failed 0/137 exactly the way five families did
earlier today.

SWEEP: 6 corrected functions, all non-jr families with 137 live stubs -> 685 BANKED / 137 failed.
Five families landed 137/137; func_80146750 failed on its own residual (undiagnosed).

GATES: R22 clean-fleet 140 passed, 0 failed of 140 — after the header batch alone AND after the
banks; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED, cdecl, audit-binaries, dedup 1886/0);
0 NON_MATCHING (G4).

METRICS: instr 86.3% -> 86.5% (11338739 -> 11372304 = +33,565 ins); fn-count 90.88% -> 91.08%
(321472 -> 322157 = +685); distinct-code 76.9% -> 76.9% (+0).

§111 GOT ITS FIRST PREDICTIVE TEST AND PASSED: all six families have a single h_exact class, so the
model predicted +0 distinct BEFORE the sweep ran, and +0 is what happened. The metric is modelled,
not mysterious.
2026-07-28 23:16:08 -06:00
Drew T 0c5df25faf feat(phase-29): T67 — audit_header_sigs.py; 61 header decls contradict byte truth, 6 corrected
THE TOOL (tools/audit_header_sigs.py, cookbook §112). A DEFINE_func_*() macro forward-declares the
functions its body calls, and that decl is visible in EVERY overlay instantiating the macro — so when
it disagrees with the byte-true definition the whole family becomes untemplatable and the failure
wears a compiler wall's clothes. Three such were found ONE AT A TIME earlier this phase
(func_80156044, func_8016163C, func_8014D610), each worth ~137 members, each costing a
diagnose/fix/re-sweep cycle. This audits all of them in one pass: parse every `extern func_X(...)` in
src/shared/*.h, find every DEFINITION in src/**/*.c (via §110's _def_head_at, not "ends in ;"),
compare with cdecl, and report only where NO definition agrees — one overlay disagreeing is loose
typing (§16/T49), all of them disagreeing means the header is the outlier.

RESULT: 3,043 decls across 1,023 functions; 265 have definitions; 61 contradict every one. The top 10
are full-fleet families (137/136/134 live stubs, 1,366 total), all with an unambiguous byte truth.

APPLIED: 6 functions / 11 decl sites, R22 clean-fleet 140 passed, 0 failed of 140 —
func_80138DE0, func_80146750, func_80161374, func_80161774, func_80161888, func_801778A8.

TWO PRECONDITIONS THE AUDIT DOES NOT YET CHECK, both found by gating rather than by reasoning:
 1. ARITY. func_80144B14 / func_8013BD34 / func_8014358C declare (void) but are DEFINED with one
    parameter. Correcting the header would break the macro's OWN call site (too few arguments), so
    they need the §99 no-prototype treatment instead. Excluded before the batch, by measurement.
 2. OTHER IN-SCOPE DECLS. The first batch of 7 FAILED the gate 2/140 with `conflicting types for
    func_80147364` — the overlays' own TUs declare it the old way (9 header sites rewritten, but
    src/ov_*/…:347 disagrees). A header correction is only safe when no other in-scope declaration
    disagrees; that one additionally needs a conform_decls pass. Excluded; the other 6 then gated
    140/140 clean.

The gate caught the bad batch immediately and the culprit was found by reading one object's real cc1
output rather than by a 7-way bisect (7 fleet gates = ~2.5h; one serial compile = seconds).
2026-07-28 23:00:55 -06:00
Drew T a6f6ccf545 docs(phase-29): T66 — item 4: the distinct-code anomaly modelled and closed (it was never a bug)
Seven sweeps moved distinct-code by +125/+125/+129 and +0 four times; I had logged it four times as
"unexplained, still not guessed at". Modelled in one pass:

    delta_distinct = (distinct h_exact classes in the family) - (classes already matched)

weighted_metrics counts distinct h_exact classes with >=1 matched instance. EXACT on all 7, no
residual: func_80135260 131-6=125; func_80133AB0 131-6=125; func_80156044 130-1=129; the four +0
families have EXACTLY 1 class across all 138 overlays (every member byte-identical), already matched
via the exemplar.

IT IS A REAL SIGNAL, NOT NOISE. A byte-IDENTICAL family is ONE piece of distinct code — the
exemplar's crack already reconstructed it, so the other 137 banks pay fleet/instr in full (each
binary now builds from source instead of pasted asm) but add NO new reverse-engineering. A
byte-VARIANT family is ~130 genuinely different functions and pays both. The two headline metrics
rank the same work differently, and both are now predictable BEFORE spending a sweep.

THE REMAINING FRONTIER, PRICED BOTH WAYS (49 eligible non-jr families):
  byte-identical  13 families   80,085 ins       0 distinct
  byte-variant    36 families  114,331 ins   2,962 distinct
  total           49          194,416 ins (~1.48 pp instr)

MY OWN BUG, CAUGHT BY VERIFYING (R14): my first ranking reported ALL 49 families as byte-identical /
0 distinct yield. Defect in my probe — I wrote int(x,16) on the member address in one comprehension
and forgot it in the next, so every sig lookup missed and every family collapsed to one class. Caught
only by spot-checking two entries against a direct count (func_80143D28 is 130 classes, not 1). Had I
reported it, the conclusion "the entire remaining harvest is worthless for distinct-code" would have
been exactly backwards for 36 of 49 families.

cookbook §111, with §106 applied: the ranking is two lines over the sigs, so it is derivable on
demand and deliberately NOT committed as a table that rots.

No src/ or config/ change: no bank, no metric move.
2026-07-28 22:41:46 -06:00
Drew T 350cc1c34c docs(phase-29): T63-T65 + SESSION-24 REVISED checkpoint — 948 banked, fleet 86.3% instr
Items 1-3 all landed 137/137 (func_8016163C, func_8014D610, func_80156044) for +30,962 ins; both
header flips byte-neutral and proven in two steps; §110 records the extract_unit definition-detection
law and the two traps in its assertion.

Full 🛑 checkpoint refreshed (the earlier SESSION-24 block predates T59-T65 and is superseded):
state, session total reconciled against the metric (fn-count 320524 -> 321472 = +948, +98,628 ins),
the one lesson earned seven times, a ranked measured NEXT list, my errors, carried defects.

Fresh session safe from here: HEAD commit:1169, tree clean but for the R23 db.*.gbf churn, R22
clean-fleet 140/140, tools-health OK, dedup 1886/0, 0 NON_MATCHING.
2026-07-28 22:30:44 -06:00
Drew T ec34c31b68 feat(phase-29): T65 — extract_unit definition-detection fixed; func_80156044 137/137 (+10,138 ins)
Item 3, and it banked the third family. extract_unit located a definition with "the line matches
<type> func_<addr>( and does not end in `;`" — wrong whenever ONE LINE holds both a declaration and a
definition, which the handwritten inline-asm wrappers do:

    extern void func_80156044(int, int); int func_80155FF8(int, int) { __asm__ … }

The line does not end in `;`, so func_80156044 — appearing there only in the DECLARATION — was taken
as a definition head. extract_unit lifted the neighbouring WRAPPER instead of the real definition
seven lines below; every sibling already defines that wrapper via its shared DEFINE_ macro, so all
137 failed with `redefinition of func_80155FF8` and it read as a compiler wall.

FIX: ask what follows the PARAMETER LIST, not what ends the line (`_def_head_at`) — `;` is a
declaration, `{` or end-of-line is a definition. Plus the R32 assertion: a unit that defines a
function other than its target cannot template, so refuse LOUDLY (`_foreign_defs`).

TWO TRAPS HIT WHILE WRITING THAT ASSERTION, both caught by regression-checking against families known
to bank: (1) _def_head_at ALONE over-fires — a call whose args wrap has nothing after the `(` on its
line, which "end of line => definition" reads as a definition; it refused THREE families that had
just banked 137/137. (2) The type-prefix test ALONE under-fires — it is what missed the wrapper
originally. The predicate needs both: split the prefix on its last `;`, require the remainder to look
like a return type, then check what follows the parameter list. All five known-banking families
extract byte-identically before and after.

RESULT: func_80156044 0/137 -> 137/137, 0 failed.

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).

METRICS: instr 86.2% -> 86.3% (11328601 -> 11338739 = +10,138 ins); fn-count 90.84% -> 90.88%
(321335 -> 321472 = +137); distinct-code 76.7% -> 76.9% (67937 -> 68066 = +129).

cookbook §110.
2026-07-28 22:29:56 -06:00
Drew T 60f9ac40f3 feat(phase-29): T64 — func_8014D610 swept 137/137 after the header correction (+10,138 ins)
Item 2, and the same story as item 1: the header correction WAS the fix. With engine_core.h
declaring the byte truth, the family swept 137/137 with zero failures — no draft change.

  before (header wrong)  0/137  `conflicting types` / a param-retyped body that could not compile
  after  (header right)  137/137, 0 failed

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).

METRICS: instr 86.1% -> 86.2% (11318463 -> 11328601 = +10,138 ins); fn-count 90.81% -> 90.84%
(321198 -> 321335 = +137); distinct-code 76.7% -> 76.7% (+0 — a SIXTH data point for the anomaly).
2026-07-28 22:10:10 -06:00
Drew T 3c380fec83 fix(phase-29): engine_core.h — func_8014D610 declared s32(s32,s32,u16*), not void(s32,void*,void*)
Same class as func_80156044 and func_8016163C: the shared header contradicted the byte truth. The
exemplar's banked definition is `s32 func_8014D610(s32 param_1, s32 param_2, u16 *param_3)`
(ov_SC07_006_jr_80140608.c:4576); DEFINE_func_8014D438 declared
`void func_8014D610(s32 a0, void *a1, void *a2)`.

That mismatch is what made --fix-def-sig retype param_3 to `void *` while the body does
`param_3[0]` -> `void value not ignored as it ought to be` (T61's param-use guard now refuses it,
naming the header as the real fix — this is that fix).

§85 sized first: 0 callers consume the return. The macro's call site passes `s16 buf1[4]`/`buf2`
into the s32/u16* params — same 4-byte values in $a1/$a2, so the retype is a warning, not a codegen
change.

Verified in two steps (T48 discipline): the header change ALONE, no src change, R22 clean-fleet ->
140 passed, 0 failed of 140. Fleet-shared (§61/§63), so R22 was mandatory.

NOTE: ov_SC07_006_jr_80140608.c:4529 records an earlier, DIFFERENT resolution of the same conflict —
a per-overlay de-macroized local decl ("do NOT re-macroize"). That remains correct and untouched;
this fixes the shared decl the other 137 overlays see.
2026-07-28 21:52:38 -06:00
Drew T a850255572 feat(phase-29): T63 — func_8016163C swept 137/137 after the header correction (+10,686 ins)
Item 1. The header flip (commit:1163's sibling, committed just before) was the whole blocker: with
engine_core.h declaring the byte truth, the family swept 137/137 with ZERO failures — no draft
change, no new lever.

  before (header wrong)  0/137  `conflicting types` / a --fix-def-sig-truncated draft
  after  (header right)  137/137, 0 failed

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).

METRICS: instr 86.0% -> 86.1% (11307777 -> 11318463 = +10,686 ins, exactly 137 x 78);
fn-count 90.77% -> 90.81% (321061 -> 321198 = +137); distinct-code 76.7% -> 76.7% (+0).

The distinct-code anomaly now has FIVE data points (T52 +125, T57 +125, T56 +0, T58 +0, T63 +0) and
still no identified variable. Unchanged as the queued probe.
2026-07-28 21:50:19 -06:00
Drew T 2d91ed54fb fix(phase-29): engine_core.h — func_8016163C declared s32(s32,u32), not void(void*,s32)
The shared header contradicted the byte truth. The exemplar's banked definition is
`s32 func_8016163C(s32 arg0, u32 arg1)`; both DEFINE_ macro decl sites said
`void func_8016163C(void *a0, s32 a1)`.

That mismatch is why the family could not template, and it is what made --fix-def-sig DEMOTE the
return to void — gcc then deleted the computation feeding it and the draft compiled to 58
instructions against a 78-instruction target (T62's self-inflicted SIZE-MISMATCH).

§85 sized first: conform_decls.consumers(func_8016163C) = 0 — both macro call sites discard the
return (`func_8016163C(a0, func_801615C4(a0, 0));`), so the return-axis flip is byte-neutral.

Verified in two steps (T48 discipline): the header change ALONE, no src change, R22 clean-fleet
`make clean && extract-all && check-all` -> 140 passed, 0 failed of 140. Fleet-shared edit
(engine_core.h reaches all 138 overlays), so R22 was mandatory (§61/§63).
2026-07-28 21:37:58 -06:00
Drew T 610a13c21c fix(phase-29): T61/T62 — items 1-4; all three families converge on one root cause (the shared header)
0 banked. Four tool fixes, one byte-neutral header correction (committed separately), and the three
families resolve to a SINGLE root problem — plus a defect I introduced and caught by measuring.

FOUR TOOL FIXES, each verified by a verdict MOVING rather than by assertion:
 1. gather_externs prefers FILE-scope decls. Its contract says "file-scope extern decls" but
    `^[ \t]*extern` also matches an INDENTED one — a block-scope decl inside some OTHER function, not
    even in scope at the exemplar's own definition. Carried to file scope in the sibling,
    `extern void func_80155FF8(void *, u8);` (ov_SC01_077 L1213) landed above that sibling's
    DEFINE_func_80155FF8() macro and collided. ORDERED, not filtered — an indented decl stays the
    fallback it always was, so a symbol declared only block-scope is unaffected.
 2. reconcile_def_sig keeps the BODY's param names (the T60 fix, cookbook §109).
 3. §85 return-axis precondition — refuses when callers consume the return (reuses
    conform_decls.consumers, R33).
 4. Param-use guard — refuses to retype a parameter the body indexes/dereferences (func_8014D610's
    header says `void *a2` where the byte truth is `u16 *param_3` and the body does param_3[0]).

A DEFECT I INTRODUCED, CAUGHT BY MEASURING: func_8016163C read as a clean DIFF after T60 and I
reported it as "genuine codegen". It is not. match_one says SIZE-MISMATCH: draft 58 ins vs target 78
(delta -20, ratio 0.74, bucket redraft). Both overlays are 78 ins and extract_unit is fine —
--fix-def-sig demoted the return s32 -> void and gcc deleted the computation feeding it as dead. My
§85 check only asked whether CALLERS consume the return, never whether the BODY returns a value. The
tool manufactured a different-sized function and the verdict blamed the draft. Guard added. A "clean
DIFF" appearing right after a transform is a suspect, not a result.

THE CONVERGENCE: engine_core.h declares all three with types that contradict the byte truth —
func_80156044 int vs void (FIXED, byte-neutral, R22 140/140), func_8016163C void vs s32,
func_8014D610 void(s32,void*,void*) vs s32(s32,s32,u16*). Both remaining flips measure 0 §85
consumers. The fix is to correct the HEADER, not to bend the drafts.

AND ONE MORE LAYER: with its header fixed, func_80156044's verdict moved to `redefinition of
func_80155FF8` — extract_unit lifted a unit spanning TWO definitions and the sibling already defines
the wrapper via the shared macro. A unit-boundary defect, a fourth distinct cause. Three fixes peeled
three layers off one family.
2026-07-28 21:34:56 -06:00
Drew T 51bac9f3e6 fix(phase-29): engine_core.h — DEFINE_func_80155FF8 declares func_80156044 void, not int
The exemplar's own @stuck note asked for this (ov_SC01_077_jr_80154C24.c L1349-1350): the
handwritten func_80155FF8 wrapper calls func_80156044 via inline-asm `jal`, so nothing consumes
the return, and ov_SC01_077 already declares it `void` inline — the MACRO was the outlier.

§85 precondition measured before touching it: conform_decls.consumers(func_80156044) = 0 callers
consume the return, so the return-axis flip is byte-neutral.

Verified in two steps (T48 discipline): the header change ALONE, with no src change, R22 clean-fleet
`make clean && extract-all && check-all` -> 140 passed, 0 failed of 140. Fleet-shared edit
(engine_core.h reaches all 138 overlays), so R22 was mandatory (§61/§63).
2026-07-28 21:30:41 -06:00
Drew T 50a108b5a8 fix(phase-29): T60 — reconcile_def_sig name bug fixed (verdicts moved); 0 banked, three causes separated
Tool fix + a sharper diagnosis. NO BANKS — the three "header-conflict" families share a SYMPTOM, not
a cause.

THE FIX (cookbook §109): reconcile_def_sig now conforms the canonical TYPES and keeps the BODY's
parameter names, parsed with cdecl (base/params/pnames, R33 — not a regex). Two re-render traps
handled: `void*` + `a1` -> `void *a1` (cdecl glues stars to the type), and an EMPTY parameter list is
handed back verbatim because `(void)` and `()` both parse to params==[] and are DIFFERENT
declarations (§99 no-prototype). Unit-tested across 6 shapes incl. both void forms and an arity
mismatch; falls back to the wholesale canonical string for fn-ptr/array params.

THE FIX IS REAL, AND THE PROOF IS THAT THE VERDICTS MOVED:
  func_8016163C  `param_1 undeclared` -> DIFF                     (plumbing CLEARED; codegen left)
  func_8014D610  `param_1 undeclared` -> `void value not ignored` (the HEADER is wrong)
  func_80156044  unchanged -> `conflicting types for func_80155FF8` (WRONG LEVER — callee conflict)

TWO FINDINGS UNDER THAT:
 1. The §85 return-axis precondition applies to reconcile_def_sig and NOTHING CHECKS IT. Conforming a
    def's return to the canonical `void` is only safe when no caller consumes the return.
    func_8014D610's callers do, so engine_core.h's `void` contradicts the byte truth and conforming
    yields `void value not ignored`. The HEADER is the wrong artifact; correcting it is fleet-shared
    blast radius (§61/§63), not a sweep-time fix.
 2. func_80156044 was never the def-signature class — its conflict is on the CALLEE func_80155FF8
    (decl 2 lines above the splice). That is cast_call_sites / canon_sig_reconcile territory.

HONEST ACCOUNTING: re-swept all three with the fix -> 0/411, tree clean throughout. The lever is now
correct (it no longer manufactures a false compile failure) but it was ONE of three causes, not the
cause. My T59 write-up grouped them as a single ~30,000-instruction block; that grouping was WRONG,
and what disproved it was re-reading each verdict after the fix rather than re-running the batch and
reporting the total.

No src/ or config/ change: no bank, no metric move.
2026-07-28 21:11:44 -06:00
Drew T 28ef237c35 docs(phase-29): T59 — the five T58 zero families diagnosed: four causes, one wall
Deliverable is the diagnosis, not banks. Method: splice ONE member, `make -j1` the single object,
read the NON-warning cc1 lines (-j16 interleaves the real error away; the §58 memcpy / "type
mismatch" warnings dominate any naive tail; §93 pipefail names the wrong stage). Tree clean after
every probe.

CORRECTION TO MY OWN T58 REPORT (R14): I said "7 remaining families all have banked exemplars".
WRONG — there were 5. 0x80175820 (276 members) and 0x8016ec0c (138) have NO matched exemplar
anywhere: INCLUDE_ASM stubs in all 138 overlays. My batch-selection test picked the first TU
CONTAINING THE NAME (a declaration) and, seeing no stub in that file, called it banked. The family
map was right all along (kind='draft-ov077', matched_members=[]) and family_sweep correctly excluded
them ("6 matched-exemplar families" — a line I read past). Use corpus.stubs(ov), never a name-grep.
Their claimed 109,296 bytes were never real fuel.

THE FIVE VERDICTS:
  0x8014d610 137  PLUMBING  shared-header signature conflict
  0x8016163c 137  PLUMBING  shared-header signature conflict
  0x80156044 137  PLUMBING  shared-header signature conflict
  0x80143d28 136  PLUMBING  conflicting types for ApplyMatrixSV (a PsyQ library symbol)
  0x801457a4 137  DIFF      compiles clean, bytes differ — the ONLY genuine codegen wall

THE HEADER-CONFLICT CLASS (3 families / 411 members ~ 30,000 ins) + A THIRD OPT-IN LEVER. The
"previous declaration" line was the tell: for func_8014D610 it points at line 1727, which is NOT a
declaration — it is DEFINE_func_8014D438(), a shared-macro instantiation whose expansion
forward-declares the templated fn with the canonical engine_core.h signature. All four conflicting
fns are header-declared; the two non-header families are exactly the two with different verdicts.
--fix-def-sig is the lever (a THIRD opt-in one, after T56's unreachable and T57's off-by-default).
Tested: 0/411, and the verdict did NOT move to DIFF — it moved to a precise new compile error:
  canonical : void func_8014D610(s32 a0, void *a1, void *a2)
  draft body: ... param_1 ...   ->  `param_1' undeclared
reconcile_def_sig adopts the canonical signature WHOLESALE (types AND param names) while the body
keeps the exemplar's param_N names. Its docstring calls this a "rare name mismatch"; it is not rare —
an exemplar drafted with the param_N convention hits it every time. Fix: conform TYPES, keep BODY
names (both are in hand at the call site).

THE PATTERN, THREE TIMES IN ONE SESSION: T56 a lever unreachable from the sweep path, T57 a lever off
by default, T59 a lever subtly broken. Every family-wide 0/N so far has been a statement about the
HARNESS, not the code. cookbook §108 records the recipe + the four causes.

No src/ or config/ change: no bank, no metric move.
2026-07-28 21:04:40 -06:00
Drew T fa6d9c88e6 docs(phase-29): SESSION-24 final checkpoint — 537 functions banked, fleet 86.0% instr
Full 🛑 checkpoint block per the checkpoint-before-pause discipline: state, session total reconciled
against the metric (fn-count 320524 -> 321061 = +537, +67,666 ins), the three strategic changes, a
ranked measured NEXT list, my errors, and the carried defects (now incl. the Jul-21 autopsy corpus).

Fresh session is safe from here: HEAD commit:1159, tree clean but for the R23 db.*.gbf churn, R22
clean-fleet 140/140, tools-health OK, dedup 1886/0, 0 NON_MATCHING.
2026-07-28 20:56:17 -06:00
Drew T 2d7694ba2d feat(phase-29): T58 — 8-family batch: 1 of 6 banked (func_8012A1BC 137/137, +10,686 ins)
Ran the batch with the T57 recipe (--band all --normalize-self-decls, live stubs derived from src/
not the stale map). 6 of 8 selected (two still filtered — selection line read this time).

  821 candidate members across 6 families
  BANKED 137 — func_8012A1BC (78 ins) 137/137
  failed 684 — the other FIVE families banked 0 each

Attribution from git diff (137 x func_8012A1BC), not the per-group log lines whose split-name field
my first aggregation mangled.

THE SHAPE OF THE REMAINING FRONTIER — the finding. Across T56->T58 the per-family outcome is BINARY
and near-total: a family banks ~137/137 or ~0/137, nothing in between. And each 0/N so far has had
its OWN distinct cause — DATA decl scope (T56), FUNCTION decl scope (T57), jtbl table-count drift
(func_8014032C), plus five more undiagnosed here. The mechanical lever is done pulling by itself:
from here each family costs one diagnosis. A batch is now a DIAGNOSIS QUEUE, not a harvest, and the
next phase of this work should be planned on that economics.

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).

METRICS: instr 86.0% (11297091 -> 11307777 = +10,686 ins); fn-count 90.73% -> 90.77% (+137);
distinct-code 76.7% -> 76.7% (+0).
The distinct-code anomaly now has FOUR data points and still no explanation: T52 +125, T57 +125,
T56 +0, T58 +0. All four families are PURE; the exemplar overlay does not separate them either (T56
and T57 both templated from ov_SC01_077 and disagree). Two behaviours, no identified variable.
Still not guessed at — it stays the queued probe.
2026-07-28 20:55:35 -06:00
Drew T cbc5665fd8 feat(phase-29): T57 — func_80133AB0 132/132 (+18,084 ins); a SECOND opt-in lever found; fleet 86.0% instr
First batch off the 64-family list. Fleet crosses 86.0% instr-weighted.

TWO OF MY OWN ERRORS, both caught by measuring:
 1. Three of five targets never ran — --band defaults to `substantial` (nins>=80) and I picked three
    at 79/78/78. The tool printed "2 matched-exemplar families" and I nearly read that as "5
    attempted, 3 refused". Read the SELECTION line, not the intent.
 2. Stale map: .run/family_hseq.json was regenerated in T55, BEFORE T56 banked func_80144090, so it
    still listed 134 live stubs for a now-complete family. Membership is stable (h_seq over original
    bytes); only the matched/unmatched split rots. Filter live stubs from src/, not from n_matched.

THE FIRST RUN WAS 0/268 — AND IT WAS A SECOND OPT-IN LEVER, NOT A WALL. Diagnosed one sibling past
the -j16 interleave and the §58 warning noise: `conflicting types for func_80133AB0` (spliced def at
2688 vs a decl at 2429) — the FUNCTION decl-conflict class, not the DATA one T56 fixed. That is
exactly what --normalize-self-decls exists for (the sibling's own caller declares the member in a
different C form than the exemplar's, which used a fn-ptr cast) — and it is OPT-IN, so it never ran.
Re-ran the identical two families with it: 0 -> 132 banked.

  0x80133ab0 (137 ins, jr_8012ACE0)  132/132 BANKED
  0x80143d28 (80 ins,  jr_80140608)  0/136 — a different, undiagnosed blocker

THE PATTERN, TWICE IN A ROW: T56 the DATA decl lever was unreachable from the sweep path; T57 the
FUNCTION decl lever is reachable but OFF BY DEFAULT. Both present as a flat 0/N that reads exactly
like a compiler wall. A 0/N from a sweep is a statement about which levers were enabled, not about
the code.

GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).

METRICS: instr 85.8% -> 86.0% (11279007 -> 11297091 = +18,084 ins); fn-count 90.69% -> 90.73% (+132);
distinct-code 76.4% -> 76.7% (67812 -> 67937 = +125).
SHARPENS the T56 anomaly rather than resolving it: 132 banked here moved distinct-code +125, and
T52's 132 also moved it +125 — but T56's 136 moved it +0. Three PURE families, two behave one way
and one the other. Still unexplained, still not guessed at.
2026-07-28 20:37:06 -06:00
Drew T 56e2d808ab feat(phase-29): T56 — wire the tu-scope lever into family_sweep; func_80144090 0/136 -> 136/136
T55's two-part next step as one job. +20,944 instructions banked.

1. THE LEVER WAS UNREACHABLE FROM THE PATH MOST FAMILIES USE (cookbook §107)
   §103 was wired into jtbl_family_bank only (T53), and that tool runs for has_mid_jr families.
   Everything else sweeps through family_sweep, which gates via PLAIN harvest_verify by design — so
   the lever existed, was byte-proven, and most families could not reach it. The symptom was
   indistinguishable from a compiler wall: func_80144090 swept 0/136 with `conflicting types for
   D_800A651C`.
   Why it does not violate the plain-harvest_verify rule: that rule exists because gate_stage's
   transforms PERTURB A CORRECT DRAFT (§19/T3). The tu-scope never touches the draft — it moves a
   DECLARATION IN THE TARGET TU. The test is not "is it a transform" but "does it change the draft?"
   Reused the existing undo instead of inventing one: family_sweep already snapshots TUs it edits at
   staging time (--normalize-self-decls) and reverts on a final MISMATCH (not byte-neutral) AND on a
   zero-bank group (§61 undo law — no dead diff). The tu-scope shares that dict and inherits both
   backstops; renamed nsd_snapshots -> tu_snapshots. Default ON with --no-tu-scope to A/B it (the T24
   --allow-pins precedent): byte-neutral by construction, a no-op when nothing collides, auto-reverted
   when it buys nothing.

2. THE DUPLICATE-DECL REFUSAL RELAXED — AND IT DID NOT MATTER
   scope_tu_externs refused N>1 file-scope decls as "ambiguous"; duplicate-IDENTICAL externs are legal
   C, so N identical decls are one decl written N times. Now compares whitespace-collapsed forms and
   refuses only on genuine disagreement. MEASURED, and my hypothesis was WRONG: D_800B9A02 is 3 decls
   in 2 DIFFERENT forms, so it was correctly refused all along — the family banked 136/136 without it.

RESULT: func_80144090 0/136 -> 136/136, 0 failed, with NO change to any draft.

GATES: R22 clean-fleet 140 passed, 0 failed of 140. tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, report/lint/dedup 1886/0). 0 NON_MATCHING (G4).

METRICS (reconciled against make report):
  instr-weighted  85.7% -> 85.8%   11258063 -> 11279007 = +20,944 ins
  fn-count       90.65% -> 90.69%  320656 -> 320792 = +136
  distinct-code   76.4% -> 76.4%   +0 (67812 unique, UNCHANGED)
FLAGGING the third row rather than explaining it away: 136 banked functions moved distinct-code by
ZERO, where T52's 132 moved it by +125, and both families are classed PURE. I do not have a verified
cause and will not invent one — either a real property of this family or a gap in the metric. Worth
one probe before that number is quoted again.
2026-07-28 20:15:11 -06:00
Drew T bcd44badc9 fix(phase-29): T55 — frontier re-mapped; 2 families swept, 0 banked, both blockers diagnosed to the line
Honest result: NO YIELD. What it produced is a re-measured frontier, a real fix to my own T53 work,
and both failures diagnosed rather than left as "0/N".

FRONTIER RE-MAPPED (T52's +132 moved it): family_hseq -> 2,647 target families, 513 substantial,
64 with a banked exemplar AND live stubs. Caveat recorded: the top two by byte-weight (0x8013c414
180KB, 0x8013c0f8 84KB) are -O0, and _o0 families are already measured at ~1/137 — do not be drawn
by their weight.

FAMILY 1 func_8014032C (183 ins x 136 ~ 25,000 ins): sample 0/8, last_err empty. Read one sibling's
real gate result (§53/§59) past the -j16 interleave and the §58 memcpy red herring — TWO causes:
  (1) conflicting types for D_80115128 — the T48/T51 class, which tu-scoped should have caught;
  (2) jtbl_rodata_pads "more rodata .align than pad specs — table-count drift vs the carve", a
      DISTINCT class jtbl_family_bank's own comment documents as NOT isolate-fixable (§91 --like
      role trap).
After fixing (1): still 0/8. Cause 2 is the live blocker — carve work, not decl work. NOT ground
further; it is a documented wall.

THE T53 DEFECT, FOUND AND FIXED: contested() scanned only the draft's BLOCK-scope externs, because
T51's motivating family had them hand-written in the body. But gather_externs carries decls in at
FILE scope, and those are exactly the ones scope_data_externs.fix DROPS when the TU already declares
the symbol — its give-up branch, the fatal case the lever exists for. Measured: scope_data_fix
dropped 3 symbols while contested() returned []. So the stage never fired on its own class. Now
scope-independent; regression-checked against T51's case using the pre-T51 TU from git (old ==
new, added []), and it now finds D_80115128 on the T55 target.

FAMILY 2 func_80144090 (154 ins x 136 ~ 21,000 ins), chosen because has_mid_jr=False avoids the
carve: 0/136. Diagnosed: conflicting types for D_800A651C (2210 vs 379) — the SAME class.
family_sweep gates via PLAIN harvest_verify by design, so it never sees the tu-scoped lever, which
lives only in jtbl_family_bank. Probed: the lever would move D_800A651C + D_800AF648 (deletion-only)
and REFUSES D_800B9A02 as "3 file-scope decls above (ambiguous)" — an over-conservative refusal,
since duplicate-IDENTICAL externs are legal C.

THE FINDING: the same decl-scope collision class gates the frontier's mechanical families — it cost
T52's family 133 of 137 siblings, and it blocks both families probed here. The lever exists and is
byte-proven; it is not reachable from the sweep path most families use.

No src/ or config/ change: no bank, no metric move. Tree verified clean after every probe.
2026-07-28 19:56:19 -06:00
Drew T f72e2344a6 fix(phase-29): T54 — correct the ADDRESSING route, and fix the reason changing it was inert
Item 1 off T53's list. Two changes: the route, and the design flaw underneath it.

THE DEFECT UNDER THE DEFECT (cookbook §106). residual_class answers two questions in one pass:
`klass` is a MEASUREMENT (expensive, from comparing instruction streams); `(profile, bucket)` is a
POLICY (a table lookup over it). autopsy persisted BOTH and verdicts() read BOTH back — so editing
_ROUTE changed nothing until someone re-ran the whole collect, and a weeks-old row could silently
out-vote the live table with no oracle to report it. The corpus on disk is dated Jul 21 and does not
even contain the SESSION-23 targets the recommendation cited.

Fixed by re-deriving at read time: residual_class.route_for(klass, detail), called from
autopsy.verdicts(). R33 — persist the measurement, derive the decision. Subtlety: LENGTH-DRIFT's
route is MAGNITUDE-dependent (permuter only when |delta|<=2 AND explains=="tail", §60b), so a naive
re-derivation from klass alone would have silently demoted 9 rows; both inputs are already in
`detail`, so the override reproduces exactly — VERIFIED 1610/1610 against the stored corpus with the
table UNCHANGED, before touching it.

THE ROUTE CHANGE: ADDRESSING ("cse","permuter") -> ("cse","structural"). It contradicted this file's
own bucket definition ("structural — local mutation CANNOT introduce it ... it wants a C-level
idiom"): the §10/§20 hoist-vs-remat shape is a multi-instruction change with a documented recipe
(gcc-2.7.2-map/cse_expr.md §2, byte-proven on func_80149374/func_801493D0). Measured (T31): both
admitted ADDRESSING targets plateaued under a §31-directed permuter, and the class was 32% of the
admission pool. After: pool 56 -> 38, exactly 18 rows changed, ALL ADDRESSING, nothing else moved;
grinder admits 45, structural skips 512 -> 530.

THE BOUND (R14), kept in the _ROUTE comment: I read the T31 record instead of the summary line, and
the summary was looser than the evidence. T31 finding 4 byte-tested the §2 recipe on func_80132F40
across six variants and it never closed (best 40 mismatches). `structural` does NOT promise a free
fix — it means "a search over local mutations is the wrong tool, try the documented idiom", exactly
what WIDTH / BRANCH-POLARITY / IMM-OFFSET already mean. Also corrected: the checkpoint cited
func_80176734 as the flat-for-32-min evidence, but that function is not in the corpus at all.

Tooling-only: no src/ or config/ change, no bank, no metric move.
2026-07-28 19:34:00 -06:00
Drew T f285de46e8 feat(phase-29): T53 — fold the T51 lever into the gate; kill gather_externs' false positive; close a revert gap
Items 1-2 off T52's list, plus a third defect found by T53's own testing. TOOLING ONLY — banks
nothing; metrics unchanged by design (85.7% instr / 76.4% distinct / 90.65% fn-count).

1. THE T51 PRE-PASS IS A jtbl_family_bank STAGE (cookbook §103, AUTOMATED)
   Order: raw -> scoped -> tu-scoped -> recovered -> reconciled. After the non-invasive stages (it
   edits the TU outside the spliced body); BEFORE the recovery stages deliberately — those bend the
   DRAFT and T48 measured both at +3 ins for this class, so they cannot succeed here. The stage
   re-runs scope_data_fix against the SCOPED TU rather than reusing the raw body: composition-correct,
   since the contested symbols no longer have a file-scope decl to be dropped against.
   COUNTERFACTUAL, byte-gated on a reproduced blocker (ov_SC01_000 restored to its pre-T51 TU):
     raw        -> compile error (conflicting types)
     scoped     -> compiles, FAILS the byte check (§8d drops the decl -> the u8 CSE costs +3)
     tu-scoped  -> BANKED
   That is the evidence the stage does the work — not T52's sweep, which ran on TUs T51 had already
   scoped by hand.
   Refactor note: a stage editing outside the spliced body must RE-FIND the splice point (the stub
   offset indexes the ORIGINAL TU). Each stage now carries its base; every pre-existing stage passes
   `orig`, where the re-search returns the identical span — same operation as before, by construction.

2. gather_externs' COMMENT-SCANNING FALSE POSITIVE — FIXED (cookbook §104)
   It scanned RAW text, so a symbol named only in the draft's PROSE counted as referenced: the
   func_80135D20 warning that fired on 137/137 and was right 0 times. Fix is a two-text discipline —
   MATCH on cdecl._mask'ed text, EMIT by span from the ORIGINAL (a masked decl is all blanks, so
   "just mask it" would splice whitespace). Same change closes a second, unobserved defect of the
   class: a COMMENTED-OUT extern could be selected as the carried decl and spliced in as live code.
   MEASURED as a no-op on output (R14): 20 (exemplar, sibling) draft pairs across 4 families, old vs
   new -> 20 identical / 0 differing. Only the false warning changed.

3. UNPLANNED — A REVERT THAT DID NOT SURVIVE AN EXCEPTION (cookbook §105)
   A wrong exemplar made remap_hseq raise AFTER the carve rewrote config/ and jr_isolate created a
   region file; the exception propagated out of bank(), the revert never ran, and the tree kept a
   rewritten carve config plus an UNTRACKED region file (git checkout -- src/ does not remove it).
   In a 132-member sweep that residue rides into the next member's build. bank() is now a
   revert-guaranteed wrapper around _bank(). Negative-control proven: the crashing invocation now
   reports {'exception': 2} and leaves git status -- config/ src/ at 0.
   "Revert on failure" != "revert on every exit"; the exits are success, gate-fail, refusal, and the throw.

GATES: R22 clean-fleet 140 passed, 0 failed of 140. tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, report/lint/dedup 1886/0). 0 NON_MATCHING (G4).

HONEST COVERAGE GAP: no live end-to-end BANK through the refactored loop — all three big families are
137/137 and the only family with live stubs (0x80191c50) has no banked exemplar, so it refuses. The
counterfactual byte-gated the exact splice on all three candidates and the 2-member run exercised
construction/refusal/revert/tally; the next real family sweep is the true end-to-end validation.

MY ERRORS: invoked the sweep with a wrong exemplar+address for a cross-address family (an unmeasured
guess about a members file I had not read — it is what surfaced defect 3); and deleted last_err's
initializer while refactoring, which would have raised NameError on the first clean gate-fail.

cookbook §103 (AUTOMATED) + §104 + §105; SETUP inventory row updated (R21).
2026-07-28 19:26:38 -06:00
Drew T d4dc533f54 feat(phase-29): T52 — func_80135260 swept 132/132 (0 failed); the family goes 4/137 -> 137/137
T51's payoff. The invocation is IDENTICAL to the T49/T50 runs; the only variable is T51's decl
scoping.

  T49/T50 (pre-T51)  4 banked / 133 gate-fail   (and all 4 were SC07 — a different cause)
  T52 sample (8)     8 BANKED / 8               52s
  T52 rest  (124)  124 BANKED / 124             13m04s
  TOTAL            132 / 132, ZERO failures

So the 133 "gate-fails" were never a codegen wall — they were one file-scope declaration per sibling
TU. Fifth time this phase a wall has resolved to tooling/plumbing.

GATES (from a genuinely clean tree): R22 clean-fleet `make clean && extract-all && check-all` ->
140 passed, 0 failed of 140. `make tools-health` OK — corpus 0 PHANTOM + 0 TRUNCATED, cdecl,
audit-binaries, report/lint/dedup 1886 validated / 0 failed (C1 239604/239604). 0 NON_MATCHING (G4).

METRICS (reconciled against make report, not asserted):
  instr-weighted   85.5% -> 85.7%   11240111 -> 11258063 = +17,952 ins
  distinct-code    76.1% -> 76.4%   67687 -> 67812 unique fns (+125)
  fn-count        90.62% -> 90.65%  320524 -> 320656 = +132 functions
  INCLUDE_ASM stubs 33189 -> 33057  = -132
+17,952 templated instructions against T50's ~18,000 estimate. distinct-code gains 125 not 132
because seven siblings are byte-identical to code already counted unique.

ALSO SETTLED:
- item 3 is now byte-confirmed, not just inspected: [gather_externs] warned "func_80135D20 ... the
  sibling will not compile" on ALL 132, and all 132 BANKED. A 100% false-alarm rate on this family,
  actively masking real causes. cdecl._mask is the fix (T51 used exactly that).
- the T51 pre-pass is now worth folding into jtbl_family_bank; T51 withheld that pending a measured
  payoff, and 4/137 -> 137/137 is it. Next task.

config/ (per-overlay splat.*.yaml + overlays.mk, written by the carve) is staged alongside src/ —
the `git add -A src/` omission this phase already recorded once.
2026-07-28 17:27:20 -06:00
Drew T 7b896a2627 feat(phase-29): T52 sample — func_80135260 8/8 banked; the T51 lever is validated
Bounded sample before scaling (the standing validate-on-a-sample invariant). The ONLY variable vs
the T49/T50 runs is T51's decl scoping; the invocation is identical.

  T49/T50 (pre-T51):  4 banked / 133 gate-fail  (and all 4 were SC07, a different cause)
  T52 sample:         8 BANKED / 8              in 52s

Committed here only because jtbl_family_bank's precondition refuses to run on a dirty config//src/
(its per-sibling revert restores from HEAD, so uncommitted banks would be silently wiped). The
remaining 124 follow in the next commit; R22 clean-fleet gates the whole task at the end.

Both config/ (the jtbl carve + overlays.mk) and src/ are staged — the omission this file already
warned about after a prior `git add -A src/`.

Also confirms item 3 empirically: [gather_externs] warned "func_80135D20 ... the sibling will not
compile" on every one of the 8, and every one BANKED. Comment-scanning false positive, as T50 said.
2026-07-28 17:04:09 -06:00
Drew T a524142eb5 feat(phase-29): T51 — the fleet-wide decl-scoping tool; func_80135260's 132 siblings unblocked
Item 1 off the SESSION-23 list. T48 proved the lever by hand on the exemplar, T50 located the same
blocker in every sibling; this builds the tool, measures the population, applies it fleet-wide, and
gates it. It BANKS NOTHING — it removes the blocker. The sweep is the next task.

MEASURED BEFORE BUILDING (R35). The blocker census over all 132 still-stubbed siblings is perfectly
uniform: 132/132 carry it, 3 contested symbols each, EXACTLY ONE file-scope decl statement per
(TU, sym), ZERO file-scope references below the decl (so the deletion is always safe), 660
block-scope re-declarations needed.

THE TOOL: tools/scope_tu_externs.py — the TU-side complement of scope_data_externs.py (§8d). §8d
fixes the incoming DRAFT and has a give-up branch that DROPS the draft's own decl when the TU already
declares the symbol at file scope. Right when the types agree; fatal when the byte-true draft needs a
different one — which is exactly how 132 byte-true siblings gate-failed wearing a codegen wall's
costume. This moves the TU's OWN file-scope decl down into every later function that references the
symbol and lacks its own block-scope decl, then deletes the file-scope line.

  FILE(u8 D_x) ... then BLOCK(u16 *D_x) below it  ->  conflicting types    (the 132 failures)
  (no file-scope decl) ... BLOCK(u8) ... BLOCK(u16 *)  ->  builds; each fn owns its own view

- contested set DERIVED, never hand-listed (R33): the remapped draft's block-scope D_ externs
  intersected with the TU's file-scope decls above the splice point; --family does it per sibling
- built on cdecl.split_statements/_mask (R33), not a 7th regex: depth-0 spans (a fn definition
  flushes at its closing '}' — a column-0 test is NOT a file-scope test, m2c emits goto labels at
  column 0 inside bodies) + length-preserving comment/string masking. That masking is what kills the
  comment-scanning false-positive class still open as item 3.
- REFUSES LOUDLY, never skips silently (R32): >1 file-scope decl above the splice point; a
  file-scope statement below the decl referencing the symbol; an unlocatable body brace
- coverage asserted as a DELTA (R32): file-scope -1, block-scope +len(consumers). An absolute
  "a block-scope decl exists" check would have passed VACUOUSLY — these TUs already carry ~18
  legitimate block-scope decls of the same symbols

VERIFIED IN TWO STEPS (T48's structure — why a 132-file edit was safe to make):
  1. the move ALONE on ov_SC01_000 -> make build -> 9052dc0e BYTE-IDENTICAL, then reverted
  2. fleet-wide -> R22 clean-fleet (make clean && extract-all && check-all) -> 140 passed, 0 failed
     of 140; make tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED, cdecl, audit-binaries,
     report/lint/dedup 1886/0). Metrics UNCHANGED at 85.5% instr / 76.1% distinct / 90.62% fn-count
     — the correct result for a declaration-only change.

The diff is uniform to the line: all 132 files +11/-3. A second --family run reports 132
nothing-to-do, 0 refused (idempotent).

Deliberately NOT done: wiring this as an automatic jtbl_family_bank stage. That waits until the
sweep measures the payoff — folding an unproven pre-pass into the gate is the same unmeasured
premise this phase keeps catching.

Also preserves .run/near6/g5260_a.c (the T48 raw crack body, allowlisted) — the sweep may need it
for --raw.

cookbook §103 + SETUP tool-inventory row (R21).
2026-07-28 16:44:46 -06:00
Drew T 69dee1d429 docs(phase-29): T50 + SESSION-23 REVISED final checkpoint
T50 func_80135260 family: the §53 carve path banked 4/137 (all SC07), 133 gate-fail, tree clean
after every revert. The 3,744-site conform_decls between runs changed NOTHING for this family (99
attempts observed mid-run, 0 banks) — it is byte-neutral and axis-complete so not harmful, but my
inference that this family shared func_80177DA8's blocker was WRONG: same SC07-only signature,
different cause.

THE REAL BLOCKER, LOCATED: staged siblings are correctly remapped (family_remap works). The draft
requires 4-byte POINTER decls for the contested data symbols — the canonical extern u8 +
(*(u16**)&sym) form makes gcc CSE &sym into two callee-saved regs, costing a 7th saved register and
3 extra instructions (the identical +3 measured on the exemplar). And every sibling host TU carries
the same FILE-SCOPE blocker the exemplar did: verified on ov_SC01_000, whose host TU declares the
contested symbols at file scope (lines 3053-3056) alongside 18 block-scope occurrences. A file-scope
decl constrains every later function in that TU — the T48 finding.

So the fix is the T48 lever replicated x137: scope those decls into their consumers per sibling TU.
Byte-neutral on the exemplar (proven in two steps). That is a TOOL, and it is worth ~18,000 ins.

Also: gather_externs' warning naming func_80135D20 is a COMMENT-SCANNING FALSE POSITIVE (the symbol
appears only in header prose and is defined in another split file). Fires on all 137, masks real
causes. Same class as the Phase-19 garbled-hint bug.

CHECKPOINT REVISED (supersedes the earlier SESSION-23 block): 975 functions banked, reconciled
against the metric (fn-count 319,549 -> 320,524) with the per-task recount agreeing exactly. Fleet
85.5% instr / 76.1% distinct / 90.62% fn-count. R22 140/140 run 11x, dedup 1886/0, 0 NON_MATCHING.
Ranked START-HERE (decl-scoping tool first), the four strategic changes, my five recorded errors —
whose common thread is inference from partial output instead of measuring — and the carried defects.
2026-07-28 16:16:42 -06:00
Drew T f3e8317bd1 feat(phase-29): T49 — func_80135260 x4 siblings + the 3,744-site decl conform
The §53 carve path banked only 4 of 137 siblings, ALL of them SC07 — the exact signature
func_80177DA8 showed before its §99 fix, so the same lever applies.

- jtbl_family_bank func_80135260: 4 BANKED / 133 gate-fail. The 4 are SC07 overlays.
- conform_decls dry run confirmed the class: byte-true def is
  `s32 func_80135260(s32, s32, s16 *, s16 *)` but 3,744 declaration sites say
  `(s32, s32, s32, s32)` — params 3 and 4 declared s32 where the byte truth is s16 *.
  Return type agrees, so the §85 return-axis precondition does not fire.
- Applied: 3,744 sites rewritten across 2,021 files; the tool's R32 assertion reports
  "non-canonical declarations remaining: 0  OK (axis complete)". It correctly SKIPPED the 5
  DEFINING TUs (the 4 SC07 banks + ov_SC01_077) — a defining TU owns its own declarations, since
  per-overlay byte-true signatures legitimately differ under §16 loose typing.
- This touches src/shared/engine_core.h, so it is FLEET-SHARED and R22 was mandatory (§61/§63):
  R22 clean-fleet 140 passed, 0 failed of 140.

Also recorded: jtbl_family_bank's [gather_externs] warning named func_80135D20 as an undeclared
referenced symbol, but that symbol appears ONLY in the draft's header COMMENTS (lines 3 and 29) —
a comment-scanning false positive, same class as the Phase-19 gen_harvest_targets garbled-hint bug.
It was not the cause of the 133 failures.

Next: re-run the carve path for the remaining 133 siblings now that the decl axis is conformed.
2026-07-28 15:47:52 -06:00
Drew T d6bd7bed9e feat(phase-29): T48 — func_80135260 BANKED; a file-scope extern is a TU-WIDE constraint on later functions
The T45 probe re-filed this as a crack target; it turned out to be a SCOPE problem, and the fix is a
new reusable lever.

DIAGNOSIS. The draft MATCHes standalone (136 ins) with block-scope `extern u16 *D_801870AC/B0/B8`,
which are byte-TRUE (they produce the target's 4-byte pointer loads). The TU carries FILE-scope
`extern u8 D_801870B0/AC/B8; extern s16 *D_801870B4;` at lines 3433-3436 — the preamble of the
already-banked func_80135168 — and a file-scope decl constrains EVERY LATER function in the TU, so
the draft's pointer decls became "conflicting types". Ordering is what makes this asymmetric: the
TU's own block-scope `extern u16 *D_801870B0;` at L2829 precedes the file-scope u8 decl and only
WARNS; a block-scope decl AFTER it is an ERROR.

TWO WORKAROUNDS MEASURED AND REJECTED, both +3 instructions with a rotated callee-saved bank:
  reconcile_tu (conform to the TU) -> 139 ins vs 136, 123 mismatched
  cast-at-use  (*(u16 **)&D_x)     -> 139 ins vs 136, 123 mismatched
So the byte-true code genuinely REQUIRES the pointer-typed declaration; the decls had to move.

THE FIX (move the decls, never the draft — §85 applied to DATA): scoped those four file-scope externs
into their only two consumers (func_80135168 and func_80135480, both of which already use the
cast-at-use idiom). Verified in two steps: (1) the decl move ALONE rebuilds ov_SC01_077 byte-identical
d19c9580 — declaration-only, no codegen change; (2) the original byte-true draft then banks clean,
verified 1 / failed 0. R22 clean-fleet 140 passed, 0 failed of 140.

THE REUSABLE LEVER: a FILE-scope extern in a shared overlay TU is a global constraint on every later
function in that TU. When a byte-true draft needs an incompatible type for the same symbol, scope the
existing decl to its consumers rather than bending the draft — bending it cost +3 here, twice.

Family sweep is NEXT and needs the §53 carve path (has_mid_jr: true, 137 siblings, PURE) —
family_sweep correctly refused it.
2026-07-28 15:16:38 -06:00
Drew T efea7473f5 chore(phase-29): preserve the T42/T43 and T47 draft ladders
.run/near6/d68_{typefix,sigfix,final,blockscope,selfcontained}.c — the five-step ladder that took
func_80140D68 from "MATCHes standalone, 0/137 family" to banked + 137/137. Each step is a distinct,
byte-measured fix (§94 type-carry -> return conform -> param conform -> block-scope typedef ->
inlined macro), so the ladder IS the evidence for the extract_unit carry-gap finding.

.run/near6/g734_{a,b}.c — the two placements proving cluster B is solvable and anti-correlated with
cluster A (13 -> 14 both ways, B goes 2 -> 0 while A goes 4 -> 7).

Both sets are cheap to lose and expensive to re-derive; the .gitignore allowlist already covers
.run/near6/*.c.
2026-07-28 15:06:51 -06:00
Drew T 5d9616739a docs(phase-29): T47 — func_80176734 grind; cluster B SOLVED but anti-correlated with cluster A
Acted on T46's verdict (K8/RC-4 local-alloc tying, lever = C-level lifetime shaping) instead of the
refuted allocation/permuter framings.

THE LEVER WORKS. Cluster B is `q = (Trk *)((u8 *)e + 0x3C)` landing in $a2 where the target uses $a0,
because arg0's last use (`self = arg0`) sat BELOW q's birth, keeping $a0 live so K3 first-fit pushed
q to the next free reg. Moving `self = arg0` above q's birth ELIMINATES cluster B in both placements
tried -- byte-measured:
  baseline          A=4 B=2 C=2 D=5 -> 13
  (a) self at top   A=7 B=0 C=2 D=5 -> 14
  (b) self after e  A=7 B=0 C=2 D=5 -> 14

BUT IT COSTS 3 POSITIONS IN A, AND THE COUPLING IS ALREADY DOCUMENTED in the draft's own header:
cluster A is a sched2 LUID tie COUPLED to lever 3 -- st1 must stay a decl-initializer (moving it into
the body forfeits the update_equiv_regs live-length doubling at local-alloc.c:1064, allocno priority
explodes, callee-saved bank rotates, frame 0x40->0x48, measured over 12 permutations), while st2/ext
must sit in the body after the index chain AND the `self = arg0` copy to reproduce the target's
save/init interleave. Moving `self = arg0` is exactly the statement that interleave is anchored on.

VERDICT: a THIRD two-knobs-one-screw in this function. st1's LUID is pinned by allocno priority ->
pins the save/init interleave (A) -> pins where `self = arg0` may sit -> decides whether q gets $a0
(B). Any future attempt must optimise A and B TOGETHER; fixing either alone is provably a wash.

Not banked, nothing regressed (draft-side only, tree clean). Cluster B has moved from an unexplained
register 2-swap to solved-but-priced-at-3-positions-in-A. Variants at .run/near6/g734_{a,b}.c.
Effort ledger recorded: Fable5 pass, wave agent (~2.6M tok, 217->13), 32-min permuter (flat),
reg_renumber oracle (framing refuted), this grind. 51,198 ins; five tiers have now bounced.
2026-07-28 15:06:36 -06:00
Drew T 3371e57ed0 docs(phase-29): regalloc.md §H — the swap oracle's two unstated PRECONDITIONS
§H sold the reg_renumber-swap oracle as THE one-gdb-run discriminator between RC-6 (allocation) and
S3 (scheduling). It has two preconditions it never stated, and both failed silently on the first
real use after the audit:

1. reg_renumber maps PSEUDOS ONLY (index >= FIRST_PSEUDO_REGISTER = 68 on MIPS, mips.h:1179). A
   contested register that is already HARD at .greg time — an incoming parameter reg, a pin, or a
   local-alloc reuse — is structurally unreachable. Check the .greg RTL first: (reg/v:SI 6 a2) with
   6 < 68 does not qualify; only (reg:SI 130)-style operands do.
2. The contest must be NARROW. The swap is global across reg_renumber, so if the pair serves many
   pseudos it destroys the allocations that were already correct.

Byte-measured on func_80176734, baseline 13: control 1<->1 -> 13 (harness validated);
<-> moved 17 pseudos -> 345; <-> moved 31 -> 97. The .greg read then showed the
destination was (reg/v:SI 6 a2) — hard, a reused incoming parameter register — so the true class was
local-alloc TYING (K8/RC-4), not RC-6, and the lever is C-level lifetime shaping.

Harness + negative control preserved at tools/oracle/reg_renumber_swap.sh.
2026-07-28 15:02:51 -06:00
Drew T ce7780b91f feat(phase-29): T46 — reg_renumber-swap oracle built + validated; it REFUTES the func_80176734 framing
Item 1's remaining half. Oracle mechanized and reusable at tools/oracle/reg_renumber_swap.sh: break
at reload entry (cc1 unstripped: reg_renumber @0x82d4330, reload @0x815d4d7), swap two hard regs
across reg_renumber, finish the compile, re-score with masked_diff REUSED not reimplemented (R33).

NEGATIVE CONTROL: a no-op swap (31<->31) reproduces exactly the baseline 13 mismatches, so the
harness faithfully reproduces the pinned compile.

RESULT: both contested swaps are far WORSE — <-> (17 pseudos) = 345 mismatches +1 insn;
<-> (31 pseudos) = 97. Baseline 13.

WHY, AND IT REFUTES THE FRAMING: reading .greg for cluster B's own insn shows
  (set (reg/v:SI 6 a2) (plus:SI (reg/v:SI 5 a1) (const_int 60)))
— the destination is a HARD register, not a pseudo. reg_renumber only maps pseudos (>=
FIRST_PSEUDO_REGISTER = 68), so that value is structurally unreachable by this oracle. The draft has
NO register __asm__ pins (header says so, grep confirms), so  is hard because it is an incoming
PARAMETER register that local-alloc reused as a destination.

VERDICT for func_80176734 (51,198 ins): the residual is NOT global-allocation 2-colouring. It is the
LOCAL-alloc hard-reg reuse / tying class — combine_regs (2.7.2 local-alloc.c:1722) +
qty_phys_copy_sugg, i.e. regalloc.md K8/RC-4, whose lever is C-level LIFETIME SHAPING, not the
permuter and not reg_renumber. That also explains the flat permuter: it was mutating a dial that
does not control this residual.

MAP REFINEMENT OWED: §H presents the swap oracle as THE way to discriminate RC-6 from S3 in one gdb
run. It has an unstated PRECONDITION — the contested registers must be held by PSEUDOS. Check .greg
first; if they appear as (reg/v:SI N ...) with N < 68 they are already hard, and a coarse swap
returns a large meaningless number (345 here) that looks like a verdict and is not one.

Tree clean; nothing banked, nothing broken.
2026-07-28 14:53:47 -06:00
Drew T 5ae6409640 docs(phase-29): T45 — data-axis probe is a CLEAN NEGATIVE (0 of 3); the blocker is codegen
Probe-before-investing, ~15 min, settles the data axis.

FIRST: I recommended a RETIRED tool. tools/reconcile_decls.py is retired (Phase 26-A, R33, "DO NOT
RE-WIRE") and its docstring states the exact caveat I had raised independently — it asks what the
FLEET calls a symbol, while C asks what THIS TU declares; measured, the fleet oracle conflicts with
the TU's own declaration in 548/3431 (16%) of cases and hands back an actively wrong decl. Re-ran
with the live successor, reconcile_tu.py.

MEASUREMENT: reconcile_tu -> drafts 3, reconciled 3, 5 data symbols, 0 coverage defects.
Whole-binary gate (bare harvest_verify) -> verified 0 / failed 3, class DIFF=3. Tree residue 0.

WHY THIS IS NOT A NULL RESULT — THE FAILURE CLASS MOVED. Before: func_80133298/func_8012E014 were
PLUMBING; after reconciliation all three are DIFF. So reconcile_tu really did dissolve the
declaration conflict and the drafts still miss the bytes. func_80135260 is the witness: CC1-FAIL ->
compiles at 139 ins vs target 136, 123 mismatched. Conforming its data types to what the TU can see
CHANGED ITS CODEGEN. The drafts' data types are byte-load-bearing and the TU's declarations are
incompatible with them: the def-side loose-typing wall in DATA form, a genuine wall not paperwork.
Do not invest further in a data-axis conform for these three.

INSTRUMENT NOTE: my first probe used rtu_match, which reported CC1-FAIL "redefinition of s8" for
two drafts — a FALSE blocker, since rtu_match does not strip the scalar typedefs common.h provides
but harvest_verify does. The authoritative gate disagreed with my probe instrument and was right.

func_80135260 (18,768 ins) is re-filed: not an integration target, a CRACK target at 3 ins over.
2026-07-28 14:46:03 -06:00
Drew T c23afcc6c3 fix(phase-29): SESSION-23 checkpoint — session total is 970, not 418
I wrote 418 by wrongly excluding T32/T33 (the NEAR-6 wave + its 548-member sweep) as if they
belonged to SESSION-22; they ran this session. Caught by reconciling against the metric rather than
trusting the running tally: fn-count 319,549 -> 320,519 = +970, and the per-task recount agrees
exactly (4 + 548 + 4 + 143 + 133 + 1 + 137). Third counting slip today, and the third caught by
measuring instead of asserting — the reconciliation step is earning its keep.
2026-07-28 14:33:01 -06:00
Drew T 4133a208bf docs(phase-29): T44 + SESSION-23 final checkpoint
T44 func_8013B83C: a MATCHING draft (272 ins) has been sitting unbanked in .run/s21_jt7/ since
Session-21 — found by measuring the preserved drafts against the right target (it is an _o0
function; my first probe used the wrong asm subdir). It does NOT bank: CARVE-REFUSED (has_mid_jr,
§53), and jtbl_carve then refuses because the _o0 subseg would host NON-CONTIGUOUS .rodata carves —
it needs jr_isolate_all (own code subseg, whale _o0b precedent) first. Tree verified clean; the
carve refused before writing. HONEST SIZING: the bank is 272 ins x1, and the 37,536 headline should
be DISCOUNTED — the exemplar is _o0 while its members are -O2, and today re-confirmed _o0 families
sweep ~1/137.

SESSION-23 FINAL CHECKPOINT written (supersedes the SESSION-22 blocks): 418 functions banked this
session, fleet 84.8->85.5% instr / 74.7->76.1% distinct / 90.34->90.61% fn-count, R22 140/140 run
7x, dedup 1886/0, 0 NON_MATCHING. Records the four strategic changes (every codegen-map file audited
vs real 2.7.2; the ADDRESSING->permuter route is wrong; the bare gate beats the ladder but has no
snapshot/restore; extract_unit's carry gap characterized), a ranked START-HERE list, my four errors
this session, and the carried defects.
2026-07-28 14:32:28 -06:00
Drew T 6fad5d19c8 feat(phase-29): T43 — func_80140D68 banked + swept 137/137; the §94 extract_unit CARRY GAP characterized
The §99 conform_decls pass REFUSED this one (§85: 414 callers consume the return), so it was solved
from the DRAFT side. Three blockers, each measured:

1. §94 type-carry, and the draft's own header asserted something FALSE — it claimed both typedefs
   already exist in engine_types.h. Measured: Hw4 1 hit, Prim4 1 hit, Env_800D29F8 ZERO. So in the
   real TU the #ifndef guard is DEFINED and the typedef vanished -> "parse error before D_800AE7BC".
2. Signature axis: conformed the DRAFT to the fleet's declared `s32 *` return, then param 2
   (s16 * -> Prim4 *). Byte-neutral because `src` is used EXACTLY once, as (s32)src. The error
   ("argument `src' doesn't match prototype") is again printed with NO "error:" prefix.
3. The family sweep went 0/137 TWICE before 137/137.

THE REUSABLE FINDING — a 0/N sweep whose exemplar banks cleanly is the signature of an extract_unit
carry gap. Measured on the staged member drafts:
  file-scope extern      -> CARRIED
  file-scope #define     -> CARRIED, but only while its expansion's deps stay file-scope
  file-scope typedef     -> NOT carried (silently dropped)
  #define whose expansion references a BODY-LOCAL extern -> NOT carried
RECIPE: make the draft SELF-CONTAINED — body-local typedefs survive (PTag_80140D68 in this same
draft was the proof all along), and if that pushes a macro's dependency body-local, inline the macro
at its use sites. 0/137 -> 0/137 -> 137/137, 0 failed, each step byte-measured.

R22 clean-fleet 140 passed / 0 failed of 140; dedup-check 1886/0.
Fleet 85.5% instr, 76.1% distinct, 90.57 -> 90.61% fn-count.
§99 arc total (T41-T43): 133 + 1 + 137 = 271 functions. Both blockers Drew green-lit are CLOSED.
2026-07-28 14:17:50 -06:00
Drew T 59785974f0 feat(phase-29): T41 — §99 conform_decls: func_80177DA8 family 4/137 -> 137/137 (0 failed)
Drew green-lit the fleet-shared change. One function at a time from a committed-clean baseline, dry
run first, R22 after each step — the discipline the T39 shared-state hazard earned.

- conform_decls --fn func_80177DA8 --apply: byte-true def `void func_80177DA8(u8 *p, u32 v, s32 idx)`;
  268 declaration sites rewritten across 268 files; the tool's own R32 completion assertion reports
  "non-canonical declarations remaining: 0  OK (axis complete)" (§85 all-or-nothing as a COUNT, not a
  hope). Nothing under src/shared, config or include. R22 -> 140/140: the decl axis is byte-neutral.
- Re-sweep: BANKED 133 / 0 failed, skipped {not-stub: 4} => the family went 4/137 -> 137/137, exactly
  reversing T40's failure. R22 -> 140/140. dedup-check 1886/0.
- Fleet 85.4 -> 85.5% instr, 76.0 -> 76.1% distinct, 90.54 -> 90.57% fn-count.

T42 func_80140D68 — the pass correctly REFUSED it (414 callers consume the return, so widening the
return type is not byte-neutral, §85). Diagnosed in the real TU instead (rtu_match + reading ALL
stderr per §95 — gcc-2.7.2 prints hard errors with NO "error:" prefix, so grepping for "error" finds
nothing):
1. parse error before D_800AE7BC = a §94 TYPE-CARRY defect, and the draft's header asserts something
   FALSE: it claims Hw4 AND Env_800D29F8 both already exist in engine_types.h. Measured: Hw4 1 hit,
   Prim4 1 hit, Env_800D29F8 ZERO. FIXED by keeping Hw4 guarded (it does exist) and moving
   Env_800D29F8 outside the guard, draft-local per §100. Still MATCH (65 ins).
2. conflicting types — conformed the DRAFT's return to the fleet's declared s32 * (still MATCH).
   Only remaining delta: param 2 byte-true `s16 *` vs declared `Prim4 *`. conform_decls still refuses
   (its return-axis precondition fires regardless). OPEN, with the next probe named.
2026-07-28 14:05:42 -06:00
Drew T 37e5a19558 feat(phase-29): T40 — sweep 143/548; the 405 failures are one named class (§99 K&R-vs-prototype)
family_sweep --hseq --band all --only <4 cores> -j12 -> 143 banked / 405 failed. R22 clean-fleet
140 passed, 0 failed of 140; dedup-check 1886/0. Fleet 85.3 -> 85.4% instr, 75.8 -> 76.0% distinct,
90.50 -> 90.54% fn-count.

Unlike T33's 548/548, this sweep mostly failed — so the failures were DIAGNOSED, not accepted:
- func_80138C60 swept 137/137 clean.
- func_8013B6A0 / func_8013B598 swept 1/137 each — EXPECTED, not a regression: Phase 20 byte-proved
  the -O0 cluster is OVERLAY-LOCAL, so their siblings need per-overlay _o0 carves that do not exist.
- func_80177DA8 swept 4/137 — ROOT CAUSED: its banked def is K&R and its own TU declares it no-proto,
  but NON-SC07 overlays declare a PROTOTYPE (extern void func_80177DA8(s32,s32,s32)), so the remap
  conflicts. The 4 successes are SC07 overlays, which declare it not at all.

THE SYNTHESIS: that is the SAME §99 K&R-vs-prototype class as item 3's func_80140D68 (K&R def vs 138
prototype callers). ONE conform_decls pass unlocks both — ~17,000 instructions. conform_decls exists
for exactly this ("the draft's signature is byte-TRUTH; move the DECLS, never the draft"). NOT run:
it is a fleet-shared 138+-declaration change and this session already tripped one shared-state
hazard, so it wants an explicit go-ahead (P5).

MY ERROR, recorded: I read a `head -6` list of modified dirs as the complete set and briefly thought
the sweep's count did not reconcile. Measured properly it does — 143 stubs removed across 142 files.
Same class as grepping the wrong field earlier today: truncated output is not exhaustive output.
2026-07-28 13:50:28 -06:00
Drew T c06f5f1e4e feat(phase-29): T39 — items 1-4; 4 reach-138 cores banked, the bare gate beats the ladder
Worked Drew's order 1->2->3->4 at xHigh (no fan-out).

ITEM 1 func_80176734: permuter COMPLETELY FLAT at 13 (8 cycles x 240s, regalloc profile chosen over
the classifier's cse because the residual is 3 register 2-swaps). Not one improving waypoint in 32
min. THIRD ADDRESSING-bucketed target in a row where the permuter under-delivers (11->7, 10->6,
now 13->13 flat) — the T31 routing finding is now well evidenced. Remaining: the reg_renumber-swap
gdb oracle; feasibility confirmed (cc1 unstripped, reg_renumber @0x82d4330, prior-art .gdb exists)
but it needs a .greg pseudo-identification pass, so not started.

ITEM 2: 4 of 7 banked — func_80177DA8, func_8013B6A0, func_8013B598, func_80138C60. THE BARE GATE
WAS THE UNLOCK: gate_stage reported failed:2 on the _o0 pair while bare harvest_verify reported
verified 2/failed 0 on the SAME drafts, and rtu_match independently confirms func_8013B6A0 matches
in the real TU. That gives the carried "ladder-vs-bare-gate asymmetry" defect a REPRODUCTION — the
ladder is destroying good drafts, not diagnosing them. All 4 are reach-138 PURE families =
35,604 templatable instructions.

SHARED-STATE HAZARD hit and repaired: the failed func_80135260 attempt left fix_arity_callers
--any-proto edits in 17 TUs holding none of my banks (the bare gate has no snapshot/restore, unlike
the ladder after the Task-14 incident). Caught by diffing the tree, not by the tool's report.
Reverted the 17, kept the 3 bank-bearing files, re-verified R22 clean-fleet 140/140. Nothing under
src/shared, config or include was touched, so blast radius was ov_SC01_077-local (§63).

ITEM 3 func_80140D68: fails even bare-gated, and the PREDICTED CAUSE WAS WRONG — there is no
DEFINE_func_80140D68 macro. Real conflict is §99 K&R-vs-prototype: draft is K&R `u32 *`, 138 callers
declare `extern s32 *func_80140D68(s32 *, Prim4 *, s32, s32, s32);`. Lever exists (§99 did this at
1,072-decl scale today) but it is a fleet-shared 138-decl change. Scoped, not attempted.

ITEM 4 func_80178004 biv-init wall: RE-PROBED and UPHELD — my own hypothesis refuted. The cited
mechanism (loop.c:3803/3823, benefit->0 eliminates emit_iv_add_mult) is verbatim present in real
2.7.2 inside strength_reduce (3214); loop.md's flagged version difference is in combine_givs, which
this verdict does not rest on. The wall stands.
2026-07-28 13:43:10 -06:00
Drew T b885acf676 docs(phase-29): T38 — fix expr.c:5535 (and the second wrong cite beside it)
Derived the correct lines myself AND had an independent agent derive them separately — warranted
after two stacked line-number errors earlier in the session. Both derivations agreed exactly.

- expr.c:5535 -> 4577 (guard 4570-4576), case INDIRECT_REF: @4540, expand_expr @4026. 5535 is a
  gcc-2.8.1 line; in our 2.7.2 it is MIN_EXPR/MAX_EXPR optab code.
- expr.c:5891 -> 4888, case COMPONENT_REF: @4748. THIS SECOND CITE WAS ALSO WRONG and had not been
  noticed: 5891 lands in case COND_EXPR: (jumpifnot/cleanups).

Two precisions from the independent derivation, folded in rather than dropped:
- the INDIRECT_REF guard is a 4-WAY OR, not a single test (SAVE_EXPR-wrapping-PLUS, aggregate-typed
  deref, and ADDR_EXPR-of-aggregate also grant /s), so "only when top-level PLUS_EXPR" was too
  strong. The cast-defeats-/s rule is now bounded to a scalar-typed deref through a plain pointer —
  which is the case the idiom is actually about.
- a SECOND MEM_IN_STRUCT_P (op0) = 1 at 4873 is conditional (BLKmode bitfield, returns early at
  4876) and is NOT the one meant — flagged so it is not cited by mistake.

Old cites struck not deleted (H5); the top-of-file provenance note corrected (it named 4904, which
is the OFFSET_REF grant — a real third site but not this idiom's). Tool limitation recorded:
sweep_citations.py cannot tell a live cite from a struck-through historical one.
Docs-only: no src/ or config/ touched; R22 not re-run and not claimed.
2026-07-28 13:25:12 -06:00
Drew T 86caa7dfdb fix(phase-29): T36/T37 — my checker had a FORM-FEED bug; the "12 fabricated" were mine, not the agents'
T36 CORRECTION (the important half). Building the cookbook sweep tool surfaced a defect in
tools/verify_map_findings.py, which I had already used to validate BOTH map audits:

- GNU C sources use FORM FEED (\f) page separators — loop.c 47, cse.c 36, reload1.c 27,
  local-alloc.c 21. Python's splitlines() splits on \f; grep/sed do not. Every line number computed
  after the first \f was shifted (up to 47 in loop.c), which is LARGER than the checker's own +/-40
  window — precisely how a real quote gets reported FABRICATED.
- Re-run after the fix: T34 regalloc 27 OK/153 NEAR/0 FAB -> 180 OK/0/0. T35 four-file
  47 OK/240 NEAR/12 FAB -> 299 OK/0/0. THE AGENTS' LINE NUMBERS WERE EXACT ALL ALONG. I had even
  written the false "off by +2..+19" claim into the T35 agent prompt.
- MY DIAGNOSIS OF THE 12 WAS ALSO WRONG. I said agents pasted map prose into source_quote and
  "verified" it by grepping — but I grepped claim_excerpt (which IS map prose) instead of
  source_quote. The real source_quote was `    record_jump_equiv (insn, 0);` at cse.c:7511, a
  correctly-located C line. Two stacked errors: a broken tool, then a check of the wrong field that
  appeared to confirm it.
- Fixed: both tools use split("\n"); verify_map_findings.py documents the trap so it cannot return;
  loop.md's "12 unverified" note is WITHDRAWN in place. Nothing was deleted on this basis (all 12
  were CONFIRMED-status, none underpinned a refutation), and the T34/T35 upheld/overturned splits are
  unaffected — those came from adversarial agents, not the checker.

T37 THE COOKBOOK SWEEP (what was asked for). New tools/sweep_citations.py puts the mechanical half of
a citation audit into zero-token tooling (offline-tooling-first): symbol-form cites are compared to
the real 2.7.2 definition line; file-form cites are localised to their enclosing function.
- matching-cookbook.md: 57 resolvable citations, all localisable. MIXED provenance but mostly sound —
  materially better than the map files. loop.c:5556, local-alloc.c:1765/1795/1825, global.c:906/917/
  924/1000, local-alloc.c:1021/1064, global.c:588/594, sched.c:820, expmed.c:556, jump.c:2131 all
  land where the prose says. GENUINE MISS: expr.c:5535 is MIN/MAX optab code; the /s grant sites are
  4577 and 4904.
- STATED LIMITATION: "lands in the right function" is weak for giants (expand_expr 4026->~6300,
  jump_optimize 139->~2200). This is a CITATION sweep, not a claim audit — proportionate because the
  cookbook's idioms are byte-proven and its cites are explanation. No idiom re-litigated.
Docs+tools only: no src/ or config/ touched; R22 not re-run and not claimed.
2026-07-28 13:20:27 -06:00
Drew T ce8f7629ae docs(phase-29): T35 — the last 4 codegen-map files audited vs real gcc-2.7.2
Scope enumerated before acting: cse_expr.md, loop.md, sched.md (full pass — T33 landed only a
partial), t7g-giant-harvest.md. 35 agents (9 derive + 26 adversarial refute), 2.48M subagent tokens.

308 findings: 174 CONFIRMED / 99 LINE-DRIFT / 26 REFUTED raised -> 20 UPHELD, 6 OVERTURNED / 9
unverifiable. cse_expr.md had the highest error density (17 refuted of 74); loop.md the lowest (3 of
96) thanks to its pre-existing caveat table.

12 FABRICATED (vs 0 last audit) — DIAGNOSED, not waved through: the agents pasted MAP text into the
source_quote field instead of compiler source. All 12 are CONFIRMED-status and none underpins an
upheld refutation, so nothing was deleted on bad evidence — but they are UNVERIFIED, they sit in
loop.md's biv-elimination area, and loop.md now records that as an open gap rather than a pass (R32).

Headline corrections:
- cse_expr: THE 1000-INSN CSE FLUSH DOES NOT EXIST IN 2.7.2 (added in 2.8.1; grep num_insns -> no
  hits). It drove THREE places — §1's killer table, §6's giant tell, §7's "shift +-insns across the
  1000 boundary" lever. A lever aimed at a counter our compiler lacks, in exactly the giants this map
  serves. All struck.
- cse_expr: §2's "kill THE class reg" is singular and wrong. The audit BYTE-REPRODUCED T31's wall on
  the pinned cc1: expand_block_move (mips.c:2350-2351) copy_addr_to_reg's BOTH aggregate addresses.
  Two byte-proven remedies recorded, with the caveat that field-by-field copy is closed when the
  target's own bytes need the block move (func_80132F40's case).
- cse_expr: assign_temp absent in 2.7.2 and no /s reset on slot reuse (recycled slots INHERIT /s);
  no BUILT_IN_MEMSET; §6's "recompute after a join is never a residual" false at -O2.
- sched: S7's EPILOGUE half false (no live define_expand "epilogue" on MIPS) — re-scoped not deleted;
  insn_cost is DEP-KIND-BLIND so restoring /s anti edges is not free.
- loop: "no memory load is EVER hoisted from a loop containing a call" FALSE — invariant_p checks
  RTX_UNCHANGING_P first; byte-proven that a const int* load hoists to the preheader. Call args are
  emitted LEFT-to-right, not right-to-left.

Remaining: matching-cookbook.md (~52 citations, MIXED provenance) — but a DIFFERENT risk profile,
since its idioms are byte-proven and citations are explanation, so a targeted citation sweep is
proportionate rather than a full audit. Not done; flagged.
Docs-only: no src/ or config/ touched, R22 not re-run and not claimed.
2026-07-28 12:43:06 -06:00