- re-baseline the target frontier from the fixed tools (R14): 228 ov077 stubs
(broken manifest saw 30); worklist/family-hseq regenerated. 68.6% instr / 49.2% distinct.
- R14 corrections to the audit handoff: the '~1,200 type-heavy' was already banked by A3h
(+2,675); the real remaining lever is 128 matched-sib families (~2.64M templ ins), and
register pins are NOT a banking blocker (func_8017A4AC banked ×134 with pins).
- Task 7 is near-miss CLOSING (close 1-5 re-gate = 0/13 through the fixed pipeline), not fresh crack.
- §52: single Fable5 on func_80178004 (regalloc-order class exemplar) = honest wall (P9),
structure-exact 163/165, residual = 3 compiler-internal register-alloc integers, likely
intrinsic; byte-verified 126/165. Correction: the 'pinned MATCH' was a myth (never matched).
6 byte-proven walker-family levers + the skeleton idiom transfer to the 11 regalloc-order
siblings (Fable5 discovers, cheap-Opus applies). decision-log entry (R31).
Closed the inserted half-phase. tooling-audit.md: DIAGNOSIS -> AUDIT-CLOSE LEDGER
(A1-A10 outcomes + the payoff 66.5->68.6% instr + remaining/handoff); the "two
rules" -> R32/R33/R34 crisp for P10 ratification at the Phase-26 PhaseEnd.
decision-log: the A10 wall-re-test verdict (R31 -- the broken tools WERE the walls;
the payoff was banked by the fixes; the closeness-0 residual is genuine; the real
deliverable is the 3 rules + the derived-oracle pattern). SETUP: the A9d-A10 tool
changes (R21). Cookbook §51 verified complete; LAW 3 tagged R34.
Observables green: final R22 clean-fleet 136/136 BYTE-IDENTICAL; make report EXIT 0
(dedup 1840/0, C1 227211/227211 signed, lint_symbol_refs wired + passing);
audit-corpus 0 slices; audit-cdecl green. Zero src/config changes this session.
Phase 26 resumes at Task 7 (fresh session).
FIRST CONSUMER MIGRATION onto the cdecl oracle — and the compiler taught me two things I had
wrong, one of which reopens a wall that has been closed since Phase 15.
1. cdecl.compatible() — "will cc1 accept these two declarations of one name?"
The predicate four tools each half-implement and get wrong: norm_sig / _norm_type collapse the
int family to ONE token, so a SIGNEDNESS change reads as "already compatible" and gets no
rewrite -- while cc1 REJECTS that redeclaration. Right about codegen, wrong about the front end,
which never reaches codegen.
2. THE ADJUDICATOR MUST BE THE COMPILER THAT COMPILES YOUR CODE (cookbook §51g LAW 9).
I wrote the rules from the C standard, then let a compiler judge. It contradicted me -- and then
the RIGHT compiler contradicted the first one. Three different answers:
declarations in one TU | standard | modern gcc | gcc-2.7.2 cc1
typedef int X; twice | error | ACCEPTS | ERROR
extern u16 X; + volatile u16 X| error | error | ACCEPTS
void X(s16); then void X(); | error | error | ACCEPTS
void X(); then void X(s16)| error | error | ERROR
--compat now adjudicates with tools/bin/gcc-2.7.2-psx/cc1, the front end that actually
arbitrates the build: 1,485/1,485 live corpus pairs agree, 0 disagree, 0 skipped.
3. THE PRIZE: the Phase-15 narrow-param wall rests on a false premise.
The no-prototype rule is ORDER-DEPENDENT. `void X(s16); void X();` COMPILES; only the reverse
fails. Phase 15 closed "the 159 arity/narrow-param conflicts" as "no clean deterministic fix --
it is simply C's default-promotion rule". cc1 does not enforce that rule in the direction the
wall assumed. Four three-line probes, 90 seconds, zero tokens. -> A10 RE-TEST TARGET.
Probe the compiler for FACTS; read its source only for LEVERS; byte-validate both. (We read
gcc-papermario for five phases believing it was 2.7.2. It was 2.8.1.)
4. THE MIGRATION: cast_call_sites canonicalized 95.1% of drafts against a TU that would never
compile them. `--src-file` is an OPTIONAL HAND-PASSED flag defaulting to src/<ov>/<ov>.c, and no
caller knows about the Phase-26 _jr_<ADDR> carves: ov_SC01_077 has 263 open stubs across 12 TUs
and only 13 are in the main .c -- while harvest_verify (A3) correctly splices into the real one.
Now DERIVED from corpus.stubs() (the INCLUDE_ASM line is self-describing), with the canonical map
derived from cdecl.tu_scope() (cpp -- so macro-injected DEFINE_func_* decls are finally visible).
Callee-conflict repair reach: 8 -> 58 of 196 drafts (7x).
5. AND THE NULL RESULT, REPORTED AS SUCH (P9/R14). Those 58 banked ZERO functions. The historical
draft tail fails on CODEGEN, not plumbing -- func_801387B8, which the audit blames on a single
unparsed `[4]`, is really 67/100 instructions off with a $s0/$s1 swap (that claim does not
reproduce on today's tree). The real gain is narrower and still worth having: 52 drafts moved
from "won't compile" to "compiles, N instructions off" -- from an INVISIBLE failure that reads as
a compiler wall into a SCORED near-miss the permuter and the §47/§48 dials can act on. That is
the audit's thesis, not a bank. THREE times in one session a confirmed mechanism produced a null
consequence.
Also: my own new audit printed "ALL ORACLES GREEN" while silently skipping 100% of its corpus (a
missing -Isrc). The exact bug class, in the tool written to hunt it. An unadjudicable check is not
a passed check.
R22 clean-fleet: make clean + extract-all + check-all -> 136 passed, 0 failed of 136
src/ untouched (0 changes) make audit-cdecl: green --compat: 1485/1485
NEXT: sig_unify + reconcile_decls carry the SAME wrong-TU bug (same --src-file flag).
Fifteen tools each carried their own regex model of "what is a C declaration", and they
disagreed — two tools in ONE pipeline disagree today about whether `extern s32 D_a, D_b;`
is a declaration at all. All fifteen shared one character class,
extern\s+([A-Za-z_][\w\s\*]*?\bD_[0-9A-Fa-f]+\s*(?:\[\s*\])?)\s*;
which cannot hold '(', ',', or a non-empty [N] — so three whole shapes were invisible to
every one of them: fn-ptr/jump-table arrays, sized arrays (one unparsed `[4]` has blocked
func_801387B8 in 134 TUs), and multi-declarators (the WHOLE line dropped, not just #2..N).
REJECTED the audit's own prescription (a shape-aware alternation per tool, ~15 coordinated
regex edits) on R33 grounds: fifteen hand-maintained models are exactly what diverged, and
an alternation only ever covers the shapes somebody remembered. The thing being scanned HAS
A GRAMMAR. C's declarator grammar is small, closed and TOTAL — it describes fn-ptr arrays,
sized/2-D arrays, multi-declarators, fn-ptr params and K&R identifier-lists without being
told they exist. ~250 lines of recursive descent: LESS code than the regexes it deletes, and
exhaustive by construction rather than by memory. (decision-log 2026-07-14.)
Two statement paths, because the inputs genuinely differ:
* tu_statements() - a TU's file scope, derived from cpp. A decl inside a DEFINE_func_*
macro body declares NOTHING until the macro is invoked (the §8c law);
a raw scan is wrong in both directions. cpp answers it exactly, in
54 ms/TU (~20 s for the fleet, cacheable).
* split_statements() - span-preserving raw split, for drafts (which get rewritten).
THREE ORACLES, whole corpus — a measurement, not a belief:
* coverage 2,952,246 depth-0 statements -> 2,731,521 declarators, 0 PARSER DEFECTS
* the real gcc 50,405 distinct declarations compiled beside this parser's reconstruction
of each one -> 0 REJECTED
* differential 0 file-scope symbols the incumbents see that cdecl misses; 26 in
engine_core.h they cannot see; 6 they wrongly promote from BLOCK scope
Two ideas worth keeping (cookbook §51g, LAWS 4-8):
* THE CANDIDATE SET IS DERIVED TOO (R33 applied to R32). At file scope C admits nothing but
declarations, so R32's over-approximating detector is *every depth-0 statement* — supplied
by the grammar, with no hand-maintained candidate regex to rot.
* GCC ADJUDICATES MY OWN COVERAGE GAP. Deciding for myself which failures "don't count" is
grading my own homework — the habit that wrote the fifteen bugs. A statement gcc ALSO
rejects is not C (my rejection is correct, the INPUT is corrupt); one gcc ACCEPTS and I do
not is MY defect. All 33 residual: NOT-C, all dead .run/drafts* scratch, none in src/.
NEW findings (docs/tooling-audit.md):
* reconcile_decls.DATA_DECL_LINE_RE finds ZERO decls in engine_core.h — it is line-anchored
and every decl there ends in a '\'. Its "authoritative tier" has ALWAYS been empty.
* gen_harvest_targets + sig_unify count BLOCK-SCOPE externs (6, byte-proven inside a macro's
function body) as file-scope canonicals — the §8d `conflicting types` confusion.
* tu_ambient's func regex ([^()]* params) drops ANY callee with a fn-ptr parameter.
* R14 near-miss: 33 drafts contain `extern if ((func_80029178(0x119) & 0xFF) != 0);`, written
by a RECOVERY TOOL — but the source bug was already fixed in Phase 19 (0 garbage / 300 sigs
today). Mechanism confirmed, consequence nil. Note what it cost while live: a draft that
cannot compile fails the byte-gate and reads downstream as an INTRINSIC COMPILER WALL.
Bugs the oracles caught in ME (and would otherwise have shipped): `extern s32 (*D_801274D0)(s32);`
parsed the BASE TYPE as the name; a K&R declaration-list flushes as SEVERAL spans, so the body
attached to the wrong one and leaked the K&R parameter names into file scope as fake globals.
SCOPE, deliberate: NO consumer is migrated here, so this cannot move a byte. The audit warns
that making the parser see more ARMS dormant transforms (reconcile_decls.data_access_subs would
mangle `D_1[i]()` -> `((u8 *)D_1)[i]()` the moment fn-ptr decls become visible to it). Migration
is one tool at a time, each byte-gated.
R22 clean-fleet: make clean + extract-all + check-all -> 136 passed, 0 failed of 136
make audit-corpus: 0 PHANTOM + 0 TRUNCATED make audit-cdecl: ALL ORACLES GREEN (new gate)
R22 CLEAN-FLEET: make clean -> extract 136 -> build 136 -> check-all = 136 PASSED, 0 FAILED.
make audit-corpus: 0 PHANTOM + 0 TRUNCATED (was 193).
=== A4: a CORPUS defect the byte-gate could never have caught ===
config/symbols.us.txt:981 declared `listCdBuffer = 0x80180000` — a correct Phase-3 name for MAIN's
LIST.CD RAM buffer. But that address is OUTSIDE main's image and INSIDE the overlay slot, and every
overlay's splat config stacks symbols.us.txt. High RAM is REUSED: an address that is a buffer to main
is live CODE to an overlay. So splat saw a symbol boundary mid-code and, across 97 of 134 overlays:
* CUT 97 REAL FUNCTIONS IN HALF (a head ending on a `lui`, no return), and
* INVENTED 96 PHANTOM ONES (a tail beginning by reading the assembler temp $at).
193 slices NOBODY COULD EVER MATCH — not "hard", not "a compiler wall": unmatchable by construction.
They sat in the harvest queue as ordinary work, so agents would burn on them forever and the failures
would be filed as intrinsic compiler residuals.
The phantom listCdBuffer.s in ov_SC01_005 literally begins:
lw $ra, 0x10($sp) / addiu $sp, $sp, 0x18 / jr $ra
splat cut a function immediately before its EPILOGUE and called the epilogue a function.
AND IT HAD ALREADY CONTAMINATED REAL WORK: in ov_SC03_031 the cut landed where the epilogue was
exactly `jr $ra; nop`, so the Phase-26 x134 sweep innocently BANKED the phantom as
`void listCdBuffer(void) {}` — byte-correct, gate-green, entirely fictitious — while leaving
func_8017FFC4 permanently unmatchable. Removed.
WHY NO GATE CAUGHT IT, AND WHY THAT IS THE POINT: INCLUDE_ASM pastes the two .s halves back VERBATIM
in original order, so the image is byte-identical either way. The byte-gate was green the whole time
and always would have been. It is a perfect CORRECTNESS oracle and a NULL COVERAGE oracle. No
assertion added INSIDE it could ever have found this. What found it was a SECOND, INDEPENDENT oracle:
tools/sig_image.py derives boundaries from the ORIGINAL bytes without splat, and DISAGREED with the
corpus (58,524/58,621 agreement with spimdisasm; correct on all 97 disagreements).
=> When one oracle is structurally blind to a class of error, the answer is not a better assertion
inside it. It is a SECOND ORACLE THAT CAN DISAGREE WITH IT. (`make audit-corpus` is now that.)
THE RULE (the mirror of R13/R15, never written down): a symbol whose address falls inside ANOTHER
binary's vram window must never enter that binary's symbol stack.
FIX: config/symbols.us.ram.txt — main-scoped symbols outside main's image — stacked ONLY by
config/splat.us.exe.yaml. Main keeps the name it needs (10 %hi / 11 %lo refs; 143dbb89 byte-identical);
the overlays never see it. Exactly one symbol was in scope fleet-wide; the resident window was clean.
AND A REAL FUNCTION THE ACCIDENT WAS HIDING: in ov_SC01_084 / ov_SC02_041 / ov_SC03_094 / ov_SC06_008
there IS a genuine function at 0x80180000 (111 / 35 / 28 / 74 ins), reachable ONLY via a fn-pointer
table (.word func_80180000) and never by `jal` — so splat cannot find it and needs the boundary
DECLARED. listCdBuffer had been supplying it by luck. Now declared honestly, per-overlay, in
config/symbols.<ov>.txt — exactly where R13/R15 says an overlay-scoped symbol belongs.
=== A5: the closeness oracle every crack agent trusts was lying on 155 functions ===
masked_diff._reloc_kind() knew 26/HI16/LO16. An over-approximating sweep of every reloc objdump emits
across all 3,367 build objects found FOUR: R_MIPS_26, HI16, LO16 — and R_MIPS_PC16 (211). PC16 fell
through to a FULL-WORD compare, but the object holds an UNRESOLVED PLACEHOLDER in the branch
displacement, so that compare can NEVER succeed.
DECISIVE TEST (derived from the invariant, not from reading the regex): INCLUDE_ASM pastes the
ORIGINAL asm, so for every stub diff_object_s() MUST be 0. Measured, coverage-asserted:
2,741 functions scored — old mask: 150 LIES; PC16 masked: 4 LIES.
(The 4 survivors are the separate length-delta defect.) A phantom non-zero sends an agent to grind at
a wall that is not there, and the wasted attempt is then booked as a MATCHING failure, feeding
reserved_walls() and PERMANENTLY BLACKLISTING a function that was never broken.
=== NEW FINDING (found by cutting the R22 corner): a STALE OBJECT CAN PRODUCE A FALSE PASS ===
`.o <- .s` is not a dependency make can see: assembly arrives via INCLUDE_ASM, expanded to a `.include`
consumed by maspsx/as AFTER cpp, while -MMD tracks headers only. Re-extract, build incrementally, and
make links a STALE object. This is not merely slow — INCLUDE_ASM pastes the ORIGINAL bytes, so a stale
object still yields the original image: SHA1 GOES GREEN while the split just changed is never exercised.
A broken config change can be "verified" by an incremental build. Live proof: 8 of 136 binaries linked
stale objects here; they failed LOUDLY ONLY BY LUCK (the dead symbol was an undefined reference) — a
merely-different-but-valid split would have gone green on all 136.
R22/H3 already legislate this, and I broke them. But a rule that needs a human to remember it is not a
gate. FIX: `extract` now invalidates the objects that include what it just rewrote (main's are top-level,
so -maxdepth 1 — verified it cannot clobber the other 1,605 objects). Structural, not advisory.
R14 self-catch, recorded: my first A5 test passed `fn=` to diff_object_s(), which takes two args; the
TypeError was swallowed by my own `except Exception: continue` and it reported 0 scored / 0 lies. I
wrote the exact bug I was auditing, inside the test for it. Caught only because 0 looked wrong. The
test now asserts its own coverage.
38 agents / 2.24M tok / 0 err. 32 findings raised -> 28 SURVIVED adversarial verification
(4 REFUTED, 16 downgraded). 40 scanners measured CLEAN. Full write-up: docs/tooling-audit.md ROUND 2.
THE ROOT CAUSE — one bug, ~10 times: a hand-maintained model of the corpus layout (a file
allowlist, a single-.c assumption, a func_-only regex, a REGION_SUB dict) sitting on top of a
filesystem that already answers the question. Every TU split silently widened it.
DECAY PROVEN: .run/fuel_manifest.json (Jul 8) recorded 130 stubs; the same tool today returns 30.
The Phase-26 splits moved ~100 stubs out from under a dict literal last edited in Phase 22 — and
nobody noticed, because an un-nominated target produces SILENCE, not an error.
MEASURED: 91.6% of ALL remaining project gain is invisible to target selection (true 994,633 ins;
the manifest sees 83,305). 117 of 127 reach-134 fns never nominated. harvest_verify cannot see
56,742 of 58,717 (96.6%) open stubs. wave_targets hands 78 of 87 targets a nonexistent asm path.
THREE RESULTS OVERTURN SETTLED CONCLUSIONS:
1. Phase-22's 'the permuter's fuel is exhausted' is UNSAFE. grinder banks through harvest_verify,
which sees ONE TU — 1,290 of its own 1,298 queued fns live in another. 99% could never have
banked. '0 banks since Phase 21' is equally consistent with 'the tool could not bank'.
2. The Phase-25/26 endgame plan is MAJORITY-FICTION. family-manifest.md advertises 2,758
multi-member families / 11.0 MB; 1,071 of them / 6.80 MB (62% of the byte-weight) are ALREADY
FULLY MATCHED. The ranking — the file's whole purpose — is sorted mostly on dead work.
3. A CORPUS defect the byte-gate is structurally blind to: symbols.us.txt:981 puts a main-EXE DATA
symbol (listCdBuffer = 0x80180000) into every overlay's symbol stack, but in overlay space that
address is CODE. splat cuts 97 real functions in half and invents 96 phantom ones = 193 slices
NOBODY CAN EVER MATCH, in 97 of 134 overlays — and the build stays byte-identical and green,
because the .s halves are pasted back verbatim. A perfect correctness oracle, a null coverage
oracle. What saved us: sig_image was RIGHT (58,524/58,621 vs spimdisasm; correct on all 97
disagreements). A SECOND INDEPENDENT ORACLE is the only reason it was visible at all.
FIX RESTRUCTURED around the root cause: ONE derived corpus oracle (A3) + ~10 DELETED scanners —
not ten fixed regexes. Plus the listCdBuffer corpus fix (A4) and the closeness oracle (A5, which
lies on 155 functions, feeding false walls into reserved_walls()).
decision-log (R31): the why, and the design lesson — a derived fact cannot rot; a hand-maintained
copy of it is a liability that grows with every structural change. We had no instrument that could
report ABSENCE: every gate we owned answered 'is this right?', none answered 'is this all?'
- Drew (2026-07-14, gate 1): run the audit inside Phase 26, then resume at Task 7.
Declined the alternative (close Phase 26 early on an unmet milestone -> Phase 27):
the audit is a PREREQUISITE to structural completion, not a successor to it — the
tooling that MEASURES the milestone is the thing at fault. Phase-3.5 precedent.
- CURRENT_PHASE.md: the Phase 26-A block (A0-A11), built FROM docs/tooling-audit.md
(40 measured findings), R33-before-R32 ordering — the best outcome is a DELETED
scanner, not a fixed regex.
- decision-log (R31): the why, the structural blind spot (a scanner extracts N, the
true count is M > N, and nobody ever compared N to M — the byte-gate is a perfect
CORRECTNESS oracle and a NULL COVERAGE oracle), and A1's first finding.
- harness task list built (R28).
The largest unmatched core in the game, walled at close=2 for the permuter (25 min, no close) and
queued for a gdb-on-cc1 read. Closed WITHOUT gdb — the RTL dumps were the oracle:
- THE TIE, byte-measured (.lreg): &g.sz1 pseudo 228 refs 13 / live_length 783; &g.sz2 pseudo 230
refs 13 / 782 -> pri = int(390000/L) = 498 == 498, an exact int-truncation tie in global.c:594
allocno_compare. Tie-break = creation order -> allocation follows emission; the target needs them
to DIFFER (allocation sz2-first, emission sz1-first). The shipped operand-permutation workaround
could only pick one (close=2 vs close=10).
- THE FIX (§47): restore NATURAL operand order (emission correct) + ONE zero-byte
`__asm__ volatile ("")` placed BETWEEN two existing GTE volatile asms (no new cse/sched barrier —
one is already there) -> +1 static insn at global-alloc time -> L 784/783 -> pri 497 vs 498 ->
the tie SPLITS toward the shorter-lived (later-created) pseudo, which is ALWAYS the direction
"allocation != creation" requires. All 10 grants cascade; MATCH 952/952 first try; the slider
emits only #APP/#NO_APP (zero bytes). PIN-FREE, ×113 template-safe.
- BANKED ×1 in ov_SC01_000 through the WHOLE-BINARY gate (jr fn — match_one is not the arbiter,
§8a): lazy isolation -> new region ov_SC01_000_jr_8017BEBC + 9-piece jtbl interleave -> splice ->
BYTE-IDENTICAL. One TU-visible decl reconcile en route (D_800B9A02: declare the TU's `short`,
force the unsigned halfword at use `(*(u16*)&D_800B9A02)` — §8d sub-class (b)).
- R22 clean-fleet 136/136 BYTE-IDENTICAL; 0 NON_MATCHING (G4). The ×113 sibling sweep is IMM-class
(scattered addresses) -> Task-8 mechanical work via the imm engine.
- cookbook §47 (the slider method + the placement rule + the direction law); decision-log (R31).
- ROOT CAUSE (R14 — the session-7 diagnosis was half right): the isolated region builds [ OK ]
WITHOUT the body, so §8b isolation was never implicated. `family_remap.gather_externs` prepends
carried decls at FILE scope; D_801812A4 is a fn-ptr dispatch table the sibling declares FOUR
incompatible ways at BLOCK scope inside its own later functions, so the carried file-scope decl
ESTABLISHES A GLOBAL THE TU NEVER HAD and every later block-scope extern must now agree with it.
Byte-proven asymmetry: BLOCK(int)->BLOCK(struct*)->FILE(void*) builds; FILE(void*)->BLOCK(int)
errors. It was the ONLY hard error in the build — all 27 carried function externs were fine raw.
- THE FIX (demote, don't reconcile): tools/scope_data_externs.py emits a carried D_ extern at BLOCK
scope inside the function body when the TU has no file-scope decl of it above the insertion point.
Byte-neutral (an extern emits no code; type + access opcodes unchanged) and never worse than raw,
so it needs no oracle, no type comparator, no fn-ptr parser. Restores fidelity — the original
declares these symbols at block scope in exactly this way. Wired into jtbl_family_bank as the
`scoped` stage: raw -> scoped -> recovered -> reconciled (scoped is the base for the later stages).
- reconcile_decls is the WRONG instrument for this class, twice: its oracle answers "what does the
FLEET call this symbol" when the question is "what can THIS TU see", and its DATA_DECL_LINE_RE
cannot parse `extern void (*D_x[])(void *);` — silently skipping the very symbols that were
failing (the phase's third silent-skip bug, after find_site braces + overlay_files splits).
- R17 TRIAGE RULE, first real test, held: `conflicting types` = the compiler REFUSED TO COMPILE =
a C front-end diagnostic = our Python. Reading cse.c/global.c would have taught nothing.
- RESULT: func_8015AE2C (562 ins, reach 134) swept 133/133 siblings, 0 failures. R22 clean-fleet
136/136 BYTE-IDENTICAL (534 changed src files); dedup-check 1813 validated / 0 failed; 0
NON_MATCHING (G4). instr-weighted 63.0 -> 63.6%; distinct-code 39.1 -> 40.5% (+256 unique fns /
+79,957 ins) — one core, ~0 agent tokens.
- knowledge captured during the producing session (R30/R31/R21): cookbook §8d, decision-log
2026-07-13 session 8, SETUP tool-inventory row; CURRENT_PHASE session-8 checkpoint.
The full 54-jr isolate-all on ov_SC01_077 now builds d19c9580 BYTE-IDENTICAL
(R22 clean-fleet 136/136) — the configuration session 5 could not build. The
heavy-jr harvest (191 cores / 5.53M templatable ins) is unblocked.
- R14 CORRECTION: session-5's "gcc-2.7.2 block-scope-extern TU-persistence" root
cause was WRONG. There is no gcc quirk — DEFINE_func_* macros expand at FILE
scope, so their leading externs are genuine file-scope decls that merely live in
engine_core.h, invisible to any col-0 .c scan (1377 macros / 3929 lines / 1462 syms).
- REJECTED the approved "global symbol->type map + shadow set" design: the engine is
loosely typed (func_80173544 is DEFINED `s32 f(void*)` yet declared `extern void
f(void);` inside func_801734BC's body), so declaring every USED symbol hoists that
block-scope shadow to file scope and CREATES the conflict a shadow-set then dodges.
Instead reconstruct the original TU's file-scope decl environment and carry it
strictly FORWARD — conflict-free by construction (every carried decl already
coexisted with every definition in the one original TU; compatibility is
order-symmetric; shadows stay in bodies and travel with their item).
- The byte-gate found two MORE lost decl sources, not predicted: (a) a definition is
itself a declaration for everything below it in its TU (func_8012B2CC undeclared);
(b) file-local typedefs used by a carried prototype (parse error, Vec3s). K&R defs
must render `extern T f();` (unprototyped), never f(void).
- LAZY per-core isolation wired into jtbl_family_bank (Drew's call — upfront-x134 =
~7,200 region files): jtbl_carve NON-CONTIGUOUS fail-loud -> jr_isolate_all --only
<core> -> re-extract -> re-carve. Proven on func_80178D40 (890x134, heaviest core):
carve blocked -> isolated (byte-neutral d19c9580) -> carve in its own subseg.
- TWO LATENT BUGS fixed (both would have corrupted the heavy sweeps):
* jtbl_carve.func_subseg derived the owning subseg from the ASM TREE, which `make
extract` never prunes -> after an isolation it returned the STALE owner and
silently re-created the very collision the isolation removed. Now config-derived.
* jtbl_family_bank/jtbl_carve revert() DELETED the shared overlays.mk carve var
unconditionally -> would destroy a COMMITTED carve (all 134 overlays have one) on
any failed sibling. Now restored to its committed value; only region files created
by this attempt are removed; dirty-tree preflight refuses to start a sweep.
- docs: cookbook §8b RESOLVED + new §8c "splitting a TU means rebuilding its
DECLARATION ENVIRONMENT, not moving text"; decision-log 2026-07-13 (R30/R31).
- parser selftest 404/404; R22 clean-fleet 136/136; 0 NON_MATCHING (G4).
- Drew's sequencing (agreed): do the 45 small jr families FIRST — not for byte-weight (~+1% instr,
129K ins) but to de-risk + harden the §8 x134 pipeline before the heavy Fable5 cores bet on it.
- decisive technical reason: jtbl_carve only built the single-jtbl carve; func_8012ACE0 is now
matched in all 133 siblings, so family #2 forces the multi-jtbl address-ordered `ld_interleave
--order` carve -> build & prove it on cheap 30-ins targets first. Also needs no Fable5.
- guardrail kept explicit: small tier = MEANS (harden pipeline + build multi-jtbl), NOT the
objective; the 191 heavy jr families (5.53M ins) remain THE byte-weight target -> pivot after.
- CURRENT_PHASE.md SESSION-3 checkpoint updated to Stage 1 (small + build multi-jtbl) -> Stage 2
(heavy 191, Fable5 un-paused). decision-log addendum with the forcing-function wiki lesson.
- decision-log (R31): §8 unblocked the SINGLE heaviest byte-weight chunk of the game — 9 of
the 10 heaviest unmatched family cores are switch (jr) functions (func_80178D40 890x134 =
477K ins alone); jr substantial = 191 fams / 5.53M templatable ins. My "45 small jr families"
recommendation (129K ins) was a light-tail trap — Drew caught it against the endgame plan
(heaviest-byte-weight-first). Corrected next play: Fable5 crack the heavy jr cores -> §8 x134
bank -> parallel R22 verify; needs Task 7 (Fable5) un-paused (§8 makes that worth it now).
- CURRENT_PHASE.md: SESSION-3 checkpoint as the fresh-session resume point (4 commits this
session: tiny-band commit:0531, §8 PoC commit:0532, §8 x134 commit:0533, R22 parallel commit:0534;
distinct-code 30.3->39.1%, instr-weighted 58.2->63.0%, R22 now ~50s)
- +266 member-matches: func_8015CD20/func_8015C128 templated x133 via --reconcile-raw (each SHA-gated
per-overlay vs config/check.<ov>.sha = byte-identical, G3). Full R22 deferred until func_80176218
releases asm/ (established per-overlay-gate + deferred-R22 pattern, as the committed 463 which R22'd 136/136).
- family_sweep: --reconcile-raw now also covers draft-ov077 (unbanked) cracks (template from the RAW seed).
- P9 CORRECTION + decision-log 2026-07-12: the 2 Fable5 cracks rtu_match-MATCH but FAIL the whole-binary
gate (both jr-functions; rtu_match masks relocs + excludes neutralized INCLUDE_ASM rodata, so it never
verifies the §8 jtbl rodata). TWO harvest gaps: §8 jtbl-rodata (blocks all jr cracks) + reconcile
data-extern (D_801891B8-class, blocks ~15/21 no-jtbl triage cracks). 6 no-jtbl reconcile-clean cracks
bank whole-binary (729 members). rtu_match is NOT a sufficient arbiter for jr-functions.
- pre-Fable5-window de-risk (Drew): validate reconcile→gate→template on the triage cracks before the window.
- reconcile→bank WORKS: raw 0/23 (§41 def-side wall) -> canon_sig_reconcile v3.2 -> 4/15 banked into ov077
(func_801506A4/8016A73C/80167540/80155800), byte-identical, R22 clean-fleet 136/136.
- templating a RECONCILED body x133 FAILS 0/4: reconciled bodies are ov077-TU-specific (canonical-sig casts
+ collision-renames) -> need per-sibling re-reconcile (§41c). Task-8 prerequisite: port the h_norm
--reconcile M2 path into hseq_sweep so the type-using families (triage cracks + the 61 Fable5 cores) can
template x134. PURE families already template plainly (Task 5: 399 banked).
- decision-log 2026-07-11: the slice paid for itself — found the templating gap BEFORE spending the window.
Paused before building the wiring per Drew.
- remap_hseq.gather_externs: carry file-scope externs for body-referenced symbols (extract_unit only
grabbed adjacent ones) — the decl class that blocked per-location bodies indexing a global. func_8015F118
gate-fail -> BYTE-IDENTICAL; the 3 tracker-miss PURE families then bank 133/133 each.
- ran the real whole-binary byte-gate on the 29 substantial matched-exemplar families:
532 members BANKED (byte-gated). Per-family: 3 tracker-miss PURE (0x8015d5e8/0x8015f118/0x801407f4)
bank 100% x133 = 399 byte-perfect (the tracker-fix free win); 1 cross-addr family 50%; 9 zero-bank
families are type-using (Work8016/Prim/...) -> the existing --reconcile/type-lift follow-on (Task 8);
16 families pinned -> Task 7 pin-free re-crack.
- VERDICT: the h_seq machinery (tracker + imm + cross-address + extern-carry) is byte-proven 100% correct
on clean families. GO to scale.
- R22 clean-fleet: make clean + extract-all-136 + check-all = 136 passed, 0 failed. 0 NON_MATCHING (G4).
Metrics: distinct-code 30.3->30.9% (+375 fns), instr-weighted 58.2->58.5%.
- decision-log 2026-07-11 (R31: stratify a mechanical-harvest rate by family/class before judging it).
- family_sweep.py: new --edit-remap MANIFEST mode (§42e) — per family, symbol-remap the
split-scope //@EDIT old||new per sibling + apply once-global engine_core.h ec_edits
(byte-neutral), stage the family_remap body, gate via harvest_verify (the sole arbiter)
- BANKED 266/266 (0 failed): func_80136824 + func_80136334 (array-decay ptr-flip) ×133
siblings each — full ×134. R22 clean-fleet 136/136, fleet 74.40% -> 74.48%, dedup 1813/0
- R14 FINDING (cookbook §42e addendum + decision-log): the other 4 byte-drift families
(func_80133AB0 zero-reg pin, func_8016DF5C/8013D9B0 GTE-pin, func_80156044 trampoline)
cc1-SIGABRT (Error 134) in the SIBLING TU — hand pins are ov077-TU-context-specific,
NOT mechanically ×134-recoverable; backlogged as ×1/permuter fuel. rtu_match/match_one
are blind here (neutralized/isolation compiles crash too); only make build is truth
- 0 NON_MATCHING in any default build (G4)
- R14 CORRECTION of the prior "family_remap limitation" call: it was a MISDIAGNOSIS. family_remap
succeeds on all droppers; the "remap-fail" family_sweep reports was a mislabeled canon_sig_reconcile
throw ("no definition of func_X found in draft") — the def-finder regex required a leading \n, so a
//@EDIT-stripped raw draft with the fn definition on line 1 was not found.
- FIX: def-finder regex \n -> (?:^|\n) (also match a def at draft start; strictly additive, low-risk).
- Recovered func_8014FE60 fully: 133/133 siblings banked (fix + engine_core.h DEFINE_func_8014FDF4
extern void->s32 global flip, byte-neutral fleet-wide; caller discards return).
- Residual (the genuine, small --edit-remap): func_8016DF5C/80136334/8013D9B0/80156044 reconcile but
byte-drift per sibling (out-of-body fixes: pointer //@EDIT, no-proto, return-flip not carried per sibling).
- cookbook §42e (the two-layer diagnosis + the forward ×134-leverage-realism rule); decision-log corrected.
- R22 clean-fleet 136/136 BYTE-IDENTICAL from a fully clean tree; NON_MATCHING 7 (0 in default build, G4).
- A/B gate-true: v4 <= v3 (marginally worse on medium, tie easy/hard) -> discard v4, keep v3 (frozen ceiling)
- 'corpus quality > size' confirmed; 7B is capacity-bound (0/5 even on trained fns, not truncation)
- decision-log (R31): the local-7B tier is off the endgame critical path; engine = frontier-crack -> deterministic-propagate -> byte-gate + permuter
- gen2-mips-matching-model: the A/B + the maxlen-2048 truncation flaw (drop-over-length OR grad-checkpointing, NOT CPU offload) for any future retrain
- CURRENT_PHASE: T4 done/failed; NEXT = T5 (Ultracode measure-wave); phase OPEN, no PhaseEnd