The one-line fix committed ahead of this run (CdFileLoc_80128C98 aliasing CdFileLoc) cleared the
largest remaining propagation-sweep class. Re-sweep: 138 member-matches banked, failures 875 -> 737,
`conflicting types for cdFileLocTable` gone entirely (136 -> 0).
Derived net (138 INCLUDE_ASM removed, 0 re-added) equals the report's 138 — they agree.
R22 clean-fleet: check-all 213 passed / 0 failed of 213.
Fleet 94.2 -> 94.3% instr / 87.9 -> 88.1% distinct / 96.15 -> 96.21% fn-count; stubs 13,780.
Residue reclassified — no symbol dominates any more: 227 PLUMBING-other, 125 DIFF (real byte
divergence, 17%), 93 CC1-FAIL(no-diagnostic), 26 memcpy, then a tail of small data-symbol
conflicts (D_80114F24 12, D_800AE620 11, D_800183E0 9, D_80126B58 6, D_80078EB4 6).
CC1-FAIL rose 77 -> 93 and that is NOT a regression: members that previously died earlier on the
cdFileLocTable conflict now reach a different compile error. Those 93 are hard gcc errors whose
text the sweep's classifier discards because it greps for `error:`, which gcc-2.7.2 never emits on
hard errors. That classifier is now the highest-value instrument fix left — three times today a
no-diagnostic verdict concealed something cheap.
`conflicting types for cdFileLocTable` was the single largest remaining propagation-sweep failure
class (136 of 875). Cause: engine_types.h defined the SAME layout twice —
typedef struct { s32 word0; s32 word4; } CdFileLoc;
typedef struct { s32 word0; s32 word4; } CdFileLoc_80128C98;
Each anonymous struct definition mints a DISTINCT C type, so a TU holding both
`extern CdFileLoc_80128C98 cdFileLocTable[]` (137 sites) and `extern CdFileLoc cdFileLocTable[]`
(9 sites) is declaring one object with two incompatible types. This is the same type-IDENTITY
collision scope_data_externs documents for S_AF634: no type-STRING compare can see it, and
cdecl.compatible correctly answers "compatible".
Fix is one line — `typedef CdFileLoc CdFileLoc_80128C98;` — so the two NAMES denote one type.
Byte-neutral by construction: identical layout, so indexing scales by the same 8 bytes either way.
NOT unified with the 3 `extern u8 cdFileLocTable[]` sites: element size drives index scaling and
those sites carry their own explicit `<< 3` (note at resident.c:656). Folding them in would change
codegen, which is exactly the memcpy-class trap.
Verified byte-identical on ov_SC01_077 and ov_SC03_099. Committed ahead of the re-sweep so the
sweep's per-member revert cannot undo it mid-run; fleet R22 lands with the sweep batch.
Found while scoping the jr carve for the 3 newly-onboarded binaries. My scoping said "one
unplaceable construct" — it was the first of four layers. Three are fixed here; the fourth is out
of this tool's scope and leaves the carve blocked.
1. asm_label_aliases: the scan could START inside a #define. `#define gte_SetRotMatrix(r0)
__asm__ volatile ("lw $12, 0( %0 );" ...)` is textually `ident(...) __asm__(...)`, and
cdecl._mask blanks string CONTENT — deleting the `;`s that would stop the greedy [^;{}]*.
The match ran 116 lines and swallowed the real `aF8012EFB8 ... __asm__("func_8012EFB8");`,
so the alias never entered the map and addr_of returned None. jr_isolate_all then refused to
carve (R32, correctly), which presented as 112 isolate-fails that looked like a per-binary wall.
Fixed: _mask_cpp_directives() — a preprocessor directive is the other place a match must not
start. Masking comments/strings fixed the comment case and left this one.
2. _split_macro_body returned a `static inline` internal HELPER as the macro's definition, so
_proto_from_lines hoisted `extern static inline void tail_8012F274(...);` into all 41 regions:
invalid C (multiple storage classes) AND the wrong function — the exported definition sits
below the helper and lost its implied declaration. Fixed: skip static definitions
brace-balanced on the masked body. A static helper needs no hoisted declaration at all.
3. A declaration that WRAPS across continuation lines was taken as one line, so half became a
`;`-less extern and the continuation was read as the definition header, producing
`extern __asm__(""); void aF801466F0(...);` in 22 regions. Fixed: accumulate until the
statement terminates, tested on the masked text. Same wrapped-declaration blindness
family_remap._alias_decl_for records fixing at S33 — never propagated here (§134/§139).
Not fixed, and why: jtbl_rodata_pads reports "consumed 0 rodata .align(s) but 4 pad spec(s) given
— table-count drift vs the carve". The carve moves jtbl-owning functions into _jr_ regions but
leaves the pad specs on the residual gap object. jr_isolate_all's docstring states this class is
NOT isolate-fixable; it needs JTBL_PADS repointing in overlays.mk. 122 jr member-slots stay blocked.
Regression-checked: 1,948 macros parse with 0 malformed externs; alias maps unchanged on three
already-carved overlays. No build impact (splitters run offline). Carve reverted, tree clean.
Exemplar ov_SC01_077 @0x80180b64 (matched-ov077), 2 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80180B64: {'BANKED': 2} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC01_077 @0x80183bac (matched-ov077), 5 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80183BAC: {'BANKED': 5} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC01_077 @0x801789ac (matched-ov077), 3 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_801789AC: {'BANKED': 2, 'isolate-fail': 1} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC02_026 @0x8017fee0 (matched), 1 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_8017FEE0: {'BANKED': 1} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC01_077 @0x80179b74 (matched-ov077), 3 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80179B74: {'BANKED': 2, 'isolate-fail': 1} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC01_077 @0x8017ae2c (matched-ov077), 3 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_8017AE2C: {'isolate-fail': 2, 'BANKED': 1} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC06_018 @0x80184c74 (matched), 3 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80184C74: {'BANKED': 3} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC06_020 @0x80180b04 (matched), 5 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80180B04: {'gate-fail': 3, 'BANKED': 2} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC02_028 @0x801884d8 (matched), 15 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_801884D8: {'BANKED': 15} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC01_077 @0x80178d40 (matched-ov077), 3 members. Per-sibling whole-binary byte-gate (G3/P9) is the
arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff byte-identical else
revert. Tally: === func_80178D40: {'BANKED': 2, 'isolate-fail': 1} ===
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be lost. Campaign-end R22 verifies the fleet.
Exemplar ov_SC02_027 @0x8018A564 (matched), 22 members, 125 ins each. Per-sibling whole-binary
byte-gate (G3/P9) is the arbiter: jtbl_carve -> make extract -> remap_hseq -> make build, keep iff
byte-identical else revert. Tally: {'BANKED': 21, 'isolate-fail': 1}.
Committed per family because jtbl_family_bank's per-sibling revert restores from HEAD — an
uncommitted prior family would be silently destroyed mid-sweep (the tool refuses a dirty tree for
this reason). Campaign-end R22 verifies the fleet.
NOTE on counting: the jtbl carve creates NEW split files, so a git-diff INCLUDE_ASM tally
over-reports (removals visible, re-additions inside untracked files not). True count settles
against the stub oracle at the campaign-end R22.
scope_data_externs §8d drops the draft's decl of any symbol the TU already declares at file scope.
It keys on the SYMBOL, but a §37 asm-label ALIAS binds a DIFFERENT C identifier to that symbol:
the TU declares `D_801851BC`, it does NOT declare `tbl_D_80187044`. Dropping the alias left the
body referencing an undeclared name, which cc1 reports with no `error:` prefix — so the sweep
classified all 132 siblings as CC1-FAIL(no-diagnostic), i.e. as a codegen wall.
The bitter part: the alias exists PRECISELY BECAUSE the TU declares that symbol with a conflicting
type (a `void (*[])(void)` dispatch table vs this function's 20-byte-stride view). The drop rule
fired on exactly the declarations written to survive it. Why 1 of 2 died was fully determined:
tbl_D_80187048's symbol is not in the TU, so it demoted normally.
Fix: is_asm_alias() — an alias is demoted into the body, never dropped (the identifiers differ, so
it cannot collide with the TU's decl). Control-tested 6 ways incl. self-labels and plain externs.
Measured: func_80132018 3/135 -> 135/135; full re-sweep +16 more. Total +148 members.
R22 clean-fleet 213 passed / 0 failed of 213. tools-health OK, dedup-check 1949/0.
Fleet 96.11 -> 96.15% fn-count, 87.8 -> 87.9% distinct; stubs 14,120 -> 13,972 = -148 (2nd oracle).
CORRECTION TO MY OWN CLAIM (R14): after the probe I said the 58% aggregate was concealing a broad
problem. The re-sweep refuted it — only 16 more banks fleet-wide. The alias class really was one
family; the first read ("outlier") was right and the correction was wrong.
875 sweep failures classified: 231 PLUMBING-other, 141 DIFF (real divergence, only 16%),
136 `conflicting types for cdFileLocTable` (ONE symbol — biggest single class left),
77 CC1-FAIL(no-diagnostic), 26 memcpy, 12 D_80114F24, 11 D_800AE620, 9 D_800183E0.
STILL UNFIXED, and the most dangerous instrument left: the sweep's failure classifier greps for
`error:`, which gcc-2.7.2 never emits on hard errors. Every hard error therefore reads
CC1-FAIL(no-diagnostic). That is how a missing declaration looked like a codegen wall across 132
functions. rtu_match was fixed for this at T0(b); this classifier was not.
family_sweep --hseq --band all -j 8 over every matched-exemplar family: 553 families /
203 overlays / 1,419 banked / 1,023 failed (58%). R22 clean-fleet 213 passed / 0 failed of 213.
tools-health OK, dedup-check 1949 validated / 0 failed.
Fleet: 93.9 -> 94.2% instr / 87.2 -> 87.8% distinct / 95.72 -> 96.11% fn-count.
Second oracle (R34): INCLUDE_ASM stubs 15,542 -> 14,120 = -1,422, equal to the diff-derived net
(1,451 removed - 29 re-added = 1,422 = 1,419 sweep + 3 probe). Three independent counts agree.
B -> C -> P IS ONE CHAIN, NOT THREE WINS. 1,102 of the 1,422 landed in ov_SC02_037 (409),
ov_SC03_107 (364), ov_MAIN_012 (329) — the three newly-onboarded binaries from C, which had never
been wired into the shared-body ecosystem, so every matched exemplar was unreachable from them.
B fixed the declarations, C wired the include, P poured through the opening. A repeat sweep will
NOT pay like this; the opening was one-time.
S47 total: 1,481 functions banked with zero agent drafting, all from removing plumbing.
Two findings recorded, neither fixed (deliberate, costed):
- --band defaults to `substantial`: the first probe returned a confident {"families": 0,
"banked": 0} on a real 135-member `mid` family. Always pass --band all.
- The alias-gather defect: probe on 0x80132018 banked 3/135, all 132 failures classified
CC1-FAIL(no-diagnostic) because gcc-2.7.2 emits no `error:` prefix. Real error is
`tbl_D_80187044' undeclared` — the exemplar declares TWO §37 asm-label aliases and uses both,
family_remap carried one. T7-S1's "gather" class. Measured as an OUTLIER (aggregate 58%),
which is why the sweep ran before the fix.
Refused by design, all named: 50 jr families / 183 member-slots (§53 interlock — it printed its
own coverage and reason), 264 STRUCT, 112 unresolved immediates, 3 not-stub.
Regenerated after the S47-B/C banks (family_hseq.py + report): docs/family-hseq.md,
docs/progress.fleet.md, docs/backlog.md. Numbers only — no analysis change.
Frontier at this HEAD (overlays only): 7,085 families / 14,508 instances / 752,073 ins.
with siblings (>=2): 2,429 fams / 9,852 members / 467,634 ins (62.2%)
- matched exemplar (propagate, ~0 tok): 460 fams / 2,861 members / 125,630 ins
- zero-crack (crack 1 -> templates to N): 1,969 fams / 6,991 members / 342,004 ins
singletons: 4,656 fams / 4,656 members / 284,439 ins (37.8%)
- matched exemplar: 143 / 6,603 ins - zero-crack (pays x1): 4,513 / 277,836 ins
Structural: the x138 era is over — 3 fleet-wide families remain and ALL 3 already have matched
exemplars, so no fleet-wide CRACK is left, only propagation. 82% of remaining code now sits in
the two worst cost profiles (x2-9 zero-crack 45.5%, singleton zero-crack 36.9%).
C, unblocked by B's declaration conform. 23/23/16 banked across ov_SC03_107, ov_MAIN_012,
ov_SC02_037 — the first non-zero result on this population (S46 got 0/142, then 0/129).
R22 clean-fleet: check-all 213 passed / 0 failed of 213. tools-health OK.
dedup-check 1949 validated / 0 failed, C1 coverage 249295 (= 249233 + 62, independent
confirmation of the count). Fleet 93.9% instr / 87.2% distinct / 95.72% fn-count.
THE TOOL REPORTED "BANKED 0 / 129" AND WAS WRONG. gate_stage's ladder hands the same
--verified-out path to harvest_verify on every rung, and each rung opens it for write: stage 0
banked 23 and wrote them, then a later rung that banked nothing truncated the file to 1 byte.
The in-memory list uses += and stayed correct, which is why the JSON verdict listed all 23 names
while the file said nothing. dedup_extend read the file, printed BANKED 0, and took its
`if not banked:` branch — skipping add_members_surgical, so the registry was missing 62
memberships for functions already spliced in and byte-verified.
- Registry repaired by deriving the banked set from git diff (+DEFINE_func_*), not from the
broken file. Post-check: 0 missing.
- ensure_include_revert did NOT fire (added_include False, include already present) — the
P29-S19 defect that once stripped a load-bearing include from 135 binaries stayed closed.
- gate_stage now writes verified_out once at the end from the accumulated truth.
Caught only because bank truth is derived from source (§55b), never from the gate report.
Residue (67) is consistent with the symbols B deliberately left: memcpy 17, ApplyMatrixSV 12,
gte_SetRotMatrix 4, plus 21 CC1-FAIL and 3 DIFF. Not separated: how much of the 62 is B's
conform vs the ladder's own recovery rungs.
Task B, re-scoped from evidence. The 129 dedup_extend failures are 106 conflicting-types /
21 CC1-FAIL / 4 undefined-ref / 3 DIFF — real byte divergence is 2%, and memcpy is 17 of 106,
not the story. Direction reversed too: the byte-true DEF of func_80128ED8 is what the target
.c files already declare; engine_core.h's macro-local extern was the stub-era guess.
Conformed 8 axes to byte-truth (func_8012F14C 2843, func_8012E5CC 2052, func_8012F038 2214,
func_8014C568 1816, func_80128ED8 1524, func_8012C750 406, func_8012C0EC 50, func_80144A04 25).
R22 clean-fleet: check-all 213 passed / 0 failed of 213. Zero functions banked by design.
Tooling (R33/R35) — three guards that asserted completeness over a narrowed population:
- NEW tools/macro_draft.py: a deduped fn has no definition in any .c (body lives in a DEFINE_
macro), so conform_decls had been refusing the largest class it was built for.
- conform_decls skipped engine_core.h wholesale as "a defining TU": 10 stale externs survived
while 1,514 fleet sites moved, and it still printed "axis complete". Skip now scoped to the
defining macro's span.
- Return-axis compare was literal: typedef int/s32 and a missing `extern` faked a return change.
Now compares normalized types.
- §85 consumer scan under-reported (the dangerous direction): a cast between `=` and the call
hid `s0 = (s32 *)func_80144A04(...)`. Now classified by position, validated both ways.
Corrections to my own predictions (R14): the documented scalar-narrowing hazard was benign
across 2,052 sites; the breaks were arity (6 call sites, fixed with §17a-1 fn-ptr casts) and
the consumer-guard gap. A header-only first probe broke ov_SC01_000 — §85 is literal.
Not done, named: memcpy (builtin codegen), ApplyMatrixSV (no DEF), gte_SetRotMatrix (link bug),
func_80147364 (unparseable macro), D_800AE620/D_80126CC4 (data axis). Cookbook §159.
- BANKED: 11 functions at 400-952 ins from the cascade (func_8017D898 952, func_8017CE58 733,
func_801902EC 673, func_8018C2D8 673, func_8018A8D4, func_8017C6F4, func_800CBB38,
func_800CF3A4, +3). check-all 213/213 from a clean tree. 6 near = jr/switch (§53 separate
banking step), 1 failed. The cascade agents wrote 6 new cookbook sections incl. §158.
⚠️ tools-health UNVERIFIED at commit (stale cookbook index fixed, confirming re-run
interrupted) — run it first next session. check-all is the byte oracle and it is green.
- WASTE PREVENTION (Drew: "prevent this from ever happening again, however you need to"):
* tools/validate_targets.py (NEW) — names 5 defect classes (NO-ASM / MID-BODY /
OUT-OF-RANGE / ALREADY-DONE / NO-BOUNDARY), exits non-zero.
* WIRED INTO wave_snapshot so it fails closed — every wave passes through there for its .s
files, so no path from target list to spawned agents bypasses validation. Negative-control:
a 3-target bad list is refused with the exact mid-body offset (+72 bytes of 100).
* The cascade `done()` predicate now short-circuits on SKIPPED as well as MATCH. It tested
only MATCH, so a non-existent target fell Sonnet -> Opus -> Fable and three agents each
proved the same phantom absent: ~29 invalid targets x 3 tiers = 87 of 119 agents, ~9.7M
tokens. A tier that cannot act must END the pipeline, not escalate emptiness.
* docs/accelerators.md A9, including that wave_snapshot's own R32 assertion REFUSED that list
(24 of 57 found) and was routed around — the one instrument warning that was right and ignored.
- B RE-SCOPED (S46-10) and deliberately NOT done: the extend blocker is INTRA-HEADER, not
target-side. engine_core.h declares memcpy FOUR incompatible ways across its DEFINE_ macros;
two in one TU collide. NOT a safe cleanup — the in-tree note at ov_MAIN_012.c:14333 records
that `extern memcpy` disables gcc's builtin and turns an inlined block-move into a CALL, so the
declaration CHANGES CODEGEN. Probe one macro in one binary and byte-gate before any sweep.
- C (dedup_extend over the 129) stays blocked on B. Full context for both in the checkpoint.
- RECOVERY PASS A (wave residue): gate_stage over the 3 big-3 draft dirs recovered
6 sites the bare gate rejected — func_80168664 x3, func_80168F40 x2, func_8012B77C
x1. That is 6 of 19 PLUMBING = ~32%, matching the 16-39% range P29 measured. Batch 1
goes 27 -> 33 of 60. R22 213/213 + tools-health green.
- HONEST SIZING (correcting my own claim): ~32% is NOT "a one-time fix for a ~50% draft
loss". It moves the batch loss from 55% to 45%. Real and free; not transformative.
- WIRED (task 9): dedup_extend now gates through gate_stage (the ladder:
canon_resident_calls -> cast_call_sites -> sig_unify -> harvest_verify) instead of
harvest_verify verbatim. Its 142 candidates failed 0/142 with reasons 118 PLUMBING /
21 CC1-FAIL / 3 DIFF — ~1 in 50 a real byte divergence, the rest declaration conflicts
in the TARGET TU, which is exactly what the ladder reconciles.
GATE_NO_ARITY=1 is forced for the child: gate_stage's arity pre-pass writes the
fleet-shared engine_core.h BEFORE the gate and a failing draft can leave that edit
behind — the F1 defect that broke 141 of 213 binaries in S45. The ladder's other rungs
are draft-local. --ladder can be disabled to restore the old path.
- DOCTRINE (step 3): gate every wave with gate_stage, not bare harvest_verify. Batch 1
needed a second manual pass only because I used the bare gate first.
- LEVERAGE METRIC CORRECTED (R14/R35): the behemoth ranking must use LIVE reach
(unmatched sharers), not total sharers. func_80144B9C reads 770 ins x 141 = 108,570
by total, but 138 of those are already banked — its true weight is 770 x 3 = 2,310.
Same x134 over-count the cookbook records in §25; build_wave_args --rank live exists
precisely for this and I used the wrong ranking. Remaining >=400 ins: 57 distinct
functions / 77 live instances / 38,968 ins, reach ~1.35 => ~0.3% instr-weighted.
60-agent wave over the big-3 (ov_SC03_107, ov_SC02_037, ov_MAIN_012), size-routed per §157.
- BANKED: 14 distinct functions, 27 sites. 11 of the 14 landed in 2-3 binaries
independently => shared engine code, so each is a propagation candidate.
- Gate: ov_SC03_107 11/24, ov_SC02_037 6/17, ov_MAIN_012 10/19 = 27/60.
check-all 213/213 from clean; tools-health OK; dedup 1949/0.
- THE CONVERSION GAP, MEASURED AGAIN: match_one claimed 53/60, the whole-binary
gate banked 27. The losses are 19 PLUMBING + 3 CC1-FAIL + 11 DIFF — i.e. ~2/3 of
the loss is declaration plumbing, not wrong code. Same ratio P29 measured
(~92% byte-correct drafts, ~27% banking) and the same class that blocked all
142 dedup_extend candidates tonight. Three independent populations, one wall.
Concentrated: D_800AF634 x6, D_800AE620 x3, RotMatrixX x2 (data-decl class ->
reconcile_decls) and func_80146A6C x3, func_801376E8 x3 (DEF-side signature
class -> canon_sig_reconcile v3.2).
- EFFORT A/B: INCONCLUSIVE, recorded as such. Yield 16/20 (default) vs 17/20
(effort:low) is noise, matching P13-T7 at the other end of the ladder — BUT the
positive control failed to materialise: the workflow journal carries only
agentId/key/type, no per-agent token usage, so "low effort is free" and "the
effort parameter silently inherited" remain observationally identical. Not
written into doctrine. A future test needs an EXTERNAL measure (per-agent
wall-clock or tool-call counts), not the agents' self-reported iteration counts.
- Sonnet's 50-59 ins arm claimed 20/20, contributing the larger bodies
(func_80142BB4, func_8012B77C) — §157 size-routing held at the top of its range.
Drew: "make it more multi-threaded... I still see my cpu idle for far too long."
Measured, fixed, and regression-tested against the S46-3 bank as a KNOWN ANSWER.
- THE MEASUREMENT: 31s saturated (33 makes/48 cc1/load 27) then ~25s with ONE build alive
while 31 cores idled, repeating. Causes: ex.map starts in list order so the giants land
last, and apply/restore is single-threaded.
- gate_all -> gate_failures: return EVERY failure the sweep already computed (~138 rounds -> 1).
- Longest-first gate scheduling; results re-sorted into `changed` order so the verdict stays
bit-identical to the serial loop's.
- PER-OVERLAY INDEPENDENT SEARCH, IN PROCESSES. My first cut used threads and the box refuted
it: 0-4 builds alive at load 3, because the work is regex over 15k-line files and 138
"parallel" searches all queued on the GIL. Same logic in a ProcessPoolExecutor: 14-29 builds,
load 34.75, search phase ~100s. Safe because the shared header is written ONCE by the parent
and each overlay owns its own .c files + build/<bin>/. Seeded with one in-process search
first — a pool submitted at once gives every worker an empty suspect list and makes all 138
pay a full bisection. place_in_overlay extracted to module level so the worker and the
in-process apply cannot drift (R33); compiles_standalone's fixed t.c is per-call now.
- THE REGRESSION (the point, not the stopwatch): revert src/+config to pre-bank, re-run the
identical command -> 29 functions (same), 141 overlays byte-identical, 682s vs ~1440s, and
285 exclusions vs ~350 => +62 MORE member instances (249,161). The old prefix-based
necessity probe was OVER-EXCLUDING (charging 4 fns to 9 overlays that did not all need
them); the per-overlay shrink minimises per overlay. The faster path is also more correct —
a timing comparison would never have shown it. R22 213/213 + tools-health green.
- STILL SERIAL, now the actual wall-clock (neither is a build): ~3min setup before the first
gate (registered_addrs() yaml-parsing a 1949-group/249k-instance registry + 213 sig loads)
and ~2.5min of sequential reconcile_caller_extern after the search.
- Captured as defaults: docs/accelerators.md A8 + memory fleet-tool-parallelism-defaults.
cookbook index regenerated (my §155c append left it stale — the gate caught it, exit 1).
The S45p9 blocker is closed, and the recovery loop that kept it from finishing is rewritten.
- BANKED: dedup_propagate --auto-from ov_SC02_037 --recover -> 29 functions propagated,
141 overlays byte-identical, dedup 1920 -> 1949 groups, member instances 246,284 ->
249,099 (+2,815). make clean && extract-all && check-all -> 213 passed / 0 failed (R22).
- WHY IT FINISHED THIS TIME: gate_all -> gate_failures returns EVERY failure from the sweep
that already computed them, and the recovery loop resolves them all per round. Converged in
3 rounds; the old one-overlay-per-sweep design needed ~138. That reframes the S45 run — it
was not nearly done when it died, it had barely started.
- Batching did NOT cost capability: per-overlay necessity probes excluded four of the nine
culprits from only the 9 overlays that needed it (not all 138), and ov_SC07_006 was
RECOVERED by the Part-B caller-extern reconcile instead of excluded.
- Plan phase parallelised: 5 min -> 26 s, plan + skip classification byte-identical. Its
compiles_standalone temp file is per-call now — the fixed `t.c` was the same fake-isolation
class as match_one's shared --work dir (P28 T5), latent until something ran it in parallel.
- docs/accelerators.md (NEW, Drew 2026-08-07): the reusable-workflow ledger — what we learned
late that a future decomp should know on day one, each entry with when we found it, when it
WAS findable, what it cost, and the honest prerequisite where one exists.
Drew's S45 idea, delivered fleet-wide + wired into the permanent references.
- THE BLOCKER WAS OUR INSTRUMENT (R35, the 3rd time): the S45 plan ("require a
register-verified reference to the run's address") returns ZERO for both byte-proved
tables. They are read by gcc's indexed global-array form —
lui $at,0x8019 ; addu $at,$at,$a0 ; lh $v0,-0x2844($at) -> 0x8018D7BC
— where the address exists only as (lui imm, LOAD offset) with the index add between.
find_addr_refs killed the lui register at the addu, so the halves never rejoined and
the tables looked unreachable. Now it carries the hi half through the index add (still
strictly register-tracked, never window-paired) and labels those hits `-indexed`.
- tools/idxtab_map.py (NEW): fleet-wide payload -> owning binary -> load address.
Controls-gated (refuses to emit unless ov_SC01_000 0x8017EEC8/37 + *0x801A3234, and
ov_SC03_001 0x8018D7BC/5 + *0x801EBC68 reproduce from the images alone). Index space
DERIVED from the extracted tree (reproduces §S44's table independently). Process-pooled.
Rejects all-zero and majority-zero runs (132 of the first pass's 452 "tables" were that).
- RESULT: 213 binaries -> 143 with a referenced table (294), 141 with a DESTPTR (141/141
resolved from the binary's OWN image), 61 payloads. The two dominant tables are
fleet-wide CONSTANTS (5-entry and 37-entry, identical in all 141 overlays); the
per-binary variable is the destination (134 distinct).
- CORRECTION 1 (R14): §S45 p6's "the SC03 trio are owned by ov_SC03_001" is refuted —
that 5-entry table is identical in ALL 141 overlays. The byte-observed parts stand.
- CORRECTION 2 (P9): this route CANNOT settle MAIN/7+9. They are absent from all 294
tables — but so are MAIN/13/20/34/42/44, which are byte-proved to load. Absence here
means "not on this route", nothing more. Recorded so it is not re-derived as a finding.
- Confidence is stated per-claim in docs/idxtab-map.md: proven (controls) / high (283
fleet-wide-class tables) / low (3 named rare rows) / UNMEASURED (recall — no oracle
for "all tables" exists beyond the 2 controls).
- Wired in permanently: docs/idxtab-map.md (the how/when/limits), memory-map.md §S46,
cookbook §155c (the generalizable law: "no code references X" is a claim about your
DECODER until it is shown to recognise the forms the compiler emits), SETUP.md
tooling inventory (R21).
The S45p9 blocker: `[FAIL] ov_MAIN_012: 0x80156600 not instantiated — REVERTED`
92 minutes into a --auto-from run, naming no mechanism.
- FIRST, the honest finding (R14/R35): it does NOT reproduce at HEAD. A replay of
apply_plan's per-file site resolution over the exact 30-fn plan resolves
0x80156600 as a stub at line 7505, and def-range/stub-line overlaps = 0 (the
splice-swallow hypothesis refuted). The failing input state was not the committed
tree — most likely a concurrent writer mid-run. So this commit does not "fix" that
run; it makes the next occurrence name itself.
- SILENT SKIP -> LOUD (R32): an address resolving as neither the sp-regex stub nor a
def just stayed in `remaining`. apply_plan now records gaps={ov:[addrs]} and the
caller fails FIRST with a per-site diagnosis (whole-overlay find_site verdict,
in-sig, file list) instead of struct_check's terse late message.
- CAPABILITY GAP that produces exactly that skip: find_site returning 'stub' was
ignored (apply_plan acted only on 'def'), so a stub whose INCLUDE_ASM asm-subdir
!= its file stem was invisible to the stem-anchored sp regex AND unhandled. Now
placed ('macro' treated as already-placed). find_site's stub match is an exact
stub_line(ov,addr) compare against THIS file's text — it cannot cross files/TUs.
- INCOMPLETE REVERT (the §156 class, different path): struct_check restored only
`touched`, leaking every kept Part-B reconcile. New _abort() undoes touched AND
every kept reconcile, then diffs the worktree against a start-of-run baseline and
reports any residue. A tree dirty in a way nobody knows about makes every later
byte-gate report `near` — that is how S45p7 lost two batches.
- NEGATIVE CONTROL: neuter ov_MAIN_012's stub -> [GAP] fires naming the exact
condition (find_site=None, in-sig=True) -> "[revert] tree restored to baseline;
no residue" -> exit 1 (fail-closed). Restore -> tree clean.
.run/attract_loadmap.jsonl (304s full attract cycle) and .run/sc03_hunt_loadmap.jsonl
(the SC03 hunt + boot chains) are LIVE CAPTURES — not regenerable without another
emulator session — so they fall under the R20/P27 curated-.run policy (irreplaceable
recon tracked, regenerable bulk ignored). They are the evidence base for:
- MAIN/7 + MAIN/9 absent across a complete attract cycle (the dead-code case)
- the 7 routing-table addresses confirmed live (the R34 second oracle for S44)
- the 0x801EF468 script-slot observation that cracked the SC03 trio
docs/memory-map.md cites attract_loadmap.jsonl by name, so leaving it untracked would
have left a doc pointing at a file a fresh clone does not have.
Caught by Drew asking 'and you checkpointed everything?' -- my earlier git add had
2>/dev/null on it, which silenced the gitignore rejection. A silenced add is a silent
skip (R32).
PARALLEL GATE (landed, verdict-proven): dedup_propagate's byte-gate loop was serial --
one `make build BINARY=<ov>` at a time over up to 141 members per function. Measured: a
propagation ran 95 minutes at load 1.6 on a 32-core box (~5% utilisation). The Makefile
has parallelised extract-all/check-all since Phase 26 (xargs -P$(JOBS)), but this tool
predates that and drives the SINGLE-binary target from Python, so it never saw any of it.
- new gate_all(): ThreadPoolExecutor over distinct overlays, 32-way by default (JOBS env
overrides; deliberately NOT capped at the Makefile's conservative 16).
- SAFE by the same argument check-all relies on: byte_gate only runs `make build`, writing
solely to per-binary-disjoint build/<bin>/**; it mutates no source. Splice happens before,
restore after -- only the VERIFICATION is parallel.
- DETERMINISTIC: ThreadPoolExecutor.map preserves order, so the reported first failure is
the first in `changed` order -- identical verdict to the serial loop. Control run: same
verdict on a clean tree.
- Measured and NOT optimised: setup (sig load + registered_addrs) is 8.6s of a 5,700s run
= 0.15%. All the time is gating. Don't thread the setup.
BANKING DEFERRED on a genuine pre-existing tool bug (NOT the parallel change -- 0 gate
batches ran, it never reached that code):
[FAIL] ov_MAIN_012: 0x80156600 not instantiated -- REVERTED
Inputs verified sound at HEAD (in sig, find_site->stub, stub line matches), so the bug is
in apply_plan's multi-function edit path. Run #1 missed it because it launched before the
15 wave-3 banks were committed; they landed mid-flight, enlarging run #2's plan.
SECOND DEFECT: the failure exit printed REVERTED but left 38 files dirty incl.
src/shared/engine_core.h -- the same incomplete-restore class as the reconcile-ledger bug
(cookbook 156), on a different path. struct_check needs the same ledger treatment.
Not patching the fleet-shared writer at the end of a marathon session -- that is how the
next 141-binary incident happens. Tree clean, 44 banks safe, propagation is pure
multiplication and can run any time.
Checkpoint p9 carries: the fix-then-resume plan, the master-IDXTAB-map design (DESTPTR half
proven 14/14), wave guidance, and an 8-item error ledger with its single root cause.
- SC03/53/54/56 SOLVED: live script modules owned by ov_SC03_001 (IDXTAB @0x8018D7BC =
224/231/232/234/233) loaded via func_80128CFC into *DESTPTR 0x801EBC68 = 0x801EF468.
Load BASE not yet proved — the byte-gate arbitrates on onboarding.
- NEXT SESSION OPENER: the master IDXTAB map (Drew's idea). Feasibility PROVEN — the
DESTPTR half extracted 14/14 sampled overlays first try and reproduces S44's one
documented case exactly. Only the IDXTAB discriminator remains (require a
register-verified code reference to the table address; validate against 2 known tables).
- Carries the dirty-tree recovery procedure: a propagation was in flight at checkpoint.
- 7 self-corrections logged with their single root cause, as a T5 rule candidate.
Found the IDXTAB: ov_SC03_001 @0x8018D7BC holds 5 s16 entries, -1 terminated:
224, 231, 232, 234, 233 — i.e. the ENTIRE parked trio (SC03/53/54/56) plus its DATA
companion (SC03/55 = 233), in one table, in the binary whose *DESTPTR points at the
script-module slot the tracer watched load live an hour earlier.
THE CHAIN (every link register-verified or byte-observed):
ov_SC03_001 IDXTAB @0x8018D7BC -> indices 231/232/234 (+233 data, +224)
func_80128CFC (the S44 wrapper) -> cdFileLocTable[idx] -> {loc,size}
register-tracked: addiu->0x800AE830, lw[0x800AE834] size, lw[0x800AE830] loc
*DESTPTR @0x801EBC68 = 0x801EF468 -> the script slot
the ONLY occurrence of that word fleet-wide; read 8x by code, 2x from inside func_80128CFC
slot confirmed LIVE by tools/cdtrace.py: SC03/76 and SC03/34 both loaded there
and 0x801EF468 lies inside SC03/54's independently-derived base window [0x801EDED0..0x801EF6C8]
VERDICT: LIVE script modules owned by ov_SC03_001. Not dead code, not boss-gated, not
chapter-gated (that framing retired — scripts swap per SCENE). Every sweep missed them
because the SC03 scenes we visited run DIFFERENT overlays (124/125/051).
WHY THE EARLIER HUNTS COULD NOT WORK: the index never appears in CODE — it lives in a
per-overlay DATA table, and so does the destination. Both invisible to fleet-wide code
scans. That is the structural reason four value-scans and three payload-side oracles failed.
NOT PROVED: the exact load BASE within the slot (the three differ in size; none observed
loading). The byte-gate arbitrates — onboard at 0x801EF468 and let the first build decide.
New tool: tools/find_addr_refs.py — register-tracked absolute-address search (cookbook 155:
no window-pairing), self-tested against cdFileLocTable, with a STRICT addu-index rule
(full-address match, not page match — 342 loose hits -> 7 real ones).
METHOD: a runtime observation supplied ONE constant, and that made a previously-impossible
static decode trivial. Neither alone sufficed. Pair the oracles, don't choose between them.
Three static oracles failed to derive the parked payloads' load addresses this session. The
runtime answer needed NO breakpoints, no Lua (no pcsx.lua wedge hazard) and no GDB stub: the
loader mirrors its whole request in RAM (cdReq_curSector / cdReq_dest), and CdReadRequest's
own MATCHED signature says cdlFile points INTO cdFileLocTable -- so (ptr-0x800AE830)/8 is the
global file index and cdReq_dest is the destination. Both readable from the RAM-dump API we
already had working.
VALIDATED FIRST (R35): cdFileLocTable's live sizes reproduce our extractor's file sizes exactly
for all five parked payloads. Then confirmed 7x against independently byte-proved addresses --
loadDestPtrTable slots [0]/[1]/[3], MAIN/10 (Phase-3 resident), MAIN/3 (S45-p2 md_MAIN_003),
MAIN/12 (the resident's func_800CF94C row), and the LIST.CD bootstrap read from matched C.
This is the R34 second oracle for the whole S44 routing table, which was static-only until now.
FINDING: the script-module slot 0x801EF468 is live and GENERAL. SC03/76 AND SC03/34 both load
there; 34 is outside the SC03/73-79 block, so S45's "chapter-2 period" label described one
tenant, not the slot -- scripts swap PER SCENE.
PRE-REGISTERED HYPOTHESIS (written before the test, kept honest): slot CONFIRMED (it lies inside
SC03/54's independently-derived base window); "chapter-gated" WEAKENED (per-scene, not per-chapter);
trio 0 sightings across 38 load events, 2 saves, multiple SC03 scenes.
NEXT (static, no emulator): 0x801EF468 is now a concrete anchor. Register-track the code that
loads into it and decode its scene->script-index SELECTOR -- answers all three at once instead
of sweeping rooms. The correctly-scoped successor to the four refuted value-scans.
Also lands the attract-cycle load map (.run/attract_loadmap.jsonl): MAIN/7 + MAIN/9 absent
across a complete 304s cycle.
I claimed the .s snapshot alone fully decoupled a drafting wave from `make clean`.
CHECKED — it does not. match_one does not merely compile: it ASSEMBLES (AS with
-Iinclude, match_one.py:59), and the assembly step needs splat's generated
include/macro.inc, labels.inc, gte_macros.inc and include_asm.h. `make clean`
deletes all four.
The gap was worse than a plain missing file: a wave would survive the clean right up
until an agent hit a macro-using (e.g. GTE) function, then fail in a way that reads as
a BAD DRAFT rather than a missing include — a phantom wall booked into the backlog.
Snapshot now copies the whole include/ root (6 files; common.h and psyq/ are tracked and
would survive anyway, copied so the snapshot is a self-contained -Iinclude root), and
asserts the four generated ones are present, warning loudly if not (R32 — a
half-populated include root must announce itself, not fail later as someone else's bug).
Verified: all 4 present in a fresh snapshot; exit 0.
tools/verify_worktree.py: check a commit out into its own git worktree, provision the
untracked build deps (cc1 from the COMMITTED tarball, checksum-verified against the
COMMITTED record; .venv + extracted/ symlinked; maspsx submodule at the expected pin),
run make extract-all && check-all there, write .run/verify/<sha>.json with verdict +
toolchain provenance.
RESULTS
GREEN at HEAD: 213 passed / 0 failed, 86.6s wall.
NEGATIVE CONTROL PASSES: a throwaway commit splicing a deliberately corrupted body over
func_8014CBE8 went RED naming exactly ov_SC02_037 (212/1 of 213). The detector fires, so
its green means something (R35 — an unproven detector's green is not evidence).
TWO DESIGN CLAIMS CORRECTED BY CONTACT WITH REALITY
1. Sparse checkout (to save ~1GB of ghidra/) was proposed, and would have owed an R34
sparse-vs-full validation. Measured free space: 941 GB. Full checkout instead —
simpler AND strictly more trustworthy; the validation obligation disappears.
2. "A pristine checkout of exactly C's tracked content rebuilds byte-identical" is NOT
ACHIEVABLE here. Only 3 files under extracted/ are tracked; the 760MB of ROM payloads
are gitignored, so a pristine checkout extracts NOTHING (first honest run: 212/212
FAIL). No commit in this repo is self-sufficient, by design. The honest claim is
"the commit's TRACKED SOURCE, built against a supplied extraction" — corrected in the
docstring AND in the emitted `licenses` string, which is what actually gets quoted.
SCOPE, REFRAMED (Drew's challenge, and he was right)
I sold this partly on concurrency. At 86.6s, serializing R22 costs almost nothing, so the
concurrency argument is WEAK. What it actually buys is commit-completeness: the worktree's
src/ holds only committed content, so a source file someone forgot to `git add` fails BY
CONSTRUCTION — the documented "a clone of such a bank commit failed to build" class.
=> Run it at checkpoints and before pushing, NOT every batch. Plain in-tree check-all is
fine for routine verification.
=> The wave-vs-`make clean` blocker that started all this was already solved, more simply,
by tools/wave_snapshot.py. Neither the worktree nor path-parameterizing was needed for it.
=> STAGE 5 (verify coalescing / auto-bisect) IS CANCELLED: it existed to handle verify
lagging commits, which cannot happen at 87 seconds.
STAGE 1 of docs/concurrency-design.md, landed and negative-control proven.
tools/shared_lock.py (NEW) — one reader/writer flock over the FLEET-SHARED state
(src/shared/*, config/overlays.mk, config/dedup.us.yaml, the overlay .c files
propagation rewrites). Per-binary resources keep gate_stage's existing per-binary flock.
- gate_stage takes it SHARED when the gate writes no shared state, EXCLUSIVE when it
does (propagate, or the arity pre-pass enabled) -- so distinct-binary gates still run
concurrently but can never overlap a writer.
- dedup_propagate and fix_arity_callers --apply take it EXCLUSIVE.
- NESTING-AWARE: gate_stage SPAWNS both writers, so a naive child lock would deadlock
against the parent. The holder exports BFM_SHARED_LOCK_HELD and children inherit.
NEGATIVE CONTROLS (all pass):
NC1 a held SHARED lock refuses a non-blocking exclusive writer, loudly, naming the lock
NC2 parent-holds/child-inherits does NOT deadlock (the real risk in this design)
NC3 two readers acquire concurrently (0.00s) -- phase-B parallelism preserved
bulk_harvest docstring CORRECTED: its "propagation is the ONLY writer of the shared
engine_core.h" claim was FALSE as written and had been asserted for phases (F1 -- the
arity pre-pass writes it from inside every worker). Now states what is actually true,
under which two conditions, plus the one-line assertion that detects a violation.
BANKS: wave-3's drafts re-gated on a CLEAN tree -> 15 of 20 banked. The same drafts
previously reported 0 banked / 20 near -- that verdict was 100% an artifact of the
broken tree, which is why they were held as UNJUDGED rather than accepted as failures.
check-all 213 passed / 0 failed. F1 bracketing assertion CLEAN.
Session total banked: 44 functions + func_8015C030 propagated x7.
R14 correction to cookbook 156 + checkpoint p7. I blamed gate_stage's arity pre-pass (F1)
for the 141/213 breakage. That was wrong: no arity journal from the session mentions
func_80146A6C (74/26/4 entries checked) and the arity undo reported success in every log.
The real cause was dedup_propagate --recover leaving an orphaned caller-extern reconcile
(now fixed + proven, commit:1521 / commit:1522). F1 remains real, unguarded, and part of the
remaining Stage-1 work -- it simply did not cause this incident.
Generalizable law added to 156: a tool that deliberately leaves an edit on disk pending an
outcome owes a LEDGER for it. 'Keep it if this succeeds' is half a transaction; the other
half is undoing it on every path that can later invalidate the success, exit paths included.
commit:1519's commit message keeps the wrong attribution (history not rewritten, corrected forward).
The full-propagation control did not fire (that run succeeded), so the guarded path was
never executed and the fix was committed honestly marked UNPROVEN. This proves it directly.
Exercises the exact overlay+fn that broke the fleet (ov_SC07_010 / func_80146A6C):
apply a REAL reconcile_caller_extern -> 35 edits across 18 files on disk
drive the ledger undo as the fixed code does when a fn leaves the plan
assert all 25 of the overlay's source files are byte-identical
PASS. The orphaned caller-extern class that broke 141/213 cannot survive this path.
The test restores what it edits and asserts it (tree clean after).
ROOT CAUSE of the 141/213 breakage earlier this session (correctly derived this time;
my first attribution to F1 was WRONG -- no arity journal ever touched func_80146A6C and
the arity undo reported success):
dedup_propagate --recover's Part B reconciles a conflicting caller extern and
DELIBERATELY leaves the edit on disk when it buys the byte-match ("keep the reconcile
on disk"). Correct while the fn survives -- but a fn can still be dropped by a LATER
iteration against a different overlay, and when the plan finally emptied, the
"all candidates dropped" sys.exit fired with NO restore. Reconciles kept for
ov_SC07_001..009 were orphaned: no-proto'd caller externs for functions that were
never propagated -> ov_SC07_010 "passing arg 2 of func_80146A6C makes pointer from
integer" -> 141 of 213 binaries failed check-all.
The byte-gate never mis-banked (it fails closed). The real cost was VERDICT VOIDING:
every subsequent gate reported "near" against the broken tree, so two whole batches
(4/4 and 20/20) were mis-read as draft failures when they measured the tree (R35).
FIX: a reconcile LEDGER. Every kept reconcile is recorded against its fn, undone the
moment that fn leaves the plan, and ALL outstanding reconciles are restored before the
failure exit -- so a failed propagation leaves the tree exactly as it found it.
HONESTY: the fix is IMPLEMENTED AND REVIEWED BUT NOT YET PROVEN. The negative control
aimed at the exact failing propagation SUCCEEDED instead (different tree state), so the
guarded path never executed. A targeted test of the ledger is still owed.
Also lands the propagation that control performed: func_8015C030 x7 overlays
(func_80168B70 excluded from 4 SC07 overlays, survived elsewhere). check-all 213/213.
- cookbook 156: a FAILED draft can poison the fleet. gate_stage's arity pre-pass writes
the shared engine_core.h before the gate; a rejected draft's caller-signature edit
survived and broke 141/213 binaries. Byte-gate held (fail-closed). The trap: a broken
tree makes every later gate report 'near' -- two batches of verdicts were void, not
evidence. Standing practice: GATE_NO_ARITY=1, assert 'git status --porcelain
src/shared config' empty after every batch, recover by revert+replay (deterministic).
- cookbook 157: the cheap-tier size cliff, measured over two controlled waves.
Haiku 4-27 ins 86% (~44k tok/match); >=50 ins 20% (~177k, 4x worse). The documented
'<=50' band was optimistic. Agent honesty 63/63 claims true across 100 drafters.
- tools/wave_snapshot.py: immutable sha1-manifested per-wave .s copy, so a running wave
can no longer block R22's 'make clean'. Coverage-asserting (exit 2 on a missing target),
negative-control proven.
- docs/concurrency-design.md (Fable5): the lane contract, the false-bank correctness
argument, and the finding that a worktree verify certifies the COMMIT -- strictly
stronger than our main-tree R22, which also compiles untracked strays.
- checkpoint p7.
29 byte-gated matches into ov_SC02_037 (626 -> 597 live stubs), from two Haiku/Sonnet
crack waves on the CORRECTED frontier (live INCLUDE_ASM stubs with cached seeds, not
the already-banked reach-141 shared core).
Gated with BOTH safety guards after a live F1 incident (see below):
GATE_NO_ARITY=1 — no fleet-shared engine_core.h writes
--no-propagate — propagation deferred to its own controlled step
F1 bracketing assertion CLEAN (git status --porcelain src/shared config empty).
R22 clean-fleet: make clean && extract-all && check-all -> 213 passed, 0 failed.
F1 CONFIRMED IN PRODUCTION (docs/concurrency-design.md, found by the Fable5 design
pass hours earlier): gate_stage's arity pre-pass (fix_arity_callers --apply) writes
the fleet-shared header + caller externs; when a draft then FAILS to bank the edit can
survive. func_80146A6C failed its gate yet left a caller signature behind, breaking
141 of 213 binaries (ov_SC07_010: 'passing arg 2 makes pointer from integer'). The
byte-gate held throughout — nothing wrong was banked, it failed closed and loud.
Recovered by revert-to-HEAD + deterministic replay from the on-disk drafts.
Cost of the guard, measured: 2 banks (24 -> 22 on the wave-2 batch).
MODEL-LADDER CALIBRATION (independently re-verified, not agent claims):
Haiku 4-27 ins: 43/50 = 86% (~44k tokens/match)
Haiku 30-49 ins: 14/25 = 56%
Haiku >=50 ins: 5/25 = 20% (~177k tokens/match, 4x worse)
=> the documented 'Haiku <=50' band is optimistic; the cliff starts ~30, collapses at 50.
Agent honesty across 100 drafters: 63 MATCH claims, 63 real, 0 false (one apparent
false claim was MY verification missing --o0 on an -O0-cluster function).
CARRIED: the 29 banks are x1 (propagation off); wave-3's 19 verified drafts are
UNJUDGED — their gate ran against the F1-broken tree, so those verdicts were void.
HONESTY LEDGER (the wave cost 2.5M tokens and banked nothing; root cause mine):
- I FABRICATED the workflow args: after generating the real target list to
args_light.json I hand-typed the array instead of reading it, inventing names
and a descending nins run. ~40 of 50 agents got nonexistent targets. The agents
refused to fabricate and returned accurate diagnoses -- the prompt's honesty
rules held perfectly under a bad input.
- I then misdiagnosed it twice with a broken check: corpus.stubs() is keyed by
INTEGER ADDRESS and I compared string names (always False), producing two
confident wrong claims. Pool was in fact 160/160 + 166/166 valid. -> cookbook
155b: check the TYPE your oracle returns; an exactly-0/N result is more often
a type error than a discovery. R32/R35 assert coverage+correctness of a tool,
but neither catches an INTERFACE mismatch at the call site.
SOLID: 9 drafts independently re-verified MATCH by re-running match_one myself
(not agent claims); all 9 are genuine INCLUDE_ASM stubs; kept at .run/s45p5/gate1.
They did not bank (0/8 near/1 failed) -- but see the open instrument question.
OPEN (do first): harvest_verify reports 619 live stubs where the single source .c
holds 626 INCLUDE_ASM, and skipped a valid stub. Until explained, the 0-banked
verdict is not evidence about the drafts (R35).
CORRECTED FRONTIER: reach-141 identifies the most-DONE work (shared core, already
DEFINE_ macros ~1,614/binary), not the most valuable. Derive targets from the build
invariant (R33): INCLUDE_ASM in committed source. Big-3 = 1,799 draftable, 1,168
already seeded -- the real II.5 fuel.
Tree restored: gate_stage left 659 files dirty; git checkout -- src/ config/ verified clean.
- exclusion_proof.py tried the proven S44 {u32 idx,u32 param} table shape; its R32
control FAILED (neither known resident table re-found) -> output void per R35.
- STANDING VERDICT: no value/shape-based scan can establish the exclusion. Small
indices (7,9) are indistinguishable from ordinary data; 4/4 attempts refuted.
Do not attempt a 5th (cookbook 155a).
- The sound instrument is CONSUMER-side: enumerate every register-tracked reference
to cdFileLocTable across all 213 binaries, resolve each index source, collect the
reachable index set. Bounded, but real work.
- Partial: the discriminating indices 231/234 appear in no pair-shaped table fleet-wide.
Refreshes the session checkpoint before pausing (checkpoint-before-pause rule):
- p3/p4's static-RE homework closed as a NEGATIVE with two independent legs of
byte-grounded evidence (resourceIdMap refuted; payloads do not encode their base).
- Two actionable by-products recorded: MAIN/7+9 = the OPDEMO (attract-demo) modules;
the ~0x801Exxxx event-module region is runtime-allocated at per-scene bases.
- Resume pointer now names the CD-read tracer + one targeted attract-mode capture as
the correct next instrument, and warns off the three refuted oracles + the shape scan.
- MAIN/7 (id 0x3A) and MAIN/9 (id 0x2D) carry 'C:\TIMPACK\OPDEMO0.PAT' /
'OPDEMO1.PAT' path strings -> they are the OPENING/ATTRACT-DEMO modules. S45 p2
checked 'OPENING' negative, so the live target is attract-mode (idle at title),
a different state. Turns a blind search into a targeted capture.
- THREE payload-side base oracles built and ALL refuted by their own controls
(R32/R35 assertions did their job; none of their answers were used):
derive_base 0/4 -- 'code follows the table' is false (MAIN/34: 0x208 gap)
vote_base 4/12 -- calls are outward + MIPS leaf fns have no prologue
vote_base2 0/4 -- self-jals 0/N: there are NO internal jal calls at all
The third is structural: a module's bytes do NOT encode its base, because its
functions are reached indirectly via the header pointer table (jalr), not jal.
- The one real constraint: SC03/54's 19 header pointers (0x801EF718..0x801EFEE8)
confine its base to [0x801EDED0..0x801EF6C8]. That window lies INSIDE SC02/9's
span (0x801E4C60+70784=0x801F60E0) -> SC02/9 + the SC03 trio are mutually
exclusive event modules sharing a ~0x801Exxxx region at DIFFERENT bases.
- => event-module destinations are per-scene/runtime-allocated, not a static slot.
This explains the empty resourceIdMap branch and why the emulator resolved SC02/9.
The CD-read tracer stays the correct instrument (R11 + Drew).
- resourceIdMap @0x80063138 decoded from the EXE using the index math in our OWN
matched C (ResourceGetCdLoc is byte-exact): exactly 162 6-byte records, 2 negative
non-CD sentinels, streamIds >=0x100 -- self-consistent with the C in every field.
- FINDING: its 98 distinct global indices include NONE of gi 7/9/231/232/234, so the
five parked payloads cannot reach ResourceGetCdLoc/StreamLoadStateMachine/D_80068B60.
The S44 'descriptor path' branch of the parked-dest disjunction is refuted; only the
per-overlay IDXTAB/DESTPTR route survives.
- R34 corroboration: loadDestPtrTable's 5 u32s re-derived independently and reproduce
the S44 table exactly (0x800CEDF8/0x80128158/0x800CAE08/0x800CCB1C/0x800C7F08).
- R14 CORRECTION to S44: 'IDXTAB ... same list fleet-wide' is wrong. The 37-entry list
at 0x8017EEC8 is real for ov_SC01_000 only; 140 of 141 overlays hold unrelated bytes
there. IDXTAB is per-overlay data at a per-overlay address; only the mechanism is shared.
- NEGATIVE TOOLING RESULT (cookbook 155a): a shape-only IDXTAB scan passes its R32
coverage assertion and is still non-discriminating (664 'tables'; hits are (offset,count)
pair data). Coverage != discrimination -- two different oracles (R34). Recorded so it
is not repeated; next instrument is a register-tracked decode of func_80128CFC (155).
- the quick hi/lo sweep paired lui/lo16 WITHOUT tracking base registers -> phantom refs
(0x800AE868 read where the true target was 0x8018E868); register-tracked rescan: the ONLY
literal loc-table ref fleet-wide is SC02/9's (solved)
- standing truth: MAIN/7, MAIN/9, SC03/53/54/56 all load via table-INDEXED paths; homework
respecified (descriptor-data hunt + ResourceGetCdLoc/StreamLoad index math)
- fn 0x80161E08's real gate: currentLocationId vs {0x3012,0x3054,0x3079,0x3096} — the
'variable 0x800C3054' never existed; cookbook §155 (track the register)
- the gate DECODED from matched C (func_8012832C case 0x300E -> func_80128998 -> streaming
API with &cdFileLocTable[144]) -> scene arithmetic named the 1ST-BOSS arena -> ONE targeted
load captured it at 0x801E4C60
- RETRO-VERIFIED: Phase-3's dumps/ram_castle.bin (2026-06-14) holds it at the SAME address,
same 6,764-B exact prefix — R10 two independent datapoints two months apart;
bossHp_SteamKnight (0x801E4398) lives inside this module's image
- onboarded md_SC02_009 (id 0x3E, TLO 0x4): BYTE-IDENTICAL first build; fleet 213;
R22 213/213; tools-health OK; audit-disc UNCLAIMED 6 -> 5, residue 0
- the last 5 (MAIN/7, MAIN/9, SC03/53/54/56) reclassified emulator->STATIC-RE targets with
decoded leads (memory-map §S45 p3); loc-id map appended to docs/debug-menu-list.txt
- negatives banked: pause menu, memory-box prompt, new-game intro, high/low game, Minku
spawn (slot-A actor 0x15 = md_MAIN_015 candidate naming)
- THE TOUR (Drew driving the retail debug menu; mode-7 hammer over the Redux web API):
all 28 script modules captured live at four byte-verified per-chapter slots
(SC03/73-79 @0x801EF468 ch2-period, SC03/132-138 @0x801E25E8 ch3, SC04/24-30
@0x801E7B28, SC05/23-29 @0x801ED988); the routing law: debug-menu AREA selects the
chapter, each CITY interior streams its own module (member k <-> interior k).
md_MAIN_011/DISELECT byte-proven 24,236/24,240 in RAM; slots A/B/boot R34-verified live.
- MAIN/3 DISCOVERED: the main-menu module (id 0x39, 121,884 B), mis-bucketed as data by
BOTH audit oracles; live byte-proven @0x800CEDF8 (42,632-B exact prefix); onboarded.
- 29 onboardings BYTE-IDENTICAL on first build -> fleet 212; R22 212/212 after three
md_MAIN_003 catches: the A4 DsMix leak; an extract-order-sensitive splat boundary
(bytes: a 1-word data sentinel in .text + fn at +4 -> pinned in symbols file);
corpus.stubs now treats D_*/jtbl_* INCLUDE_ASM as blob includes (mirrors progress.py)
- module-id census (offline, disc-wide): 77 id-law code payloads, 0 further misses;
SC03/55 = confirmed DATA. audit-disc: UNCLAIMED 34 -> 6, residue 0 — the 6 carry
byte-checked negative evidence; next tier = the CD-read tracer
- docs: memory-map §S45 (slots + routing + debug-menu ops), disc-completeness S45
addendum, decision-log R31 entry, docs/debug-menu-list.txt (Drew's transcription)
- .run/s45 evidence allowlisted (tour logs/scripts/rosters); 104 ram dumps LOCAL-ONLY
- new baseline: 93.8% instr / 95.68% fn / 87.2% distinct over 212