cast_self_callers casts a function's call sites in PREPARATION for banking it.
When the draft then fails, the cast must come back out — the tool journals every
edit for exactly that, and I did not run the undo.
The cost was concrete: the leftover cast on func_8017F8B8 made ov_SC07_000 fail
to COMPILE at HEAD, so every subsequent gate verdict on that binary was measuring
a broken baseline rather than the draft. Two drafting agents reported it as
BASELINE-RED before I noticed.
24 casts reverted across 11 files in 7 binaries; all 7 rebuild green. This is the
discipline recover_integration already documents ('REVERTS the caller edits for
anything that doesn't bank') applied to the new tool.
Reverts commit:3472. The binary was RED at HEAD: sha1 9c94d36a vs expected
8bc09c42. The gate that banked it ran with --r22 disabled because 24 drafting
agents were live (R22 does make clean, which deletes asm/ under them), so the
one check that would have caught it was the one I had turned off.
The revert must carry the CARVE STATE, not just the C: the bank moved
JTBL_PADS 0,0,4,4 -> 0,0,4,4,4 plus the splat yaml, and a src-only revert left
4 tables against 5 pad specs ('table-count drift vs the carve'). Reverting the
whole commit restores BYTE-IDENTICAL.
Found only because two drafting agents independently reported their target's
binary as BASELINE-RED and I checked their claim against the bytes.
main func_80020A28
main func_80021284
main func_800221A8
main func_8002374C
main func_80026514
main func_8002D904
main func_800377D8
main func_8003DC90
A no-proto decl is ILLEGAL against a definition whose parameter is affected by
the default argument promotions (s16 here): C89 requires the parameter types be
promotion-stable when one declaration has no prototype. So fix_arity_callers'
--any-proto cannot reach this case (it skips it as 'narrow-param').
With the call sites already cast (§378) the decls emit no code, so syncing them
to the draft's exact signature is byte-neutral: 3 decls in src/800.c rewritten to
extern void func_80036D58(s16). Byte-identical, main.
The §376 class's real blocker: after fix_arity_callers no-protos the conflicting
forward decl, the draft's definition becomes the prototype in scope and the TU's
own call site fails with 'too few arguments'. Casting THAT call site to a 0-arg
function pointer is byte-neutral (gcc-2.7.2 folds a cast of a known symbol back
to a direct jal, §20) and banks the function.
MY HYPOTHESIS WAS WRONG AND THE AGENT SAID SO. I predicted the ownership oracle
was blind to verbatim-asm owners. It is not. 0x800cedf8 is the §154-A LEADING
RODATA ISLAND (the module-id header + jtbl/ptr table at segment offset 0), which
rodata_carves already exempts via 'off == 0 and sub == ov'. The S68 first carve
legitimately renamed that subseg to md_MAIN_003_jr_800D12D0 (§371: spimdisasm
rodata migration is same-subseg-only), so the 'sub == ov' conjunct stopped firing
and offset 0 leaked in as a 'carve'. The island has NO single owner BY DESIGN --
which is why the exemption exists -- so widening owner kinds could never have
restored 1:1.
The fix drops one conjunct: offset 0 alone is the honest structural key, because a
carve is a table LIFTED OUT OF THE DATA TAIL and can never sit at the segment's own
offset 0. Verified across all 213 configs: every offset-0 .rodata piece is an md_*
leading island; ov_*/main have none. The R32 hard abort is UNTOUCHED -- this widens
the recognised-island set, it does not soften the refusal.
NEGATIVE CONTROL (R39) over all 184 binaries with .rodata pieces: OK 182 -> 183,
ABORT 2 -> 1, and exactly ONE verdict moved (md_MAIN_003). The remaining us.exe
abort (UNOWNED 0x80073238, the LZSS jtbl carve whose owner LzssDecodeSector does
not live under src/us.exe/*.c) is byte-identical before and after -- PRE-EXISTING,
not newly hidden, and logged rather than silently absorbed.
Carve byte-neutral and bank byte-identical, both re-verified by my own rebuild:
sha1 dd1b32ecf1103c6f7cf1943d25546a3046e17b14 == config/check.md_MAIN_003.sha.
md_MAIN_003 12 -> 11 stubs.
THREE o0_subsplit GAPS surfaced and hand-finished, and they must be fixed before
the remaining 7 -O0 stubs here are carved: build_new_config drops a cut at the
object start so region 0 kept the -O2 name while the tool PRINTED the _o0 name;
parse_overlay_c folds pre-anchor text into the FOLLOWING anchor, so a verbatim body
inside region 0 attached to region 1; and the island .rodata piece needs repointing
to whichever TU ends up holding its emitters.
Both banked in the MAIN TREE after parallel_gate's worktree reported 'banked 0'
TWICE. The drafts were never the problem:
* baseline ov_SC06_010 builds byte-identical (05c2d8c4 == check.sha) -- so the
binary was not red the way main was;
* both drafts probe MATCH in their REAL TU via rtu_match/blocker_probe;
* harvest_verify in the main tree: verified 2 / failed 0, final SHA
05c2d8c46363483a1dce434ee745957b76d0530e BYTE-IDENTICAL.
So the worktree gate has a second binary it cannot handle, and it reports that as
'banked 0' -- indistinguishable from a wave of bad drafts, and the reason I re-ran
this gate twice before doubting the harness instead of the model. Same shape as the
main worktree defect: the failure is SILENT and its symptom points at the wrong
suspect. Root cause not yet identified for ov_SC06_010 specifically; recorded here
rather than left as folklore.
func_8017BEBC's unlock is worth keeping: its card said 'no banked twin', but a
MATCHED 755-instruction near-twin sat in the DESTINATION FILE ITSELF 3,700 lines up
(func_8017CAD4), and its header comment documented the four levers the target
needed. seed_ref joins on signature hashes, so a structurally-similar
non-hash-identical neighbour is invisible to it -- and a same-TU neighbour is
exactly where the richest context lives.
Recorded honestly for the accounting: this function is banked as a raw __asm__
transcription of the target disassembly, NOT as decompiled C. Its epilogue
(jr $ra with addiu $sp in the delay slot, two restores above) is unreachable from
C at this project's pinned triple -- the §177/§188 toolchain-wall class.
It is a legitimate route by the project's own established convention, verified
before banking rather than assumed: src/800c3.c already banks InitHeap,
FlushCache and func_8005CE38 exactly this way, and the agent followed the
convention of its immediate neighbour func_8005E3AC in the same TU.
Note it was NOT on .run/S67_walls.txt or the exclude list, so it was fairly drawn
and the wall was DISCOVERED by drafting it. That is a gap in the walls ledger
worth closing: an epilogue-shaped wall that no one has met yet is invisible to the
draw filter, so the next wave can spend an agent rediscovering it.
harvest_verify in the main tree: verified 1 / failed 0, final SHA
143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL. main 1045 -> 1044.
Also fixed en route: a maspsx sltu-operand parse quirk needs no spaces after
commas in the transcription (the submodule itself is UNCHANGED -- verified).
The single-object module binaries could not be carved at all: o0_subsplit planned
correctly and then jr_isolate_all refused with 'unaddressable content'. That
blocked 9 of the 12 remaining -O0-in-an--O2-TU functions fleet-wide, including a
byte-correct 345-instruction draft with nowhere to go.
THREE ROOT CAUSES behind the refusal, all fixed here:
* overlay_src_split.load_ov_syms: an interior YAML comment terminated the
symbol-file list. md_MAIN_003's yaml annotates the list body, so only
symbols.us.txt loaded and D_800D3200 resolved to None -> refusal.
* jr_isolate_all._partition: a trailing content chunk (the verbatim-asm pair after
the last addressable anchor) now attaches to the LAST region when every symbol it
defines resolves at/after the last cut, instead of hard-refusing.
* _file_scope_decls: bare tag forward decls (struct S_D2394;) exempted from the
dedupe refusal; plus addr_of's D_<hex8> fallback.
THEN A LINK FAILURE THE CARVE CAUSED, worth knowing: spimdisasm migrates rodata
referenced by exactly one function into that function's .s ONLY within the same
subseg. The carve moved func_800D30D0 into the jr subseg while the .rodata island
stayed on md_MAIN_003, so three dlabel string blocks were SILENTLY DROPPED ->
undefined reference to D_800CEE58/D_800CEE80. Adding INCLUDE_RODATA does not
resurrect them (splat marks them migrated segment-wide and emits nothing). The fix
is to rename the .rodata subseg to the jr object, where every island emitter lives.
The regenerated func_800D30D0.s came back byte-identical to the pre-carve .s.
Makefile: the -O0 glob widened to src/md_*/md_*_o0?.c. Without it the region file
compiles -O2 -- byte-neutral while stub-only, but every -O0 draft banked into it
would mystery-fail the gate (§362's trap class). This is why the Makefile and tool
hunks MUST land with the carve: a fresh clone would otherwise lose the -O0 flag.
VERIFIED INDEPENDENTLY of the agent that did it: sha1
dd1b32ecf1103c6f7cf1943d25546a3046e17b14 == config/check.md_MAIN_003.sha, from a
rebuild I ran myself; md_MAIN_003 13 -> 12 stubs; func_800D0D6C absent from
corpus.stubs. interleave_check's DRIFT on this binary is PRE-EXISTING (identical on
a clean tree, verified before any change) -- md_MAIN_003 has no _JTBL_INTERLEAVE
block and must not get one; forcing ALIGNED moves the leading rodata island after
.text and shifts every address by 0xD8. config/overlays.mk untouched (R59/R60).
8 of the 9 md_MAIN_003 -O0 stubs remain: they need drafts and follow-on carves.
The first main banks since the two harness defects were fixed. All three were
proven byte-perfect in the real link by the Fable investigation BEFORE any fix,
and reported {banked:0, near:3} purely because:
* gate_stage compared main against ov_SC01_077's SHA (build/main/main never
exists, config/check.main.sha never exists, DEF_SHA took over), and
* psyq_integrate dropped 'firstfile = 0x80061FA8;' on every incremental relink,
so main's BASELINE was already 2 bytes red before a draft was spliced.
func_800242D0 additionally needed one reconcile: it declared 'extern u16
D_80063870' while the just-banked func_800241C0 declares 'extern s16
D_80063870[]' at file scope. gcc-2.7.2 rejects the conflicting redeclaration at
file scope AND at block scope (the block-scope shadow was tried and also
rejected), so the draft now matches the banked spelling and takes the address by
array decay. Only the address is used (t4 is a 'register s16 *'), so the element
type never reaches codegen -- and the whole-binary SHA proves it.
harvest_verify in the main tree: verified 3 / failed 0, final SHA
143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL. main 1048 -> 1045 stubs.
NOTE for the next session: parallel_gate's WORKTREE still cannot gate main (its
generated-input staging covers the 3 Makefile-named files but main's link needs
more). main is one binary, so gate it in the main tree with harvest_verify --
there is no parallelism to lose.
The same shape as func_801457A4 at the whale's end boundary (cookbook §362), one
region lower: _o0d spans 0x80183178..0x80183830 and func_80183830 is the FIRST
function of the -O2 jr_80183830 object immediately after it. Its target carries the
-O0 prologue tell, so it can only bank in an -O0 object -- and _o0d's .text ends
exactly at its address, so the def lands correctly with NO splat change.
ATOMIC ACROSS TWO FILES: append the def to <ov>_o0d.c AND drop the INCLUDE_ASM from
<ov>_jr_80183830.c in one edit, so the two object sizes cancel and no address moves.
Body mechanically remapped from the banked twin ov_SC03_014:0x801842E0 (2
per-overlay symbols substituted); match_one closeness 0 on both before gating.
Both BYTE-IDENTICAL against their check.sha.
rollout_o0 could not drive this: 'family with exemplar func_80183830 not found in
the map' -- the family map has no entry, so the driver refuses. The recipe is the
tool; the map is not a precondition for it.
func_80144B9C (770) + func_801457A4 (79) in each. Both BYTE-IDENTICAL against
their check.sha. ov_SC02_037 now has 0 open stubs (BINARY COMPLETE);
ov_SC03_107 has 1 left.
Per-overlay the whale's 770 instructions are byte-identical (sha1 74186b97e5d9
across all six overlays sampled) so the shared header is exact; func_801457A4
differs by exactly one data symbol, remapped per overlay:
ov_MAIN_012 D_8017E338 | ov_SC02_037 D_80183BC0 | ov_SC03_107 D_8018245C
This closes the LAST 6 of 6 route-ready -O0 whale members fleet-wide
(rollout_o0 --all-o0 reported TOTAL {'no-o0b': 6} before this).
Same split as ov_MAIN_012: 0x80144B9C..0x801458E0 out of <ov>_jr_8013F350.
2 unmatched stubs, 0 already-matched islands, carve repoints (none),
config/overlays.mk UNCHANGED. Both byte-neutral against their check.sha and both
interleave_check ALIGNED (33/33 and 30/30). Derived and carved under
.run/auto/gate.<ov>.lock.
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).
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.
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.