Replaced the carve's generated _o0d.c wholesale with the minimal fleet-standard
whale TU. Keeping the generated §8b carried decl layer was NOT an option: it
conflicts with shared/func_80144B9C.h on 7 symbols (func_80015978 void*/s32,
func_800CF854 void/s32, func_801336E8, D_801274C8/CC/D0). Legitimate to drop it
here because the region holds ONLY these two functions (0xD44 = 0xC08 + 0x13C
exactly), so nothing in the TU needs the carried decls.
func_801457A4 remapped from ov_SC01_077_o0b.c: its 79 instructions differ across
overlays by exactly ONE data symbol (D_80186AD0 -> D_8017E338).
Byte-identical: d6b3e8b971cdd6c53aea8c4f265afb82b363283c == config/check.ov_MAIN_012.sha.
corpus.stubs(ov_MAIN_012) = 0 -- the binary has no open stubs left.
NOT via rollout_o0: its stub_file_of() skips any basename containing _o0, and the
carve moved BOTH stubs into _o0d.c, so the driver is structurally blind to them
and would have reported 'no-stub / already banked?' and banked nothing.
o0_subsplit: 2 unmatched stubs (func_80144B9C 770 ins + func_801457A4 79 ins), 0
already-matched islands interleaved, so K=0 and one -O0 region is correct. carve
repoints (none); config/overlays.mk UNCHANGED (the whale object owns no .rodata
carve anywhere in the fleet: 0 of 213 splat yamls carve .rodata to an _o0b).
BYTE-NEUTRAL, which is the whole claim of a carve:
build d6b3e8b971cdd6c53aea8c4f265afb82b363283c == config/check.ov_MAIN_012.sha
interleave_check ALIGNED. Derived AND carved under .run/auto/gate.ov_MAIN_012.lock
(the commit:2791 rule: a plan derived outside the lock can describe a tree state that
never existed).
An escalation that re-derives what the cheaper tier already closed pays twice for
the same instructions. This passes the prior draft, its measured closeness and its
full residual report into the prompt, tells the agent to reproduce that closeness
first (and to STOP and report if it cannot -- R40), and forbids re-trying the
levers the prior agent already ruled out.
Points the agent at the escalation path the project actually has for a
compiler-internal residual (R17): the pinned gcc-2.7.2 source in-repo,
docs/gcc-2.7.2-map, and pass-disabling as a DIAGNOSTIC only.
Requires a 'new_idiom' field in the verdict: a Fable run that closes a function
but names no reusable lever has bought one function; one that names the lever
buys the class.
Drains a drafting wave's finished drafts into parallel_gate, grouped by binary,
while drafting keeps streaming. Accumulates to --min-drafts because same-binary
drafts must share a build (S67 had ov_SC05_010 x3 in one batch). --r22 by
default: it re-verifies the whole fleet from make clean after the merge and
aborts instead of committing a red binary -- the guard that would have caught
S67's '13 of 213 red, every one a jtbl binary' at once, for ~2.5 min.
Ledger keyed 'binary:fn' (R48 -- func_8017BEBC is a different function in
different overlays). A draft absent from its wave's targets.json is REFUSED
LOUDLY, never guessed at (R43); negative-controlled both directions: synthetic
unresolvable draft -> exit 1, clean tree -> exit 0, normal path unchanged.
Propagation, twin_sweep and harvest stay periodic and operator-driven: they need
aggregate, and cookbook 330 existed only because four instances landed in one wave.
The S67 FINAL-3 OPEN item, plus the two defects found while doing it.
* fix(dedup_propagate): the tool could not run AT ALL. S67's -j patch wrote
`os.environ` at module level in the one module that imports `os as _os`, so
every invocation died with NameError before doing any work. Propagation was
not deferred, it was impossible. Import-checked the other 7 -j-patched tools.
* propagation, honestly scoped: the real closable set is 11, not 32, derived two
independent ways that agree (seed_ref exact+same_addr, and a direct corpus
derivation). The 3,161-entry --auto-from plan over 53 overlays is dedup
hygiene over already-matched code and closes almost no open stub.
Applied: 2 banked byte-green (ov_SC04_018 func_80181270, func_80182AF8);
3 gate-refused and cleanly reverted; 6 blocked with named blockers
(3 CARRY-FIXABLE, 3 func_80144B9C not-inline-def -> needs the o0 whale carve).
R22 clean fleet: extract 212/212, check 213 passed 0 failed of 213, rc 0/0/0.
Frontier 453 -> 451.
* fix(seed_ref): REFUSE targets in LINKED subsegs. The playbook calls this tool
"the fleet-wide answer" and it reported 82 open stubs with a banked twin --
43 of them main stubs whose TUs the linker script never references. Any C
written there compiles, links and leaves the SHA1 green WHETHER OR NOT IT IS
CORRECT, so a mechanical twin lane fed from that list could have minted up to
43 gate-green FALSE matches the byte gate cannot see. draw_waves has refused
these since S66; this oracle did not. The refusal is counted and printed, not
silent. NC: guarded 39 subset of raw 82, all 43 dropped are main, the non-main
population is identical.
* wave drawn: .run/S68o1 (24 opus 187-770 ins) + .run/S68m1 (30 main), cards +
packs + wave_args asserted, queue of 53. Drafting opened at concurrency 5.
The S67 first attempt banked jtbl bodies in worktrees and left their carve config behind, so 13 of
213 went red (reverted commit:3396). A carve writes THREE things and the merge must carry all or none:
1. src/<bin>/*.c per-binary, adopted like any bank
2. config/splat.<bin>.yaml per-binary, adopted whole, baseline-checked
3. config/overlays.mk SHARED — adopt ONLY this binary's BLOCK
ovl_block()/splice_ovl_block() cut on the headers, so two workers carving
different binaries edit disjoint regions and cannot clobber each other. Same pinned-baseline refusal
as the per-file adopt, at block granularity — never a blanket file add
(the carve-state-files-never-blanket-add rule).
Verified: block round-trips byte-identically and leaves other binaries' blocks untouched.
Drew: "we need to parallel the jtbl stuff too. nothing should be serial."
THE BLOCKER: harvest_verify's jtbl carve runs `make extract`, and a worktree's asm/ is a SYMLINK to
the main tree (parallel_gate.py:77) — so a carving worker would rewrite the MAIN tree's asm while
other workers read it. That is the only reason jtbl drafts had a serial lane, and it cost ~1 hour to
gate 16 binaries in order to protect ONE jtbl draft this session.
THE FIX IS CHEAP, and the measurement is why: asm/ is 448 MB but ONE binary's subtree is 3.6-5.0 MB.
isolate_asm() replaces the blanket symlink with a real directory that SYMLINKS every other binary
(read-only, free) and holds a real COPY of just the binary being carved. `make extract BINARY=<b>`
then writes only inside the worktree. ~5 MB per worker on a box with 32 GB free.
Applied per JOB, not per worktree, because worker slots are reused across binaries — _drafts_carry_jtbl
uses the SAME predicate harvest_verify carves on (a jtbl_ reference in the target .s), so the router
and the gate cannot disagree (R33/R34).
NEGATIVE CONTROL: _drafts_carry_jtbl agrees with gate_wave.split()'s independent classification on
all 37 binaries of the S67 draft set, both directions.
Completes the S67 audit: 0 remaining 'make build' call sites without -j. family_sweep is the one
that matters most of the three — twin_sweep delegates to it, so every sibling remap now gets the
6.1x build too.
NOT parallelizable, checked: 'make extract' is a single `splat split` process, so -j cannot help
it; its cost is splat's own runtime. The 4 extract calls in harvest_verify's jtbl carve path stay
as they are.
MEASURED on ov_SC03_010 (35 objects), clean each time, byte-verified against the locked SHA:
make build 7.18 s real / 6.84 s user <- SERIAL, one core, on a 32-thread box
make -j16 build 1.18 s real / 11.3 s user <- 6.1x, IDENTICAL bytes
Negative control at -j32 over ov_SC03_010 + ov_SC01_004 + md_MAIN_031: all rc=0, all byte-identical
to config/check.<bin>.sha.
WHY IT WAS MISSED: the Makefile's `JOBS ?= 16` is parallelism ACROSS binaries (`xargs -P`), which
parallel_gate already uses for extract-all/check-all. Parallelism WITHIN one binary's ~35 objects was
never passed by any tool, though docs/SETUP.md:416 documents `make -j$(nproc) build` as the form.
PATCHED the two hot sites:
* harvest_verify.py — the gate's build, run ONCE PER DRAFT (--chunk 1). Every gate in the project.
* dedup_propagate.py byte_gate — run once per propagation candidate, which is why a wide
propagation dominated a 33-minute gate this session.
Both honour BFM_BUILD_JOBS, else os.cpu_count().
SAFE BY CONSTRUCTION: these builds feed a locked-SHA comparison, so a bad parallel build FAILS the
gate rather than banking wrong bytes. The error direction is a false NEGATIVE, never a false bank;
G3/P9 remains the sole arbiter.
Correction recorded: I earlier extrapolated "9 serial binaries x 30 min" from ONE 33-minute
measurement. That was unfounded — propagation time scales with how many sites a body reaches, and
other gates today propagated x19/x8/x7 quickly. One slow binary is not a rate (R41).
Seven sections from 30 single-function opus workflows on 187-297 instruction targets (30/30 MATCH):
§339 a 2-case switch OMITS gcc's low-bound range test (stmt.c emit_case_nodes) — so slti/bnez
between two beqs is a COUNT TELL that a case node is missing from your draft
§340 §194-K corollary: a 'scheduler' residual can be sched.c's ALIAS ORACLE inventing a false
true-dependence; source order picks the edge's DIRECTION, so reverse it into an anti-dep
rather than fighting it (10->0, zero bytes; 3 alternatives refuted with reasons)
§341 an HImode store temp reweights a sched2 tie no statement order can reach
§342 NEW LAW: a twin's param cast in a local is NOT byte-neutral when a later param also
needs a callee-saved reg — and the §333 converse does NOT hold (gcc may already pad the gap)
§343 decl_prior's fleet MAJORITY can be wrong about the true signature — read the RIVALS.
Measured: void(s32) x1374 vs the truth s32(s32) x163. The tool is honest, the corpus is wrong.
§344 raise a biv's global_alloc priority with a zero-byte REFERENCE; a register pin kills LSR
§345 volatile STORE evicts the MEM from cse and keeps sh; volatile LOAD blocks combine and
degrades lh into lhu+sll+sra — the qualifier is not symmetric
Also: seed_ref validated on a live A/B. The same 187-ins body cost 102,193 tokens / 476 s in
ov_SC03_107 when the card said 'no banked twin', and 72,077 tokens / 135 s in ov_SC07_006 once the
card carried the twin — 30% fewer tokens, 3.5x faster. A second instance (func_8017F62C) went
63,595 vs 118,485 tokens. others_open=137 on that one exemplar, so it compounds.
Measured a SECOND time in S67, and the first fix was incomplete. A waiter using the bracketed
pattern still matched itself and spun 1h35m, because the same shell command had LAUNCHED the job —
so its own command line carried the unbracketed 'gate_stage.py --binary ov_SC07_007' from the nohup
half. The regex gate_[s]tage.py does not match the literal bracketed text, but it happily matches
the plain text sitting earlier on the same line.
Rule is now: launch and wait in SEPARATE shell invocations, or better, wait on a completion MARKER
the job writes to its own log rather than on process liveness.
THE DOC GAP, and it cost tokens this session. `docs/automation-runbook.md` was titled "the
autonomous campaign, as it actually runs" while documenting the RETIRED OpenRouter/ox-alpha system
whose lanes are all deliberately DEAD. The current Claude-wave pipeline existed only as two dense
tooling-inventory rows in SETUP.md — reference, not procedure. Three of this session's costliest
mistakes were procedural and a playbook prevents each:
* hand-typed a refill target -> invented func_80184F60 (2nd instruction of a matched function), 58k
* hand-rolled a serial gate loop when parallel_gate existed -> ~1h for what took 103s
* re-derived a function banked verbatim in ~20 overlays -> 102k
NEW docs/wave-playbook.md — start to finish, each guard paired with the MEASUREMENT that produced it
(that pairing is the part a generic decomp guide cannot have, and the seed of the future template).
automation-runbook.md retitled HISTORICAL with a pointer; SETUP.md §6.9 links the playbook.
NEW tools/seed_ref.py — the cross-TU banked twin, joined on corpus signature hashes (no atlas knn,
~2s fleet-wide), wired into t5_cards.py. FLEET: 87 open stubs have a banked twin; 41 of them sit in
twin_sweep's refusal ledger, invisible to BOTH tools at once. Documents twin_sweep's two holes:
load_sigs covers 141/213 binaries (main, resident, all md_MAIN_* absent), and one curated symbol
name silently disables an entire binary via a bare `except Exception: pass`.
Schema note: seed_ref's binary/fn are the EXEMPLAR's, because api_agent greps src/{binary} for {fn};
naming them after the target would send every agent grepping for itself — caught pre-ship.
HARVEST §333-§338 from the s67o2_1/pool_1 waves:
§333 frame size is set by DECLARED aggregates, not used ones — an unreferenced trailing local is a
dial (3 instances; one worth 30 of 32 residual rows)
§334 a reload spill slot rounds to BIGGEST_ALIGNMENT for align AND size: one 4-byte pseudo grew a
frame by 16 (82->53)
§335 `extern u16 A[]` at a variable subscript allocates ~8B/access of dead stack temps that inflate
the frame with ZERO extra instructions — invisible in a body diff (141->20)
§336 the §5a barrier goes at the BOTTOM of the twin; find_cross_jump walks BACKWARD
§337 the CC1-ONLY blocker class: blocker_probe's static oracle says "none" and cc1 still fails
§338 _sltiu_bounds misreads a non-switch sltiu as a bounds check, over-spanning the table
gate_wave.py now STREAMS both lanes (R55) — it captured output and printed at the end, leaving a
zero-byte log indistinguishable from a hang.
Wave s67m2_1: 7 sonnet agents, 1 MATCH banked, 6 NEAR — but 4 of the 7 are NOT drafting failures:
* func_8005FA94 / func_8005D244 — oracle_reorder.py bypass gives 0/55 and 0/62 diffs: the C is
byte-correct, the pinned as -O1 cannot emit the §188 epilogue. func_8005D244 is additionally
libpad pdent3.o, an SDK object owned by psyq_integrate.py — it should never have been drawn.
* func_80062144 / func_8005DBD8 — §332, traced to the compiler sources: gcc-2.7.2 emits a symbolic
la as ONE atomic length-2 insn (no HIGH/LO_SUM split in this backend), eligible_for_delay requires
length==1, so it can never fill a jump delay slot; the retail split is ASPSX macro-hopping that
maspsx does not replicate. Byte-verified by running maspsx over cc1's raw -dS output.
6 such functions fleet-wide, NONE banked.
§332a records the draw-policy consequence: main's cheap population is spent and the residual is
ENRICHED in toolchain walls, so main's apparent match rate is contamination, not a model signal.
Wall ledger at .run/S67_walls.txt for the --exclude mechanism.
HARVEST — the s67o1/s67m1 wave banked 7 cookbook sections:
* §325 a shared small constant stored twice in the pre-loop block is a LOCAL-ALLOC $s-occupant that
steals the argument allocno's register — pin the ARGUMENT-derived local, not the constant
(pinning the constant reached only closeness 15). byte-proven func_80184F18.
* §326 spelling two reads of the same halfword differently (sym[i] vs *(s16*)(base+i*4+2)) yields
different address rtx and DEFEATS address-CSE, restoring separate %hi/%lo groups. func_8017FAAC.
* §327 a range test must be HImode: with s32 + a (u16) cast gcc PROVES the mask redundant and drops
the andi — a real -1 length drift that reads as a schedule. +3 levers. func_8017EC34.
* §328 NEW LAW: the volatile alias must be an aliased OBJECT; `*(volatile s32*)&sym` unfolds %lo
into a separate addiu (+1 ins). func_80181B8C.
* §329 fold-const narrows `(int)s16 & 0xFFF` onto the RAW HImode pseudo, breaking the
sign-extend/mask register tie; a zero-byte `s32 e = t;` widening temp restores it (30 rows -> 0).
* §330 the NEIGHBOUR-SHAPE lever, four independent instances in one wave — copy an already-banked
in-TU function's SPELLING before any codegen reasoning (one dissolved 18 REGALLOC-PERM rows in a
single compile). Corollary: a warm start from another binary is often worth LESS than the
neighbour 20 lines away.
* §331 OPEN GAP, recorded as unsolved: no lever eliminates an UNWANTED DUPLICATE copy at a
branch-target block head (main/func_80013154, closeness 12, ~16 iterations, 5 approaches refuted).
TOOLIFY — tools/gate_wave.py: split the batch on the per-draft jtbl predicate, run parallel_gate
and the serial jtbl lane CONCURRENTLY. Measured this session: 4 binaries in 103s wall through
parallel_gate (87/87/88/102s each) vs ~6 min serially; I had gated all 16 serially to protect ONE
jtbl draft, ~1 hour. The split precedes the run because a jtbl worker does NOT fail cleanly — it
re-extracts through the worktree's asm/ symlink and writes the MAIN tree while other workers read it.
Its own negative control found two defects in it before first use:
* listdir counted gate_stage's _xform output dirs (-cn/-cast/-rc/-sd, written as SIBLINGS inside
the drafts root) as binaries: 20 "binaries" for a 16-binary wave. Now validated against
progress.BINARIES and refused loudly (R32/R43).
* a post-hoc control over BANKED functions cannot reproduce a split (has_jtbl has no stub to read);
re-controlled against a live draft set, where it correctly routes the two functions the gate had
independently reported CARVE-REFUSED.
Wave s67m1: 7 sonnet agents, 0 errors. 5 MATCH banked after bisection in 9 rebuilds;
the 2 NEAR drafts rejected exactly as their agents predicted (func_80013154 close=12,
func_8005ECC0 close=6).
One draft's declaration refused the whole batch first: func_8002A088 declared
`extern s32 func_8002A108(void);` while src/800.c DEFINES it as (s32) at line 15270.
Fixed with the no-proto half of cookbook §324 (`extern s32 func_8002A108();`) — a
no-prototype decl is compatible with a promotion-safe definition and leaves the 0-arg
call unchecked, byte-neutral for the emitted jal. Argues for wiring §324 into the ladder
rather than hand-applying it; it cost a full main gate cycle.
NOTE for the ledger: func_8005E8E8 and func_8005EC00 are verbatim file-scope __asm__
transcriptions, not decompiled C — both hit the §188 wall (2 callee-saved regs with
jr $ra + addiu $sp in the delay slot, unreachable from cc1 under the pinned as -O1),
and both follow established in-TU precedent (func_8005E79C, func_8005EB28).
WALL LEDGER candidates: func_8005ECC0's epilogue tail is proven unreachable via
oracle_reorder.py; only idx24-26 (a beq delay-slot steal) remains open there, and it has
now resisted 8 prior wave attempts plus 3 today.
R22 caught it: 212/213 after the S67-cc1 gate run. `ov_SC04_018` was RED.
ROOT CAUSE (from the diff, not inferred). Commit commit:3354's propagation replaced three bodies in
`ov_SC04_018_jr_80135D20.c` with DEFINE_func_*() instantiations and deleted the 981 lines they
occupied — INCLUDING the TU's file-scope declaration layer, which the two surviving non-deduped
bodies still referenced. A duplicate copy of those decls survived at line 225, BELOW the function
that uses them at line 42, so C89 ordering made it fatal (`D_8018D7A4' undeclared).
THE STRUCTURAL GAP: gate_stage byte-gates the SOURCE binary, then propagation writes to N OTHER
binaries and nothing re-verifies them. "fleet 99.2%" in the commit subject is a metric, not a gate.
This is the blind spot R50 exists for, and only the periodic whole-fleet R22 could see it.
REPAIR: restored src/ov_SC04_018 to commit:3354^, re-extracted (banking had pruned the .s stubs the
restored INCLUDE_ASM lines need), rebuilt rc=0 at the locked SHA fe9b413f. dedup-check clean
(2193 validated, 0 failed, C1 255302/255302). Cost: the 2 banks in that binary.
NEW tools/restore_dropped_decls.py — compiler-driven recovery for this failure mode: build, read
which identifiers cc1 calls undeclared, look each one up in the pre-deletion git ref, insert it
above the leading #include block, repeat. Two defects found and fixed in it while using it:
* anchoring after "the last extern in the first 400 lines" inserts BELOW the point of use, so the
build fails identically and the loop re-inserts forever (measured: 25 rounds, 100 dead decls).
The only safe anchor is the top of the file.
* a no-progress guard now REFUSES when a round asks for what the last round already inserted.
It also correctly refused when the failure changed class (link-level undefined references), which
is how the wider damage was found rather than papered over.
NOT a defect of the S67 §8d rung: scope_demote_drafts only ever writes draft dirs under .run/.
FLEET: make clean + extract-all + check-all = 213 passed, 0 failed of 213.
FRONTIER: 530 -> 526 (4 functions closed this session, measured from corpus.stubs).