- waves C-N: ~448 banked, stubs 12,059 -> 11,549, fleet 95.4% instr, 213/213 after every
batch, main 0 -> 175 matched.
- records the three campaign-changing findings (main is open and was never hard; gate-group
packing is the throughput lever; match_one verifies SHAPE not SYMBOL IDENTITY), the tools
built/fixed, the prioritized open work, and the methodological lesson.
- cron be8fb48c cancelled; nothing in flight at handoff.
- 44 atlas mass cards on ov_SC04_011, avg 51 ins (up to 112) -> 43/44 shape-verified,
40 banked, ONE gate group. stubs 11,589 -> 11,549.
- data point that matters for the endgame: the mass lane holds at ~98% draft on the LARGER
band (51 avg vs the 12-42 cousins the night started with). Since the public metric is
instruction-weighted, that is the band that moves it — and it is in reach of haiku/sonnet,
not only the frontier tier.
- 1 near enqueued (func_80188A30, close=9: gcc reorders a beqz + delay-slot nop; core logic
verified correct) as grinder fuel.
- each draft compiles STANDALONE so it carries its own 'typedef struct {...} SVECTOR;'. Once
one such function banks, that typedef lives in src/800.c forever and every later draft
defining its own collides — a C89 duplicate-typedef error, not a byte miss. harvest_verify
already handles this; gate_main did not, and wave L lost a verified-correct draft to it.
- strip_dup_typedefs() drops typedefs the destination file (or an earlier body in the same
batch) already defines, and keeps novel ones.
- R39 NC: drops the duplicate, keeps the novel one, leaves the function body untouched, and is
a strict no-op when there are no duplicates.
- records the night's totals and, more importantly, HOW to run the main lane: gate_main.py
batch clean-rebuild, never gate_lane/gate_stage (incremental + .ld rewrite = false diff).
- notes the one mechanical improvement left (strip duplicate typedefs on substitution) and
that the ~10 conflict-dropped main drafts are verified-correct and recoverable.
- 44 atlas mass cards -> 44/44 shape-verified, 42 banked (2 dropped: a duplicate SVECTOR
typedef and a u8[] vs char[] decl conflict). main stubs 955 -> 913. fleet stubs -> 11,589.
- first wave carrying law 1c (match_one masks relocations => verifies SHAPE not SYMBOL
IDENTITY; re-check every symbol against the target .s relocation lines after MATCH).
- the compile-error shortcut paid for itself: named 'SVECTOR at src/800.c:94' and the exact
draft in seconds, where the old bisect path burned 28 minutes producing nothing.
- KNOWN NEXT IMPROVEMENT: gate_main should strip duplicate typedefs on substitution the way
harvest_verify already does — src/800.c carries a local SVECTOR from a previously banked
function, so any later draft defining its own collides. Mechanical, recurring, cheap.
- 44 atlas mass cards on main -> 44/44 standalone, 41 banked after 3 in-TU decl-conflict drops.
gate_main clean rebuild -> 143dbb89 BYTE-IDENTICAL, then full-fleet R22 213/213.
main stubs 996 -> 955; main REAL 92 -> 133 (incl. propagated/dedup credit).
- took 5 attempts; each exposed a real defect, the last one substantive:
* conflict checker too coarse (u8 D_x == u8 D_x[]) -> fixed + NC'd
* my pkill pattern matched its own shell -> stop shell process-matching
* FALSE PASS: sha() read a stale binary when the build failed -> rm output + check returncode
* bisect burned clean rebuilds; the compiler names the culprit -> read the error instead
* THE REAL BUG: func_8002A234 stored to the WRONG GLOBALS (v1->D_80078EE8/0->D_80078EE4,
target is the reverse). 2 bytes, both at %lo relocation offsets.
- STANDING CAVEAT (write this into the wave prompt): match_one MASKS jal/HI16/LO16, so it
verifies INSTRUCTION SHAPE, NOT SYMBOL IDENTITY. A draft that calls the wrong function or
stores to the wrong global passes standalone every time. Only the whole-binary gate sees it
— same class as the PsyQ symbol-name errors in waves F/G.
- clean_build() ran 'make build' and then sha()'d build/us/SLUS_007.26 off disk. If the build
FAILED (compile error), the PREVIOUS successful binary was still there, so sha() returned the
good hash and the tool reported BYTE-IDENTICAL for a build that never ran.
- that is exactly how it claimed '43 banked' for wave K on a TU that did not compile; the
clean-fleet R22 caught it ([FAIL] main). A verifier that can pass without building is worse
than no verifier.
- fix: rm the output before building, and treat a non-zero make return as no-hash/never-pass.
- also: unbuffered print (a 16-min run looked hung with an empty log) and read the compile
error to name the culprit instead of bisecting at a full clean rebuild per step.
- gate_main reported wave K BYTE-IDENTICAL; the clean fleet R22 then failed [FAIL] main.
Three drafts declared D_80078D98 inconsistently (1 scalar, 2 array) and my conflict checker
could not see the difference: typesig() split on the symbol and kept only the prefix.
- fixed to retain the declarator suffix ('' vs '[]'); NC'd both directions — the wave-K
conflict is now caught, wave J's known answer (34/5) is unchanged.
- BOTH failure modes now documented in the tool: v1 too STRICT (compared parameter names,
discarded 2 good drafts), v2 too COARSE (ignored [], passed a real conflict). R22 caught
what the tool missed, which is exactly why the clean-rebuild rule exists.
- phase log: recorded the night's methodological lesson — every serious stall was an
instrument trusted without a control, never the compiler.
- main CANNOT be gated incrementally: its extract runs the EXE-only psyq_integrate +
ld_interleave steps that REWRITE the .ld, so gate_lane/gate_stage's incremental build
re-runs that on an already-rewritten script and yields a FALSE diff (R22's own rationale).
That cost a night: 4 byte-correct drafts gated 0/4 and I wrote up a nonexistent linker
defect before the null-draft control refuted it.
- gate_main substitutes the whole batch -> make extract BINARY=main -> make build -> compare
SHA. ONE clean build verifies the WHOLE batch (34 banked in one rebuild); bisects on failure
so a single bad draft can't sink the rest.
- handles both main-specific hazards: (1) in-TU cross-draft decl conflicts, resolved greedily
on TYPE SIGNATURES ONLY (comparing parameter names wrongly discards good drafts, R39 — my
own first version did exactly that); (2) stale .s after a revert (extract before resolving).
- NC'd against wave J's known answer: 34 compatible / 5 dropped, matching the hand result.
- first full wave against the main EXE: 40 atlas mass cards -> 39/40 standalone (98%), ALL haiku.
main drafts exactly like an overlay; the only special handling is the gate path.
- gated via the clean-rebuild batch path: substitute -> make extract BINARY=main -> make build
BINARY=main -> 143dbb89 BYTE-IDENTICAL; then full-fleet R22 213/213. main stubs 1030 -> 996.
- NEW CLASS: in-TU cross-draft declaration conflicts. Batching N drafts into ONE .c means their
externs must agree with EACH OTHER (D_800A4ED4 s16-vs-u16; func_8001C9D0 void/void*/s32).
Resolved greedily (keep-in-order, drop incompatible) at a cost of 5 recoverable drafts.
- the recovery lever, proven on func_80037368: adopt the shared header's decl VERBATIM
(extern u8 D_80076251;) and adapt at the USE site ((&D_80076251)[i]) instead of redeclaring.
- NOTE on my own tooling: my first conflict detector compared parameter NAMES and wrongly
dropped 2 good drafts ((s32 *_) vs (s32 *)); comparing type signatures only recovered them.
Second time tonight a refusal check of mine discarded good work (R39).
- 44 atlas mass cards on ov_SC07_007 -> 44/44 standalone MATCH (independently re-verified),
symfix clean on all 44, and the binary builds BYTE-IDENTICAL with every draft substituted.
- includes a fleet-shared engine_core.h arity fix (func_8017FD2C(u8*) -> ()), so this batch
needed the full-fleet proof: make clean && extract-all && check-all -> 213/213.
- stubs 11,751 -> 11,706.
- TOOLING NOTE for next session: gate_lane SWALLOWS gate_stage's stderr. Two gate attempts
reported 'banked 0, near 0, failed 0' -- an unreported crash that reads exactly like a
legitimate 'nothing banked'. Running gate_stage directly surfaced the real cause at once
(corpus.CorpusError naming 16 missing .s files, fallout from my own make clean). gate_lane
should surface stderr / distinguish CRASH from NOTHING-BANKED.
- func_80013228, func_8001CB00, func_800142C8 (src/800.c) + func_8001099C (src/boot.c, the -O0
boot module) all byte-gated together: make clean && make extract BINARY=main && make build
BINARY=main -> 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
- these are the drafts the incremental gate rejected 0/4 last night. The drafts were CORRECT;
the gate path was wrong. R22's own rationale names this exact trap: psyq_integrate rewrites
the .ld in place, so an incremental build re-runs it on an already-rewritten script and
produces a FALSE diff. main cannot be gated incrementally -- it needs make clean first.
- main was 0.5% matched and treated as the project's hardest remaining mass. It is minable.
- I reported 'main is blocked on a link-resolution defect' with confidence. REFUTED by the
control I should have run first: with src/ fully reverted and NO draft, make build BINARY=main
still produced the same c4546248 and the identical 2-byte jal diff. A defect that reproduces
with zero drafts is not caused by drafts.
- byte-proven both ways: 'make extract BINARY=main && make build BINARY=main' -> 143dbb89
BYTE-IDENTICAL (twice); 'make build' alone -> c4546248 + the 2-byte diff, deterministically.
- cause: main's extract runs the EXE-only psyq_integrate + ld_interleave steps that REWRITE the
linker script; gate_stage/harvest_verify build without re-extracting, so main gates against a
stale .ld. Overlays have no such step and are unaffected (all overlay banks are R22-clean).
- implication: main's 79,510 ins @ 0.5% are behind a TOOLING gap, not a wall. Fix the main gate
path to re-extract, then re-gate the 4 preserved drafts in .run/wave_p31e/main/.
- the earlier 'archive-member selection' evidence is a downstream symptom of the stale .ld.
- wave H: 40 atlas mass cards on ov_SC02_005 -> 38/40 standalone (95%), 34 banked, ONE gate
group. R22 213/213. stubs 11,788 -> 11,751. fleet 95.4%.
- §175 (NEW): a register pin to a CALLER-SAVED reg is not a scheduling hint, it changes program
meaning. func_80182EB0: value written before a jal and read after it; the $2 pin let gcc treat
the pre-call store as dead across the call and SILENTLY DELETE addiu v0,zero,-1 (49 vs 50 ins),
post-call read = garbage. Fix was to DROP the pin and kill the cross-call live range in C.
Rule: never pin a caller-saved reg to a value whose live range crosses a jal — use a
callee-saved $s0-$s7 (safe by ABI, the §17 lever) or restructure. A one-instruction count
mismatch on a caller-saved-pinned draft is this bug until proven otherwise.
- 2 NEARs enqueued with full diagnoses (prologue $ra-save scheduling; S3 chain-priority).
- call site is libmcrd1/func_80060D9C.s +0xD8, referencing the callee BY NAME (jal func_80061FA8)
- our build resolves that name 0x2A0 later, which is EXACTLY the .text size of build/src/800c2.o,
the object starting at 0x80061FA8 -> the name binds to the END of the object (the next
section's first symbol, firstfile in psyq/apicard/A66.o) instead of its start
- 3 ordered hypotheses recorded (duplicate func_80061FA8/.NON_MATCHING pair; object dropped or
reordered when src/800.o gains an undefined ref; an absolute symbol shadowing the object's)
- everything to test it is in the map + the 4 preserved main drafts
- I withheld 3 drafts on symfix STALE/AMBIGUOUS flags; all 3 already used the CORRECT PsyQ
names (RotMatrixY, RotTransSV). What symfix called 'draft-only symbols' were LOCAL
identifiers: a typedef, two inline-asm macro names, two local struct typedefs.
- gated unchanged -> 3/3 banked. wave G closes 35/36.
- two symfix defects behind it: (a) local typedef/macro names counted as symbol references;
(b) draft-symbol extraction misses some extern forms, so a name the draft DOES declare
still reports as asm-only.
- OPERATING RULE: symfix is advisory, the whole-binary gate is the arbiter. Never withhold a
standalone-MATCH draft on a symfix flag alone (R39 applied to my own judgement).
- wave G: 36 atlas mass cards on ov_SC03_006 -> 36/36 standalone (100%, independently
re-verified), 32 banked of 33 gated, ONE gate group. R22 213/213. stubs -> 11,788.
- two TU-packed waves now confirm the shape: ~1 rebuild per wave instead of 23.
- law 1b added to the wave prompt: agents reconstruct CODE at 91-100% but INVENT PsyQ symbol
names (S80131E00->Square0, Blk20_...->RotMatrixY, SRM_...->RotTransSV). Dangerous because
match_one MASKS relocations -> a wrong callee name still reports MATCH; only the
whole-binary gate + symbol audit catch it, after a wasted rebuild.
- 3 wave-G drafts held by that audit (would previously have crashed it pre-commit:2330).
- deltas[int(new[-8:],16) - int(old[-8:],16)] assumed every symbol is func_XXXXXXXX/D_XXXXXXXX.
A draft whose target calls a PsyQ symbol by name (Square0, RotMatrixY) raised
ValueError and aborted the WHOLE audit — in wave F one such callee took down a 55-draft
batch, and the rename itself (re.subn) had already succeeded.
- a curated pair is a plain 1:1 rename with no address delta: count it as 'named-1:1' and
carry on (R32 — one unparseable pair must never answer for the batch). Delta print made
mixed-key safe.
- verified on the literal incident values: old aborts on 'Square0'; new completes, counting
2 named renames + still bucketing the hex pair by its 0x484c delta
- wave F: 60 atlas 'mass' cards (fresh crack, NO seed body) -> 55/60 standalone pre-repair
(59/60 post-repair), 50 BANKED of 53 gated, in a SINGLE gate group. R22 213/213.
- the throughput thesis is confirmed: wave D banked 45 across 23 whole-binary rebuilds;
wave F banked 50 across 1. gate cost scales with (binary,TU) groups, not drafts.
- the fresh-crack lane converts like the seeded lanes (~91%) => the atlas's ~7k draftable
candidates are all reachable, not just those resembling a prior match.
- grinder: 0 banked across 8 seeds / 11 ILS cycles (band exhausted); re-exposed the Phase-22
split-file blindness (no .s under md_MAIN_027).
- KNOWN DEFECT logged: aprop_symfix crashes on non-hex symbols (int('Square0',16)) — one
PsyQ-named callee aborts the whole audit; needs 1:1-rename handling + per-pair isolation.
- stubs 11,876 -> 11,826
- wave D (48 adapt cards): 47/48 standalone, 45 BANKED across two gates (40 + 5 late-repair).
Operational lesson: build the gate slate AFTER the repair stage lands — the first slate was
built early and 5 rescued drafts needed a second gate.
- wave C (35: 11 tell + 24 weak): 32 banked, 91% gate. Weak lane proven 24/24 on haiku.
- MAIN BLOCKER DIAGNOSED (the night's most valuable finding): main drafts are byte-correct yet
gate 0/4. Byte-diff of the built EXE = exactly 2 bytes in 413,696, NOT in the drafted fn: a
jal at 0x80060E74 retargets func_80061FA8 (game code, 800c2.o) -> firstfile (PsyQ libapi
A66.o). Adding one C fn perturbs symbol resolution between game code and the LINKED PsyQ
archives. main is an INTEGRATION wall, not a matching wall -> its own lane; excluded from
build_wave_atlas by default.
- build_wave_atlas.py now packs by (binary, TU) — the REAL gate-group key, since each group is
one whole-binary rebuild (wave D: 42 drafts / 23 groups = the throughput ceiling).
- 3 NEARs enqueued as grinder fuel incl. func_80183578 at close=1 DELAY-SLOT (§60a precedent).