gate_main printed ONE recovery chain for every dropped draft, and it was the
SELF chain (fix_arity_callers --any-proto + cast_self_callers) regardless of
what the clashing symbol actually was. Two of the three classes are not that
chain:
CALLEE — §378 does not transfer; cast_self_callers reads the return type off
the draft and cannot cast a callee, so --any-proto runs unprotected
over every call site. S69 measured 60 decls no-protoed, binary RED.
DATA — neither tool in the printed chain touches a data extern at all.
Measured cost of the wrong route THIS session: func_8006252C was dropped on a
clash with itself; following the shape of the printed chain I reached for
scope_demote_drafts first, which aliased D_80078D08 through __asm__ and BROKE
the build. The real blocker was one --sync-decls away. Three tools, wrong
order, one destructive — because the report named a chain instead of a route.
A route is an ORDERED LADDER, not a prediction: for a DATA clash the choice
between adopting the TU's spelling and demoting to block scope depends on
whether the draft can live with the TU's type, which no classifier can know.
The byte gate remains the sole arbiter (G3/P9). Refusals come first (R43/R61a):
a verbatim draft and a NEAR are not declaration problems.
NEGATIVE CONTROL (R39): all 7 S77 drops whose winning tool was already known
route correctly — 2 SELF (cast_self_callers), 1 CALLEE (sync_tu_decls via a
definition header), 4 DATA — and the DATA ladder's order matches which rung
actually won in each case (sync for D_80072978, demote for D_80072960 and
D_80074818). Verbatim draft refused; real-C draft not refused.
Playbook §4b and SETUP updated in the same change.
The first version of §479, written earlier this session on 3 data points, said
the permuter is a one-shot at <=4 mismatched. Five more runs make it 3 of 8,
and the failures are not marginal: a residual of 1 failed while a residual of 4
banked, so mismatch count predicts nothing.
The real predictor is prior-attempt history. All three winners were drafts
nobody had worked. Every failure was a body an agent or prior wave had already
optimised (5, 6 and 4 prior levers respectively). A draft a competent search has
plateaued is plateaued for the permuter too — its wins come from unexplored
neighbourhoods, not from small numbers.
Same predictor as §479's triage paragraph, reached from the opposite direction.
Five new levers, all in the draft header. The headline one (L5): STATEMENT
ORDER IS THE ALIAS ORDER — a mem/s local matrix store can never be hoisted over
by a mem/s varying p-> load, because true_dependence's exemption needs one side
non-struct AND non-varying. Writing the matrix init in NATURAL OFFSET ORDER
closed the whole 45-instruction init block, and the same law one scope down
removed the +1 length drift.
Also: an inline-asm "r" operand that is a bare symbol_ref has NO pseudo and is
allocated by reload ($t0); assigning it to a local first makes it a pseudo and
local-alloc gives $v0 — worth 10 instructions.
A scripted 858-candidate sweep PROVED mode/rot/shift placement inert, which is
what redirected the hunt from LUID to DAG/allocation.
gate_main: BANKED 1, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
replace_decl returned True whenever the PATTERN matched, even when the
substitution produced identical text. So a draft that already carries the TU's
exact spelling looped until --rounds ran out, spending ONE CLEAN REBUILD PER
ROUND, and then printed 'gave up after 6 rounds (6 synced)' — which reads as
six useful syncs.
Measured on func_8005FA94: 6 rounds, every one
'D_80072960 -> extern void (*D_80072960)(void *);', zero change to the draft,
five wasted rebuilds and a misleading report. R61(a): a no-op must not be
reported as work.
Two guards: no text change ends the loop naming the already-correct spelling
and saying the residual is elsewhere; and a symbol the gate names twice in one
run ends it too, since re-syncing it cannot help.
The comparison is LINE-NORMALISED because the pattern ends in \s*$ and the
substitution eats the matched line's newline — a byte compare called that a
change. Caught by a known-true check (identical/different/absent), not by
reading the code.
18 -> 0 via three levers, all worth reading in the draft header: the sibling
func_8002FF0C's block-scope scalar spelling of D_800A46D2 (the array spelling
lets cse cache 'la $s1' across the call); splitting a $17 pin so only the
b*24 intermediate is pinned (expand_mult passes accum_target=target, and a HARD
target survives expand's generate-into-pseudos guard, so pinning the result
drags the whole chain); and pinning the DESTINATION for idx98, because
'addu $s0,$s1,$s0' is expand_binop swapping commutative operands to make
op0==target, not tree order.
Gated compatible on the first try — the agent had verified the spliced TU
compiles rc=0 with an instruction stream identical to the standalone compile.
gate_main: BANKED 1, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
Took three tools in order, and the first two were wrong:
- scope_demote_drafts BROKE it (it aliased D_80078D08 through __asm__ and the
build failed) — the clash was never a data extern
- the real clash was func_8006252C ITSELF: TU void(void) vs draft s32(void),
i.e. self_decl_tu -> cast_self_callers --sync-decls, 4 call sites
- then sync_tu_decls closed func_800625DC and func_80062644
The BODY is the interesting part and is now cookbook §482: two INDEPENDENT asm
re-ties, ordered, because one barrier fixes one residual and re-creates the
other.
gate_main has no scope-demote rung — only gate_stage's ladder calls
scope_demote_drafts, so a MAIN draft never saw §8d. Running it by hand demoted
7 file-scope data externs to block scope (D_80072960 among them, whose TU
spelling is void(*)(void) against the draft's void(*)(void*)) and the byte gate
then accepted the body.
gate_main: BANKED 1 of 3 after bisection, 143dbb89f34491258bbc27810d0a12ec8b43a8dd
BYTE-IDENTICAL. The other two are genuine rejects: func_8005FA94, and
func_8005D33C whose rejection shows the mass symbol shift its own agent
predicted from the jump-table rodata placement.
func_8005F0C8 and func_8005ECC0 both live in the 800c3 REORDER_TUS island,
whose real build path is reorder_passthrough + as -O2. Their S68 wall verdicts
were measured under maspsx + as -O1 — the oracle S76 fixed. Re-measured under
the correct path they are ordinary near-misses: 3 of 88 (ADDRESSING) and 2 of
35 (DELAY-SLOT), not walls.
Both stay excluded because neither is SOLVED (permuter_ils reached 3 and 5, not
0), but the reason now says UNSOLVED rather than impossible. An exclude list
records what the tooling could not do; a wrong reason is how real work gets
filtered out permanently.
Found because a wave agent noted that six prior 'IMMOVABLE §177/§188 epilogue'
verdicts on func_8005FA94 were all wrong-oracle artifacts — the same
provenance, so the same suspicion applied to the neighbouring entries.
Both bodies were already solved in S76 and had never banked. Neither needed a
codegen change — they needed the gate to stop applying a rule cc1 does not
(§481 / the _depth0 fix): func_8001FC08 renames its struct to MTX_8001FC08 and
declares D_80074818/D_80075018 at block scope, and func_8002FF0C shadows
D_800A46D2 with a block-scope scalar because the array spelling forces la and
costs 12 mismatches.
gate_main: BANKED, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
DECL is `^\s*extern`/MULTILINE, so it matched an INDENTED extern inside a
function body, and the pre-check then compared that block-scope declaration
against the TU's file-scope spelling — making the checker STRICTER THAN CC1.
Per cookbook §481, gcc-2.7.2 raises `conflicting types' as an ERROR only in the
SAME scope; across scopes it degrades to `type mismatch with previous external
decl', a WARNING the build already emits elsewhere. So a block-scope extern
cannot clash, and dropping on one refuses correct work.
Measured in a single gate: func_8001FC08 (400 ins — a deliberately renamed
MTX_8001FC08 at block scope, which is the ONLY legal fix there because two
anonymous struct typedefs in one TU are never compatible in C89) and
func_8002FF0C (166 ins — a deliberate block-scope scalar shadow of
D_800A46D2). 566 instructions of byte-correct body refused by a rule the
compiler does not apply.
_depth0() blanks brace-nested regions, string literals and comments before
DECL runs, on both the TU side and the draft side.
NEGATIVE CONTROL (R39): on a synthetic TU it keeps both file-scope decls and
excludes the block-scope, in-string and in-comment ones; on the two real drafts
it removes EXACTLY the three disputed symbols (D_80074818, D_80075018,
D_800A46D2) and leaves all 23 other declarations in each untouched.
R39 governs the direction: a check that discards good work is worse than one
that lets a failure through, and a real conflict still surfaces via the
COMPILE-conflict path plus a byte gate that cannot be fooled. R61(b).
Plain C, no §265 verbatim needed — the pack's prior FAILED attempt had
over-thought it. Two levers: omit the forward decl for func_8005E188 so the
call is implicit K&R (fixing both an s0/s1 order swap and a spurious
sign-extend a visible prototype would insert), and close the 2 trailing pad
words with a file-scope __asm__ per the §295 class.
gate_main: BANKED 1, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
The escape hatch for the declaration-conflict class the reconcile/sync ladder
cannot reach — two anonymous struct typedefs in one TU are never compatible in
C89, so no duplicate spelling works, but block scope turns the error into the
warning the build already emits elsewhere. From main:func_8001FC08 (400 ins),
whose body was solved in S76 and had never banked because nobody asked why.
gate_main: BANKED 2, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
func_8005EAE8's agent resolved its own integration conflict — it adopted the
TU's declarations for func_8005DCA0 and D_80072974 and cast at the use site
rather than declaring its own incompatible externs.
func_8005E13C reproduces a trailing orphan pad nop (belonging to neither it nor
SysEnqIntRP) with a file-scope __asm__; the verbatim detector correctly does
NOT flag that as a §265 body.
playbook §2b has called neighbor_ref the biggest measured cost lever in the
wave since S68 (~20x token swing) and documented it as a MANUAL per-card
command wired into nothing — so it ran for approximately zero cards. Packs now
carry an ALREADY-MATCHED NEIGHBOURS block, same additive never-fail contract as
the past-attempt notes. First run: 30/30 targets had a matched neighbour.
It also shipped with a defect that would have silently un-done it:
neighbor_ref reports the SYMBOL-TABLE name, and for an unnamed function that is
Ghidra's FUN_8003a0e4 — which appears nowhere in src/*.c, where the function is
func_8003A0E4. An agent sent to read FUN_8003a0e4 finds nothing and concludes
there is no neighbour. _src_name resolves against the destination TU's own text,
falls back to the address, and shows the symbol-table spelling in parentheses.
Measured: 150 of 150 neighbour names needed resolving; 0 primary names remain
Ghidra-style. Checked against known-true cases first (resolves FUN_8003a0e4,
leaves func_8003A0E4 alone, leaves an unknown name untouched).
R61(b): the pack was asserting a name true of the symbol table and false of the
world the agent works in.
wall_sweep --emit-exclude lists 9 fleet-wide; the list carried 5 of them.
func_8005D734, func_8005D8B4, func_8005ED4C and func_8005F450 each have a
%lo in a branch/jal delay slot — the second half of an assembler macro gcc
emits as ONE atomic insn, so no C can place it there. An agent handed one
returns a NEAR with an unexplainable tail, which is indistinguishable from a
hard function; the playbook measured a main wave spending 4 of 7 slots that way.
Drew ratified in-session after the S77 census: eight instrument defects, all one
shape — a tool asserting about a DRAFT what was true only of the HARNESS.
harvest_verify: verified 1 / failed 0, ef86fe1e403998a82ead42f4466ac4bc80f2c8d1
BYTE-IDENTICAL. The static probe had called this a `local_type' Blk16 conflict;
the real gate strips TU-provided typedefs and then named the true blocker.
harvest_verify named the step-2 signature exactly: `too few arguments to
function func_80182A00' at ov_SC01_084_jr_80182A00.c:534. 4 call sites cast.
Baseline green with NO draft substituted: ef86fe1e403998a82ead42f4466ac4bc80f2c8d1.
The residual was a two-instruction adjacent swap in the target's favour:
idx 49 MINE lh $a1, 0($sp) TARGET sra $a2, $v1, 16
idx 50 MINE sra $a2, $v1, 16 TARGET lh $a1, 0($sp)
Hand lever tried first and REFUTED by bytes: hoisting `a0 = a0 >> 16` above
the load is semantics-preserving (a0 is untouched in between) but scores
23 mismatched at 67/68 ins — it lets gcc fold an instruction away entirely.
permuter_ils --klass SCHEDULE reached score 0 on cycle 1. Its winning edit is
a clean C-level one: drop the `a1 = *(s16 *)sp;` temporary and inline the load
into both comparisons, which is what moves the sign-extend ahead of it.
gate_main: BANKED 1, 143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
T11 4/7, T12 13 banked of a 34-draft pool, T13 R22 213/213 twice (a green
baseline before the overlay banks and again after all 17).
main REAL 895 -> 899, stubs 46 -> 42. Fleet stubs 82 -> 65, distinct-code
99.3% -> 99.4%, MAIN game-code 57.1% -> 57.3%.
A §265 verbatim draft — the target's own asm in a file-scope __asm__ —
assembles to the bytes it was copied from, so BOTH of this tool's oracles emit
the strongest possible signal: static `none`, real cc1 `MATCH`. The routing
then reads "byte-correct body, nothing blocking it", the byte gate refuses it
for free, and progress.py moves by exactly zero.
Measured: of the 13 MATCH rows in the S77 overlay pool, SIX were verbatim
(md_MAIN_003 x3, md_MAIN_020, ov_SC05_005, ov_SC06_010). The whole 13-draft
cohort gated 0, and the probe had scoped it as the highest-value work
available.
S76 closed exactly this hole in gate_main, harvest_verify and
api_agent.prior_draft. This is the fourth consumer — and the one that SCOPES
the work, so it is the one whose blindness costs a session's plan. Uses the
gate's own detector (DP.is_verbatim_asm_draft) so the two cannot drift (R33),
and a VERBATIM row is excluded from the agreement arithmetic rather than
counted as a match.
NEGATIVE CONTROL (R39): md_MAIN_020's verbatim draft now reports
`verbatim_asm / VERBATIM (not a decompile)`; ov_SC04_018's two real-C drafts
still report `none / MATCH` exactly as before.
The same class that produced four banks in main, applied across the overlay
fleet. `blocker_probe` over all 26 binaries holding a stranded S76 draft found
11 whose blocker is `self_decl_tu`; harvest_verify named a twelfth
(ov_SC03_111:func_80181344, `conflicting types for func_80181344').
ov_SC01_005 func_8017FBCC ov_SC04_005 func_80185CEC
ov_SC01_006 func_8017FBCC ov_SC04_007 func_80182358
ov_SC02_041 func_801832F8 ov_SC04_011 func_8018985C
ov_SC03_105 func_8017F018 ov_SC05_003 func_80181720
ov_SC03_111 func_80181344 ov_SC05_018 func_80181294
ov_SC03_124 func_8018095C ov_SC04_002 func_80182CBC
35 edits, 0 refusals. cast_self_callers is binary-generic — only sync_tu_decls
is main-only — so the checkpoint's "extend it or drive recover_integration per
binary" needed neither.
Every one of the 12 binaries was baseline-checked with NO draft substituted and
all 12 build their locked SHA, so the casts move zero bytes fleet-wide, exactly
as they did in main.
Verified in-tree by harvest_verify, final SHA af117efbe4c0142d204bd243e41fd53e6ea5e350
BYTE-IDENTICAL.
Notable because parallel_gate had just reported `banked 0` for this exact
binary and this exact draft dir, in a 106s worker run — see the follow-up
investigation. The in-tree gate is the one that agrees with the bytes.
`cast_self_callers --undo-journal .run/S77_selfcast.json --keep
func_80013154,func_8005EAC8,func_8005E3AC,func_8005E79C` — reverted 6 edits
across 2 files, kept the 4 that banked.
The three reverted are func_80015608, func_80015760 and func_80039DEC, all
proven NEARs (closeness 3, closeness 9, and 8 differing bytes at 0x80039ded
respectively) — body residuals for the DIFF lane, not plumbing. Their §378
chain is one command to regenerate when a corrected body arrives.
main rebuilds 143dbb89f34491258bbc27810d0a12ec8b43a8dd after the revert.
round 1: D_80072978 -> extern s32 (*D_80072978)(void);
round 2: func_8005E804 -> extern void func_8005E804(u8 *arg0);
BANKED func_8005E79C after 2 declaration syncs
Round 1 is the ordinary extern path; round 2 is the definition path added in
commit:3807, and the draft could not have been reached without it. The same draft
had previously reported "no declaration conflict named" — that was the gate
refusing on a dirty tree, which is the misattribution the second fix removes.
The TU spells D_80072978 as a pointer-to-function; the draft had guessed `s32`
and cast at the use site. The TU's spelling is authoritative and the cast still
folds, so the bytes are unchanged.
Two defects, both found by driving the last two self_decl_tu drafts to a bank.
1. tu_decl looked only for an `extern … sym …;` line, so when the clashing
symbol is a function the TU DEFINES it stopped with
stopping: func_8005E480 clashes with the TU itself but src/800c3.c has
no `extern` line to copy.
though the authoritative spelling was in the definition's own header at
src/800c3.c:916. This was the terminal blocker of BOTH remaining drafts
(func_8005E3AC on func_8005E480, func_8005E79C on func_8005E804). The
definition is now preferred over an extern when both exist — it is the one
cc1 checks every other declaration against. Banked func_8005E3AC in one
round. Checked against known-true cases before being trusted: definition
path on func_8005E480/func_8005E804, extern path still verbatim on
func_8005D734, absent symbol still None.
2. gate_main refuses outright on a dirty src/ or a red baseline and never
reaches a per-draft opinion. The round loop matched neither DROP_RE nor
COMPILE_RE in that output and fell through to "no declaration conflict
named; stopping after 0 sync(s)" — reporting a HARNESS refusal as a property
of the DRAFT (R40). Measured on func_8005E79C, whose gate was refused
because the bank one command earlier had left src/ uncommitted. The refusal
is now surfaced and exits 3.
sync_tu_decls looked only for an `extern … sym …;` line, so when the clashing
symbol is a function the TU DEFINES it reported
stopping: func_8005E480 clashes with the TU itself but src/800c3.c has no
`extern` line to copy.
and gave up, though the authoritative spelling was sitting in the definition's
own header at src/800c3.c:916. That was the terminal blocker of BOTH remaining
self_decl_tu drafts. tu_decl now reads a definition header and renders it as an
extern, preferring it over an `extern` line when both exist — it is the one cc1
checks every other declaration against.
round 1: func_8005E480 -> extern void func_8005E480(void *arg0);
BANKED func_8005E3AC after 1 declaration sync
Checked against cases whose answer was already known before trusting it:
definition path OK on func_8005E480 and func_8005E804, extern path still
verbatim on func_8005D734, absent symbol still None.
progress.py main: REAL 897 -> 898, INCLUDE_ASM stubs 44 -> 43.
The first two banks off the `self_decl_tu` class: the TU declared the very
function the draft defines, with a different signature, so the draft could not
compile no matter how correct its body was.
func_80013154 src/800.c tu s32 (s32,s32,s32) | def s32 (s16,s16,s16)
func_8005EAC8 src/800c3.c tu void (void) | def void (void*)
func_80013154 was a §265 VERBATIM-ASM bank — it is now real decompiled C.
gate_main: 3-draft slate, bisected in 5 rebuilds, 2 banked,
143dbb89f34491258bbc27810d0a12ec8b43a8dd BYTE-IDENTICAL.
progress.py main: REAL 895 -> 897, INCLUDE_ASM stubs 46 -> 44.
Rejected by the byte gate, correctly, and handed to the DIFF lane:
func_80039DEC 8 differing bytes at 0x80039ded (a NEAR, not a plumbing miss)
func_80015608 sync_tu_decls refused up front: NEAR at closeness 3
`blocker_probe --binary main` over the 36 stranded S76 drafts classifies 7
whose blocker is `self_decl_tu` — the TU declares the very function the draft
defines, with a different signature:
func_80013154 src/800.c tu s32 (s32,s32,s32) | def s32 (s16,s16,s16)
func_80015608 src/800.c tu void (s32,s32) | def void (void*,u32*)
func_80015760 src/800.c tu void (s32,s32) | def void (Obj*,s32*)
func_80039DEC src/800_c.c tu void (void*,s16,u8) | def void (void*,s16,s16)
func_8005E3AC src/800c3.c tu void () | def s32 (Ctx*,s32)
func_8005E79C src/800c3.c tu void () | def s32 (void*,void*)
func_8005EAC8 src/800c3.c tu void (void) | def void (void*)
14 edits: each call site cast to a no-proto function pointer (§20 — gcc-2.7.2
folds the cast of a known function symbol back to a direct `jal`, so the
caller's bytes do not move), then the forward declaration synced.
Verified byte-neutral BEFORE any draft is substituted: main builds
143dbb89f34491258bbc27810d0a12ec8b43a8dd with these edits alone.
Committed ahead of the gate because gate_main `git checkout`s main's TUs
before substituting and would otherwise destroy these edits. Journal at
.run/S77_selfcast.json — `--undo-journal --keep <banked>` follows the gate.
`--sync-decls` copied the draft's parameter list verbatim into the TU. A draft
names types that are not in scope where the declaration sits, and both forms
of that broke the COMMITTED baseline build in one apply:
src/800.c:2631 extern void func_80015760(Obj_80015760 *obj, s32 *ot);
-> the type is draft-local; the TU has never heard of it
src/800c3.c:866 s32 func_8005E3AC(Ctx *s, s32 size);
-> `Ctx' is typedef'd at line 941, 75 lines BELOW the decl
src/800c3.c:866: parse error before `*'
src/800.c:2631: parse error before `*'
Caught by gate_main's BASELINE RED check with no draft substituted, so the
failure was attributed to the plumbing and not to seven innocent drafts.
THE FALLBACK FOLLOWS THE TOOL'S OWN DOCTRINE. Once the call sites are cast, the
declaration emits no code; it only has to be COMPATIBLE with the definition and
PARSE. `<ret> fn();` satisfies both without naming a type, and C89 6.5.4.3
makes it compatible with a prototyped definition exactly when no parameter is
affected by the default argument promotions. So the draft's own spelling is
still preferred — it is the byte-proven behaviour and it keeps the declaration
informative — and the no-proto form is used ONLY where that spelling cannot
parse at that line. Where it cannot parse AND a narrow parameter forbids
no-proto, the tool refuses loudly and names the type (R43).
NEGATIVE CONTROL (R39) over all 40 main recovery drafts: 68 edits before and
after, 65 byte-identical. The three that changed are exactly the declarations
naming an out-of-scope type — Obj_80015760, Ctx, and Slot54/Rec14 — and no
already-correct declaration is churned.
BASELINE PROOF: with all 14 plumbing edits applied and NO draft substituted,
main builds 143dbb89f34491258bbc27810d0a12ec8b43a8dd — byte-identical. The
casts move zero bytes, as the §20 fold predicts.
`blocker_probe --binary main` over the 36 stranded S76 drafts classifies 7
whose blocker is `self_decl_tu` — the TU declares the very function the draft
defines, with a different signature:
func_80013154 src/800.c tu s32 (s32,s32,s32) | def s32 (s16,s16,s16)
func_80015608 src/800.c tu void (s32,s32) | def void (void*,u32*)
func_80015760 src/800.c tu void (s32,s32) | def void (Obj*,s32*)
func_80039DEC src/800_c.c tu void (void*,s16,u8)| def void (void*,s16,s16)
func_8005E3AC src/800c3.c tu void () | def s32 (Ctx*,s32)
func_8005E79C src/800c3.c tu void () | def s32 (void*,void*)
func_8005EAC8 src/800c3.c tu void (void) | def void (void*)
14 edits: each call site cast to a no-proto function pointer (§20 — gcc-2.7.2
folds the cast of a known function symbol back to a direct `jal`, so the
caller's bytes do not move), then the forward declaration synced to the
draft's own spelling, which emits no code once the sites are cast.
Committed ahead of the gate because gate_main `git checkout`s main's TUs
before substituting and would otherwise destroy these edits. Journal at
.run/S77_selfcast.json — `--undo-journal --keep <banked>` follows the gate.
`return func_X(a0, a1);` has the exact shape of a forward declaration —
leading identifier, name, parenthesised argument list, `;` — so every
permissive "<type> <fn>(...);" regex in this tool read that CALL as a
DECLARATION. One misclassification, three consumers, two opposite failures:
* `is_declaration` -> `cast_sites` SKIPPED the call site, leaving the
caller's bytes exposed to the synced (narrowed) prototype.
* `sync_decls` -> REWROTE the whole statement into a declaration,
silently deleting the function's `return`.
* `DEF_RE` -> read the same line as "the definition itself", and
in `draft_signature` could have handed back ret="return".
Witnessed on a dry run before anything touched src/:
src/800.c:713
- return func_80013154(a0, a1, a2);
+ s32 func_80013154(s16 x, s16 y, s16 step);
One shared `_kw_prefixed()` guard, called from all three sites (R33 — the
guard lives in one place, never duplicated into three regexes).
BLAST RADIUS: 524 `return func_X(...);` lines across 482 files fleet-wide.
AUDIT: no past journal records a keyword-prefixed `before`, so no committed
source was corrupted by this.
NEGATIVE CONTROL (R39), old vs new over all 40 main recovery drafts:
68 edits each, 67 byte-identical, zero false positives. The single
difference is the defect itself — the corrupting declaration-rewrite
replaced by the correct cast:
- return func_80013154(a0, a1, a2);
+ return ((s32 (*)())func_80013154)(a0, a1, a2);
Written for a fresh session: what banked, what did not and WHY (classified by
compiling each stranded draft in its real TU, not guessed), the five tool
defects and their fixes, the four refuted walls and the one proved, and the
five things I got wrong so the next session does not inherit them.
Headline state: REAL 895 (was 882), main game-code 57.1% instruction-weighted
(was 55.8%), 46 open stubs in main and 82 fleet-wide, 143dbb89 byte-identical.
R22 clean-fleet NOT run since the banks.
Next four tasks are logged with their evidence and their traps.
Three gaps found by auditing instead of asserting.
§462 and §463 were MISSING from the cookbook although their commits are
ancestors of HEAD and added 37 and 34 lines. Same silent loss as §464, which
I caught only because I happened to re-check the three sections I had just
written. Both restored from their own commits; all of §460-§476 now verified
present one by one.
SETUP.md had no record of either new tool (R21). Added gate_main_parallel and
sync_tu_decls, plus the oracle corrections a reader needs in order to
re-judge older verdicts: the REORDER_TUS routing in match_one/rtu_match, the
draw_waves --main no-op, the verbatim-draft refusals at three points, and the
§179-C conversion guard.
The playbook had nothing on what to do when a gate banks far less than it
staged — which is exactly what happened this session. Added the triage step:
probe first (CC1-FAIL 16 / DIFF 18 / MATCH 6 on main's 40), sync declarations
for the plumbing class, hand self_decl_tu to cast_self_callers, and expect a
cascade because every bank changes the declaration environment for the drafts
that follow it.
Two gaps found by running it over all 16 candidates.
It only parsed the slate-load 'DROP … clashes with …' path, so five drafts
whose conflict surfaced AFTER the build as 'COMPILE conflict on `SYM'' looked
unrecoverable when they were the same class one symbol deeper. Both forms are
read now.
And a CC1-FAIL classification says the declaration blocked COMPILATION, never
that the body underneath is right: five candidates compiled once synced and
then failed the byte gate because they were NEARs (closeness 12-89) all along.
It now scores the body first and refuses a NEAR, so a gate is not spent
learning what match_one already knows (R37).
That check had the §238 bug it exists to prevent — I called match_one without
--asm-subdir, so it defaulted to asm/resident/nonmatchings/resident, judged a
DIFFERENT function, returned no verdict, and let the NEAR through. The subdir
now comes from the stub oracle. Caught only by controlling the guard against
a case whose answer I already knew.
Controls: closeness-12 draft REFUSED as a NEAR; func_80013154 still refused as
self_decl_tu with the correct redirect.
The dominant reason a byte-correct draft does not bank is not codegen: the
draft and its destination TU spell a shared symbol differently and gcc-2.7.2
rejects the redeclaration. gate_main's pre-check already NAMES the symbol and
which side it kept, and the TU holds the authoritative spelling — so the fix
needs no judgement. Copy the TU's extern line verbatim into the draft,
re-gate, repeat.
Done by hand this session it banked func_8005EB28 in one round and
func_8005EC00 in two, both stuck across multiple slates, both byte-identical
after. The conflicts are typically a CASCADE: banking one function gives the
TU a real definition that then contradicts the stale extern every later draft
in that TU still carries.
Refuses the self_decl_tu class loudly (the TU declares the function being
banked, so the call SITES must change too — that is cast_self_callers
--sync-decls), and refuses any binary but main, whose gate is the one that
names the symbol (R43).
Controls: on an already-banked function it reports no conflict rather than
claiming a bank; on func_80013154 it refuses with the right reason. The byte
gate remains the sole arbiter — every round ends in a real gate run.
Same §378 class as func_8005EB28, two symbols deep: the draft declared
D_800729DC as void* where the TU says u32, and D_80072974 as void(*)()
where the TU says void(*)(void*). Copying the TU's own extern verbatim for
each, in the order the gate named them, banked it in two rounds.
143dbb89 BYTE-IDENTICAL.
The loop is the §378 chain's essence: ask the gate which symbol conflicts,
copy the TU's declaration into the draft, re-gate, repeat. No judgement
required — the gate names the symbol and the TU holds the authoritative
spelling.
A CASCADE, not a new defect: banking func_8005DE78 earlier this session gave
src/800c3.c a real definition at :690 with signature s32 (s32, s32). The
func_8005EB28 draft still carried extern void func_8005DE78(void *, s32) from
when the callee was a stub, so the TU and the draft now contradicted each
other and the gate refused with a compile conflict.
Fix is the §378 shape by hand: drop the redundant extern (the TU's own
definition is above the splice point) and cast the two call sites to the
banked signature. 143dbb89 BYTE-IDENTICAL.
The general point: every bank CHANGES the declaration environment for every
later draft in the same TU. A draft that was compatible before a bank can be
incompatible after it — the same shape as the rescan-twins-after-every-bank
rule, applied to declarations instead of the twin graph.
The recover_integration probe compiles each stranded draft in its ACTUAL TU
and reported 6 of main's 40 blocked drafts as MATCH there — i.e. not
integration problems at all, just casualties of batch-internal conflicts in
the earlier slates. Gating those 6 alone banked 2 (143dbb89 byte-identical).
The remaining 4 are a finding in their own right: gate_main's resolve_conflicts
pre-check dropped them while real cc1 accepts them. Gating them individually
next.