The largest target on the T14 census: 138 members x 289 ins = 39,882 templated instructions at stake.
conform_decls --check showed 418 sites in 3 forms, pointer-type-only with NO narrowing warning and no
return-type change — the func_80179B74 shape that banked 137/137. Applied (418 sites / 414 files),
gated (jtbl carve succeeded first try), R22 clean-fleet 140 passed / 0 failed of 140.
Committed BEFORE the family bank because jtbl_family_bank refuses to run on a dirty tree — its
per-sibling revert restores from HEAD, so an uncommitted axis would be destroyed. The tool enforcing
H4 in code, correctly.
Also recorded: family_sweep REFUSED this family rather than returning 0/137 — func_80135EB0 is
has_mid_jr, and §53's carve law says a carve-less sweep there yields "a 0% that is a TOOL artifact,
not a wall". That refusal is the good version of today's pattern: every false wall untangled this
session (func_8016B6BC 0/137, the 4 fabricated CC1-FAILs, Phase-28's B2 0/8) came from a tool that
ANSWERED instead of refusing.
BANKED 273 member-matches / 0 failed across 137 overlays (func_8014CF04 136 + func_8015D1B8 137).
R22 clean-fleet 140 passed / 0 failed of 140; report fail-closed green (dedup 1886/0, C1 coverage
239604/239604, 0 NON_MATCHING).
MEASURED from the committed digests, not projected: fn-count 317,898 -> 318,171 (+273);
instr-weighted 83.2 -> 83.4% (+26,770 ins); distinct-code 72.3 -> 72.5% (+129 unique fns).
A USEFUL NEGATIVE RESULT: both families swept cleanly across ov_SC07_006/007/010/011 — the same four
overlays that refused func_80176218's sweep earlier today. So that set is not broken; the 4/137
refusal is family-specific (the _jr_8016AE5C.c carve), which is the per-sibling INTEGRATION signal
§59 describes rather than a codegen or overlay-level wall. Carried, still not concluded.
SESSION-22 total: 3 exemplars + 406 members = 409 functions.
THE BANK: the T14 PLUMBING census showed func_8014CF04 blocking THREE drafts at once. Conforming its
decl axis banked func_8014CF04 + func_8015D1B8 (func_80135260 is a genuine DIFF, agreeing with its
independent SESSION-21 diagnosis). R22 clean-fleet 140/140; report fail-closed green (dedup 1886/0,
0 NON_MATCHING). fn-count 317,896 -> 317,898; distinct 66,110 -> 66,111.
BUT THE AXIS WAS A 1,748-FILE T2 WRITE SET (the --check per-form counts read "1"), and R22 came back
139/140 -- TWICE -- on a change the per-binary gate called BYTE-IDENTICAL. Three defects (§98):
1. THE REGEX CROSSED NEWLINES. `[^;]*` matches '\n', so a match starting at a DEFINITION line ran
past the `{` to the first `;`, swallowing `s32 func_8014CF04(...) {` PLUS the register pin on the
next line and replacing both with a prototype -> undefined reference. Fixed to `[^;{\n]*`: a
definition is now unmatchable by construction.
2. IT REWROTE INSIDE COMMENTS (H5, 3 lines). Now scans cdecl._mask() and rewrites by SPAN (R33 --
that length-preserving primitive already existed for exactly this).
3. THE REAL CAUSE -- IT ASSUMED ONE SIGNATURE FITS THE FLEET. ov_SC07_006 carries its own banked
definition with a DIFFERENT byte-true signature ((s32,s32,void*) vs (s32,void*,void*)), under a
decl marked "per-overlay-local decl (byte-true sig); do NOT re-macroize". That is the Phase-16
loose-typing wall inside a tool that structurally assumes it away. NEW RULE: a TU that DEFINES the
function owns its own declarations; a fleet axis is meaningful only for CONSUMING TUs. This grows
more common as banking proceeds -- every overlay that banks a function becomes an exception.
Then the R32 completion assertion cried wolf on its own by-design skip ("HALF-AXIS -- DO NOT BUILD"
for a complete rewrite): an assertion must be exact about its DOMAIN, not just its condition. Scoped
to consuming TUs -> 1,747 sites, 1 excluded by design. Also hardened to PLAN -> VALIDATE -> WRITE;
the refusal path had aborted mid-write while claiming nothing was modified, creating the very
half-axis §85 calls a guaranteed break.
META (R22's premise, re-earned): after fixing defect 1 I EXPECTED R22 to pass; it failed again for an
unrelated reason, and an individual `make build` of the failing binary SUCCEEDED by reusing objects
the clean run rebuilds. An incremental pass does not refute a clean-tree failure.
A 15-draft harvest_verify batch reported CC1-FAIL=4 and `final SHA None`. Three of the four were the
HARNESS, not the compiler. Checked the tree FIRST (the MISMATCH is a tree alarm, not a result),
reverted to the committed baseline rather than reasoning about a half-applied state, rebuilt ->
d19c9580 BYTE-IDENTICAL. No banked result was ever at risk: the byte-gate cannot manufacture a match,
but it CAN manufacture a verdict — and verdicts are what the backlog and roadmap are built from.
ORDERING PROVED THE CASCADE (R14): items 1-11 are real (9 PLUMBING, 2 DIFF), all before item 12 —
jtbl_carve REFUSING func_8013B83C (§59(3) non-contiguous same-subseg table). Items 13-16 are four
CC1-FAILs on the SAME ov_SC01_077_o0.o = one refused carve counted four times.
THREE DEFECTS FIXED:
1. `_ok` was computed and IGNORED — a refused carve was spliced and built anyway into a guaranteed
Error 33, filed as CC1-FAIL. Now a named CARVE-REFUSED class, skipped (one build cheaper).
2. attempt() never restored on failure, so the tree was dirty BETWEEN drafts — and _jtbl_snapshot()
snapshots the tree AS IT FINDS IT, so a later carve captured an earlier FAILED draft's splice and
its undo faithfully RE-APPLIED it, after the final _write(baseline). That is the entire
`final SHA None` mechanism. Invariant restored: the tree is at baseline except while a draft is
under test (atomic AND bisect branches).
3. The recovery's own `make extract` rc was unchecked (_sh does not raise — §93's sibling). Now loud.
Plus an R32 assertion on the cleanup: at 0 verified a non-empty git status is residue, not a result;
it names the files and the recovery command. It fired correctly on its first real run.
MEASURED RECOVERY (same drafts, clean tree): func_8013B83C -> CARVE-REFUSED; func_801789AC ->
PLUMBING (actionable); func_8017C974 -> DIFF (corroborates its agent's global_alloc spill diagnosis);
func_80140958 -> CC1-FAIL (genuinely its own). final SHA None -> d19c9580; tracked diff empty.
BLAST RADIUS OF §96, HONESTLY: the reconcile_tu span fix unblocked func_80176218 (banked, swept
133/137) and no other draft in the batch. 7 of the 9 PLUMBING are `conflicting types for <the
function itself>` = the DEF-side self-decl axis conform_decls owns — the next lever, now a measured
target list rather than a guess. cookbook §97.
+134 functions banked total for this exemplar (1 + 133 members). Measured from the committed
progress.fleet.md, not projected: fn-count 317,762 -> 317,896; instr-weighted 82.9 -> 83.2%
(+43,818 ins); distinct-code 71.5 -> 72.3% (+126 unique fns — these members are genuine byte-
VARIANTS that each count distinctly, not free dedup).
VERIFY: R22 clean-fleet (make clean && extract-all && check-all) -> 140 passed, 0 failed of 140.
make report fail-closed green: dedup-check 1886 validated / 0 failed, C1 coverage 239604/239604,
0 NON_MATCHING in any default build (G4).
THE 4 FAILURES ARE CARRIED, NOT CONCLUDED. All four are ov_SC07_006/007/010/011 and all four differ
from the other 133 in exactly one way: their sibling TU is _jr_8016AE5C.c, not _jr_801734BC.c —
carved under func_8016AE5C, which was banked and swept in SESSION-21. That is the SAME four overlays
and the SAME carve the SESSION-21 checkpoint flagged as "worth checking first" for func_8016B6BC's
0/137, which turned out to be a transitive type-carry (§94) rather than a wall. Each sibling reverted
its byte-neutral self-decl edit cleanly, so no dead diff is left behind. Per §59 a sweep failure is a
per-sibling INTEGRATION signal, not a codegen verdict — read one sibling's real gate result
(COMPILE-fail vs byte-DIFF) before concluding.
THE DRAFT was failing in a CHAIN, one "next conflict" per gate cycle. Applied §95's own diagnostic
law instead — splice once, dump EVERY cc1 error — and the whole set named the cause immediately:
three errors on TWO axes (one data decl, two callee decls), not three problems.
THE DATA ERROR WAS reconcile_tu AGAIN, ONE SHAPE DOWN (§96). split_statements returns comment-
STRIPPED text WITH SPANS; the rewrite re-found each planned statement by comparing that text to a raw
LINE, so `extern u8 D_80078E78; /* cur base ($s5) */` never matched. The decl was left unconformed
WHILE THE USE-CAST PASS STILL FIRED -> a draft whose uses are cast for the TU's storage against the
draft's own declaration -> cc1 reports `conflicting types` AT THE VERY DECL THE TOOL JUST CLAIMED TO
FIX, exit 0, "reconciled: 3 symbols".
FIX: rewrite by SPAN (the primitive existed — its docstring says spans are preserved *because drafts
get rewritten*). Plus the R32 assertion the old code was missing: it had a dropped_check counter
incremented in two places and NEVER COMPARED — "a loud failure nobody counts is exactly as invisible
as a silent one" in miniature. Now declarators-in vs -out AND a per-symbol check that each planned
tu.declaration() actually landed, both as `!!` notes so --strict exits non-zero.
MEASURED: 3 -> 4 data symbols reconciled on the same draft; trailing comments preserved (H5).
THE TWO CALLEE CONFLICTS were the other axis (reconcile_tu skips kind=='func' by construction):
cast_call_sites (§20) conformed func_80177AD4 (TU `void (int, unsigned int)`) and func_80178298
(TU `(u32*, u8*, short, short)`) and cast each call site to the draft's intended widths.
GATE: verified 1 / failed 0, d19c9580 BYTE-IDENTICAL. Write set is one overlay-local TU = T1 per the
§63/§85 blast-radius taxonomy, so the per-binary gate is sufficient; the ×137 sweep is the T2 case
and takes a full R22.
THE DEFECT (on the banking path — gate_stage runs reconcile_tu): its rewrite replaced the draft's
declaration LINE with the TU's declaration of the ONE conflicting symbol. A statement can declare
several: 'extern u16 D_80078EB2, D_8011F82A, D_8011F82C, D_80078EB4, D_8011F8C4;' where only EB4
conflicts became 'extern s16 D_80078EB4;' — four symbols silently gone.
WHY IT HID: the draft does not fail at the declaration. It fails later with 'D_8011F82A undeclared'
at a USE, several conflicts down a peeling chain, nowhere near the cause. I peeled four separate
'next conflicts' out of func_80176218 before dumping ALL cc1 errors in ONE build and seeing three
undeclared symbols that the tool itself had removed.
FIX: group the plan by STATEMENT rather than by symbol; re-emit EVERY declarator (TU's version for
the conflicting ones, the draft's own for the rest); note multi-declarator statements; and when a
statement cannot be re-parsed, say so loudly instead of emitting only the planned symbols.
VERIFIED: all 5 declarators survive, and the same draft now reconciles 3 symbols instead of 2 —
the dropped ones had been hiding a further conflict.
cookbook §95. The law (R32 again): a transform that REPLACES a syntactic unit must account for
everything that unit contained — the STATEMENT, not the line, is the unit of a C declaration.
Diagnostic: when a draft fails in a chain, stop peeling one error per gate cycle; splice once and
dump every cc1 error, because the shape of the whole set names the cause.
The family that failed its sweep twice (once in the 274-member batch, once after the §91 guard) and
looked like the §86 bimodal 'some families just don't template' case. It was not.
DIAGNOSIS (§59 + §93): spliced ONE sibling and read cc1 directly. It reported `c`, `v`, `off`
undeclared — ordinary locals that ARE declared in the remapped body. cc1 says 'undeclared' because it
aborted the declaration block at an unknown TYPE and every later declaration fell out with it. Read
the FIRST error, not the loudest: a visibly-declared variable reported undeclared means suspect its
type.
THE LIFT MUST BE TRANSITIVE. Lifting the type the body names directly (M8_8016B6BC) changed nothing —
still 0/137. The real set was four, found by following each definition's own references:
M8_8016B6BC -> Prim_8016B6BC -> Vtx_8016B6BC (named only inside Prim's body) -> DVec_8016B6BC.
lift_types.py --apply, byte-gated ALONE first (neutral, d19c9580 unchanged), then swept.
RESULT 0/137 -> 137/137, zero failures. R22 clean-fleet 140/140. cookbook §94.
Cost of not diagnosing: this family sat recorded as 'doesn't template' across two sessions. Pointed
at one sibling's real stderr it took under an hour and was worth 137 members.
CHECKPOINT HYGIENE: between T11 and T12, wave 2 + a bank + a new tool guard were recorded ONLY in
commit messages — CURRENT_PHASE.md and the cookbook were stale for that stretch. The quiet periods
were background sweeps/R22 (~2h each) during which the tree cannot be touched, but that does not
excuse leaving the durable record behind: a stale checkpoint is worse than an absent one. Closed.
cookbook §92 — conforming a DECLARATION: pointer changes are caller-neutral, scalar-WIDTH changes
are NOT. Byte-proven both ways (func_80179B74: 1,600 sites / 523 files / 3 forms, banked, R22
140/140; func_80175DA8: PLUMBING before the conform, DIFF after — the conform did not fix the draft,
it changed the callers). Plus the arity case that broke 138/140, and the counting lesson:
func_8015B950 looked like ~926 call-site casts and needed ONE — its only 0-arg call sits in an
engine_core.h DEFINE macro the preprocessor expands 926 times. Count SITES, not expansions.
cookbook §93 — `set -o pipefail` attributes a pipeline failure to the LAST stage, not the failing
one: cc1 exit 33 reads as an assembler error because `as` ends the recipe. The 2-minute fix is to run
the stages by hand printing each rc, then re-run the failing one with stderr visible. Turned an
opaque Error 33 into a one-line fix twice today. Corollary (§88e, earned): hand a stuck function over
as an UNDIAGNOSED observation, never as a named cause — flagged that way, the agent found the true
cause (cc1 `conflicting types for 'Ent'`) immediately.
func_8014D820 family swept 137/137. func_8016B6BC 0/137 reproducibly — recorded as a DIAGNOSIS task
per §59 (a sweep 0/N is a per-sibling integration signal, not a codegen verdict), never as a wall.
Checkpoint fn-count corrected 89.88 -> 89.80 against the measured report; stray a.out removed (R12).
WAVE 2 (9 never-drafted exemplars, ultracode): 9/9 returned, 5 MATCH / 4 near, 2.25M tokens.
BANKED: func_8014D820 (304 ins ×138) — and its agent ROOT-CAUSED the failure I left undiagnosed.
It was never an assembler problem: cc1 exit 33, `conflicting types for 'Ent'` vs
engine_types.h:434, surfaced by the recipe's `set -o pipefail` and MISATTRIBUTED to `as` because
`as` is the last stage in the pipe (Makefile:560). Fixed by moving V4/Desc/Ent to BLOCK scope —
byte-neutral and collision-proof across all 138 member TUs. Vindicates flagging it to the agent as
UNVERIFIED rather than passing my own guess forward as fact (§88e).
R22 clean-fleet 140/140.
NEW GUARD — SCALAR NARROWING IS NOT CALLER-NEUTRAL (byte-proven, and it cost 3 gate cycles):
conform_decls treated all decl type changes alike. A POINTER change is caller-neutral (func_80179B74
conformed 1,600 sites s16*/short* -> u16* and stayed byte-identical fleet-wide). A SCALAR WIDTH
change is NOT: narrowing `s32 a0` -> `u16 param_1` changes argument promotion at every call site.
MEASURED on func_80175DA8: decls reverted -> gate says PLUMBING; conform applied -> gate says DIFF.
The conform did not fix the draft, it changed the CALLERS. Now warned explicitly (not refused — the
draft's sig is still byte-truth for the callee and the gate arbitrates), with the instruction that a
DIFF after this conform means examine the callers (§17a-1 pair), not the body.
Verified the guard discriminates: fires on func_80175DA8 (s32->u16), silent on func_80179B74.
STILL OPEN from wave 2: func_80176218 + func_80175AB8 (DATA-symbol conflicts, D_80078EB4 /
D_8011F7BC -> reconcile_decls) · func_80175DA8 + func_80135EB0 (need the §17a-1 caller pair, not a
bare conform) · 4 near-misses with precise residuals recorded (func_80176734 129 length-drift,
func_80140958 49 inverted-hoist, func_80177B5C 19 sched tie, func_8017C974 83 -> permuter).
134/134 BANKED on the remainder after 3/3 on the probe — the FOURTH full-family sweep this session,
all four unblocked by the --like role guard, three of them 100%.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
BANKED: func_8015B950 (271 ins) · func_8016AE5C (85) · func_80179B74 (111).
SWEPT: func_8015B950 137/137 · func_8016AE5C 136/137 (ov_SC03_108 refused, left a stub rather than
forced). Three full family sweeps this session, all unblocked by the --like role guard.
FLEET 82.4% instr · 70.4% distinct-code (crossed 70%) · 89.72% fn-count. R22 140/140 throughout.
conform_decls has now been right in BOTH directions: it REFUSED func_8015B950 (which by hand broke
138 binaries) and CLEARED func_80179B74 (1,600 sites / 523 files, three decl forms, pointer-type
only). Then it found its OWN coverage gap: it required a leading `extern`, so it reported "no
declaration found" for a TU declaring the function on line 23 without one — a silent miss that reads
exactly like "nothing to do" (R32). `extern` is now optional and PRESERVED where present.
⚠️ INSTRUMENT FAILURE, recorded: mid-session `grep <pat> <file> | head` began printing NOTHING while
exiting rc=0 (i.e. matching). Read showed the line plainly; re-done in Python the file has 3
occurrences including a CALL at line 68. It cost one wrong intermediate claim ("no extern anywhere"),
which conform_decls immediately contradicted. NO banked result is affected — every bank passed the
whole-binary byte-gate and a clean-tree R22, neither of which reads shell output. A broken diagnostic
wastes time; it cannot manufacture a match. Diagnostics moved to Python. §90a, aimed at the shell.
func_8013BD74 is NOT a wall: its byte-true def takes a draft-LOCAL struct `A`, conforming the
prototype fails `parse error before '*'` (A undeclared that early), and the prototype cannot be
deleted because a call at line 68 precedes the definition at 71. Needs the §20/§64 type-lift.
1,600 decl sites across 523 files, in THREE different forms (s16 *a0 / short * / short *p),
conformed to the byte-true 'void func_80179B74(u16 *p)'. conform_decls ALLOWED this one: the arity
is unchanged, so no 0-arg call site can break, and the return is unchanged, so §85's precondition
does not apply. Gated BYTE-IDENTICAL; R22 clean-fleet 140/140.
The tool has now refused one axis (func_8015B950, correctly — it would have broken 138 binaries)
and cleared another (this one, correctly). Both verdicts held under R22.
133/134 BANKED on the remainder after 3/3 on the probe. ONE sibling refused (ov_SC03_108,
gate-fail) and is left as a stub rather than forced — a 136/137 recorded honestly beats a 137/137
that needed a shortcut. Third full-family sweep this session, all three unblocked by the --like
role guard.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
134/134 BANKED on the remainder after 3/3 on the probe — the second clean full-family sweep this
session, both unblocked by the --like role guard. func_8015B950 is stubbed in NO overlay.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
At 271 ins x 137 members this is the session's largest single family by instruction weight.
The same axis that broke 138 of 140 binaries an hour ago now lands clean, because conform_decls'
NEW arity guard located the actual obstruction instead of leaving me to absorb it by hand.
THE OBSTRUCTION WAS ONE LINE. Conforming `extern s32 func_8015B950(void)` -> `(s32 arg0)` turns
every 0-arg CALL SITE into `too few arguments`. My hand attempt assumed those were spread across the
926 TUs and would need 926 casts (the func_8012AAAC precedent, where it really was 137 separate
sites). They are not: there is exactly ONE call, in `src/shared/engine_core.h`'s
`DEFINE_func_8015BEE4()` macro body — expanded into all 926 TUs by the preprocessor.
func_8015BEE4 is a THUNK: `return func_8015B950();` with $a0 passing straight through from its own
caller. So the 0-arg call shape is byte-CORRECT and must be preserved, not fixed —
`return ((s32 (*)(void))func_8015B950)();` keeps it exactly (§17a-1; gcc folds the cast of a known
symbol to a direct jal, and the s32 return is unchanged so the thunk's value still flows).
Sequence: 1 cast -> conform_decls --apply (925 sites, R32 completion assertion: 0 remaining) ->
gate BANKED byte-identical -> R22 clean-fleet extract-all 139/139, check-all 140 passed / 0 failed.
The draft's 2 callee-decl conflicts (func_801725A4, func_80147078) dissolved with the axis.
Worth 37,398 templatable ins; the ×137 family sweep is next.
BANKED: func_8016AE5C (85 ins ×138). R22 clean-fleet 140/140.
⚠️ I BROKE 138 OF 140 BINARIES AND R22 CAUGHT IT — the per-binary gate could not.
Conforming func_8015B950's decl from `(void)` to its byte-true `(s32 arg0)` across 926 sites gated
BYTE-IDENTICAL on ov_SC01_077 and broke 138 other binaries with Error 33. §63/§85 exactly: a T2
write set is provable only by R22, and the binary the gate authorises is not the binary that breaks.
MECHANISM: conforming a decl to a signature that TAKES parameters makes every existing 0-ARG CALL
SITE a hard `too few arguments` error once a prototype is in scope. Not a declaration-only change.
PROCESS NOTE (mine): a first R22 reported 138 failures, an individual rebuild of a "failing" binary
said BYTE-IDENTICAL, and I nearly filed it as a flake. The second clean R22 reproduced it exactly —
the individual build passed only by reusing objects the clean run rebuilds. An incremental pass does
not refute a clean-tree failure; that is R22's whole premise, pointed at me. Reverted to a known-good
baseline (a stray jr_isolate region file was also in the tree) and redid the one good bank cleanly.
NEW tools/conform_decls.py — because applying this axis by hand three times in one session is how a
half-axis happens. Derives the byte-true signature from the DRAFT's definition (§58b), rewrites EVERY
site, asserts completion (R32). Encodes both preconditions: the §85 return axis (refuse if any caller
consumes the return) and a NEW arity precondition (refuse if 0-arg call sites exist, naming the cost).
The guard immediately gave a better diagnosis than my hand-fix had: func_8015B950's 0-arg call is in
ONE place — src/shared/engine_core.h, a DEFINE macro body — expanded into all 926 TUs. That fix is a
SINGLE cast, not 926 edits. Named as the next step rather than run on tired context.
Also lands cookbook §91 (the --like role trap) from the previous step.
- 134/134 BANKED on the remainder (after 3/3 on the probe) => the family is 137/137, ZERO failures.
func_8012AAAC is now stubbed in NO overlay. R22 clean-fleet: extract-all 139/139, check-all
140 passed / 0 failed.
- FLEET 81.9 -> 82.0% instr · 89.60 -> 89.64% fn-count · distinct-code 69.3 -> 69.5%.
- THE METRIC POINT, reproduced twice in one session and in BOTH directions: this jtbl family is
byte-VARIANT (each overlay's table holds its own addresses), so every member is a genuinely new
unique function and distinct-code MOVED. The h_seq PURE families swept earlier added 274 members
and moved distinct-code by +0.0, because those members were already counted via their shared
exemplar. SESSION-20's routing rule, now byte-demonstrated: target byte-VARIANT families to move
RE-completeness; high-reach h_exact families move only the display number.
- cookbook §91 — "a structure-TRANSFER is only valid where the structure corresponds": the --like
role trap, plus the three-hypothesis trail (two wrong, and instructive: the sibling call-site casts
were a real defect that fixed nothing, and my own carve-alone test was a false lead that departed
from the tool's real sequence). The law: any "same family => same structure" transfer must state
which structural fact it assumes and CHECK it on both sides — and a tool that drops an error class
it cannot act on should still SURFACE it, because a bare `gate-fail` repeated 137 times cost far
more than printing one line would have.
ROOT CAUSE of the 0/3 (found by reading the tool's ACTUAL invocation, not by guessing):
jtbl_family_bank calls `jtbl_carve <sibling> --func <fn> --like <exemplar_ov>`, and the role-transfer
keys on the SUBSEG ROLE (`ov_SC01_077_a` -> `_a`). Its premise — "same family => same span
structure" — silently breaks when the exemplar and the sibling host the function in subsegs with
DIFFERENT roles, which happens whenever the exemplar has a split the sibling does not.
MEASURED: func_8012AAAC lives in `ov_SC01_077_a` (role `_a`) in the exemplar but in the MAIN subseg
(role ``) in every sibling. The transfer therefore looked up `ov_SC01_077` — an unrelated SEVEN-table
span belonging to entirely different functions — and stamped those starts onto a sibling span holding
one table. jtbl_rodata_pads then refused with `consumed 1 rodata .align(s) but 2 pad spec(s) given —
table-count drift`, and jtbl_family_bank deliberately does NOT treat that error as isolate-fixable,
so all 137 siblings returned a bare `gate-fail` with the cause discarded.
THE FIX: transfer only when the exemplar's subseg for THIS FUNCTION has the sibling's role; otherwise
derive the span from the sibling's own carve (which was already computing it correctly). Fail-open is
not acceptable here — a wrong table set corrupts the image, so the guard defaults to local derivation.
MEASURED RESULT: the 3-member probe goes 0/3 -> 3/3 BANKED. R22 clean-fleet: extract-all 139/139,
check-all 140 passed / 0 failed.
Two earlier hypotheses were tested and are recorded honestly in CURRENT_PHASE.md: the sibling
call-site casts (real conflict, fixed, byte-neutral — but NOT the blocker) and my own carve-alone
test (which fails by construction for this shape, because a stub object does not emit the table its
2-entry spec describes — the tool splices the body BEFORE building, so its path is the valid one).
The ×137 member sweep returned 0/3, and §53/§86 say a 0% is a diagnosis task, not a verdict.
Diagnosed: every sibling TU has the IDENTICAL shape to ov_SC01_077 — the INCLUDE_ASM stub, then
'extern void func_8012AAAC();', then a 0-arg call LATER in the same file. Splicing the definition
in puts a prototype in scope, so gcc rejects the call with 'too few arguments' — the exact failure
the exemplar hit, reproduced 137 times.
Measured, not assumed: 137 sibling TUs hold BOTH the stub and a 0-arg call — exactly the member
count. Cast one call site per TU to ((void (*)(void))func_8012AAAC)() (§17a-1; gcc folds the cast
of a known symbol to a direct jal). The 137 'extern void func_8012AAAC();' DECLARATIONS were
deliberately left alone — an early count of '274 sites' was the calls AND the externs, and casting
an extern would have been meaningless churn.
Byte-neutrality PROVEN before committing, not asserted: R22 clean-fleet extract-all 139/139,
check-all 140 passed / 0 failed. This must be committed BEFORE the sweep because
jtbl_family_bank reverts each sibling from HEAD — an uncommitted fix would be reverted by the very
tool that needs it.
The first jtbl-routed bank of the session, and it validates the whole chain end-to-end:
1. jtbl_carve SPLIT-TABLE repair (this session): jtbl_801D7FB0 28 -> 50 words (112 -> 200 B),
authorized by func_8012AAAC's own `sltiu 0x32`.
2. NEW FIX — SINGLE-TABLE PREDECESSOR: adding a second table to a subseg whose existing carve was
single-table lost the FIRST table's start entirely (new_offs has only the new one;
overlay_jtbl_addrs cannot see the old one because its owner is banked and extract PRUNED the
stub .s; and single-table carves persist no tables= to rebase). The span then failed its own
validator with "first must equal the span start" — the invariant naming the missing entry.
A single-table carve spans exactly its one table, so ITS SPAN START *IS* THAT TABLE'S START:
inference, not persistence, so it also works for spans carved before tables= existed. This is
the RECOVERABLE half of the documented func_8013F350 lesson (that one was a pre-§8e merged
DOUBLE — two tables, no record, genuinely unrecoverable).
Result: ov_SC01_077_a JTBL_PADS := 0,0 tables=+0x0,+0x14. Carve alone byte-gated BYTE-IDENTICAL
BEFORE the bank was attempted (§81 step 2).
3. ARITY axis, all-or-nothing: 1,244 decl sites / 1,240 files `(void)` -> `()` + an R32 completion
assertion (old-form remaining: 0).
4. ONE call-site cast: the definition lands at line 811 and a 0-arg call sits at 822, so gcc sees
the prototype and rejects it — `((void (*)(void))func_8012AAAC)()` (§17a-1; gcc folds the cast
of a known symbol to a direct jal). Only 1 of the 1,386 fleet-wide 0-arg call sites needed it:
the others see only the `extern ()` decl, which permits a 0-arg call.
DIAGNOSIS NOTE: the failure read CC1-FAIL with only a warning visible under make. Running the
pipeline stage-by-stage (cpp | cc1 | maspsx | jtbl_rodata_pads | as) put it on cc1 rc=33, and cc1's
own stderr named it exactly: "too few arguments to function func_8012AAAC" at line 994. Isolating
the stage was what turned an opaque Error 33 into a one-line fix.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
family_sweep correctly REFUSED this exemplar (§53: a jr-family must route through
jtbl_family_bank.py; "a 0% from this path would be a TOOL artifact, not a wall") — the ×137 member
sweep is the next step and needs a clean tree, which this commit provides.
- family_sweep --hseq over the 3 newly-banked exemplars: BANKED 274 member-matches / 137 failed
across 137 overlays, for ~0 agent tokens. Session total: 3 exemplars + 274 members = 277 fns.
- THE STALE-MAP STEP, hit and handled: the first sweep returned "0 matched-exemplar families"
because .run/family_hseq.json still listed the fresh cracks as draft-ov077. Regenerated
(matched-sib families 60 -> 63) and the sweep found them — the documented bank-x1 -> regen ->
sweep path (memory crack-wave-sweep-map-regen).
- §86 REPRODUCED CLEANLY: 2 of 3 families templated ~137/137; the third failed ~137/137. Not a
rate — a BIMODALITY. One probe per family, then sweep or skip; never a blended pool average.
- R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed (second clean-tree
verification this session). dedup 1886/0, C1 coverage 239,604/239,604, 0 NON_MATCHING (G4).
- FLEET 81.7 -> 81.9% instr · 89.52 -> 89.60% fn-count · distinct-code 69.3% UNCHANGED — correct
and expected: these are h_seq PURE propagation-class families, and SESSION-20's routing rule says
propagation moves only the DISPLAY metric (members were already counted once via their exemplar).
To move RE-completeness, target byte-VARIANT families. Stated plainly so the next session picks
targets by the metric it means to move.
- drive-by: family_sweep --help crashed (argparse %-expands help text; a literal "0%" needed "0%%").
BANKED (whole-binary byte-gate, the sole arbiter): func_8014D2A0 (80 ins ×138) · func_80158638
(87 ×138) · func_8016B6BC (94 ×138). Stubs in ov_SC01_077: 150 -> 147, 0 new stubs.
R22 CLEAN-FLEET: extract-all 139/139, check-all 140 passed / 0 failed. dedup 1886/0,
0 NON_MATCHING (G4). Fleet 81.7% instr / 69.3% distinct-code / 89.52% fn-count.
- WAVE STOPPED at Drew's request with 15/24 agents returned, ALL 15 status=match. Only the
completed drafts were gated; in-flight ones are still being written (§90d).
- PRE-GATE, both oracles, all 15: match_one MATCH + reloc_verify ALL RESOLVED. Routed 7 plain /
8 to the §81 jtbl carve chain.
- THE BLOCKER, MEASURED: 7 of 7 plain drafts failed PLUMBING, 0 DIFF, 0 compiler walls — the same
shape as SESSION-20's T0.2. §58b applies: the draft sig is byte-TRUTH (it MATCHed), the header
decl is the stale stub-era guess, so conform the DECLS.
- §85 RETURN-AXIS WIDEN, all-or-nothing: 3,471 decl sites / 1,736 files, precondition verified
(ZERO callers consume the return => byte-neutral by construction) + an R32 completion assertion
(old-form decls remaining: 0). func_8014D820's s32 return is load-bearing — forcing `void` costs
2 instructions (302 vs 304), so the decls had to move, not the draft.
TWO HONESTY ITEMS:
1. I REPORTED "0 of 7 banked"; the true number was already 2. My diagnostic pass printed only
lines starting with "- func_" (the failures) and hid its own successes while I read it for
error text. A script that prints only failures cannot tell you it succeeded — the R32
silent-skip shape aimed at my own instrumentation. Ground truth is the stub count (§55b(3)).
2. A REAL FINDING fell out of that mistake: same drafts, same tree, minutes apart — gate_stage's
full ladder banked 0/7 while bare harvest_verify banked 2/7. The LADDER REGRESSED two drafts
the bare gate accepts (§19's "sig_unify regresses already-canonical drafts", one level up, and
the exact mirror of SESSION-20's missing-ladder false 33%). Neither "always ladder" nor "never
ladder" is right — run both, let the byte-gate arbitrate. One build per draft.
OPEN: func_8014D820 still a stub — after the widen its error moved from `conflicting types` to an
assembler-stage failure, not finished diagnosing. Recorded as open, NOT as a wall.
Each law is one this session paid for, and each fix CHANGED the answer its tool gave:
§90a a comparison tool must share its reference oracle's index space exactly (objdump -dr vs -drz
elided 6 nops -> 104 vs 110 ins -> phantom mismatches; and the .s word field is little-endian
hex TEXT, not the instruction integer). Five false alarms preceded the first true one.
§90b 'byte-neutral' is not 'wanted' — undo on the SUCCESS path (a 0-bank run left 123 files of
dead diff because the backstop only fired on a NON-neutral edit). §61 applied forward.
§90c a library-callable function must FAIL CLOSED on unconfigured module state — an empty result
is indistinguishable from a true negative (R32's silent skip in a different hat).
§90d do not measure a live wave's drafts — §87 staleness in real time (same function, two verdicts
two minutes apart; its agent rewrote the file 12s earlier).
§90e an agent's CONCLUSION and its EVIDENCE fail independently: re-derive the premise, design the
fix from what you can prove. The agent's jtbl bug was real and confirmed 3 ways; its 'zero
xrefs' premise was false and its proposed remedy would have deleted a referenced symbol.
The fix instead gates on the function's own sltiu bound — the program declaring its own table
length. Negative control (228 combinations, exactly 1 change) is half the deliverable.
THE BUG (real, found by a wave agent): jtbl_range() ends a carve at the next data dlabel, assuming
every dlabel is an object boundary. spimdisasm can CUT ONE JUMP TABLE IN HALF and emit the tail
under an invented D_ label — func_8012AAAC's 50-word table is jtbl_801D7FB0 (28) + D_801D8020 (22).
The carve then reserves 112 B for an object supplying 200 B of .rodata, shifting every later symbol.
§84-class: match_one is structurally blind; it surfaces only as a whole-binary DIFF.
THE AGENT'S EVIDENCE WAS WRONG (R14): it reported D_801D8020 as having "ZERO xrefs anywhere in the
tree" and proposed deleting the label. It has TWO (.word D_801D8020 and +0x2 in tail.data.s) —
almost certainly spimdisasm mis-symbolizing packed halfword data, but "almost certainly" is not a
gate, and the proposed remedy would have deleted a symbol two emitted words reference. I built the
xref census first, watched it refuse, and only then found the references.
THE GATE USED INSTEAD — the function's own `sltiu N` range check, which gcc emits right before the
indexed load, so the PROGRAM declares its own table length (func_8012AAAC: sltiu 0x32 = 50). Absorb
only when the next label is immediately adjacent, its words are all code addresses in the overlay's
text, and absorbing lands on an EXACT sltiu bound (the SET, not max() — a multi-switch function has
several and no way to say which owns this table).
Three further corrections, each caught by testing rather than assumed:
- the absorption fired and the trailing-pad trim immediately UNDID it (re-trimming against the
first dlabel's 28 words); the trim now sees the whole absorbed table;
- a continuation ends at ITS OWN last .word, not the next dlabel (D_801D8020 ends 0x801D8078; the
next dlabel is 0x801D8158, 224 B on) — using the next dlabel is the assumption being repaired;
- the shortfall warning now fires only on an unambiguous single-bound pairing (it fired ~90 times
across 38 tables before the guard — a warning that fires on ambiguity is noise, not a signal).
VERIFIED: the split table 28 -> 50 words (112 -> 200 B), matching the agent's 3 independent
confirmations; and across 38 jtbls x 6 functions = 228 combinations, EXACTLY ONE range changes —
that table, for its owning function only.
- 8 of 24 agents completed before the session limit (16 errored on the limit, none technically);
resumed from cache. 8 MATCH / 0 near / 0 fail, stake 210,726 templatable ins, 2.48M subagent tokens.
- CANDIDATES not banks (§58) — but DIAGNOSED ones: the prompt required symcheck + a named blocker,
so instead of 8 opaque MATCHes there are 8 with their banking prerequisite stated.
- THE FINDING: 6 of 8 are blocked on the SAME jtbl/rodata carve class — one mechanical lever in
front of ~153,596 templatable ins in this batch alone. Corroborated independently by
tools/reloc_verify.py, which flagged the identical class on the drafts it could check (R34).
- A REAL jtbl_carve BUG found by an agent, confirmed 3 ways: jtbl_range() ends the carve at the
next data dlabel, but splat split ONE 50-word table across jtbl_801D7FB0 (28) + D_801D8020 (22,
zero xrefs) -> 112B carve for a 200B .rodata. §84-class: match_one is blind; it surfaces only as
a whole-binary DIFF. Fix queued.
- Agents touched zero tracked files (write-set constraint held).
The SESSION-20 carry item ("promote it — it closes 3 of the 4 blindness classes"), generalized:
base vram DERIVED from the target .s (was hard-coded to one function, R33) and the parse
coverage-asserted (R32 — a target that parses to zero instructions refuses to report a verdict
rather than reading "ALL RESOLVED"). Resolves jal callees, %hi/%lo data addresses (recovering the
implicit REL addend objdump -r never prints — the §84 trap) and internal j destinations.
IT TOOK TWO OF ITS OWN BUGS TO TRUST IT — both found by cross-checking masked_diff (R34):
1. `objdump -dr` instead of `-drz`: without -z objdump ELIDES identical-instruction runs, so
func_801330E0 read 104 ins vs masked_diff's 110 (6 elided nops) and every later index compared
against the wrong instruction — 2 phantom mismatches on a clean draft. A comparison tool MUST
share its reference oracle's index space exactly.
2. the .s word field is little-endian HEX TEXT, not the instruction integer; masked_diff byte-swaps
it and this did not — reporting "word differs" on three byte-IDENTICAL sites.
Now classifies instead of alarming: JTBL (gcc emits its own switch table via a local label => nothing
to relocate; the §81 routing signal — bank via jtbl_family_bank, never plain harvest_verify) ·
BAKED-LITERAL (same constant materialized inline: byte-correct here, but if the symbol is
per-overlay the exemplar matches and every SIBLING breaks — the §84 shape) · real mismatch.
Recorded: measuring a live wave's drafts is itself the §87 staleness error — a draft rewritten 12s
before the check gave a different verdict. Draft QA happens after the wave returns.
(The wave's gate driver lives at .run/s21_gate.py — gitignored scratch, §55b orchestration law
built in: --no-propagate per TU group, commit before the fleet propagate, and BANKED derived from
the stub set rather than read from gate_stage's accumulating verified-file.)
- progress.linked_subsegs() was FAIL-OPEN: gated on the module global BINARY that set_binary()
assigns, it returned an EMPTY SET when imported as a library without that call — i.e. "no
linked library subsegs", which for main is confidently wrong (there are 49) and silently
reclassifies ~960 already-byte-identical PsyQ-linked stubs as outstanding game-code work.
Now raises when unconfigured; the CLI path is untouched (set_binary assigns before calling).
Caught by hitting it myself while measuring bucket #2.
- ENDGAME-MAP CORRECTION (measured, zero-token): the map's "main EXE game code ~59,765 ins /
~1,048 stubs" conflates two populations. Correctly split: game code 1,042 stubs / 31,888
measurable ins; LINKED PsyQ library 960 stubs / 27,877 ins (already byte-identical). Bucket #2
is ~47% smaller than quoted. Caveat kept: 467 game-code stubs have NO sig row (the documented
main second-oracle gap), so the true weight is above 31,888 and not currently measurable —
re-price when the main second oracle lands, do not quote either number alone.
- .run/s21_zerocrack.json: the 60-family zero-crack pool enumerated (45 plain / 15 jr) and
honestly discounted — its top entries (0x8013c414 -O0 wall, 0x80144090 LENGTH-DRIFT,
0x80133ab0 pinned) are already-diagnosed refusals, so ~95k of the 208,499 is not available.
- T1 FRONTIER MEASURED (zero-token, R35: family map regenerated on fresh sigs first — it was
stale by ~657 banked members): 36,020 stubs / 2,345,599 weighted ins remain, and only
9.0% are h_exact-FREE. PROPAGATION IS TAPPED (238 distinct classes / 3,245 instances);
22,498 distinct classes / 1,680,097 distinct ins is what is actually left. The mass is FLAT
across all 139 binaries (~300-550 sub-500 stubs each) -> "pick the best overlay" is not a
strategy. .run/s21_frontier.py + .run/s21_frontier.json
- T2 THE AXIS IS THE FAMILY, NOT THE LOCATION: 1,342 substantial h_seq families /
1,298,135 templatable ins = 55% of ALL remaining weighted instructions. Routed by blocker:
jr/§81 181 fams (33.6%) · DRAFT-with-cached-Ghidra-C 91 (28.6%) · DRAFT-modal 1,023 (27.5%)
· zero-crack 45 (6.5%) · permanent walls 2 (3.9%). Live+cached+non-wall in ov_SC01_077 = 54
families / 589,502 ins, value steeply concentrated (top 24 = 96%).
.run/s21_targets.py + .run/s21_targets.json + .run/s21_draft_pool.json
- T3 WAVE 1 LAUNCHED: tools/workflows/family_core_wave.js (NEW) — 24 xHigh drafters, one per
family exemplar, stake 575,488 templatable ins (24% of remaining). Supersedes worker_wave.js
for family work: carries each target's family STAKE, encodes the four §58/§87 integration
rules at source (splat D_<UPPERHEX> not Ghidra DAT_; never invent a symbol; canonical callee
sigs; leave decl plumbing to the ladder), and requires symcheck.py on any claimed MATCH.
- T3b LADDER HYGIENE, both SESSION-20 carry items fixed — one defect, two masks: a byte-NEUTRAL
transform was left in the tree when it banked nothing. family_sweep's --normalize-self-decls
backstop only fired on MISMATCH (left 123 files of dead diff on a 0/123 run); gate_stage's
ARITY undo narrowed to src/shared/ and left ~40 TUs. Both now restore the full snapshot when
NOTHING banked (no banks to preserve => the splice hazard cannot apply). §61 on the success path.
- T3c BACKLOG addr DEFECT fixed (R32/R33): new addr_of() derives the address from `name`,
assert_addr_coverage() fails loud on an unkeyable row, append_record fills both directions.
Found a latent bug doing it: load_best() keyed on `addr or name`, splitting one function into
two "best" records. Keyable rows 128/1,701 (7.5%) -> 1,701/1,701 (100%).
bulk_harvest's Phase B has been a ProcessPoolExecutor over DISTINCT binaries (per-binary flock,
per-worker result files, compute_fleet=False) since Phase 23 — but welded to Phase A's LLM drafting.
Family sweeps stage drafts differently (family_sweep --stage-only), so the farm was UNREACHABLE from
that path, and SESSION-20 gated 389 + 268 + 104 members SERIALLY for no architectural reason (~8-16x
throughput loss on a 32-thread box). This is a thin adapter: same gate_stage.run_gate, same
per-binary lock, NO new gate logic.
Also fixes the phantom-dir bug at source: a bare .run/sweep/*/ glob matches gate_stage's own
intermediate ladder dirs (-cn/-cast/-rc/-s2in/-uni) and calls them as binaries — 24 phantom
PARTIAL 0/1 lines that inflated one run's notbanked from 0 to 56. Requires config/splat.<bin>.yaml
to exist (R33/R36: derive the binary set, never glob it). Smoke-tested: the phantom is skipped and
named, real binaries kept.
§89 records both throughput rules the project already had and was not following.
39 files from the three behemoth agents: the matched drafts (s21_func_80183814_b2.c,
s21_func_8017D2DC_b1.c, s21_func_8017DC1C_b1.c), their reports with do-not-re-buy tables AND BASES
(§80), and the reusable harnesses — including s21_g21_reloc_verify.py, which resolves every
relocation (incl. the implicit MIPS-REL addend objdump -r does not print) against the target and is
the missing rung between match_one and the binary (§88f). ~735k agent tokens of work; .run/giants is
the curated allowlist.
§63 has defined T0/T1/T2 since Phase 26 and never enforced it. Two measured consequences in
SESSION-20: ~13 full clean-fleet verifies (~15 min each) for batches that were provably T1
(over-verification), AND two cases where the write set was LARGER than the belief about it — the §85
widen believed contained to one overlay broke ov_SC01_077, and gate_stage's ARITY pre-pass silently
rewrote 40 TUs (under-verification, the dangerous half).
A tier is a CLAIM about the write set; this turns it into a MEASUREMENT. --expect t1 fails loud when
the tree disagrees. Binary list DERIVED from config/splat.*.yaml (R33, never hardcoded — R36's
incident was a hardcoded set missing 4 real binaries). Coverage ASSERTED (R32): an unclassified path
exits 2 and names itself rather than being silently skipped. overlays.mk is attributed per-binary by
parsing its diff, so a change confined to one binary's var-block stays T1.
Zero functions >1000 ins remain unmatched anywhere in the fleet.
func_80183814 (5,122 ins — the LARGEST function in the game) — round 2 closed it: length 5127->5122
exact, structural residual 36->0, register-sensitive 1201->0, frame -256 -> -0xF8 exact, saves
10 -> .mask 0x807f0000 exact. Verified independently (R14): match_one MATCH (5122 ins).
ROUND 1's DIAGNOSIS WAS WRONG and the agent refuted it properly: the +5 length was a SYMPTOM, not
the lever, and the §83d max_reg/cse.c:8340 story does not hold — a 15-line reproducer reproduced the
case-0/3 CSE exactly (so it cannot be max_reg-gated), max_qty only gates extension ACROSS blocks,
and the target leaves $s7/$fp unused (no pressure story). Confirmed from a second direction: C01 has
the identical two groups over the identical symbols with ZERO residual, because a `break` puts a
CODE_LABEL between them. The two biggest levers were pure DECLARATION SCOPE (§45/§76), not pins.
func_8017DC1C (1,518) — MATCH first round, pin-free, zero __asm__ dials. NOT a jr fn (0 mid-fn jr).
func_8017D2DC (1,586) — MATCH first round (banked in the previous commit).
BANKING ORDER MATTERS — a new failure mode found and worked around: banking func_8017DC1C BEFORE the
carve chain broke the build. Its draft establishes the canon for 39 previously-undeclared externs;
jr_isolate_all's re-partition (overlay_src_split) then DROPPED ALL 39 across the new split boundary
(`D_801C1EB0 undeclared`), leaving them in NEITHER file. The §77 preamble-drop class, in a third tool.
FIX = ordering, not patching: run the §81 carve chain FIRST on a clean tree (gated BYTE-IDENTICAL),
then bank. Reverted, re-sequenced, both banked clean.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.6 -> 81.7% · distinct-code 69.1 -> 69.3% · fn-count 89.52%.
T0.7 — the §86 one-member probe applied to the remaining FREE families: 9 LIVE / 6 DEAD / 5 unstaged.
The three highest-value families by raw size (18,084 / 11,234 / 10,880 ins) all probed DEAD — the
probe skipped them instead of burning ~400 gate cycles rediscovering it. Swept the 9 live: 104 banked,
8 of 9 families fully cleared (func_8017BEF8 has 8 stragglers).
BEHEMOTH 2 of 3: func_8017D2DC (1,586 ins, ov_SC01_001) MATCHED and BANKED — closed in ONE agent
round, pin-free. Verified independently (R14): match_one MATCH (1586 ins).
§81 carve chain: the agent predicted step 1 unnecessary; jtbl_carve REFUSED (the subseg already
hosts a .rodata carve and the new table's start != span start). The refusal was RIGHT and is the
instruction to run step 1 — jr_isolate_all --only (2 fns/1 object) -> BYTE-IDENTICAL, then
jtbl_carve -> BYTE-IDENTICAL, then the ladder banked it.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.5 -> 81.6% · distinct-code 69.0 -> 69.1% · fn-count 89.49 -> 89.52%.
T0.6 measured: the autopsy's integration bucket (315 match_one MATCHes, 'blocked only on plumbing')
banked 0/27 through the full gate_stage ladder. Two causes, neither plumbing:
(1) UNDEFINED DATA SYMBOLS — the gate fails at LINK on symbols defined in NO overlay's symbol file.
match_one compiles one TU and never links, so an extern resolving nowhere is structurally
invisible to it. (Refuted the obvious alternative: the drafts WERE authored for the right binary.)
(2) STALE DRAFTS — 'redefinition of struct S80172C50': the struct was since lifted into
engine_types.h, so the draft's own copy collides. A stored draft is scored against TODAY's tree.
=> FOUR match_one blindness classes now catalogued: §81 jump tables, §84 masked %lo, §87 link, §87
staleness. A match_one MATCH is 'this TU compiles to the right bytes with relocations masked' —
nothing about linking, nothing about the current tree. A stored MATCH is a CLAIM WITH A TIMESTAMP.
Consequence: with §83's 44%-misfiled finding, docs/backlog.md's headline count is NOT a work queue.
Re-gate a sample before planning against any stored-draft pool. Cheap discriminator added (grep each
D_ symbol against the binary's symbol files; any UNRESOLVABLE will fail at link regardless of ladder).
MY RECOMMENDATION WAS WRONG: I ranked this pool first on 'highest certainty of any pool we have'.
The certainty was an artifact of a tool that cannot see link errors. 0 banked, 0 tokens, tree clean.