Commit Graph

1439 Commits

Author SHA1 Message Date
Drew T 0f73618fa8 feat(phase-30 S39/S4): propagate func_801820DC family (94 ins x 2 members) - R22 140/140 2026-08-04 23:29:28 -06:00
Drew T 0414171237 feat(phase-30 S39/S4): 8/35 draft-exemplar heads re-gate and bank (+474 ins, 4 gate cycles, 0 agent tokens)
The 39 draft-exemplar families all have their exemplar still OPEN in ov_SC01_077 -- a
draft-exemplar family cannot propagate until its head banks. Re-gated the newest stored
ov077 draft per head, in 4 small batches (§61: a wide harvest broke a TU in S38).

Set aside the top 4 heads (65% of the pool's weight, all known-hard): func_801412A8 +
func_80178004 ARE S6's two giant walls (198x138 + 165x138 = 50,094 ins riding on 2 cracks),
func_8017C974 is today's byte-proven close=47, func_8017C294 its 246-ins neighbour.

  batch 0  1/9      batch 1  3/9      batch 2  4/9      batch 3  0/8     = 8/35 (23%)

BANKED: func_8017EC7C func_8018281C func_801820DC func_80182988 func_80183BAC
        func_80183AF0 func_80183CF4 func_80182E7C
(+474 ins x1 now; ~1,441 ins of templatable weight behind them once their families propagate.)

CALIBRATION REFINEMENT (docs/calibration.md, S39): this population re-gates at 23%, vs 8%
for the general stored pool and 4/6 for fresh post-repair drafts. Three different populations,
three different rates -- which is exactly why the rule is "re-gate what a repair plausibly
touched", not "re-gate the ledger". ov_SC01_077 is the split-heaviest overlay, so the S38
alias-deletion repair plausibly touched all of these.

VERIFIED: make clean && make extract-all && make check-all -> 140 passed, 0 failed of 140.
Fleet 12410275 -> 12410749 instr (+474), distinct +474 / +8 uniq, fn-count +8. audit-digest OK.
0 NON_MATCHING (G4).
2026-08-04 23:22:42 -06:00
Drew T 9e5ff203e7 docs(phase-30 S39): refresh checkpoint — final fleet numbers, re-gate rates, the reconciliation-gate item 2026-08-04 22:44:07 -06:00
Drew T 957dada2f8 docs(phase-30 S39): decision-log — MASTER_REMAINING proposal: adopt the goal (denominator completeness + a reconciliation gate), reject the maintained list (R33) 2026-08-04 22:43:28 -06:00
Drew T 7b5eda0424 feat(phase-30 S39/S4): re-gate probe — A10 broadly stands; 4 banked from the reverted overlays (+146 ins)
Tested whether decision-log A10 ("stored drafts re-gate at 0/958", measured in T1) survives
S38's tool repairs. Three populations, plain re-gate, no draft edits:

  fresh wave-6 drafts (diagnosed "blocked on a class")   4/6
  stored pool, unbiased sample (every 96th of 1,155)     1/12   <- hit was in a REVERTED overlay
  the two REVERTED overlays, targeted                    3/17

A10 BROADLY STANDS. ~8% on the general stored pool is not a harvest, and a 1,155-wide sweep
(= 1,155 whole-binary builds) is not justified by it. Do NOT generalise the fresh-draft rate
(4/6) onto the stored pool -- different populations. The honest rule is narrower and cheaper:

  after a tool repair, re-gate the drafts THAT DEFECT plausibly touched, targeted by its
  blast radius -- not the whole ledger. (R35 applied to the backlog, not just to metrics.)

BANKED (+146 ins): ov_SC06_030 func_80161208 + func_80162CCC; ov_SC07_010 func_801506A4 +
func_8016F0AC. R22 clean-fleet 140 passed, 0 failed of 140 -- which also proves byte-neutral a
fleet-shared engine_core.h edit the bank required (extern s32 func_801506A4(s32,s32) -> the
no-prototype form), reaching all 138 overlays (T2 blast radius).

Fleet 12410129 -> 12410275 instr; distinct +95 / +1 uniq; fn-count +4. audit-digest OK.

Also documents the LEDGER MECHANICS in calibration.md (Drew asked): .run/backlog.jsonl is
append-only and nothing is deleted on bank -- open-ness is DERIVED from corpus.stubs at every
read (load_best drops now-banked rows per-binary, P9) and `make report` runs `backlog.py prune`.
Membership is therefore self-maintaining and currently clean: 863 rows, 0 already-banked, 14
duplicate-addr (was 6,867 rows / 98% banked before Phase-29 compaction). What pruning does NOT
re-validate is the VERDICT on surviving rows -- closeness + residual class are as old as the
tooling that wrote them (Phase 28 found a corrupt one: func_80178004 close=0 -> 91). That is
the staleness that matters, and it is exactly what this probe measured.
2026-08-04 22:42:54 -06:00
Drew T 819a4c5317 docs(phase-30 S39): session checkpoint — stale-digest finding, 4 instrument fixes, +4,727 ins, fresh-session safe 2026-08-04 22:17:31 -06:00
Drew T a2a50635b3 feat(phase-30 S39/S4): func_801878E8 family 4/4 siblings banked ×N (+2,052 ins, ~0 agent tokens)
Propagation behind the crack banked this session. jtbl_family_bank.py over the 4 open
h_seq siblings of func_801878E8 (513 ins each):

  ov_SC03_001  BANKED    ov_SC03_124  BANKED
  ov_SC04_019  BANKED    ov_SC05_017  BANKED

ROUTE NOTE (§53, worth keeping): family_sweep --hseq REFUSED this family by design --
has_mid_jr => it needs the jtbl carve, not the remap sweep, and the interlock says plainly
that "a 0% from this path would be a TOOL artifact, not a wall". Taking the refusal at face
value and using the named tool banked 4/4 first try. This is the same lesson as the rest of
the session from the other side: the instrument told the truth about its own limits.

jtbl_family_bank also enforces a CLEAN tree (it reverts from HEAD per sibling, so an
uncommitted prior bank would be destroyed) -- which is why the ×1 banks committed first (H4).

VERIFIED: make clean && make extract-all && make check-all -> 140 passed, 0 failed of 140
(each sibling carves its own jtbl => config changed => fleet blast radius). Fleet
instr-weighted 12408077 -> 12410129 = +2,052, exactly 4 x 513; distinct +1,539 / +3 unique
fns (the 4th sibling shares an h_exact class already matched); fn-count +4. audit-digest OK.
0 NON_MATCHING (G4).

Session running total: +4,727 instructions (whale 770 + 4 drafts 1,905 + family 2,052),
12405402 -> 12410129, every step R22 clean-fleet 140/140.
2026-08-04 22:16:33 -06:00
Drew T f2696653ef feat(phase-30 S39/S4): 4 wave-6 drafts bank UNCHANGED — the block was our tooling, not the code (+1,905 ins)
The 6 still-open wave-6 drafts were triaged against S38's own diagnosis table; 4 banked,
R22 clean-fleet 140/140.

  func_801919A0  ov_SC06_032  710 ins   (was: undefined ref func_8018B878 -- "alias class")
  func_80189030  ov_SC03_001  557 ins   (was: undefined ref func_80186F88 -- "alias class")
  func_801878E8  ov_SC04_018  513 ins   (was: undefined ref func_801848DC -- "alias class")
  func_8018A564  ov_SC02_027  125 ins   (was: CC1-FAIL Error 33)

THE FINDING: all four banked with NO change to the drafts. S38 recorded them blocked on a
class that needed cracking ("cracking this one class frees 6 drafts at once"); they had
ALREADY been freed by S38's own tool repairs -- the jr_isolate_all/overlay_src_split
alias-DEFINITION-deletion blindness and harvest_verify._reload_corpus. The drafts were
correct all along; the instruments were failing them. That is the FIFTH recorded "wall"
this phase to resolve to our own tooling.

  => RE-GATE STORED DRAFTS AFTER ANY TOOL REPAIR before treating a stored verdict as a
     fact about the code. A verdict is only as current as the instrument that produced it
     (R35 applied to the backlog, not just to metrics).

Each bank also performed a jtbl carve, so config/ changed => fleet blast radius => full R22
(clean + extract-all + check-all) = 140 passed, 0 failed of 140.

Metrics move exactly as the model predicts: instr 12406172 -> 12408077 = +1,905, the exact
sum of the four (710+557+513+125); distinct +1,905 / +4 unique fns; fn-count +4.
audit-digest OK. 0 NON_MATCHING (G4).

LEFT ON THE BACKLOG as genuine codegen residuals, not forced (P9):
  func_8017C974 (ov_SC01_077, 947 ins, close=47, REGALLOC-PERM, 12 permuter variants inert)
  func_80188C68 (ov_SC03_124, 551 ins, close=370, the only target with no twin anywhere)

NEXT: the func_801878E8 family (4 open siblings x 513 ~= +2,052). family_sweep --hseq
correctly REFUSED it via the §53 interlock (has_mid_jr => jtbl carve route; "a 0% from this
path would be a TOOL artifact, not a wall"), and jtbl_family_bank.py requires a clean tree
because it reverts from HEAD per sibling -- which is why this commit lands first.
2026-08-04 22:10:49 -06:00
Drew T 3c0f60861d feat(phase-30 S39): the whale is 138/138 — ov_SC07_010 carved + banked (+770 ins, R22 140/140)
Closes the first of S38's two reverted R22 failures. ov_SC07_010 was the lone overlay
still shipping func_80144B9C (770 ins) as INCLUDE_ASM while the other 137 banked it.

Its _jr_80140608 object ran 0x184b0..0x2c2f4 straight through the whale; the sibling
ov_SC07_006 carves the same span into _o0d (0x1ca44) + _jr_801457A4 (0x1d64c). Note
0x1ca44 + 0x80128158 = 0x80144B9C exactly.

  tools/o0_subsplit.py ov_SC07_010 --lo 0x80144B9C --hi 0x801457A4
    -> 1 unmatched stub, 0 ALREADY-MATCHED in range (so no §126 island; K=0 => 3 regions)
    -> split BYTE-NEUTRAL first (d7b5875d), then banked via ../shared/func_80144B9C.h

The S38 cause ("its -O0 split reused an EXISTING _o0c instead of a fresh _o0d") did NOT
recur: o0_subsplit.free_letters derives the unused suffix (_o0c is free in THIS overlay).

Two decl conflicts on the way, enumerated with `cdecl` in ONE pass (R33) rather than one
build at a time — of the whale header's 94 symbols the §8b carried layer re-declares 3,
and 2 conflict: D_801274D0 (layer `s32 (*)(s32)`) and D_801274CC (layer `void *`) vs the
header's canonical `s32`. Dropped both: nothing in the region uses them, they are carried
from an earlier region of the old object, and 0 of the 137 other whale-including files
carry either. Decls emit no code => byte-neutral (§8c), and byte-gated.

ov_SC06_030/func_8017E120 needed NO work — it is already banked (defined at
ov_SC06_030_jr_8017C8D0.c:3491). S38 reverted the surrounding batch, not that function.

VERIFIED: make clean && make extract-all && make check-all -> 140 passed, 0 failed of 140
(config changed => fleet blast radius, R22 mandatory). Fleet instr-weighted
12405402 -> 12406172 = +770, exactly the whale's size. distinct-code unchanged by design:
that h_exact class was already matched via the other 137, so the 138th adds fleet
instructions but no new DISTINCT function. audit-digest OK (the new S1e gate, on its first
real use). 0 NON_MATCHING (G4).

Metric note (R30, same class as S1e): a body banked by #include-ing a shared header is
invisible to fn-count's NUMERATOR (the definition is not in the .c) while its stub leaves
the denominator -- 341186/353718 -> 341186/353717. The weighted metrics counted it
correctly because they derive from corpus.stubs, not re-parsed C. Trust the weighted pair.

Still open from S3: the 61 SC07 -O0 members (untouched).
2026-08-04 21:58:53 -06:00
Drew T 1576570271 fix(phase-30 S1e): the distinct-code "regression" was a STALE DIGEST — alias lever ungated
The S38 checkpoint gated the phase's best lever ("do NOT scale the alias lever") on
distinct-code falling 89.3 -> 89.2. It never fell.

PROOF (each commit's metric recomputed from its OWN committed tree, 0 unresolved):
  commit:1426 TRUE     : instr 12394533  distinct 5022306  (77895 uniq)
  commit:1426 COMMITTED: instr 12402412  distinct 5029324  (78025 uniq)   <- stale
  HEAD TRUE == COMMITTED: instr 12405402  distinct 5025082  (77952 uniq)
  => true delta 843->HEAD: instr +10869, distinct +2776 ins / +57 uniq. ALL ROSE.
The 843 digest was generated from a working tree still holding work REVERTED before the
commit landed (+7,879 ins / +130 uniq overstated) and never regenerated, so the next
HONEST digest read as a fall. => THE ALIAS LEVER IS UNGATED (scale it, §61 small batches).

Both recorded leads were wrong (R14): progress.py:423's SIG regex feeds fn-count ONLY
(neither weighted metric sees a C identifier — both derive matched = sig - corpus.stubs),
and "the harvest reverted functions to INCLUDE_ASM" died on one grep (483 removed, 0 added).
The 3-grep proof: identical sigs + unchanged tools/ + zero +INCLUDE_ASM => HEAD's stub set
is a strict subset => both numerators are FORBIDDEN to fall.

THREE INSTRUMENT DEFECTS, all one class (a bare except around a fail-CLOSED oracle):
- progress.py stub_addrs wrapped corpus.stubs in `except Exception: return set()`. An empty
  stub set means "could not answer", not "no stubs", so matched = sig - stubs credited EVERY
  function. Byte-witnessed: instr 100.00% / distinct 100.00% in a tree with no asm/. Now
  propagates.
- cast_call_sites.tu_for + reconcile_tu.tu_for had the identical swallow, falling back to the
  default <ov>.c instead of the jr/-O0 split TU — silently reinstating the exact bug
  cast_call_sites' own docstring says it exists to fix. A wrong-TU reconcile fails the gate,
  and this phase's base rate is ~24k PLUMBING vs 4,917 DIFF, so it presents as a codegen wall.
  Now propagate CorpusError; ValueError fallback for curated names preserved; derived-TU path
  re-verified (a _jr_ split stub resolves correctly, both tools agree).

NEW GATE (R34 — the byte-gate is a null oracle for DOCUMENTS; check-all stays 140/140 over a
stale digest forever): tools/audit_digest.py + `make audit-digest`, wired into tools-health
after report. Recomputes the three headline metrics from the current tree and fails if the
committed digest disagrees. Compares INTEGERS, not percentages — the +7,879-instruction
staleness printed as "94.4%" on both sides. Negative-control-proven against the stale 843
digest (fails, exit 1) and green on HEAD.

Verified: make report exit 0 (dedup-check 1910 validated / 0 failed, C1 coverage
241216/241216); audit-digest OK; cookbook-index OK (398 sections); metrics unchanged by the
fix (94.40% / 89.18%). No src/ or config/ edits — no bytes touched, nothing banked.

cookbook §140 · decision-log 2026-08-04 · SETUP.md inventory (R21) · R14/R32/R34/R35.
2026-08-04 21:48:57 -06:00
Drew T d8016c49c8 docs(phase-30 S38): checkpoint v4 — POST-S1d, fresh-session safe
Refreshes a checkpoint that had gone stale (v3 predated S1d) — stale is worse than absent.

FLEET 96.46 / 94.4 / 89.2, +37,166 instructions this session, ~0 agent tokens after the opening
wave. R22 run thirteen times: 140/140 on eleven, TWO REAL FAILURES (ov_SC07_010, ov_SC06_030), both
caught by the clean-tree rebuild after passing their per-binary gate, both reverted and recorded.

Records the session's biggest find: the §37/§124 DEFINITION-SIDE ASM-LABEL ALIAS is a CLASS lever,
not a one-off. It cracked the 208-conflict narrow-parameter class 138/138 after cast_call_sites,
--normalize-self-decls and --fix-def-sig were each eliminated BY MEASUREMENT. S33 proved it once and
it was never generalised.

Carries the unresolved accounting anomaly prominently (new task #11 / S1e): distinct-code FELL
89.3 -> 89.2 across the alias harvest while fn-count ROSE, which no pure naming artifact explains.
The bytes are proven; the yield number is not. Next session starts there, before scaling the lever.

Also records eleven tool defects fixed (nine of ten "walls" were our own instruments, two of them
mine), that §134 has now appeared in SIX tools and wants cdecl._mask rather than a seventh patch,
and seven process errors of my own including piping away a gate summary I then could not report.
2026-08-04 21:25:18 -06:00
Drew T 936cd07b58 feat(phase-30 S38/S1d): generalised alias harvest — R22 140/140, but distinct-code DROPPED (unexplained)
Applied the def-side asm-label alias transform to all 971 staged member drafts (of 997; the rest
had no func_<ADDR> definition head) and gated them: 192 source files changed, ~5,985 insertions.
R22 clean-fleet 140/140 AFTER reverting ov_SC06_030 (below).

METRICS, AS MEASURED — one of them moved the WRONG WAY and I cannot yet explain it:
    fn-count      96.32% -> 96.46%   (+483 fns)
    instr-weighted 94.4% ->  94.4%   (+2,990 ins)
    distinct-code  89.3% ->  89.2%   (78,025 -> 77,952 uniq, -73)
A revert of ONE overlay to HEAD cannot lose 73 distinct functions, so something else is going on.
LEAD (NOT CONFIRMED): progress.py's definition scanner records the identifier immediately before the
paren (SIG at tools/progress.py:423), so an alias definition `void aF80146A6C(...)` is recorded as
`aF80146A6C`, not as func_80146A6C. That same scanner's docstring documents this exact blindness for
K&R defs, where it "silently erased ~190k banked instructions". BUT that lead does not explain why
fn-count ROSE while distinct-code FELL — they should move together under a pure naming artifact.
DO NOT treat the alias harvest's yield as established until this is resolved: the bytes are proven
(R22), the ACCOUNTING is not.

ov_SC06_030 REVERTED: `D_800AF648' undeclared in func_8017E120 — a draft that gated fine broke once
the REST of the harvest landed in the same TU (its declaration presumably displaced by another
banked draft's preamble). SECOND instance today of "per-binary acceptance is not a fleet claim"
(§61), after ov_SC07_010. In a WIDE harvest it is not even a per-TU claim, and only the clean-tree
R22 catches it. The narrow single-family run (138/138) was clean precisely because it was narrow.
2026-08-04 21:13:19 -06:00
Drew T a5e97739fb feat(phase-30 S38/S1d): the def-side asm-label alias cracks the 208-conflict class — 138/138 banked
Family 0x80146ab4 (18 ins, x138, PURE) had been failing 0/138 with `conflicting types for
func_80146A6C` — 208 of the ~398 conflicts in the sweep residue, its single dominant blocker.

DIAGNOSED BY READING THE DRAFT, after three levers were eliminated by measurement:
    draft def : void func_80146A6C(s16 a0, s32 a1, s16 a2, s16 a3, u16 a4, s32 a5, s32 a6)
    TU decl   : extern s32 func_80146A6C(s32 a0, void *a1, s32 a2, s32 a3, s32 a4, s32 a5, s32 a6);
The NARROW PARAMS are the wall: C's default argument promotion means s16/u16 cannot agree with an
s32 prototype, and the `()` no-prototype escape is ILLEGAL precisely when a param promotes. Neither
declaration side can move.
  - cast_call_sites: already on by default; wrong axis (fixes CALLEE decls, not the def's own).
  - --normalize-self-decls: 0 banks + non-neutral reverts; wrong axis (the target's decl in callers).
  - --fix-def-sig: measured 0/138, error UNCHANGED — it cannot reconcile a promoting param at all.

THE ESCAPE (§37/§124, S33-proven on func_80147364 — definition (u16,u16) vs 4,046 fleet decls,
banked x137 first try; 1,725 in-tree precedents): give the DEFINITION a private C identifier and
bind the emitted symbol with a GNU asm label, so the TU's declaration never meets the definition and
its type becomes irrelevant. Zero blast radius on every caller; byte-neutral by construction.

    void aF80146A6C(<byte-true params>) __asm__("func_80146A6C");
    void aF80146A6C(<byte-true params>) { ... }

RESULT: 138/138 banked, ~2,484 instructions, ZERO agent tokens. R22 clean-fleet 140/140.
.run/alias_defs.py applies the transform to a staged draft set.

NEXT: this is a CLASS lever, not a one-family fix — generalise it across the remaining sweep residue.
2026-08-04 20:43:14 -06:00
Drew T 10f9546272 chore: regenerate the fleet digest + backlog after S1/S2/S3
docs/progress.fleet.md is the authoritative metric source the checkpoint's staleness self-check
compares against — committing it keeps that check meaningful for the next session.
2026-08-04 20:10:23 -06:00
Drew T a5c5526a8a docs(phase-30 S38): checkpoint v3 — S3 lands at 137/138, and the session's one R22 failure
FLEET 96.32 / 94.4 / 89.3 — +34,176 instructions this session, the great majority for ~0 agent
tokens. R22 run ten times: 140/140 on nine, ONE REAL FAILURE.

That failure is the most important line in the checkpoint: ov_SC07_010 passed its PER-BINARY build
and failed the clean-tree R22. Committing on that per-binary "BANKED" would have shipped a broken
overlay and reported 138/138. A per-binary pass is not a fleet byte claim (§61).

S3's recorded framing ("the T2 Arm-A %lo +0x20 carve defect") was WRONG and is corrected in place:
the carve was byte-neutral on the first attempt in all four overlays. The blocker is that carving
out of a jr file hoists the parent's file-scope decls into the region as its ambient set, so the
fleet's loose-typed spellings meet the shared header's for the first time.

Ten tool defects fixed this session, two of them mine — including an alias scanner that reproduced
the exact §134 comment-blindness defect I had documented hours earlier, caught by the R32 guard I
had added that same morning. §134 has now appeared in SIX tools, and the checkpoint records that the
real fix is routing line-shape decisions through cdecl._mask (R33) rather than patching a seventh.

Seven process errors of my own recorded, including reporting 138/138 before R22 had spoken.
2026-08-04 20:10:11 -06:00
Drew T 15f936e2ea feat(phase-30 S38/S3): the whale banks in 3 of the 4 SC07 overlays — 137/138, not 138/138
func_80144B9C (770 ins) was matched in 134/138 overlays and open in the four SC07s — 3,080
instructions of code BYTE-IDENTICAL (h_exact, reloc payloads included) to what was already banked,
blocked by a missing file boundary. Now banked in ov_SC07_006 / _007 / _011 for ~0 agent tokens.
R22 clean-fleet 140/140.

THE PLAN'S FRAMING WAS WRONG. This was recorded as "the SC07 carve defect (T2 Arm-A %lo +0x20)".
The carve was never broken: o0_subsplit reported `split byte-neutral` on the FIRST attempt in all
four. The real blocker is that carving the whale out of a jr file makes jr_isolate_all hoist the
parent's file-scope decls into the new region as its `ambient` set — so for the first time the
fleet's loose-typed spellings share a TU with the shared header's (`extern void *D_801274CC` vs
`extern s32 D_801274CC`). In the 134 working overlays the whale sits in a CLEAN -O0 file (common.h +
the header, nothing else) and the two never meet. Fix: drop, in that one file only, the ambient
decls the header already declares — the header being the byte-proven side.

Three iterations, each exposing the next layer of the ambient set, every one a DECLARATION:
  1. data symbols  (D_80126B58, D_801274CC, D_801274D0)
  2. function symbols (func_801336E8, func_8005C324)
  3. the alias form terminated by a trailing COMMENT, which an endswith(';') test skipped —
     the §134 comment-blindness shape for the THIRD time today.

ov_SC07_010 REVERTED and left open (hence 137/138). Its split landed in an EXISTING _o0c file
rather than a fresh _o0d, producing region _jr_801457A4 whose asm dir splat never generated. It
passed its per-binary build and FAILED the clean-tree R22 — the first R22 failure of the session,
and precisely why a per-binary pass is not a fleet byte claim (§61). Committing on that per-binary
"BANKED" would have shipped a broken overlay.
2026-08-04 20:09:10 -06:00
Drew T 3f7534c10a fix(tools): the alias scanner I added this morning had the §134 defect I documented this morning
asm_label_aliases scanned the RAW source with a greedy `[^;{}]*` that spans newlines. Byte-witnessed
on ov_SC07_006: a match STARTED inside a comment (`… -> MATCH (40 ins)`), ran through the `*/` and
two blank lines, and swallowed the real declaration below it — so the map recorded
`'MATCH': 'func_80146AFC'` while the actual alias `aF80146AFC` never appeared, and a
`register u8 *p __asm__("$6")` pin contributed `'void': '$2'`.

That is exactly the §134 multi-line-comment class whose project-wide answer is `cdecl._mask` (R33,
ONE masking oracle) — which I described in §139 this morning and then did not use. Sixth tool.

Two non-obvious things the fix had to get right:
 - REJECTING a bad match after scanning the source does not work: finditer resumes at the END of the
   match it yielded, so the greedy comment-spanning match CONSUMES the real declaration and
   rejecting it loses that declaration entirely (verified: aF80146AFC stayed invisible with a reject
   in place). The scan must run on the MASKED text so a match cannot start in a comment at all.
 - `_mask` blanks string content AND its quotes, so the scan pattern cannot require them. Added
   `_ALIAS_SCAN` (quotes optional) for the masked scan; the real symbol is read back from `src` at
   the same offsets, which is legal precisely because the mask is length-preserving.
`$N` targets are excluded — a hard-register pin is not a symbol alias.

Found because the R32 partition guard added earlier today REFUSED to rewrite the file rather than
silently dropping the function — the guard working as intended, on its first real encounter.
2026-08-04 19:50:11 -06:00
Drew T f9eaa671b7 docs(phase-30 S38): checkpoint v2 — S1/S2 landed; the residue is TWO symbols, not a missing tool
FLEET 96.32 / 94.3 / 89.1 (+19,300 instructions this session, the great majority for ~0 agent
tokens). R22 140/140 seven times.

S38 was eight tool defects, not a compiler problem — all eight enumerated in the checkpoint. The
headline: harvest_verify._reload_corpus deleting the stub it had just followed through a carve (10
of 16 wave-6 drafts vanished with no verdict), and jr_isolate_all SILENTLY DELETING definition-side
__asm__-alias functions during a repartition (the fifth tool with that same blindness, already fixed
in family_remap and never propagated).

T7 progress: S1a done (895 types lifted), S1c done (97 members banked, were 0), S2 done (10 members
banked + 7 families now CLASSIFIED by the reporting fix), S1b CLOSED BY MEASUREMENT — its premise
was wrong, cast_call_sites/tu-scope/scope_data_fix are all already on by default in hseq_sweep, and
--normalize-self-decls got a fair post-fix re-test and does nothing here.

The real residue is 383 of ~398 conflicts on TWO symbols: func_80146A6C (fleet decls UNIFORM -> the
draft is wrong, a bug hunt) and func_80161208 (four incompatible fleet shapes -> the genuine
Phase-16 loose-typing wall). Recorded as task S1d, explicitly NOT scoped as "add a reconcile pass".

Six process errors of my own recorded, the worst being that I asserted family_sweep hides its
per-member errors when 23,211 classified files exist and both of the day's zeros were already
diagnosed in them.
2026-08-04 19:44:31 -06:00
Drew T b410df3517 feat(phase-30 S38/S2): jr-family sweep — func_80183AE0 x3 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr families
BY DESIGN — a 0% from the carve-less tool is an artifact, not a wall). Each sibling
individually byte-gated: carve -> extract -> remap -> whole-binary build, kept iff
byte-identical else reverted. Committed per family because jtbl_family_bank requires a
clean tree between families. R22 clean-fleet runs once over the batch.
2026-08-04 19:40:33 -06:00
Drew T ec7345217f feat(phase-30 S38/S2): jr-family sweep — func_8017FEE0 x4 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr families
BY DESIGN — a 0% from the carve-less tool is an artifact, not a wall). Each sibling
individually byte-gated: carve -> extract -> remap -> whole-binary build, kept iff
byte-identical else reverted. Committed per family because jtbl_family_bank requires a
clean tree between families. R22 clean-fleet runs once over the batch.
2026-08-04 19:37:24 -06:00
Drew T 5e30186847 feat(phase-30 S38/S2): jr-family sweep — func_801380E0 x3 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr families
BY DESIGN — a 0% from the carve-less tool is an artifact, not a wall). Each sibling
individually byte-gated: carve -> extract -> remap -> whole-binary build, kept iff
byte-identical else reverted. Committed per family because jtbl_family_bank requires a
clean tree between families. R22 clean-fleet runs once over the batch.
2026-08-04 19:35:45 -06:00
Drew T 2ab4aa3571 feat(phase-30 S38/S2): jr-family sweep — func_80181CE4 x3 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr families
BY DESIGN — a 0% from the carve-less tool is an artifact, not a wall). Each sibling
individually byte-gated: carve -> extract -> remap -> whole-binary build, kept iff
byte-identical else reverted. Committed per family because jtbl_family_bank requires a
clean tree between families. R22 clean-fleet runs once over the batch.
2026-08-04 19:33:14 -06:00
Drew T df99a71732 fix(tools): jtbl_family_bank reported "gate-fail" with no reason — the third missing-payload defect today
`last_err` was only ever set when a STAGE failed to produce a candidate. A candidate that built to
the wrong bytes — i.e. the actual gate rejection — recorded nothing, so every caller saw a bare
"gate-fail" and the failure was unroutable (measured: wave 6's 13 sibling failures could not be
classified at all). Now captures the build's hard diagnostics, keeping BOTH ends of the line so the
symbol survives (never left-truncate — same fix as harvest_verify.classify_fail earlier today), and
says "built, bytes differ (genuine DIFF)" when there is no diagnostic.

Third instance of one defect class this session: a tool reporting an OUTCOME without the EVIDENCE
that routes it (harvest_verify.classify_fail truncating the symbol away; .run/s6f_gate.py booking a
crashed child as silence; this). All three were quietly converting recoverable plumbing into
apparent walls.

Also adds .run/jr_family_sweep.py — the S2 front end for the has_mid_jr matched-exemplar families
that family_sweep --hseq refuses BY DESIGN (§53). Families are DERIVED from the map and looked up by
EXEMPLAR (a just-banked head leaves the `members` array — that cost a round earlier today), members
re-checked against corpus.stubs, --raw passed when a seed exists, and it commits per family because
jtbl_family_bank requires a clean tree between families.
2026-08-04 19:31:33 -06:00
Drew T be2eaa1842 feat(phase-30 S38/S1c): re-sweep the matched-exemplar families after the type lift — 97 members banked
Families that returned 0/N before the 895-type lift now bank: 97 member-matches across 137 families
(1,138 failed; skipped 186 STRUCT-class by design, 106 unresolved-immediate, 6 not-stub).
R22 clean-fleet 140/140. Fleet 94.2 -> 94.3% instr / 88.9 -> 89.1% distinct / 96.29 -> 96.32% fn.

RESIDUE PRICED FROM THE SWEEP'S OWN .classified.txt PAYLOADS, not inferred: in the 400 most recent
failure records, 133 are genuine DIFF and the clear majority are `conflicting types for <sym>` —
the §103/§20 extern-conflict class, across ~12 overlays. That confirms S1b (wire reconcile_tu /
cast_call_sites into the --hseq path) is the correct next lever, and it is now justified by
measurement rather than by the plan's projection.

Method note worth keeping: those per-member diagnoses have been written on every sweep run for a
month and were never read — including by me, until after I had spent two probes and a manual
--stage-only round rediscovering one of them. Every remaining task now starts by reading the payload.
2026-08-04 19:04:56 -06:00
Drew T c57860db4e feat(phase-30 S38/S1a): lift 895 local types to engine_types.h — kills the type-scope sweep class
The free-sweep "wall" was a C parse error: a remapped member body names the EXEMPLAR's TU-local
types, which are undeclared in the sibling's TU, so gcc-2.7.2 parses the declarator as an expression
and dies before ever reaching codegen. extract_unit does carry typedefs, but only ones immediately
preceding the function in the preamble — types declared elsewhere in the exemplar's TU are missed.

Rather than patch the scanner per-family, remove the class: lift every liftable local type into the
shared header once. 895 types lifted, 6,196 local definitions stripped across 1,259 files.
R22 clean-fleet 140/140.

EXCLUDED s8/s16/s32/u8/u16/u32/f32/s64/u64/f64 — lift_types classified those common.h scalars as
liftable and lifting them would have been actively harmful. The tool's own visibility guard kept 8
local defs in src/ov_SC01_077/ov_SC01_077_o0.c, which does not include engine_types.h (stripping a
type out of a TU that cannot see the replacement DELETES it, and the link error that follows names
an unrelated data symbol).

Mechanism proven before scaling: lifting just 3 types took 0x801833f0's family from 0/6 to 6/6.
2026-08-04 18:46:30 -06:00
Drew T 97a6864677 docs(phase-30): adopt the Fable-5 frontier plan as T7 + persist the report
The Fable-5 agent's harness blocked it from writing to disk, so its full analysis existed ONLY in
the completion notification — one session away from being lost. Transcribed verbatim to
.run/fable_frontier/ANALYSIS.md and force-added (with its computed pools, pool_ovres.json), and
CURRENT_PHASE.md's new T7 points at it so future sessions know where the detail lives.

T7 supersedes T6's wave ordering on one number: across every sweep run this project has done,
blockers are ~24k PLUMBING vs 4,917 DIFF (5:1). Fixing plumbing moves work from the ~490 tok/ins
wave column to ~0, and S1 restores the propagation multiplier that makes every later wave ~3x
cheaper. DO S1 BEFORE ANY WAVE.

S1-S7 recorded with reachable-ins and tok/ins estimates. Provenance is explicit: the two headline
refutations were independently re-verified (23,211 classified files exist; 0x801833f0 went 0/6 ->
6/6 after the type lift, R22 140/140); the POOL NUMBERS are the agent's own computation and are
NOT re-verified — flagged in the file to verify before scaling a wave onto them (R14).

Standing pre-probe rule added: check h_norm identity across members BEFORE probing (an h_norm-
identical family returning 0% is a compile-error certainty), and read the classified files before
theorising about a sweep failure.
2026-08-04 18:40:14 -06:00
Drew T d053d71a27 docs(phase-30 S38): STRIKE my "x138 era is over" reading — the base rate is 5:1 PLUMBING, not codegen
A Fable5 frontier pass (read-only) refuted four S38 claims, each against data already in the repo:
 - "4 of the 8 big families collide by chance" -> all four classify PURE or IMM x138, and PURE means
   every differing word sits at a RELOC position, i.e. the OPPOSITE of chance collision. I printed
   diff_class in my own table and misread it. 0x80161418 has 552 already-matched siblings.
 - "0x80175820 / 0x80132018 are not templatable" -> IMM x137 (7,535 ins) and PURE x132 (6,072 ins).
 - "the free-sweep zeros are undiagnosed" -> both were PLUMBING, both diagnoses written by the sweep
   itself at probe time into .run/hseq_failed.*.classified.txt (23,211 such files exist).
 - "family_sweep hides the per-member error" -> false, struck.
THE BASE RATE settles it: across all sweep runs, blockers are ~24k PLUMBING vs 4,917 DIFF (5:1).

What survives: the open-member distribution really does skew small. What does not: the conclusion I
drew from it. Only the 2 GIANT walls survive scrutiny, and they are EXEMPLAR walls whose 138 members
each classify PURE — 50,094 ins riding on 2 cracks.

Newly visible and untargeted: 1,689 open-only h_norm clusters / 5,956 fns / 326,261 ins = 46% of all
open overlay instructions, at a 2.7x propagation multiplier; plus 250 fns / 12,981 ins byte-identical
to matched code (incl. the whale, open only in the 4 SC07s).

Ranked plan recorded: S1 (fix the 2 plumbing classes, re-sweep the 148 matched-exemplar families)
reaches 35-60k ins at ~10-25 tok/ins against a 490 t/ins wave baseline. S1-S4 move 80-115k ins to
~0 tokens and restore the propagation multiplier. DO S1 BEFORE ANY WAVE.

Standing pre-probe rule added: check h_norm identity across members first — if they are h_norm
identical, a 0% is a compile-error certainty, not evidence about codegen.
2026-08-04 18:36:51 -06:00
Drew T 91bb3ac76b feat(phase-30 S38): the free-sweep "wall" was a TYPE-SCOPE parse error — 0/6 becomes 6/6
family_sweep --hseq returned 0/6 on 0x801833f0 (328 ins, PURE, matched exemplar) and I recorded it
as evidence that h_seq families do not template. It was a C PARSE ERROR: the remapped member body
carries the EXEMPLAR's TU-local type names (PTag_801833F0 / Ft4_801833F0 / Drm_801833F0), which are
declared only in ov_SC02_028's TUs. Undeclared type -> gcc-2.7.2 parses the declarator as an
expression -> "parse error before `vtx'" two lines later. Never reached codegen.

Lifting the three types to src/shared/engine_types.h (lift_types --apply; each had ONE canonical
definition, no variants) turns the same sweep into 6/6 banked. R22 clean-fleet 140/140.

This is the §20 propagation cap resurfacing on the h_seq sweep path, where nobody had checked for it.

MY ERROR, RECORDED (R37/R14): I claimed in the S38 checkpoint and in commit commit:1410 that
"family_sweep reports banked/failed WITHOUT the per-member build error". That is FALSE. There are
23,211 .run/hseq_failed.*.classified.txt files on disk; the diagnosis for BOTH of today's zeros was
written by the sweep itself at probe time (0x80128c98's says "PLUMBING: conflicting types for
`cdFileLocTable'"). I asserted a tool limitation without checking for it, and then spent two probes
plus a manual --stage-only round rediscovering what was already in a file. Probe before costing.
2026-08-04 18:36:04 -06:00
Drew T 2483fc902a fix(tools): jr_isolate_all was SILENTLY DELETING asm-label-alias definitions during a repartition
ROOT CAUSE (byte-witnessed, P30 S38 — the fifth tool with this same blindness).
A function banked under the §37/§73 DEFINITION-SIDE ASM-LABEL ALIAS form is spelled with a private
C identifier and bound to its real symbol by a GNU asm label:

    void aF8018A860(s32, s16 *, u8 *, u8 *) __asm__("func_80183AF8");   <- decl, stays in preamble
    void aF8018A860(s32, s16 *, u8 *, u8 *) { ... }                     <- THIS emits func_80183AF8

overlay_src_split.addr_of() resolves `func_<hex>` arithmetically and everything else through `syms`.
`aF8018A860` matches NEITHER, so it returned None — and partition() keeps only items with a
resolved address, so the definition was dropped from EVERY region. The file was then rewritten
without it and nothing said so. One carve of ov_SC02_028 deleted the definitions emitting BOTH
func_80183AF8 and func_80184268; the overlay stopped linking with `undefined reference`, and six
wave-6 drafts were written off against that as a plumbing/compiler wall.

TWO FIXES:
 - CAUSE: overlay_src_split now builds an asm-label alias map from the source and resolves a
   definition through its EMITTED SYMBOL rather than its C name (verified: aF8018A860 -> 0x80183AF8,
   aF8018AFD0 -> 0x80184268 — exactly the two symbols the link was missing).
 - SILENCE: partition() and jr_isolate_all._partition() now REFUSE to rewrite a file when any
   construct's address does not resolve (R32), instead of discarding it. That guard alone would
   have surfaced this the first time it happened.

RESULT: 3 of the 6 alias-class wave-6 drafts bank immediately, for ZERO agent tokens —
func_801884D8 (137 ins) · func_80180B04 (251) · func_801380E0 (438). R22 clean-fleet 140/140.
The other 3 (the three LARGEST: 557/513/710 ins) have a second, size-correlated cause — open.

NOTE FOR THE FLYWHEEL: family_remap._alias_decl_for ALREADY handled this exact form, and its
docstring records the identical lesson ("that blindness was the WHOLE of the h_seq sweep's 137 'no
matched unit' skips. The tool, not the compiler (R35)"). The fix was never propagated. The alias
form needs ONE shared oracle, the way §134 comment-masking ended up on cdecl._mask — five tools
have now independently rediscovered it.
2026-08-04 18:30:01 -06:00
Drew T c21a310cce docs(phase-30 S38): measure the family frontier — the x138 era is over; the free-sweep lane is 0/0 UNDIAGNOSED
Answers "can we still derive an exemplar and free-crack hundreds?" with the distribution:
families YES, hundreds-per-crack NO. Only 8 families have 100+ open members — 2 permanent walls,
2 ledgered not-templatable, and 4 that are 14-25-ins functions whose h_seq skeleton collides by
CHANCE (0x80128c98 probed 0/138). The frontier is now 1,534 families at 2-4 members plus 3,807
singletons. A crack is worth 2-9 members, not 138 — wave 6's func_8017FEE0 (19 members, 4,485 ins)
was near the TOP of what remains. Throughput of cracks now beats leverage per crack, which argues
FOR the 64-target wave shape rather than against it.

The 147 matched-sibling families (66,971 ins) looked like free sweep fuel. Two probes returned 0
(0/138 small-skeleton, 0/6 on the top 328-ins PURE non-jr family). RECORDED AS A PENDING QUESTION,
NOT A WALL: the cause is undiagnosed, and this project has manufactured a false wall from exactly
this evidence before (P26's "families template ~0%" was overturned in P28 when the 0/8 turned out
to be a missing carve and the family then banked 102/115). family_sweep reports banked/failed
without the per-member build error, so DIFF and PLUMBING are indistinguishable at this level —
the same missing-payload gap as jtbl_family_bank's bare "gate-fail". One member through
harvest_verify with the error read settles ~26k instructions.
2026-08-04 18:20:12 -06:00
Drew T 8057709407 fix(.run/w6_pool.py): "attempted" must mean a wave that RAN, not a manifest that exists
Two fixes to the pool derivation:
 - the previously-attempted glob was `s*_wave.json`, which misses `w6_wave.json` — so wave 6's own
   failed targets would have been silently re-offered to wave 7;
 - but widening it to `*_wave.json` introduced the opposite defect: a STAGED manifest counted as
   attempted, so deriving a 16-target wave and then a 64-target wave made the second exclude the
   first's 16 best entries (pool 2,928 -> 2,913 -> 2,893 across three derivations, each poisoned by
   the last). Now a manifest counts only if its drafts directory exists: `<tag>_wave.json` ran iff
   `.run/<tag>/*/*.c` is non-empty. A manifest with no drafts is a PLAN, not a result.

Wave 7 staged in two sizes off one clean pool (2,913 fresh families / 432,020 open templ ins):
  .run/w7_wave.json    16 targets / 19,088 templ ins
  .run/w7_wave64.json  64 targets / 52,483 templ ins   <- the ~50k shape
NOTE the 50k now costs 64 targets, not 16: wave 6 harvested the head of the distribution (its top
single target was 15,152 ins), so the frontier is now a long tail of ~1,000-1,500-ins families.
2026-08-04 18:12:18 -06:00
Drew T bc04f65562 docs(phase-30 S38): checkpoint — wave 6 banked (+10,616 ins), the gate defect fixed, 9 drafts diagnosed
FLEET 96.29% fn / 94.2% instr / 88.9% distinct, R22 140/140 twice. Session banked 7 wave-6 heads
+ 25 propagated siblings = +10,616 instructions (12,368,236 -> 12,378,852; the digest delta matches
the hand-derivation exactly).

wave-metrics.md gains Findings 5 and 6:
 - RANK WAVES BY INSTRUCTIONS, NOT HEADS. Wave 6 banked ~45% more instructions than wave 5 while
   banking less than half as many heads, because a bigger head carries more instructions AND its
   family propagates at the same cost per sibling (func_8017FEE0: ONE 299-ins head -> 4,485 ins
   across 15 siblings, ~0 agent tokens). The metric to beat is POOL REALISATION (21%), not bank
   rate — and wave 6's bank rate is NOT comparable to waves 3-5 because the difficulty knob moved
   deliberately (median target 438 ins vs 143, mostly has_mid_jr).
 - A WAVE TALLY IS A COVERAGE CLAIM and needs its own assertion.

The 9 unbanked drafts are diagnosed and preserved, and 6 of them are ONE class worth cracking
first: `undefined reference` to a sibling that IS defined in the overlay but only via a
definition-side __asm__ alias (§37/§124) — the carve repartitions the object and separates the call
site from the alias definition. One fix frees 6 already-paid-for drafts and will recur in every
carve-heavy wave. The other two are genuine near-misses (close=47 REGALLOC-PERM, close=370).

Tooling committed for reuse: w6_pool.py (the pool derivation, now a script), w6_diag.py (runs the
REAL gate path — s36_capture.py splices without the carve and is wrong for jr targets),
w6_jtbl_prop.py (carve-path propagation with the per-family commit jtbl_family_bank requires).
2026-08-04 18:10:19 -06:00
Drew T 6b21af6a14 fix(tools): classify_fail truncated its diagnostic from the LEFT, severing the symbol name
These diagnostics are "<long path>:<position>: <message>", and the MESSAGE names the symbol —
which is the entire routing value of the PLUMBING/CC1-FAIL label. The old [:90] spent its budget
on the path and cut the payload mid-token, so a real wave-6 failure was labelled:

  PLUMBING: src/ov_SC03_001/..._jr_8017AE2C.c:(.text+0xc090): undefined reference to `func_801

with the symbol severed at four hex digits — unroutable. New _squeeze() keeps a short head (the
file is still identifiable) and the tail (the symbol), so the same line now ends
"undefined reference to `func_80143C74'".

Same family as the §58 red-herring guard directly below it and §136a: a label that cannot
distinguish its inputs carries no information, and one that drops the payload is worse than none.
2026-08-04 17:55:51 -06:00
Drew T 7247c68b60 feat(phase-30 S38): wave-6 propagation — func_8017EA84 x2 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr
families by design). Each sibling individually byte-gated: carve -> extract ->
remap -> whole-binary build, kept iff byte-identical else reverted. Committed here
because jtbl_family_bank requires a clean tree between families (its per-sibling
revert restores from HEAD). R22 clean-fleet runs once over the whole batch.
2026-08-04 17:55:31 -06:00
Drew T 81c5cc559a feat(phase-30 S38): wave-6 propagation — func_80184C74 x5 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr
families by design). Each sibling individually byte-gated: carve -> extract ->
remap -> whole-binary build, kept iff byte-identical else reverted. Committed here
because jtbl_family_bank requires a clean tree between families (its per-sibling
revert restores from HEAD). R22 clean-fleet runs once over the whole batch.
2026-08-04 17:54:54 -06:00
Drew T 8f3d8662b6 feat(phase-30 S38): wave-6 propagation — func_80183AE0 x6 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr
families by design). Each sibling individually byte-gated: carve -> extract ->
remap -> whole-binary build, kept iff byte-identical else reverted. Committed here
because jtbl_family_bank requires a clean tree between families (its per-sibling
revert restores from HEAD). R22 clean-fleet runs once over the whole batch.
2026-08-04 17:53:33 -06:00
Drew T c44b980fe4 feat(phase-30 S38): wave-6 propagation — func_80181CE4 x6 sibling slots
jtbl_family_bank (the §53 carve path; family_sweep --hseq refuses has_mid_jr
families by design). Each sibling individually byte-gated: carve -> extract ->
remap -> whole-binary build, kept iff byte-identical else reverted. Committed here
because jtbl_family_bank requires a clean tree between families (its per-sibling
revert restores from HEAD). R22 clean-fleet runs once over the whole batch.
2026-08-04 17:52:14 -06:00
Drew T 65f0184f7d chore: regenerate docs/family-hseq.md after the wave-6 banks
Derived digest; committed so the next family map regen has a clean base.
2026-08-04 17:50:35 -06:00
Drew T ba87a9b256 feat(phase-30 S38): wave-6 propagation family 1 — func_8017FEE0 x15 siblings
jtbl_family_bank (the §53 carve path — family_sweep --hseq correctly REFUSED these as has_mid_jr
families, warning that a 0% from the non-carve path would be a TOOL artifact, not a wall).
func_8017FEE0 (299 ins, cross-address family of 19 open siblings): 15 BANKED, 4 gate-fail.
Each sibling individually byte-gated by jtbl_family_bank (carve -> extract -> remap -> build, keep
iff byte-identical else revert). R22 clean-fleet runs once over the whole propagation batch; this
commit exists because jtbl_family_bank REQUIRES a clean tree between families (its per-sibling
revert restores from HEAD, so an uncommitted prior family would be destroyed).
2026-08-04 17:50:05 -06:00
Drew T e977205bb8 docs(cookbook): §139 — a gate that greps for verdicts must assert 1:1 accounting
The wave-6 gate printed BANKED 5 / FAILED 1 over SIXTEEN drafts and nothing said so. Captured with
the three corollaries that outlive the bug (R30 — written in the session that produced them):
 (a) the byte-gate is a perfect CORRECTNESS oracle and a NULL COVERAGE oracle (R34 again), so every
     wave-level tally is a coverage claim and needs its own assertion;
 (b) a pipeline's exit status is the LAST command's — "family_sweep ... | tail" hid a non-zero exit
     in this same session; use pipefail/PIPESTATUS or the tail IS the error handler;
 (c) a reverted CONFIG needs a re-extract before the next measurement means anything (the Phase-20
     R22 corollary; skipping it made three genuinely-banked functions read as failures).
Plus the inverse-lookup trap: a just-banked head LEAVES its family's members list and becomes
exemplar.kind='matched', so a member-list lookup reports "no family" for exactly the heads you just
banked. Distinct from §138 rule 4, which governs target SELECTION.

Index regenerated: 393 sections, 3 new symptom keys.
2026-08-04 17:50:05 -06:00
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 fd1bdd4b2f chore(phase-30 S38): preserve wave-6 drafts before gating (5.2M agent tokens)
16 drafts, 14 claiming MATCH. .run/ is gitignored, so these are force-added: the wave-5 restart
proved the Workflow resumeFromRunId cache is SAME-SESSION-ONLY, and a draft that has to be re-earned
costs ~300k tokens. Gate follows; the byte-gate is the sole arbiter (G3/P9) and these are claims.
2026-08-04 17:24:04 -06:00
Drew T e5380ff6fb fix(tools): family_hseq's stdout claimed "fleet" for an OVERLAYS-ONLY number
load() globs .run/sig.ov_*.jsonl, so main and the resident are absent from every denominator —
the numbers run ~0.3-1.0pp above the authoritative `make report` fleet (measured today: 96.6/94.6/
89.7 vs the digest's 96.28/94.1/88.7). docs/family-hseq.md has always carried the "(overlays)"
qualifier; the stdout print did not.

That asymmetry matters because stdout is the channel a session actually reads and transcribes into
a checkpoint — a right number under a wrong label is how a wrong number propagates (R35: the
measurement was fine, the instrument's LABEL was the defect; R14: a checkpoint that disagrees with
the digest must lose, and it can only lose if the disagreement is visible).

Print-only; the metric itself is unchanged and correct for its scope.
2026-08-04 16:14:13 -06:00
Drew T 139e21118b docs(phase-30 S38): wave 6 launched — pool derivation is a script now, + 2 derive-don't-assert fixes
- .run/w6_pool.py: THE pool derivation, finally a script instead of inline python (R33).
  Ranks by OPEN templatable weight (corpus.stubs over the family map's member list, §138 rule 4 —
  never off the map's `exemplar` field), carries the walls/ledgered-residual exclusion set, drops
  any family whose address was attempted-and-still-open in a prior wave (83 addrs), and enforces
  one target per (overlay, TU) so a wave cannot manufacture its own §138-rule-2 type-tag conflict.
  Pool: 2,928 fresh families / 476,611 open templatable ins.
- wave 6 = 16 targets / 50,596 open templatable ins (3x wave 5), large-function heavy
  (median ~270 ins, max 947); prompt carries the S37/W6 block (stage the big ones; cross-address
  families need STEP 0 because searching by address cannot find their twin; the PAIR rule).
- TWO R37 FIXES TO THE MANIFEST ITSELF, both "asserted, never derived":
  * `seed` was an arbitrary i%2 alternation carried over from the previous wave's shape — it
    controls whether the prompt claims a cached Ghidra seed EXISTS. Now derived from
    .run/ghidra_c/ on disk: 9 of 16, not 8 by alternation.
  * `model` routed by MEASURED band, not the stale §136i ladder: Sonnet is measured 81-100%
    first-pass over 125-793 ins, so func_8017C974 (947) is outside it and goes to Opus directly.
- CURRENT_PHASE.md: in-flight checkpoint (the wave-5 restart lost 2.7M tokens of ungated drafts;
  this block says force-add the drafts and resume by GATING, never by re-running).
2026-08-04 16:13:14 -06:00
Drew T bd097a9010 docs(phase-30): checkpoint — name the code commit correctly + add a staleness self-check 2026-08-04 13:47:42 -06:00
Drew T b42b1885ae docs(phase-30): SESSION-33..37 checkpoint — wave 5 banked, nothing owed; wave-metrics findings reordered 2026-08-04 13:43:53 -06:00
Drew T af16c38a62 feat(phase-30 S37): wave 5 banks 16/16 with ZERO reconcile + 26 members; wave metrics logged
Fleet 96.27 -> 96.28% fn-count / 94.1% instr / 88.6 -> 88.7% distinct (77,765
uniq). R22 clean-fleet: 140 passed, 0 failed of 140. dedup 1910/0.

16 targets / 16,884 templatable ins. 19 agents, 2.71M tokens, 82 min wall.
The gate banked 16/16 — the FIRST perfect gate of the session, and the first
needing NO reconcile at all. Sweep: +26 members / 3 failed across 17 overlays.

NEW: docs/wave-metrics.md — the wave-by-wave performance log, with the derivation
commands so future rows are COMPUTED, not hand-transcribed (R33). Four findings,
each recorded with its caveat rather than as a bare number:

 1. THE PROMPT IS THE LEVER, AND THE AGENTS WRITE IT. Bank rate 76 -> 77 -> 100
    -> 100 -> 100% with models and gate held constant. The jump was STEP 0 (a
    magic-literal grep of src/, ahead of engine_core.h) — which came from a
    wave-2 agent's index_gap report. Caveat recorded: waves 3-5 targets also
    trended easier, so the mechanism is the durable claim, not the exact %.
 2. pipeline() vs batched parallel(): 136 min/14 targets -> 82 min/16 targets,
    parallelism 2.5x -> 3.8x. The two-batch design was a hard barrier with 37-50
    min dead gaps; the harness already caps at 16 so it bought nothing. Floor
    recorded honestly: the slowest agent is still ~50 min of real match_one
    iteration, so the lever there is target SELECTION, not concurrency.
 3. ECONOMICS: ~170-300k tokens per banked head in the stable regime — but a head
    is not the unit of value. Head + propagated members is, and sweep yield is
    BIMODAL not average (21/21 vs 18/165), because it is a property of the FAMILY.
    Averaging those two predicts nothing.
 4. A perfect gate is a signal the prompt rules landed. Waves 1-4 each needed 1-2
    post-gate reconciles; wave 5 needed zero. The reconcile lane is the fallback,
    not the plan. Lifetime 21/22.
2026-08-04 13:42:58 -06:00
Drew T 3f51101ca9 docs(phase-30): SESSION-33..37 checkpoint — PAUSED; 16 ungated wave-5 drafts preserved in git
Paused at Drew's request for a Windows restart. Nothing running, tree lock free,
tree clean (R23 db churn aside). R22 run 19x this session, 140/140 every time.

THE ONE THING OWED: `.run/s37/*/func_*.c` — 16 wave-5 drafts, all claiming MATCH,
NONE gated. Force-added to git (.run/ is gitignored) because they cost ~2.7M
agent tokens and Workflow's resumeFromRunId cache is SAME-SESSION-ONLY, so it
does not survive the restart. Resume by GATING them, not by re-running the wave.
Also preserved: the wave scripts (.run/s36w.js, .run/s37w.js), manifests, the
hardened gate driver, and the four capture drivers.

MEASURED THIS SESSION (both answer questions Drew asked):
- The wave PROMPT is the lever. Bank rate 76% -> 77% -> 100% -> 100% on the same
  models and the same gate, prompt the only variable. The jump was STEP 0 (a
  magic-literal grep of src/, ahead of engine_core.h) — and that step came from a
  wave-2 agent's index_gap report, i.e. the agents write the next prompt.
- The pipeline() fix, before/after: wave 4 parallel() 14 targets / 136 min /
  2.5x parallelism; wave 5 pipeline() 16 targets / 82 min / 3.8x. 40% faster on
  14% more targets. The two-batch design was a hard barrier with 46-min dead gaps
  at each boundary; the harness already caps at 16 so the batching bought nothing.

Housekeeping: removed 3 stray cc1 intermediates (t.i, t.i.greg, t.s) that an
agent left at the repo ROOT — scratch belongs under .run/ (R12), same class as
the gccdump.lreg noted at the Phase-24 close.
2026-08-04 13:29:51 -06:00
Drew T 9064840757 feat(phase-30 S36): wave 4 banks 14/14 + 26 members — step 0 is now the agents' default move
Fleet: R22 clean-fleet 140 passed, 0 failed of 140. dedup 1910/0.

14 targets / 16,844 templatable ins. 17 agents, 3.0M tokens. Claimed 14/14; the
whole-binary gate banked 13, the 14th on reconcile. Sweep: +26 members / 0 failed
across 18 overlays. Reconcile lane 21/22 lifetime.

STEP 0 HAS BECOME THE AGENTS' DEFAULT MOVE. Nearly every wave-4 verdict cites the
cross-overlay magic-literal grep BY NAME, several reporting `index_gap: none`
because it resolved the target in one pass with no cookbook derivation needed:
 - func_8018BED0: grep 0xE100000A -> func_80188C04 (ov_SC03_089), verbatim, MATCH first try
 - func_8018BAB4: grep D_800A6610/D_800B9A02 -> func_801887E8, verbatim + callee swap
 - func_8017ED54: grep named all 5 family members -> reused func_8017D9F0's body, 14 data remaps
 - func_8017C290: grep found byte-identical twins ALREADY banked in two other overlays
Bank rate by wave, same models + same gate, prompt the only variable:
  76% -> 77% -> 100% -> 100%.

THE ONE FAILURE IS THE §138 RECONCILE-DIRECTION RULE, in its purest form:
`redefinition of struct B16_8018A758` — the agent copied its sibling's struct tag
verbatim, and that sibling had banked into the SAME TU earlier in THIS wave. Decl
ABOVE the splice => DELETE the duplicate (do not rename it). Worth noting the
mechanism: a wave can create its own reconcile work when two targets share a TU.

func_8018A808's own family swept 0/14 — its members are the per-location kind
that do not template (the settled h_seq ceiling), not a plumbing failure.
2026-08-04 12:05:32 -06:00
Drew T cbf0bce26c feat(phase-30 S35): wave 3 banks 13/13 — the flywheel paid off one wave later
Fleet 96.24 -> 96.25% fn-count / 94.0% instr / 88.4 -> 88.5% distinct.
R22 clean-fleet: 140 passed, 0 failed of 140. dedup 1910/0.

13 targets / 17,644 templatable ins. 14 agents, 2.7M tokens. Claimed 13/13;
the whole-binary gate banked 12, the 13th on reconcile (another §37/§124
SELF-axis alias — the TU declares `(void)`, the def takes an s32). Reconcile
lane 20/21 lifetime. Sweep: +21 members / 0 failed across 16 overlays.

THE FLYWHEEL, MEASURED ACROSS THREE WAVES (same models, same gate):
  wave 1  baseline prompt              15/17 claimed -> 13 banked (76%)
  wave 2  + the S33 rules              11/13         -> 10        (77%)
  wave 3  + S34 magic-grep as STEP 0   13/13         -> 13       (100%)
Multiple wave-3 agents report the cross-overlay magic-literal grep landing the
answer on the FIRST search. One found a banked twin whose own header comment
already documented it as byte-identical to the new target, so the body
transferred verbatim with only file-local type suffixes renamed. That is the
wave-2 discovery paying off one wave later (R16).

Sweep quality also differed for a reason worth keeping: 21/21 here vs 18/165 in
wave 2. Wave 2's two big families are the per-location kind I then probed and
ruled out (BUILD OK + byte diff = genuine per-member codegen, not plumbing);
wave 3's are genuinely templatable. The sweep rate is a property of the FAMILY,
not of the wave.

TOOLING: an agent left 8 scratch files (test_licm*.c) in the drafts dir and the
gate driver died on `int('full', 16)`, taking the whole gate with it. Hardened to
treat a non-conforming filename as a NAMED, COUNTED skip rather than a crash
(R32) — a drafts dir is agent-writable by design, so it must not be trusted to
contain only deliverables.
2026-08-04 09:42:58 -06:00