Commit Graph

478 Commits

Author SHA1 Message Date
Drew T 73321fe2c2 fix(phase-30 S38): the gate was booking CRASHES as silence — 10 of wave 6's 16 drafts vanished
THE DEFECT CHAIN (byte-witnessed, both ends fixed):
1. harvest_verify._reload_corpus re-applied the `--src` filter AFTER a jtbl carve. Following a
   carved stub to its NEW TU is that function's entire documented purpose, and it was deleting the
   very stub it had just followed. Then:
     _stubs loses fn -> render() raises KeyError -> UNCAUGHT -> _jtbl_restore(snap) never runs
     -> the carve is STRANDED in config/ + src/ -> every LATER group in the same gate run then
     built against a tree the earlier crashes had mutated.
   line 136 already calls --src "an optional filter, not a location oracle"; this was the one place
   treating it as one. Fix: the filter never drops a draft under verification, wherever it now
   lives, + an R32 loud report if a working stub vanishes across a reload (which also repairs
   _touched/baseline — a carved fn missing from _stubs left its new TU unbaselined, so the revert
   path could not have restored it either).
2. .run/s6f_gate.py never checked the child's returncode — it grepped stdout for VERIFIED:/FAILED:
   and booked "neither" as NOTHING, printing a clean-looking tally over 10 missing verdicts. This
   is the §136a defect I logged against my own capture tool last session, in the gate itself.
   Fix: 1:1 accounting assertion (banked+failed+no-verdict == drafts), the child's rc + output tail
   on anything unaccounted, and exit 1 — a crashed child may have stranded a carve, so it must
   never look like success.

PROOF THE FIX IS NOT COSMETIC: func_8017EA84 (579 ins) now carves and banks BYTE-IDENTICAL. The old
tool reported it as nothing at all.

BANKED 7 (R22 clean-fleet 140/140 from `make clean` + extract-all + check-all):
  func_8017EA84 ov_SC02_000 (579) · func_8017FEE0 ov_SC02_026 (299) · func_80181CE4 ov_SC03_111 (491)
  func_80183AE0 ov_SC03_112 (240) · func_80184C74 ov_SC06_018 (288) · func_80180F98 ov_SC03_097 (263)
  func_801841C8 ov_SC02_035 (44)

MY OWN ERROR, RECORDED (R37/R14): after reverting the stranded carves I re-extracted ONE overlay,
not all 16 — the Phase-20 R22 corollary (a reverted CONFIG needs a `make extract`, not just a
revert) which I know and skipped. Run 2 therefore read WORSE than run 1: three genuinely-banked
functions failed against stale asm. Re-extracting the 16 touched binaries produced the honest run.
A gate result measured against stale asm is not a measurement (R35).

.run/w6_diag.py: run the REAL gate path for one (ov,fn) with the child's full output. s36_capture.py
splices without the carve, which is the wrong path for a table-bearing fn (§61b: the carve must
follow the splice) and produces a failure that is an artifact of the diagnosis.
2026-08-04 17:41:43 -06:00
Drew T b497ee1649 feat(phase-30 S33c): PROPAGATE head COMPLETE — func_801466F0 x137 took three fixes + a type-lift
Fleet 96.17 -> 96.21% fn-count / 93.8% instr / 88.0% distinct; dedup 1909 -> 1910
groups, 0 failed, C1 241216/241216. R22 clean-fleet: 140 passed, 0 failed of 140.

The head is now 5/5 classes, 18,545 templatable ins, all banked this session from
a standing start of 0.

func_801466F0 had sat since S6b behind THREE separate blockers, each of which
looked sufficient on its own to explain the failure:
 1. Its definition is under a §37/§73 ASM-LABEL ALIAS (`aF801466F0` in C, bound to
    the real symbol by `__asm__`), and dedup_propagate.find_site anchored its head
    regex on the literal `func_<ADDR>` — structurally blind to the form, returning
    None, which every caller reads as "not matched". Now reuses
    family_remap._alias_decl_for rather than growing a second matcher (R33).
 2. That matcher was itself blind to the WRAPPED (multi-line) declaration — the
    §134 shape, third tool. Fixed by matching over the joined text and mapping the
    offset back to the decl's FIRST line (extract_unit carries from there).
    Regression control: the single-line form still resolves. Fleet census after:
    2,768 of 2,768 alias sites resolve, 0 missed.
 3. Its record type was a draft-local typedef, so the body failed
    compiles_standalone. Lifted Rec801466F0 to src/shared/engine_types.h INSIDE
    the include guard (the SESSION-19 double-include note) and switched both the
    macro and the exemplar to it — byte-neutral, gate-proven.

Probed on ONE member before the fleet run: byte-identical 9052dc0e first try.

MEASURED, NOT INHERITED (R37): the S6b note frames the alias-regex gap as a CLASS
of missed work. It is ONE function — 91 distinct alias decls fleet-wide, the
per-line matcher resolved 90. Recording it so a future session does not scope a
phase against a class that does not exist.

cookbook §138 extended with the alias-form tool boundary and the three-blocker
story; index regenerated.
2026-08-04 00:44:33 -06:00
Drew T 4f6b0e8de1 feat(phase-30 S33b): PROPAGATE head 82% banked — 15,257 of 18,545 ins, four levers
Fleet 96.10 -> 96.17% fn-count / 93.7 -> 93.8% instr / 88.0% distinct.
dedup 1908 -> 1909 groups, 0 failed, C1 241078/241078.
R22 clean-fleet: 140 passed, 0 failed of 140.

  func_80147364  4,110  x137  definition-side asm-label alias
  func_8016BA68  3,886  x134  dedup_extend + the MIRROR decl relax
  func_8012F274  3,973  x136  hand-authored macro, source overlay excluded
  func_8012A598  3,288  x138  cdecl._mask backscan fix + shared-type switch
  func_801466F0  3,288  OPEN  the wrapped-alias regex — measured as ONE function

THREE DISTINCT CARRY VARIANTS were hiding in one "CARRY-FIXABLE" bucket, and
only one is a tool bug (-> cookbook §138):
  - a MULTI-LINE comment halts the preamble backscan -> fix the tool (cdecl._mask)
  - a draft-local `struct Tag {…}` -> switch the exemplar to the SHARED type
  - a file-scope `static inline` helper -> hand-author, EXCLUDE the source overlay
The third is the sneakiest: gcc-2.7.2 accepts implicit function declarations, so
the extracted body PASSED compiles_standalone with the helper undeclared and the
miss surfaced only as a whole-binary byte DIFF 137 gates later. Instantiating
that macro in the SOURCE overlay is a duplicate definition (its file-scope helper
is still there), so the shape is `--source-overlay X --binaries <all-but-X>`;
`--binaries` alone removes the source from the scan pool and errors.

TOOL BOUNDARY: once a group's members are DEFINE_func_*() sites, dedup_propagate
cannot extend it (find_site never returns a `def`). dedup_extend is the tool for
an already-macro-ized group — and `dedup_extend --check-only` across ordinary
overlays is a cheap fleet-wide wiring census (measured: exactly 1 group per
overlay, so no hidden backlog).

MEASURED, NOT INHERITED (R37): the S6b note frames _alias_decl_for's single-line
regex as a CLASS of missed work. It is not — 91 asm-label alias decls exist
fleet-wide, the regex matches 90, and the single miss is func_801466F0. Worth
3,288 ins, but a one-function fix. Correcting the expectation so a future session
does not scope against it.
2026-08-04 00:30:56 -06:00
Drew T 62042f65ca fix(phase-30 S11): multi-line-comment blindness in dedup_propagate; func_8012A598 x138
Fleet 96.06 -> 96.10% fn-count / 93.7% instr / 88.0% distinct; dedup 1907 -> 1908
groups, 0 failed, C1 240807/240807. R22 clean-fleet: 140 passed, 0 failed of 140.

func_8012A598 (3,288 templatable ins) was being written off as CARRY-FIXABLE.
It took TWO fixes; either alone leaves it skipped.

1. TOOL (R33) — find_site's preamble backscan. The SESSION-18 fix handled blank,
   `//`, and SINGLE-LINE `/* … */` lines, but a MULTI-LINE block comment still
   halted the walk: its middle lines start with `*` and its last line ends `*/`
   without starting `/*`. So the three externs above the body were dropped and
   the body then failed compiles_standalone on now-undeclared data. This is the
   §134 multi-line-blindness class — S6b fixed the identical shape three times in
   family_remap (D1/D2/D5) and this copy was never reached.

   Fixed by deciding skippability on `cdecl._mask` — the project's ONE masking
   oracle — instead of on line syntax: it subsumes every comment form at once and
   cannot be fooled by a `/*` inside a string, with an R32 assertion on the
   length-preservation invariant it rests on. Strictly monotone (it can only
   carry MORE preamble), and dedup_propagate is a byte-gate feeder, so a bug here
   can fail to bank but never falsely bank.

2. EXEMPLAR — the body also declared a draft-local `struct BigCopy164` tag, which
   the tool refuses by design (two macros defining one tag would redefine it in a
   single TU). The shared `struct BigCopy` (engine_types.h L312) is the identical
   layout and is ALREADY used this exact way at engine_core.h:16158, so switching
   the exemplar to it is byte-neutral and drops the alias too.

Probed on ONE member before scaling (R37/S29): byte-identical 9052dc0e first try;
then 138 overlays byte-identical.

PROPAGATE head accounting after this: 7,398 of 18,545 ins banked (func_80147364
4,110 + func_8012A598 3,288). Still open, each with a NAMED cause and none yet
diagnosed against a build: func_8012f274 (3,973, dropped), func_8016ba68 (3,886,
4/138), func_801466f0 (3,288, the S6b D4 wrapped-alias gap).
2026-08-03 23:53:50 -06:00
Drew T c7ad41c8a3 feat(phase-30 T6/S11): the propagation lag — EXTEND 31/36, and the PROPAGATE head measured
Continues the S11 lane. Fleet 96.01 -> 96.06% fn-count / 93.6 -> 93.7% instr /
88.0% distinct; dedup 1905 -> 1907 groups, 0 failed, C1 240669/240669.
R22 clean-fleet: 140 passed, 0 failed of 140. 0 NON_MATCHING (G4).

EXTEND (SC07): the 16 volatile-blocked DIFF slots banked on retry after the
data asm-label alias -> lane total 31/36.

PROPAGATE head, measured rather than projected. .run/s8_lag.json re-split: the
checkpoint's "45 classes / 20,837 ins" is really 5 classes carrying 18,545 ins
(89%) and 41 carrying 2,316. Per-class outcome:

  func_80147364  30x137 = 4,110  BANKED x137 (definition-side asm-label alias)
  func_8012f274  29x137 = 3,973  DROPPED — byte-diverges in ~130 overlays
  func_8016ba68  29x134 = 3,886  4 of 138 banked; excluded from ~130
  func_8012a598  24x137 = 3,288  SKIPPED, cause NAMED by the tool
  func_801466f0  24x137 = 3,288  no source found — the S6b D4 gap, still open

  func_80147364's byte-true definition is `(u16, u16)` while 4,046 fleet decls
  say `(u16, s32)`. u16 is a default-promotion type, so the `()` no-prototype
  escape is ILLEGAL (the documented gcc-2.7.2 dead-end) and conforming the decl
  would change caller codegen. The DEFINITION-SIDE asm-label alias gives the def
  a distinct C identifier while emitting the real symbol -- zero blast radius on
  every caller. Probed on ONE member first (1 build, not 137 -- the S29
  discipline): byte-identical 9052dc0e first try; then 137 overlays clean.
  In-tree precedent for the form: 1,725 files.

MEASURED NEGATIVE, recorded not buried: `dedup_propagate --recover` banked only
4 of 138 on func_8016ba68 and dropped func_8012f274 entirely (137 [exclude]
lines). The caller-extern reconcile that is 16/16 lifetime ON DRAFTS does NOT
transfer to PROPAGATION of these two. Cause not yet diagnosed -- probe one
excluded overlay's build output before any further attempt (§136a), do not
re-run the lever hoping.

NAMED NEXT (cheapest first): func_8012a598 skips on `missing file-scope extern
(CARRY-FIXABLE): D_801151D4, D_80126DB8_a, D_80127504` -- the SESSION-18
preamble-backscan class. Its body is 2 statements and `struct BigCopy` is
ALREADY in the shared engine_types.h (L312) with the identical statement already
macro-ized at engine_core.h:16158, so a hand-authored macro (the func_80147364
path) should take it x137 for ~0 tokens.

Process errors recorded in CURRENT_PHASE.md, all three one mechanism -- the
signal sampled is not the thing waited for: (1) a `nohup CMD &` wrapper's exit
read as the fleet check finishing (it stood at 63/140); (2) a corpus.stubs probe
mid-rebuild, which R32's coverage assertion refused rather than answer wrongly;
(3) CORRECTION to the S10 checkpoint's own rule -- `pgrep -x make` is right for
one make and WRONG for a campaign of sequential makes (it fired in a gap and
reported a live campaign done), and `pgrep -f <pattern>` SELF-MATCHES so that
waiter can never exit. Wait on the campaign process or `treelock.sh --status`.
2026-08-03 23:41:34 -06:00
Drew T 16a1dabc79 feat(phase-30 T6): SC07 EXTEND lane 0/36 -> 15 banked; the 4 "DIFF"s are a volatile decl
Banked 15 h_exact members into the 4 SC07 overlays via dedup_extend, and
diagnosed the class that dedup_extend's own header records as UNDIAGNOSED.

THE 4 DIFFs ARE NOT A CODEGEN WALL. dedup_extend's correctness argument says an
h_exact match guarantees byte-identity including relocs, so a DIFF should be
impossible. Both halves of that tension resolved against the bytes:

  1. The contract HOLDS. func_80162FF4's original bytes are sha1-identical in
     ov_SC07_006 and ov_SC01_000 (af1aceb2...), so the registry is not lying.
  2. The cause is TU CONTEXT. The SC07 host TU (_jr_8015C32C.c:1177) declares
     `extern volatile s32 D_80127090/94/98` at FILE scope; none of the 134
     working overlays' copy of that TU does. Volatile makes the macro's three
     stores a scheduling barrier, so `addu $a0,$s2,$zero` cannot sink into the
     `jal func_80146D30` delay slot -- the built body emits it early plus a nop,
     one instruction longer. Measured word-for-word against the payload:
       built  +0x090 addu / lui,sw x3 / jal / NOP
       ref    +0x090 lui,sw x3 / jal / addu-in-delay-slot
     All 4 DIFF macros touch exactly those 3 symbols, which is why all 4 fail in
     all 4 binaries and nowhere else.

  Fix: the §37/§124 DATA asm-label alias inside the 4 macros
  (`extern s32 aD_80127090 __asm__("D_80127090")`) -- a distinct C identifier is
  immune to any TU's declaration of the symbol, and is byte-neutral by
  construction in the other 134 (same symbol, same type, same non-volatile
  semantics). In-tree precedent: ov_SC06_008_jr_80135D20.c:1434.

R22 clean-fleet: make clean && extract-all && check-all -> 140 passed, 0 failed
of 140, with the 15 banks AND the alias edit in.

Remaining in this lane, both named not walled: func_80144B9C x4 (the whale --
its registry `func` field is a bare name, not a DEFINE_ macro, so write_drafts
emits a CALL; it needs the §38 -O0 shared-header route, and dedup_extend should
refuse-and-name it per R32) and func_80149954 x1 (blocked behind func_80147364,
whose u16 params make the `()` no-prototype escape illegal -- the documented
gcc-2.7.2 default-promotion dead-end; needs the alias or a de-macroize).
2026-08-03 22:57:51 -06:00
Drew T d74f63a74b feat(phase-30 S6c): jr family bank — func_8012ACE0 rest (ov_SC07_007/010/011) 2026-08-03 09:25:09 -06:00
Drew T cb1dcaa5aa feat(phase-30 S6c): jr family bank (pre-func_8012ACE0 rest) 2026-08-03 09:24:21 -06:00
Drew T 8554e8589f feat(phase-30 S6c): jr family bank (pre-func_801734BC rest) 2026-08-03 09:24:04 -06:00
Drew T 9603d25896 feat(phase-30 S6c): jr family bank (pre-func_8016AE5C) 2026-08-03 09:12:14 -06:00
Drew T 4ed1c8cd2b feat(phase-30 S6c): jr family bank (pre-func_8012ACE0) 2026-08-03 09:12:03 -06:00
Drew T ed4680f3a6 feat(phase-30 S6c): jr family bank (pre-func_80178D40) 2026-08-03 09:09:45 -06:00
Drew T 1b27550fdf feat(phase-30 UC): func_80159A20 jr sibling sweep 2026-08-01 11:15:43 -06:00
Drew T 1653a8be53 feat(phase-30 UC): func_801549f8 jr sibling sweep 2026-08-01 10:51:45 -06:00
Drew T facd998d1b feat(phase-30 UC): func_80159a20 jr sibling sweep 2026-08-01 10:37:44 -06:00
Drew T 98775dd1d5 feat(phase-30 S1): func_80135260 zero-crack sweep (136 ins) 2026-08-01 09:06:58 -06:00
Drew T 3ffd9e5419 feat(phase-30 S1): func_8017AE2C zero-crack sweep (174 ins) 2026-08-01 09:06:50 -06:00
Drew T 0367bde898 feat(phase-30 S1): func_80159C84 zero-crack sweep (337 ins) 2026-08-01 09:00:17 -06:00
Drew T e845a3ab48 feat(phase-30 S1): func_8015444C zero-crack sweep (363 ins) 2026-08-01 08:58:30 -06:00
Drew T c373e5a2dd feat(phase-30 S1): func_8013FFD8 zero-crack sweep (213 ins) 2026-08-01 08:56:08 -06:00
Drew T 91973fb2f7 feat(phase-30 S1): func_8017A4AC zero-crack sweep (536 ins) 2026-08-01 08:55:01 -06:00
Drew T 8f837f6196 feat(phase-30 S1): func_8015AE2C zero-crack sweep (562 ins) 2026-08-01 08:54:33 -06:00
Drew T 79b896caac feat(phase-30 S1): func_8015A3C8 zero-crack sweep (493 ins) 2026-08-01 08:54:07 -06:00
Drew T da5090ee03 feat(phase-30 S1): func_8017BEBC zero-crack sweep (952 ins) 2026-08-01 08:51:48 -06:00
Drew T b96352e9ec feat(phase-30 S1): func_8014032C retry without --span-rel — the spans that hold only the NEW tables 2026-08-01 08:45:39 -06:00
Drew T 7c5d308c49 feat(phase-30 S1): func_8014032C sibling sweep — 183 ins x137 (see log) 2026-08-01 08:42:12 -06:00
Drew T 10dc6d3635 feat(phase-30 S1): --span-rel unblocks the zero-crack head; func_8014032C ×1 probe banks (§132b)
S1's head family (0x8014032C, 183 ins ×137 = 25,071 templatable ins) gate-failed on its probe
sibling. Diagnosis (the §132 ladder, one build): the object emits FOUR tables — BOTH functions in
it are multi-switch (8+5 entries each) — while the carve derived THREE starts.

The missing start belongs to the ALREADY-MATCHED owner func_8013FFD8, and neither oracle can see
it: its stub .s is pruned by extract, and its second table abuts its first with NO pad (8 entries
= 32 B = 0 mod 8, so `.align 3` emits nothing) — precisely the honest limit §132's payload
zero-word recovery documents. Note the true pads [0,0,4,0] are exactly what natural alignment
produces; the build breaks only because a SHORT spec gets written.

Fix: thread the documented `--span-tables` escape through the sweep as `--span-rel` — offsets
relative to the FIRST NEW table, which func_jtbls reads from the sibling's own .s. Byte-verified
family-invariant before use (identical relative offsets on 3 sampled siblings; same code, same
entry counts, only the base moves). Empty by default => every other family untouched.

Also clears my own §132a guard of suspicion (R14): the --like transfer was inert here regardless
(exemplar subseg `ov_SC01_077` vs sibling `_jr_8013FFD8` — roles never matched).
2026-08-01 08:26:30 -06:00
Drew T 73570a5089 feat(phase-30): func_8013B83C ov_SC07_010 + the --like over-transfer guard (§132a)
The one sibling both sweeps failed on. `--like <exemplar>` transfers the exemplar span's table
STRUCTURE, and jtbl_carve matches donor to recipient by the subseg's ROLE NAME. ov_SC07_010's
-O0 region is named `_o0` — the same role as ov_SC01_077's, and the ONLY other overlay so named
(the other 136 are `_o0c`, whose role never matched, which is the only reason the sweep worked).
The exemplar had just banked 2 more owners than the sibling has, so the transfer unioned its
rebased 4 offsets with the sibling's real 2 + the new table: SIX starts for THREE emitted tables
-> jtbl_rodata_pads refused ("consumed 3 rodata .align(s) but 6 pad spec(s)").

Guard (tools/jtbl_family_bank.py like_arg): suppress --like when the sibling already carries a
committed `tables=` for the target subseg — its own record is authoritative, and jtbl_carve's new
payload zero-word recovery covers the incomplete-record case that --like used to paper over.
Derived via jtbl_carve.func_subseg, the same derivation the carve itself uses (R33); never blocks
a bank (any failure falls back to the old behaviour). Inert for all 136 siblings already banked;
fixes exactly the broken one. Both fns now bank on ov_SC07_010 => 137/137 siblings each.
2026-08-01 00:11:35 -06:00
Drew T bd92809da6 feat(phase-30): func_8013BD74 ov_SC07_010 — the --like over-transfer guard unblocks the last sibling 2026-08-01 00:11:14 -06:00
Drew T bfabf0906d feat(phase-30): func_8013B83C sibling sweep — jtbl_family_bank ×N (see log) 2026-08-01 00:08:50 -06:00
Drew T 5ea269af84 feat(phase-30): func_8013BD74 sibling sweep — jtbl_family_bank ×N (see log) 2026-07-31 23:55:22 -06:00
Drew T 8cd0c50f6d feat(phase-30): func_8013BD74 ×1 (ov_SC01_000) — the 1-sibling sweep probe banks 2026-07-31 23:40:18 -06:00
Drew T b9efe66f91 fix(phase-30): the JR-PAIR "wall" was TWO instrument defects — pair banked, class retired
S28 ledgered `JR-PAIR-IN-ONE-O0-OBJECT` (two jr fns matched in one -O0 object => a clean
build that cannot link: `undefined reference to $L105` + `func_8013C938`) with §81 step 1
(isolate one into its own code subseg) as the untested escape. BOTH the class and the escape
are REFUTED — no isolation, no compiler wall, both fns banked from a genuinely clean fleet.
The 4th consecutive "structural wall" to resolve to our own tooling (§124/§125/§126/§131).

- DEFECT 1 (tools/jtbl_carve.py): ov_SC01_077_o0's carve at 0xb01a4 predates the §8e
  `tables=` persistence and is a MERGED DOUBLE (func_8013C0F8 $L75 + func_8013C414 $L105);
  the 2nd owner is MATCHED so extract pruned the stub .s naming its table. The single-table-
  predecessor inference derived 3 starts where the object emits 4 tables -> JTBL_PADS 0,4,4
  -> jtbl_rodata_pads refused mid-stream, correctly. FIX: R32 coverage assertion + payload
  recovery at the single choke point (spec_from_starts) — every zero word inside a span is an
  original `.align 3` pad (the tool's own axiom), so the word after it STARTS a table;
  recovered starts are logged. No-op where structure is known (the 134 sibling _o0c spans
  carry tables=+0x0,+0x70). Honest limit: tight (0-pad) boundaries stay unrecoverable but
  fail LOUD via the filter's count guard — never silent.
- DEFECT 2 (Makefile): no .DELETE_ON_ERROR, so `as` (a pipeline consumer) left a TRUNCATED .o
  on disk — 12 of 16 T func_, undefined $L57/$L59/$L63/$L75/$L76 — newer than its .c, and the
  NEXT build linked the corpse. That IS the S28 link error, one build downstream of a loud,
  correct compile error. Negative-control-proven on a scratch invocation.
- BANKED: func_8013B83C (272 ins) + func_8013BD74 (198 ins) in ov_SC01_077 (d19c9580).
  Byte proof: 4 tables 0x801D8254/828C/82FC/836C (13/27/27/27 entries, each zero-pad
  separated); span 0xb00fc..0xb0280 = 388 B = 52+4+108+4+108+4+108 exactly; spec 0,4,4,4.
- R22 clean-fleet (make clean + extract-all + check-all): 140 passed, 0 failed of 140.
  The incremental result was NOT trusted (§130). Fleet 93.25% fn-count / 89.2% instr /
  80.5% distinct; dedup 1905/0; 0 NON_MATCHING (G4).
- cookbook §132 + index (356 sections): the mechanism, the fingerprint (an undefined $L<n> in
  a LINK error is a truncated object, never codegen), the 30-second standalone-TU ladder that
  named the 4th table owner before any build, and the transferable rule — a fail-loud guard is
  only as trustworthy as the artifact hygiene around it.
2026-07-31 23:35:14 -06:00
Drew T 0958120006 fix(phase-30): jtbl_carve OVER-SPAN clamp — the one real instrument failure, root-caused and fixed; behemoth func_80191C50 (710 ins) banked
The last surviving "compiler wall" of the session turned out to be an off-by-one-word
carve. Chain, all byte-grounded:

SYMPTOM   jtbl_carve diverges on ov_SC06_018 after a BYTE-NEUTRAL jr_isolate_all.
SHAPE     image -3 bytes; 853 differing bytes in 699 scattered runs; **812 at byte 0 of a
          word** = the low byte of a 16-bit immediate, every sampled one changing by
          exactly -4. Not a shift, not a pad: hundreds of `%lo` operands 4 bytes low.
CAUSE     func_80191C50's own `sltiu $v0,$v1,0xC` names 12 entries; the emitted .rodata
          holds 12 words; the carve reserved 13. The 13th word (0x3038200A) is ordinary
          NON-ZERO data that spimdisasm ran into the dlabel span (the next dlabel sits
          past it). Reserve 13 / supply 12 => the .rodata piece UNDER-FILLS by one word
          => every later symbol slides down 4 bytes.
WHY MISSED The trailing-trim's axiom is "0x00000000 cannot be a jump target", so it only
          trims ZERO words. This surplus is non-zero, so nothing trimmed. The tool already
          treats `sltiu` as ground truth - it EXTENDS a table spimdisasm cut in half, and
          it WARNS when a span is too short - but had no clamp for a span too LONG.

FIX: an over-span clamp with the SAME authorization as the existing extension logic -
only when the `sltiu` bound is UNAMBIGUOUS (one bound; a multi-switch fn cannot say which
table owns which), and only when the surplus words are NOT plausible code addresses. If any
surplus word looks like a real entry it REFUSES LOUDLY rather than silently dropping one.

  jtbl_carve: jtbl_801D3BA4: clamped 1 trailing NON-ZERO word(s) - func_80191C50's own
  `sltiu 12` names 12 entries and the surplus is not code

Then: carve BYTE-IDENTICAL, draft spliced, **func_80191C50 (710 ins) BANKED**.
R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.

Retires the JTBL-CARVE-BREAKS-BYTES ledger class: the one instrument failure that survived
this morning's retraction round was REAL, and is now a named bug with a fix - not a wall.
2026-07-31 20:02:55 -06:00
Drew T f5af36c955 Revert "chore(phase-30): jtbl carve for func_8013BD74 in ov_SC01_077 (byte-neutral, R22 140/140)"
This reverts commit commit:1287.
2026-07-31 14:40:36 -06:00
Drew T fc08b12df6 chore(phase-30): jtbl carve for func_8013BD74 in ov_SC01_077 (byte-neutral, R22 140/140)
The SS81 chain's step 2, landed on its own so the bank can build on it: harvest_verify
refuses to run on a dirty tree (SS97 gate hygiene), and the carve necessarily dirties
config/. Carve applied -> byte-identical -> R22 140/140 -> committed. The draft bank
follows as its own step.
2026-07-31 14:37:47 -06:00
Drew T e1b0cd9acd feat(phase-30): jr cluster family func_8013C414 swept 137/137 (329 ins x137), R22 140/140
The second and last has_mid_jr family of the -O0 cluster. Same route as func_8013C0F8:
jtbl_family_bank (the SS81 carve chain per sibling) -> 137 BANKED / 0 failed.

Both jr families together: 274 members, 483 ins x137 = ~66k instructions, 0 failures.
Neither was bankable before commit:1270 routed the destination TUs to -O0.

R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
2026-07-31 11:36:31 -06:00
Drew T 5d1e2ac36f feat(phase-30): jr cluster family func_8013C0F8 swept 137/137 (154 ins x137), R22 140/140
The first of the two has_mid_jr families SS53 correctly refused from the carve-less
family_sweep. Routed through the tool its tier needs (jtbl_family_bank, the SS81 carve
chain per sibling) it banks CLEAN:

  jtbl_family_bank func_8013C0F8 ov_SC01_077 0x8013c0f8  -> 137 BANKED / 0 failed
  ~7s per sibling (carve + extract + remap + whole-binary gate), ~16 min total

Only possible now because commit:1270 routed each destination TU to -O0; before that the
member's home file compiled -O2 and no body could ever match there (SS116).

INDEPENDENT CONFIRMATION OF THE SS125 DIAGNOSIS: this is the SAME tool that returned
gate-fail on every group-B (func_8017BEBC) probe earlier today. 137/137 here versus 0/4
there, same jr machinery, is exactly what the carve-vs-body diagnostic concluded --
group B's failures are its BODY, not the carve. I had first blamed the carve; the
body-free probe corrected it, and this run corroborates the correction from the other side.

R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
2026-07-31 11:20:10 -06:00
Drew T b5362c7b7f feat(phase-30): the -O0 cluster HARVEST — 1,364 banks for ZERO agent tokens; fleet 92.71->93.09% fn / 88.3->88.6% instr / 78.7->79.3% distinct
The payoff of routing the cluster to -O0 (commit:1270). These functions were ALREADY
CRACKED in ov_SC01_077 and could not be banked anywhere else purely because every
destination file compiled -O2. With the destinations now -O0, they template in
deterministically -- no drafting, no agents.

  dedup_propagate --recover  0x8013C360 (h_exact x138)  -> 137 overlays byte-identical
  family_sweep --hseq        10 variant families         -> 1,227 banked / 133 failed (90%)
                                                            1360 staged across 136 groups
  ------------------------------------------------------------------------------------
  1,364 new banks

FLEET: fn-count 92.71 -> 93.09% · instr 88.3 -> 88.6% · distinct-code 78.7 -> 79.3%
(71,756 / 87,459 unique fns; +1,162 unique). dedup 1904 -> 1905 groups, 0 failed;
C1 coverage 240496/240496. 0 NON_MATCHING in any default build (G4).
R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.

--recover WAS LOAD-BEARING (SS75): without it dedup_propagate took its historical
all-or-nothing branch -- one failing overlay (the SOURCE, ov_SC01_077) dropped the whole
function and it printed "all candidates dropped", which reads exactly like a wall. Reading
the exclusion code instead of believing the message showed the remedy: --recover excludes
only that overlay (kept x1 with its own inline match) and propagates to the other 137.

The two has_mid_jr families in the cluster were REFUSED BY DESIGN, not attempted (SS53
interlock): 0x8013C0F8 (154 ins) and 0x8013C414 (329 ins), ~137 members each = ~466
members queued behind the jtbl carve path they actually need, rather than a fake 0% from
the wrong tool.

REMAINING in the cluster: the 133 sweep failures + the 2 jr families + the 3 addresses
never cracked anywhere (0x8013B83C, 0x8013BD74, 0x8013C08C) -- the last are genuine
drafting work, now finally possible since their TU is -O0.
2026-07-31 11:00:42 -06:00
Drew T b297c78193 feat(phase-30): T2 sweep — the -O0 cluster ROUTED fleet-wide (135 overlays, 2,200 stubs), R22 140/140
Applies tools/o0_subsplit.py across every overlay whose 0x8013B568..0x8013C98C -O0
cluster was trapped inside an -O2 jr split. This is the population the phase opened
against, and it has never been buildable-at--O0 before.

  135/135 sub-splits applied, 0 tool refusals
  140 new -O0 region files; corpus.o0_sources() 137 -> 277
  0 of them invisible to the -O0 oracle (verified explicitly -- a SILENT -O0 miss is
    the exact failure SS126 warns about: the region would compile -O2 and every residual
    it produced would be a pure artifact)
  **2,200 open stubs now live in a genuinely -O0 translation unit**

R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
make tools-health OK: corpus(+resident) 0 PHANTOM/0 TRUNCATED; cdecl ALL ORACLES GREEN;
audit-binaries 140 onboarded, every one a full citizen (R36); dedup-check 1904/0,
C1 coverage 240359/240359; cookbook-index 336 sections.

The transform is byte-neutral by construction (it only moves subseg boundaries and
repartitions source), so the whole batch was gated by one clean-fleet R22 rather than
135 individual builds -- after a single-overlay probe (ov_SC01_000) proved it (R37).

NOTE THE POPULATION CORRECTION (R14, mine): I earlier reported this cluster as "275 open
stubs across 18 overlays". That was WRONG -- the sizing scan ran while R22 was rebuilding
in the background, so corpus.stubs() raised for most overlays and my bare `except:
continue` SWALLOWED the very coverage assertion R32 exists to raise. The true figure is
2,184 open stubs across 138 overlays, which vindicates the T0(f) pin of "2,192 open
members" that I had called stale. The target list for this sweep was rebuilt with NO bare
except, so R32 can do its job.

Drafting fuel confirmed present: cached Ghidra-C seeds exist for the cluster's addresses.
Drafting the 2,200 is crack-wave work (T3), not T2.
2026-07-31 10:13:44 -06:00
Drew T d2b48b7680 feat(phase-30): tools/o0_subsplit.py — the T2 carve-within-a-carve driver; +3 banked in ov_SC03_015
Promotes the proven probe (commit:1266) into a real tool, and validates it FIRST-TRY on a
fresh overlay.

tools/o0_subsplit.py <ov> --lo <vram> --hi <vram>:
  - derives the range's contents from the SOURCE ANCHORS (overlay_src_split.parse_overlay_c:
    `asm` = unmatched stub, `define`/`def`/`nonmatch` = already matched), NEVER from an asm
    scan -- a matched fn emits no .s, which is exactly the blindness that made the range look
    like a clean contiguous run (SS126 / SS124's shape);
  - computes the -O0 bound as (address range MINUS already-matched bodies) and emits ONE
    sub-region per maximal run of unmatched anchors (K matched islands => K+1 regions);
  - names each `<ov>_o0<letter>` picking free suffixes, so the widened Makefile glob selects
    them; refuses loudly if it runs out or if the range spans >1 object or is already -O0;
  - honours the one-carve-per-region law (forces a cut at every already-banked jr in the
    object) and reuses jr_isolate_all's plan/build_new_config/ascending-unique validation
    verbatim, so carve-repoint + source-repartition stay on the proven path;
  - warns (does not refuse) when a stub in an -O0 run lacks the frame-pointer prologue --
    the byte-gate is the arbiter, not the heuristic.

VALIDATION on ov_SC03_015 (untouched by the manual probe): the tool independently derived the
SAME structure found by hand on ov_SC03_014 -- 2 matched -O2 islands (func_80184440,
func_801848E4), 2 -O0 regions (8 + 7 fns), same 5 cuts. Sub-split -> BYTE-IDENTICAL. Then 3
drafts, each global DERIVED FROM THAT OVERLAY'S OWN ASM (%hi operand) rather than copied:
3/3 match_one --o0 MATCH (22 ins), 3/3 through the whole-binary gate.

BANKED this commit: func_801846E4 / func_8018473C / func_80184794 in ov_SC03_015 (6 across
the two overlays now). The other 24 stubs in the region are undrafted -- the route makes them
DRAFTABLE (they were un-bankable at any effort before); drafting them is crack-wave work.

R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
cookbook SS126 (the address-range-is-not-an-optimization-region law + the probe ladder).
2026-07-31 08:41:54 -06:00
Drew T 45d26cc413 feat(phase-30): T2 carve-within-a-carve PROVEN end-to-end; Arm-A does not bite; 3 -O0 fns banked
T2's central unknown is resolved, and it is NOT what the phase plan predicted. The plan
named the Arm-A splat `%lo +0x20` defect as T2's real substance. Four probes, each
isolating ONE variable, SHA vs config/check from a clean tree (SS125 rules):

  1. sub-split the jr object at ARBITRARY addresses, pure -O2  -> BYTE-IDENTICAL
     ** the re-carve is NEUTRAL; Arm-A does not bite here **
  2. same split, middle region routed to -O0                   -> DIVERGED (2 vars at once)
  3. same NAME as probe 1, only the -O0 flag added              -> DIVERGED
     ** therefore the FLAG, not the subseg name **
  4. -O0 regions cut to EXCLUDE the matched bodies             -> BYTE-IDENTICAL
     ** route PROVEN end-to-end **

THE REAL OBSTACLE: an address range is not an optimization region. Interleaved among the
15 -O0 stubs at 0x80183CF0..0x80184920 are TWO already-MATCHED functions (func_80184440,
func_801848E4) that expand from engine_core.h and compile at -O2. Flipping the FILE to -O0
recompiles them too. Cut around them and it is byte-clean.

MY EARLIER "15 contiguous -O0 fns, clean cut" WAS AN UNDER-COUNT (R14 on myself): I derived
it by scanning asm/**/*.s for the frame-pointer prologue, and a MATCHED function emits no
.s -- so the scan was structurally blind to exactly the bodies that break the flip. Same
shape as SS124. Any T2 driver must derive -O0 bounds as (address range MINUS already-matched
bodies), never from an asm scan.

BANKED (3, whole-binary gate the sole arbiter): func_801846E4 + siblings func_8018473C /
func_80184794. Each global DERIVED FROM THE ASM (%hi/%lo operands), not taken from the
draft's comment; 3/3 match_one --o0 MATCH (22 ins) with a -O2 control showing the mismatch;
3/3 verified through harvest_verify; bank truth read from the SOURCE (SS55b trap 4).

MAKEFILE: the -O0 glob widened `ov_*_o0b.c` -> `ov_*_o0?.c` so ANY lettered -O0 sub-split is
covered by one rule. A MISSED rule is silent -- the region would compile -O2 and every
residual it produced would be a pure artifact (SS116). corpus.o0_sources() re-verified: 137
sources, resolves `?` via glob, both pre-existing rules intact.

CONFIG: ov_SC03_014_jr_8017EB7C sub-split into 5 regions (pre / _o0c / matched-O2 /
_o0d / post), reusing jr_isolate_all's plan+build_new_config+validation verbatim so the
carve-repoint and source-repartition semantics are the proven ones.

R22 CLEAN-FLEET: extract-all 139/139 (+main) ; check-all 140 passed, 0 failed of 140.
2026-07-31 08:36:42 -06:00
Drew T 8451f349c0 feat(phase-30): jr behemoth func_80181CDC (769 ins) banked via the SS81 carve chain; 2 byte-refused
The 3 jr behemoths carried from SESSION-27 (staged .run/beh-gate/), each run through
the SS81 3-step chain SERIALLY under tools/treelock.sh (every step calls `make extract`).
Re-derived the stub set on a QUIESCENT tree first: 4 of the 7 staged fns were already
banked in S27 and the staging dir still held all 7.

BANKED (1):
  ov_SC05_010 func_80181CDC (769 ins) — jr_isolate_all --only -> BYTE-IDENTICAL,
  jtbl_carve --func -> BYTE-IDENTICAL, then harvest_verify --chunk 1. Bank truth read
  from the SOURCE (INCLUDE_ASM absence), never the gate report (SS55b trap 4).

BYTE-REFUSED (2) — to the wall ledger WITH EVIDENCE, not forced, not labelled walls:
  ov_SC01_009 func_8018057C  — jr_isolate_all reported success (2 jr in 1 -O2 object,
    2 region .c) but the post-isolate build was NOT byte-identical. Aborted at step 1.
  ov_SC06_018 func_80191C50  — isolate clean; jtbl_carve reported success (48-piece
    carve set, 22 tail pieces) but the post-carve build was NOT byte-identical.
    Aborted at step 2.
  Both TOOLS claimed success; the whole-binary gate refused (G3/P9 doing its job).
  Neither is diagnosed, so neither is called a compiler wall — on this project that
  guess has been wrong four times running (R35). Drafts + logs preserved under .run/.

R22 CLEAN-FLEET (config changed => T2 blast radius => mandatory):
  extract-all: 139 extracted, 0 failed of 139 (+ main, serial)
  check-all:   140 passed,  0 failed of 140      <- BYTE-IDENTICAL

DRIVER DEFECT FIXED (mine, SS105/SS61): v1 of .run/s28_beh.sh `continue`d past a failed
gate WITHOUT reverting, leaving two half-carved overlays in the tree; only a full
`git checkout -- src/ config/` recovered it. Nothing was ever committed, so the cost was
build cycles and zero work. v2 reverts that target's config+src on EVERY failure path.
2026-07-31 08:03:08 -06:00
Drew T 0130fb340a feat(phase-30): wave-4 resumed 10/10 MATCH banked + h_exact leg (14 propagated); R22 140/140
The 12 agents killed by the usage-limit pause were resumed and ALL returned MATCH (2 had already
banked from their partial drafts, so 10 ran). h_exact propagation leg completed over all 112 banked
exemplars: 14 propagated, 42 benign skips (h_seq tier, correctly routed away per §123), 0 failures
— the 0x801466F0 'halt' was a third benign-refusal phrase, not a partial write.

fn-count 92.61 -> 92.67% | instr 88.2 -> 88.3% | distinct 70,581 -> 70,590 unique fns.
2026-07-31 07:25:47 -06:00
Drew T 471314da54 feat(phase-30): T3 waves 2+3 + 4 behemoths — 52 cores banked, 911 members propagated; R22 140/140
WAVE 3 (48 agents / 8 binaries, dealt across binaries so BANKING fans out): 48/48 match_one MATCH,
48/48 banked through 8 PARALLEL per-binary gates. WAVE 2: 15/19. BEHEMOTHS: 4 non-jr confirmed
(func_8017E120 884ins x14, func_8017FA5C 728, func_8017CAD4 755, func_8017E35C 719).
Tier-routed propagation (§123): family_sweep --hseq banked 911 members across 137 overlays.

fn-count 92.32 -> 92.59% | instr 87.9 -> 88.2% | distinct 78.3 -> 78.7% (70,506 unique fns)
R22 clean-fleet 140 passed / 0 failed, under one campaign lock (treelock.sh).

CORRECTION (R14): the 'per-binary bank-rate cliff' I reported from the pre-incident gate run
(SC03_014 1/6, SC04_018 1/6, SC06_018 2/6) was an ARTIFACT — those gates ran against a tree
propagation was concurrently rewriting. Re-gated clean: 6/6 everywhere. A measurement taken during
corruption is not a measurement; I should not have theorised a cause before re-running it.
2026-07-30 22:22:35 -06:00
Drew T 159317d3fe feat(phase-30): T3 wave-1 — 8 cores banked + 4 propagated fleet-wide; R22 140/140 (fn-count 92.00 -> 92.16%)
Ultracode wave of 14 agents over fresh reach-138 cores: 14/14 match_one MATCH, 8 accepted by the
whole-binary gate (the §52b law reproduced exactly). Propagated per-function (the incident fix):
0x8012E014, 0x80151C54, 0x8012F49C, 0x80151B98 -> +573 instances. R22 clean-fleet 140/140;
instr 87.5 -> 87.7%, fn-count 92.00 -> 92.16%, distinct 69,828 -> 69,836.

The other 4 banked cores are h_seq (PURE/IMM) families: dedup_propagate is h_exact-only, so its
'reach<2' / 'not self-contained' refusals were statements about the TOOL's tier, not the functions
-> cookbook §123 (the §53 carve-law generalized to the propagation-tier axis) + a routing table.
They bank via family_sweep --hseq next.
2026-07-30 19:57:54 -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 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