Commit Graph

3453 Commits

Author SHA1 Message Date
Drew T e32fdd47dd feat(decomp): parallel gate — 2 fns across 2 binaries (4 workers)
md_MAIN_011    func_800D1254
  ov_SC05_017    func_80189240
2026-08-31 22:22:43 -06:00
Drew T da32350873 feat(decomp): main in-tree gate — 1 fn(s)
main  func_80013B64
2026-08-31 22:18:35 -06:00
Drew T ba394da75a docs: spec the TRIAGE LADDER for next session, with its 32 free banks listed
The companion to neighbor_ref.py (built). Written so a session with none of S68's
context can finish it: what it is, where it sits in the pipeline, that
tools/residual_rules_b.py is already ~70% of it with its held-out scores, the
asymmetric failure mode that makes the R39 acceptance test mandatory (a false
'skip - already banked' silently drops a bankable function), and the falsifiable
predictions.

Includes the immediate payoff, verified by me rather than taken on trust: 10 drafts
are byte-MATCHES once an extern derived from the target's own .s is added (10 of 10
confirmed with match_one, closeness 0, patched drafts at .run/rules_b/*/autodecl.c),
plus 22 more that already match standalone and were misfiled as failures.

Also records the experiment's most important number: two INDEPENDENT
implementations converged at ~1-2% on pure cookbook-shape rules, so that tier's
ceiling is the POPULATION (surgical residuals live at the end of escalations, not
in first-pass wave output) and it belongs in escalation loops, not wave triage.
2026-08-31 22:09:11 -06:00
Drew T e936556ff0 feat(cards): neighbor_ref.py — retrieve MATCHED functions as worked examples, ranked
seed_ref answers 'is there a byte-identical twin?'. This answers the weaker but far
more common question: 'which matched function should I READ before drafting this?'

S68 measured a ~20x swing on that variable. Every cheapest large match came from an
agent finding a matched neighbour (func_800D1254 555 ins/72k; func_800D12D0 657
ins/122k FIRST COMPILE; func_8018AD9C 397 ins/87k; func_8017BEBC 753 ins/177k),
while main functions with no neighbour ran 200-350k for ~80 instructions.

THE FAILURE THAT MOTIVATED IT: func_8017BEBC's card asserted 'no banked twin' while
a MATCHED 755-instruction near-twin sat 3,700 lines up IN ITS OWN FILE, its header
comment documenting the four levers the target needed. seed_ref joins on signature
hashes and the two bodies are not hash-identical, so it was structurally invisible.
Three other S68 agents found their unlock the same way, unprompted.

Ranks on what actually worked, not intuition: SAME TU first (solved against the same
decl environment, and its header records the levers), then same binary, then shape
(li-normalised skeleton / call-sequence hash / reloc-kind sequence / CFG counts /
opcode-histogram cosine, all precomputed in .run/feat.*.jsonl), then instruction-
count proximity, with a HARD PENALTY for opt-level mismatch (§116 — an -O2 example
actively misleads an -O0 target). It surfaces the neighbour's HEADER COMMENT, which
is the payload agents actually consumed.

Explicitly NOT a remap claim: §168 law 1 measured cousins at 0/26. A neighbour is a
worked example to READ; seed_ref remains the tool for the byte-identical case.

Validated against ground truth: for main/func_80024054 (265k tokens, ended NEAR 32)
the top three neighbours are func_8003A0E4, func_800242D0 and func_800241C0 -- all
three MATCHED THIS SESSION, same TU, same call sequence, same reloc-kind sequence.
src/800.c holds 657 matched functions and the card offered none of them.

Bug fixed en route, and it is a repeat: the atlas writes addresses as hex STRINGS
while corpus.Stub.addr is an int. T4's verifier already lost rows to exactly this
string-vs-int mismatch (the R32 silent-no-op class). Normalised in _addr().
2026-08-31 22:08:34 -06:00
Drew T 7392fcf7c3 docs: accelerators #15 (the differential-oracle harness) + the generic decomp package thesis
#15 — the tool worth building FIRST in any decomp, because it works at 0% and
compounds: run every question down TWO independent paths on a schedule and fail on
disagreement. Ten-plus S68 blockers had one shape — a tool computing a TRUE number
about a NARROWER world than we believed it covered — and EVERY one was caught by
two measurements disagreeing, never by review. R32/R34/R40 already say this and
were not enough: they are rules applied by whoever writes the tool, and in S68 I
wrote R34's warning into one docstring and rebuilt the exact defect it warns about
an hour later in another file.

Includes Drew's scheduling half, which this project only ever did by accident: the
widening is PERIODIC. Tooling is not wrong when written, it goes STALE as new
idioms reveal populations it cannot see. At every phase close ask 'which scanner's
denominator just got wider?' — that question converts new knowledge into free
banks. The §332 sweep is the worked example: one review, 10 fns / 1,027 ins
reclassified, one in-flight escalation stopped mid-spend.

generic-decomp-package.md — what a NEW decomp inherits on day one and does BEFORE
cracking: mine the COMPILER SOURCE and sibling projects for idioms (this project's
best late idioms came from reading gcc-2.7.2's own passes and needed no matched
function at all — week-1 work done in month N), port the families/twins/dedup/carve
layer first, then the oracle harness, and only then crack. With the honest caveat
that tooling-first makes the cheap half free and does NOT shrink the hard tail.
2026-08-31 22:06:20 -06:00
Drew T d8b7fb4e49 docs(playbook): §1b — the §332 walls are now ENUMERATED, wire the sweep into the draw
tools/wall_sweep.py --emit-exclude feeds draw_waves --exclude directly. 10
functions / 1,027 instructions over 1,378 open-stub .s files, against §332's
'6 fleet-wide' with two named.

Recorded what it caught immediately: main/func_8005D734 was already escalated to
Fable at closeness 8 when the sweep listed it, and its site is exactly the residual
that agent described -- stopped. Filtering the live queue dropped two more before
they were drafted (func_8005D9C4 133 ins, func_8005F450 159 ins).

The §188 epilogue half is still NOT built and is now named as such rather than left
implied: its detector exists inside oracle_reorder.py and has never been run as a
sweep.
2026-08-31 20:02:56 -06:00
Drew T cd03c67652 feat(walls): wall_sweep.py — ENUMERATE the §332 delay-slot macro walls, 10 fns / 1,027 ins
§332 states the class is "6 FUNCTIONS FLEET-WIDE, NONE BANKABLE FROM C" and names
TWO of them. §332a then says, correctly, "Filter before drafting" -- but a filter
needs the LIST, and the rest were never written down, so the draw kept handing them
to agents. A COUNT WITHOUT AN ENUMERATION CANNOT DRIVE A FILTER.

Measured cost of that gap today: main/func_80061FA8 -- a fable agent produced C
that oracle_reorder proves BYTE-CORRECT (0 diffs / 103 ins) and that the pinned
triple still cannot emit. 92,684 tokens to rediscover a documented class. Plus
main/func_8005F0C8 at 289k tokens, the same story via §188.

The sweep is now the list: 10 functions, 1,027 instructions, derived from 1,378
open-stub .s files with 0 unreadable.

TWO DEFECTS IN MY OWN DETECTOR, both caught by demanding it reproduce members I
already knew -- the same rule I have been applying to every other tool today:
  * It returned a confident 0 across all 1,378 files. The .s lines carry a
    slash-star offset/addr/bytes star-slash comment prefix, and my regex anchored
    the mnemonic at start-of-line, so it matched NOTHING. A sweep returning 0 must
    prove it CAN return non-zero before the 0 means anything.
  * Widened, it found 6 but MISSED func_8005DBD8, which §332a names. Its delay slot
    holds a store through %lo -- the tail of a lui-%hi / store-%lo MACRO, not a la.
    Same mechanism, different mnemonic: ANY %lo in a delay slot is the second half
    of an assembler macro that gcc emits as one atomic insn, so C can never put it
    there.

IT PAID FOR ITSELF WITHIN MINUTES: main/func_8005D734 is in the list, and I had
escalated it to Fable at closeness 8 twenty minutes earlier. The sweep's site for
it is EXACTLY the residual that agent described. That escalation could never
succeed and has been stopped.

Ledger: .run/S68_walls_332.txt (--emit-exclude form, ready for draw_waves).
2026-08-31 19:59:27 -06:00
Drew T 0e50fbc84f docs(playbook): §1b — the walls ledger is always incomplete, and each gap costs an agent run
A wall nobody has met yet is invisible to the draw filter, so new ones are found by
PAYING an agent to hit one. Twice in S68 on main: func_8005E228 (a full run, then
banked the §265 verbatim-asm way) and func_8005F0C8 (289k tokens to reach closeness
36 with the residual confirmed as §188's epilogue by oracle_reorder.py).

Neither is a model failure. An agent handed a wall returns a NEAR with an
unexplainable tail, which looks exactly like a hard function -- and an escalation
cannot beat the toolchain, so escalating one is guaranteed waste.

The fix is named rather than left as folklore: run the §188 epilogue-shape detector
over every open stub AT DRAW TIME. It already exists inside oracle_reorder.py and
has never been run as a sweep. Until then, treat 'NEAR with an epilogue-shaped
tail' as a walls candidate and check it with the oracle BEFORE escalating.
2026-08-31 19:54:36 -06:00
Drew T e2f64a7c62 feat(o0): md_MAIN_003 second carve — func_800D12D0 (657 ins) banked as real -O0 C
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.
2026-08-31 19:46:10 -06:00
Drew T 3f9430e569 feat(gater): retry IN-TREE when the worktree gate FAILS every draft and banks none
The worktree gate is silently unable to build some binaries and reports it as
'failed', which is indistinguishable from bad drafts. Measured twice this session:
main (its psyq_integrate link inputs are not staged) and ov_SC06_010 (root cause
still unknown) each reported 'banked 0' while the SAME drafts banked byte-identical
through harvest_verify in the main tree. In the ov_SC06_010 case that was 1,191
instructions I re-gated twice and nearly wrote off as bad drafts.

Now any binary whose worker failed EVERY draft and banked none gets one in-tree
retry. A genuinely bad draft fails there too and costs one build; a harness-blind
binary banks. A real NEAR is left alone -- only all-FAILED is treated as suspicious.
The whole-binary SHA remains the sole arbiter (G3/P9), so this cannot launder a
wrong draft into the tree; it only stops the harness misattributing its own
blindness to the model.
2026-08-31 19:28:36 -06:00
Drew T 3897886acb feat(decomp): ov_SC06_010 func_8017E764 (438) + func_8017BEBC (753) — 1,191 ins
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.
2026-08-31 19:27:37 -06:00
Drew T c42b3dbc35 docs(cookbook): §373 — the dead-reset cse-breaker, the anti-dep pin, and pri(asm)=1
From the fable escalation that closed ov_SC06_010/func_8017E764 (8 -> 0, BOTH
clusters), and it is three findings not one:

1. DEAD-RESET CSE-BREAKER. To stop cse merging two computations of the same
   expression WITHOUT an asm's scheduling footprint: name it, use it, then
   'p = 0;' immediately after. cse invalidates at the second set and flow deletes
   the dead set BEFORE sched1 -- zero bytes, zero LUID disturbance. An empty-asm
   re-tie by contrast is a REAL pre-call insn whose def->asm->arg chain fronts that
   argument's addiu, and on this function that WAS the second residual cluster
   (§361 confirmed: the lever caused the bug it was later blamed on). Removing the
   dead-reset costs +2 ins / +8 frame bytes, so it is load-bearing.

2. A REGISTER PIN THAT DELETES A sched2 ANTI-DEP. sched1's birthing boost sinks a
   single-set 'la' to its consumer, local-alloc reuses the freed scratch, and
   sched2 is then walled by store-reads-$v0 -> la-writes-$v0. A pin on the address
   pointer deletes the anti-dep. Note this is where a pin is RIGHT, against §368
   where pins measured worse -- the discriminator is breaking a false
   anti-dependence (works) vs out-arguing local-alloc about an allocation (fails).

3. HARD FACT: gcc-2.7.2 insn_cost (sched.c:1363) sets LINK_COST_FREE on any dep
   whose consumer is unrecognizable (INSN_CODE<0 = every inline asm), so
   pri(asm)=1 ALWAYS. An asm can never inherit a load's latency into its priority.
   That closes off a whole family of plausible levers.

Also cross-referenced §370: this run was briefed to test that bound FIRST and
reported it did NOT explain the residual. §370's claim is unchanged and still
narrow; the transferable habit is checking whether a recorded bound covers your
case before declaring a residual unreachable.
2026-08-31 19:20:46 -06:00
Drew T 30caa67127 fix(r22 guard): record liveness, do not infer it — my mtime heuristic failed BOTH ways
I shipped a guard that used drafting-scratch mtimes as a liveness proxy. It failed
in both possible directions within minutes:

* FALSE PASS: the find included '.run/*wave*', which expanded past ARG_MAX
  ('Argument list too long'). find then matched nothing, the guard PASSED, and I
  ran clean: removed build/, expected/, and the regenerated splat tree (asm/, assets/, include macros, undefined_*_auto.txt). on a live lane — deleting asm/ under five drafting agents. I
  restored it immediately (extract-all 212/212) but that is damage control, not a
  design.
* FALSE PASS, structurally: even with the glob fixed, an agent that THINKS longer
  than the window is indistinguishable from a finished one — the exact flaw I had
  already written into gater_lane's docstring for the verdicts file ('a quiet file
  mtime is deliberately NOT accepted as one') and then rebuilt here anyway.

tools/lane_inflight.py is the fix: liveness is RECORDED, not inferred. The
orchestrator adds a target when it launches the workflow and removes it when the
verdict returns — both actions it already performs, so the ledger cannot drift
without skipping a step that is taken anyway.  exits non-zero when any agent
is live, which IS the guard, and both r22_verify.sh and parallel_gate --r22 now use
it instead of touching the filesystem.

Negative-controlled both directions: refuses with 5 live agents named and their
start times; passes when the ledger is drained.

The lesson worth more than the fix: I had already identified 'a quiet mtime is not
a completion signal' as a defect class, documented it, and then re-implemented it
in a different file. Writing a rule down does not stop you applying its opposite
somewhere else.
2026-08-31 18:59:50 -06:00
Drew T 8a347cdb93 fix(r22_verify): rewrite — my own edit had left a SECOND make check-all in it
A scripted patch I applied inserted three lines that (a) re-ran the whole
check-all inside a process substitution and (b) grepped /dev/null. Caught by
reading the file back instead of trusting the edit reported success.

The rewrite does what was intended: capture check-all's output ONCE, clear
.run/R22_DEBT only when the summary line says '0 failed' AND the exit code is 0
(R53 -- a failed build leaves the previous binary on disk and sha1sum reads green,
so the exit code alone is not enough), and leave the debt standing otherwise.
2026-08-31 18:57:14 -06:00
Drew T 601a34f728 fix(pgate): guard --r22's own make clean, and make the deferred check COUNTABLE
The exclusivity guard I added to tools/r22_verify.sh left the path actually used
most -- parallel_gate --r22 -- unguarded, because the destructive 'make clean'
lives in BOTH. Four times this session a drafting agent reported 'asm/<binary> is
MISSING from the tree' mid-draft; one survived only by finding an old snapshot and
still returned MATCH, which is luck, not safety. Drafters never write src/, which
is precisely why a dirty-tree check cannot see them: they DEPEND on state the
operation destroys. A guard belongs where the operation is (R54).

Skipping R22 DEFERS a fleet check rather than performing one, and a deferred check
nobody tracks reads as 'verified' at session close -- the same failure mode as a
loud error nobody counts (R32). So the skip now appends to .run/R22_DEBT with the
commit it deferred after, and a green clean-fleet run DELETES that file. The
session checkpoint must quote it.
2026-08-31 18:56:43 -06:00
Drew T 2a8120f539 feat(decomp): parallel gate — 1 fns across 1 binaries (2 workers)
ov_SC01_000    func_8017D490
2026-08-31 18:55:28 -06:00
Drew T 672431e325 fix(r22+gater): R22 now REFUSES while drafters are live; §372 the copy-capture pair
tools/r22_verify.sh (NEW, promoted from .run so it survives the session):
'make clean' deletes asm/ AND build/, and THREE times this session that raced a
live lane -- a subagent authorised to splice src/800.c produced a FALSE
'212 passed, 1 failed' red, and two drafting agents reported their target's asm/
tree MISSING mid-draft (one survived only by finding an old snapshot). Drafting
agents never WRITE src/, which is exactly why 'check for a dirty tree' does not
catch them: they DEPEND on state this operation destroys. The guard refuses when
any wave scratch dir was touched in the last 6 minutes, names the live agents, and
offers R22_FORCE for a drained lane. R54 -- a guard that is not running is not a
guard, so this refuses instead of relying on me remembering.
Negative-controlled BOTH directions: refuses with 5 live agents named; passes on an
idle lane AND on a lane whose scratch is 30 minutes stale (no false positives).

fix(gater): the in-tree main commit message said '0 fn(s)' for a commit that
contained a real bank. corpus memoizes, so querying corpus.stubs immediately after
the bank returns the STALE pre-bank set. Derive the list from harvest_verify's own
verified-out file instead (R33: derive from the invariant the tool already wrote).

§372 ★★★ THE COPY-CAPTURE PAIR. Tell: a REGALLOC-PERM residual whose wrong-register
rows READ the destination of a nearby MATCHING copy insn. Two passes re-base uses
onto a copy's destination -- cse.c make_regs_eqv (canonical-reg rewrite of later
same-EBB uses) and local-alloc.c optimize_reg_copy_1 (forward-substitution when the
copy's src does not die in it) -- and BOTH die to one zero-byte edit: spell the copy
'P = X + zr' so SET_SRC is a PLUS, which is not a reg-reg copy and records no reg
equivalence, while emitting the byte-identical 'addu $rd,$rs,$zero'.
Notably the escalation was told to CHECK whether §368's tell applied rather than
assume it; it reported that it did NOT (pure shift/slti rows, no commutative
operands) and found the real cause from RTL dumps. That is §361's procedure working.
2026-08-31 18:51:21 -06:00
Drew T 0dacc7b3f9 feat(decomp): main in-tree gate — 0 fn(s) 2026-08-31 18:50:44 -06:00
Drew T 86918184da feat(decomp): main in-tree gate — 0 fn(s) 2026-08-31 18:47:29 -06:00
Drew T f1e963f3e8 fix(gater): commit what the in-tree main gate banks
The worktree path commits via parallel_gate; the main path runs harvest_verify
directly in the main tree and did not. A banked function therefore sat UNCOMMITTED
until I noticed, and the next tool to see a dirty src/ either refuses (parallel_gate
does, correctly) or sweeps it into an unrelated commit. Caught on the func_8005E228
bank. R42: commit banked work the moment it exists.
2026-08-31 18:44:00 -06:00
Drew T 70843992a7 feat(decomp): main func_8005E228 (83 ins) — a §265 VERBATIM-ASM bank, not a C crack
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).
2026-08-31 18:43:37 -06:00
Drew T 54c0624e49 docs: §371 the module-binary -O0 carve route + the spimdisasm rodata trap; SETUP.md S68 tooling rows (R21)
§371 ★★ carving a SINGLE-OBJECT module binary. One 'unaddressable content'
message was THREE stacked causes (interior-YAML-comment symbol-list truncation, a
trailing verbatim-asm chunk with no region, bare tag forward decls) -- fix one and
the message does not change, which is why it read as an impassable wall.

Then the reusable part: spimdisasm migrates single-referenced rodata into a
function's .s ONLY within the same subseg, so a carve that moves the function
silently DROPS it, and INCLUDE_RODATA cannot bring it back (splat marks it migrated
segment-wide and emits nothing). Rename the .rodata subseg to the object its
emitters moved to; the regenerated .s coming back byte-identical is the proof.

Also recorded: the Makefile -O0 glob hunk is PART of the carve, not a follow-up;
interleave_check's DRIFT on md_MAIN_003 is PRE-EXISTING and must not be 'fixed';
the still-open second-carve refusal (UNOWNED rodata 0x800cedf8); and the §126 plan
for the remaining 8 -O0 stubs (three are ADJACENT so one region covers them).

SETUP.md (R21): three tooling-inventory rows covering gater_lane/escalate_fable/
o0_boundary, the six overlay-layout fixes, and the module-binary carve route.
2026-08-31 18:34:38 -06:00
Drew T 650bb7285d feat(decomp): parallel gate — 1 fns across 1 binaries (1 workers)
md_MAIN_011    func_800CFDB4
2026-08-31 18:22:18 -06:00
Drew T a731022d96 docs(phase-31): S68 post-reset — module-binary -O0 route opened, twin flywheel closed 2026-08-31 18:18:01 -06:00
Drew T 7a969d1c61 feat(o0): md_MAIN_003 carve — the module-binary -O0 route opens, func_800D0D6C banked (345 ins)
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.
2026-08-31 18:13:42 -06:00
Drew T c10ee098dc feat(decomp): parallel gate — 3 fns across 3 binaries (3 workers)
ov_SC07_011    func_8013DD68
  ov_SC07_010    func_8013DD68
  ov_SC06_029    func_80184564
2026-08-31 17:58:06 -06:00
Drew T 2a0808e3dc docs(cookbook): §370 — a HARD BOUND from sched.c, plus the reorg slot-steal diagnostic
The third fable escalation did NOT close its function (main/func_8001BC6C,
33 -> 28 over ~45 measured compiles), so the checkpoint's '2 for 2' is corrected
to 2 closed of 3. The failure is banked because a negative result that tells
future agents when to STOP is worth its tokens.

THE BOUND: sched.c schedule_select ALWAYS fronts a ready load over an
equal-priority ALU leaf (potential_hazard), so no C spelling can emit an ALU chain
before loads that are simultaneously-ready same-priority leaves. If a target shows
that order, look for reorg slot-steals, hard-reg dependency walls, or late in-block
consumers BEFORE burning compiles on statement permutations.

Also banked: the reorg fill_simple_delay_slots slot-steal diagnostic and its
split-tree precondition (the accumulator must live outside the $v0-heavy tail to
be eligible), three supporting levers, and three REFUTED ones with measurements --
a dead-init boost-kill is a no-op because cse delete_dead_from_cse removes it
before the final reg_scan, dense-block re-ties cost +4 to +9 because each re-tie
re-anchors its own load, and the -fno-schedule-insns oracle does not discriminate
when the residual is a multi-pass composition.

This run applied §361 CORRECTLY -- it removed the prior agent's pin first and
exonerated it for the head -- which is why its four-pass diagnosis can be trusted
where the previous single-tie claim could not.
2026-08-31 17:29:45 -06:00
Drew T 0816ea0576 chore: refresh the backlog + fleet digests after the S68 banks 2026-08-31 17:27:15 -06:00
Drew T 64230e40ea docs(phase-31): S68 FINAL checkpoint — 23 closed (453 -> 430), fleet 213/213, main unblocked after two stacked harness defects 2026-08-31 17:26:38 -06:00
Drew T 92e3ff9272 docs(cookbook): S68 harvest round 2 — §363-§369, including the reload-remat constant
§363 ★★ the OVERLAY-LAYOUT assumption is a systemic bug class and main is the
     exception that finds it — SIX measured instances, four in one session, each
     of which presented as 'the model wrote bad drafts'. Pass the fact you have
     (corpus.Stub.path/.asm_dir, the Makefile's <b>_OUT/<b>_CHECK_SHA/...); never
     reconstruct it. Two of the six were the SAME tool one call deeper with an
     IDENTICAL symptom, which is what makes a one-layer fix feel complete.
§364 ★ the libgpu P_TAG bitfield spelling is OPT-LEVEL DEPENDENT: required at -O0
     (store_fixed_bit_field fixes the or's operand order), byte-WRONG at -O2
     (MEM_IN_STRUCT_P lets the alias oracle CSE a load across the tag store,
     -4 ins/block). First case where the right answer flips with opt level.
§365 pin BOTH masks or neither (one pin measured 36/34, both -> MATCH)
§366 ★★ group_case_nodes merges STACKED consecutive case labels — give every case
     its own duplicated body and let cross_jump fold them back. The three stacked
     runs were EXACTLY the -25 length drift. First-try MATCH on 360 ins.
§367 reconciling a decl conflict between two drafts for the same TU: match the
     already-banked spelling and adapt the USE SITE; a block-scope shadow works
     for a typedef but NOT for an object.
§368 ★★★ the RELOAD-REMAT CONSTANT — a function-scope single-set local that
     global-alloc cannot color makes reload rematerialize the constant per use and
     choose the register by order_regs_for_reload, reaching registers no
     'register __asm__' pin can (pins measured WORSE). The tell is a
     wrong-register row whose COMMUTATIVE OPERANDS are also swapped.
§369 reuse the compare constant's own variable for a coalescing mask; and
     aggregates take their frame slot at BLOCK ENTRY while scalars take one only at
     &x, so an inner-block pad is a frame ORDERING dial (sharpens §333/§358).

Index: 1020 sections, 14 symptom buckets. Cookbook 383 -> 399.
2026-08-31 17:25:47 -06:00
Drew T 19b7f1b607 feat(decomp): parallel gate — 2 fns across 2 binaries (2 workers)
ov_SC06_033    func_8018AD9C
  md_SC07_004    func_801AEC38
2026-08-31 17:24:19 -06:00
Drew T 814a6df6aa feat(decomp): parallel gate — 1 fns across 1 binaries (1 workers)
ov_SC01_001    func_8017EC28
2026-08-31 17:14:56 -06:00
Drew T 7a6bd844dd fix(gater): gate main IN-TREE via harvest_verify, never in a worktree
parallel_gate's worktree staging copies the three generated files the Makefile
NAMES (<b>_LD_SCRIPT / <b>_UNDEF_SYMS / <b>_UNDEF_FUNCS), which is enough for every
overlay. main's link additionally runs the psyq_integrate chain, whose inputs the
staging does not carry, so a worktree gate of main returns '0 banked' with NO
error -- measured repeatedly this session while the SAME drafts banked
byte-identical through harvest_verify in the main tree (3 of 3).

main is ONE binary, so routing it in-tree loses no parallelism. R43: handle the
input correctly rather than processing it wrongly and reporting a number about it.
2026-08-31 17:11:55 -06:00
Drew T f7fee7fcde feat(decomp): main banks again — 3 fns (func_80012B58, func_800241C0, func_800242D0)
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.
2026-08-31 17:08:32 -06:00
Drew T 3ebbec9426 fix(psyq_integrate): main was RED on every incremental relink — make the externals file monotonic
THE TRUE IDENTITY OF THE LONG-STANDING 'main link defect' (2026-08-15). The extra C
function never broke the link; the RELINK it forced did.

integrate() derives each *_externals.ld from trial_undefined() against the CURRENT
ld_path, so its answer depends on how much of the linker script has ALREADY been
rewritten. On a virgin splat .ld the apicard region is still the stub object
(defining only firstfile2), so at the libmcrd stage 'firstfile' is undefined and
gets an entry. On an already-rewritten .ld, A66.o is present and defines
'firstfile' at 0x80062248, the trial no longer reports it undefined, and the entry
'firstfile = 0x80061FA8;' is DROPPED -- after which LIBMCRD's jal binds to A66.o
and main comes out 2 of 413,696 bytes different from retail (file 0x51674,
VA 0x80060E74, retail jal 0x80061FA8 vs built jal 0x80062248).

That is why main was green ONLY on the first build after a fresh extract, and it
is why NO main draft could ever bank through an incremental gate: the baseline was
already red before any draft was spliced.

integrate()'s own comment already CLAIMED this operation was idempotent ('a re-run
on an already-rewritten .ld only redoes syms'). This makes it true: the externals
map is merged with the file's prior contents, newly-derived values winning on a
name collision, names the new derivation no longer sees kept at their previous
address. The file becomes a function of the tree, not of how many times this ran.
It reports what it kept rather than doing it silently.

VERIFIED, three builds:
  fresh extract + build ...... GREEN (unchanged)
  INCREMENTAL relink ......... GREEN (was RED -- the failing case)
  third relink ............... GREEN (monotonic across repeats)
and the merge is observed firing: 'kept 6/15/2 extern(s) this re-run no longer saw
as undefined' across the integrate stages.

Root-caused by a Fable agent, verified here against the bytes.
2026-08-31 17:03:50 -06:00
Drew T 14a72240d2 feat(decomp): parallel gate — 2 fns across 2 binaries (5 workers)
ov_SC01_000    func_8017E594
  ov_SC07_001    func_8017F834
2026-08-31 16:59:49 -06:00
Drew T fd28dd714d fix(gate_stage): main was gated against ov_SC01_077's SHA — stop synthesising out/good_sha
The third instance of the overlay-layout assumption, and the worst of them.

gate_stage synthesised --out 'build/<bin>/<bin>' and --good-sha from
'config/check.<bin>.sha'. For main BOTH are wrong: its image is
build/us/SLUS_007.26 (Makefile main_OUT) and its locked hash is
config/check.us.sha. So sha1(out) was None, _check_sha('main') found nothing, and
good_sha fell through to DEF_SHA -- ov_SC01_077's hash. EVERY main draft was
compared against a DIFFERENT BINARY'S SHA, auto-failed, reverted regardless of the
build, and reported as 'near' -- indistinguishable from a real codegen residual.

harvest_verify already owns these facts (its own comment: 'the Makefile and
config/check.<bin>.sha already state these facts; do not keep a second copy') and
refuses loudly when it cannot derive them. gate_stage's synthesised flags bypassed
both. Now they are passed through ONLY when a caller explicitly sets them. Same
defect the 2026-07-22 comment fixed on the CLI path for good_sha and left alive one
argument over, and in run_gate's API path.

Measured: three main drafts proven byte-perfect in the REAL link (whole image
differs from retail by 2 of 413,696 bytes, both a pre-existing baseline defect
unrelated to the drafts) reported {"banked": 0, "near": 3}.

NEGATIVE CONTROL (R39), zero-build, all 213 binaries: the (out, good_sha) pair
reaching harvest_verify is UNCHANGED for 212 of 213; main is the only one that
moves, from ('build/main/main', DEF_SHA=ov_SC01_077) to
('build/us/SLUS_007.26', 143dbb89...). 0 binaries have no derivable sha. The
derivation agrees with the Makefile's own $(BINARY)_OUT / $(BINARY)_CHECK_SHA for
main, resident and an overlay.
2026-08-31 16:59:17 -06:00
Drew T 3c534853f2 fix(gater): key verdicts AND the ledger by ARM; add --skip-binary for lanes that may be writing
Three defects, all found by the tool's own zeros rather than by reading it.

1. VERDICTS KEYED BY ARM. An escalation is BY DEFINITION launched while the lower
   tier's verdict already exists, so keying completion by (binary, fn) let the
   in-flight FABLE draft be staged on the strength of the OPUS verdict -- the same
   in-flight bug the verdict gate exists to prevent, one level up. Caught in a dry
   run before it gated anything. An arm-less row still counts for every arm so a
   hand-written backfill keeps working.

2. LEDGER KEYED BY ARM. Gating the opus draft of a function currently being
   escalated used to ledger away the fable draft that follows it -- silently
   discarding the escalation's product. The already-banked check is what stops a
   genuine duplicate: once a function banks its stub is gone and every arm's draft
   is skipped as banked-elsewhere. Legacy binary:fn entries for still-OPEN
   functions were dropped so they get re-judged (9 of 14); banked ones kept.

3. --skip-binary. A gate that races a lane writing that binary's src/ produces a
   FALSE verdict on a draft that is fine. Measured this session, by me: a
   clean-fleet R22 raced an authorised src/800.c splice and reported '212 passed,
   1 failed of 213' on a tree that rebuilt byte-identical minutes later. Being
   clean RIGHT NOW is not the test; nothing being able to dirty it during the run
   is -- and that is not something timing can be trusted to arrange.
2026-08-31 16:56:32 -06:00
Drew T 7153f881c9 feat(o0): o0_boundary.py — the stranded-boundary -O0 sweep, and its honest null
The class banked 5 functions today (func_801457A4 x3 at the whale's end boundary,
func_80183830 x2 one region lower) so it deserved a sweep rather than a third
hand-derivation. It reads every splat yaml's _o0<letter> 'c' subsegs, takes the
START of the NEXT subseg as the boundary vaddr, and reports an open stub sitting
exactly there whose target carries the -O0 prologue tell.

RESULT: 141 binaries with an _o0 subseg, 288 boundaries examined, 0 candidates.
THE CLASS IS EXHAUSTED -- today's five were the last of it.

A sweep returning 0 must prove it CAN return non-zero, so that null is
negative-controlled: the 288 computed boundaries include 0x801457A4 in 138
binaries and 0x80183830 in exactly ov_SC03_118 + ov_SC03_119 -- i.e. it does find
the addresses it banked, they simply have no open stub any more.

Every rejected boundary is printed WITH ITS REASON and the denominator is printed
(R32): a sweep that reports only its hits cannot be told from one that scanned
nothing. It deliberately does not consult the family map -- rollout_o0 refuses this
recipe for a bookkeeping reason ('family with exemplar ... not found in the map'),
not a structural one, and is separately blind to any _o0 basename.
2026-08-31 16:50:09 -06:00
Drew T c746204c32 feat(decomp): -O0 stranded-boundary bank — func_80183830 in ov_SC03_118 + ov_SC03_119, zero agent tokens
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.
2026-08-31 16:48:34 -06:00
Drew T 9c96b47ce0 docs(phase-31): S68 progress — whale carve (6 fns/2,547 ins), the main-layout bug class, gater hardening 2026-08-31 16:46:57 -06:00
Drew T ff99f4acec fix(gater): never gate a draft whose workflow has not returned a verdict
A draft file appears at <wave>/<arm>/<fn>.c long before its agent is finished --
agents iterate in place and the wave brief tells them to write the file, not to
write it last. Gating one mid-flight spends a build on unfinished work, records an
honest-looking rejection, and then LEDGERS it, so the FINISHED draft is skipped as
'already-gated' when it lands. That is a silent loss of the whole draft.

Measured this session: ov_SC01_000:func_8017E594 was gated at 0 banked while its
workflow was still running, and its ledger entry had to be cleared by hand.

Completion is now an explicit signal -- .run/gate_lane/verdicts.jsonl, one object
per RETURNED verdict, appended by the orchestrator. A quiet file mtime is
deliberately NOT accepted as one: an agent thinking for four minutes between edits
looks identical to a finished agent. --any-draft opts out, and says what it costs.

  [gater] skipped 1 (IN-FLIGHT (no verdict yet)): ov_SC01_000:func_8017E594
2026-08-31 16:45:58 -06:00
Drew T c55cebea9b docs(cookbook): S68 harvest — §354-§362, nine sections from the wave and the whale carve
§354 the giv worth-while test as a dial (re-associate the addend into the index
     term; strength_reduce declines and $fp is freed) - ov_SC03_105/func_801824CC
§355 a remapped sibling's SOURCE bias is not its EMITTED bias; gcc re-anchors
     reduced givs, so do NOT hand-shift offsets to match the asm
§356 measure a draft in the TU it will live in: 39 of 43 'undeclared' cc1-fails
     were the standalone probe's environment, not the draft (R35)
§357 one struct pointer, not two - a second source variable builds a THIRD iv
§358 sharpens §333: an UNREFERENCED fixed-size aggregate local is load-bearing;
     expand_decl slots every aggregate, so an unused decl is a frame-layout knob
§359 spell a sign-widen as an explicit two-step function-scoped temp; a single
     (s16) cast and a register pin both measured FAILED
§360 the 'compiler found a shorter equivalent' pair - with its third lever marked
     REFUTED rather than deleted, so nobody re-derives it
§361 ★ a loop-tail byte signature that names its source shape, and the law that a
     'scheduling tie' may be an artifact of your own earlier lever. The prior
     agent's sched1 diagnosis was WRONG and its own hack was the cause.
     Escalation economics: sonnet 229k tokens no bank, fable 74k tokens MATCH.
§362 two traps when a carve moves a stub into the -O0 TU (rollout_o0 goes blind;
     the §8b decl layer conflicts with the shared header on 7 symbols)

Index regenerated: 1013 sections, 14 symptom buckets.
2026-08-31 16:37:35 -06:00
Drew T d3b72e8f64 fix(rtu_match/blocker_probe): main was structurally unprobeable — pass the TU path and asm subdir, never reconstruct them
rtu_match built its TU as src/<source>/<split>.c and its asm dir as
asm/<source>/nonmatchings/<split>. That is the OVERLAY layout. main keeps its
sources as LOOSE FILES in src/ (src/800.c) with asm at asm/nonmatchings/800, so
blocker_probe's 'stub.path.split("/")[1]' handed rtu_match '800.c' as the source
dir and it looked for src/800.c/800.c, then asm/src/nonmatchings/800/<fn>.s.

Every main draft came back ERR with an EMPTY detail -- indistinguishable from a
bad draft. The corpus Stub already carries both facts (.path and .asm_dir);
reconstructing them was the whole bug. rtu_match now takes --tu and refuses a
nonexistent TU with the reason instead of handing it to cpp (R43).

Proof it was the instrument, not the drafts: the same 4 main drafts, unchanged,
now probe MATCH 69 / DIFF 69-36-mismatched / MATCH 68 / MATCH 71.
3 of 4 are real-TU MATCH. Before this they were 4 of 4 ERR.
2026-08-31 16:34:01 -06:00
Drew T 8f89cbcccd fix(pgate): main could never bank in a worktree — derive link inputs from the Makefile, and REFUSE when they are absent
stage_generated hard-coded build/<bin>/{<bin>.ld,undefined_*_auto.txt}. That is the
OVERLAY convention. main's Makefile variables put its linker script at
build/us/SLUS_007.26.ld and BOTH undefined_*_auto.txt at the REPO ROOT, so a
worktree got none of them, could not link, and every main draft came back rejected
-- indistinguishable from a wave of bad drafts. Measured this session: main banked
0 of 3 while the same drafts were match_one MATCH.

The tell was already being recorded and thrown away: the results JSON carried
missing_generated: [main.ld, undefined_syms_auto.txt, undefined_funcs_auto.txt]
and nothing consumed it -- R32's corrected form, a loud failure nobody counts is
exactly as invisible as a silent one. Same shape as R43's 'sweep_parallel accepted
main and banked 0/105'.

Now: paths come from the Makefile's own <b>_LD_SCRIPT / <b>_UNDEF_SYMS /
<b>_UNDEF_FUNCS (R33 -- derive from the invariant), are mirrored at the same
repo-relative location in the worktree, and a missing one REFUSES the binary with
the reason instead of gating it anyway (R43).

Negative control (R39): resolved and existence-checked across all 213 binaries --
0 would be refused, so the previously-succeeding population is untouched.
2026-08-31 16:29:45 -06:00
Drew T c1200003ac fix(gater): escalation supersedes — highest-tier arm wins a (binary,fn) collision
Arm dirs are walked alphabetically, so 'fable' < 'opus' < 'sonnet' and the staging
copy silently OVERWROTE: a sonnet NEAR would have replaced the fable MATCH that was
escalated to rescue it. Measured live on main/func_800241C0 (sonnet closeness 19,
fable MATCH) -- the escalation's entire product would have been lost to a directory
listing order, and the gate would have reported an honest failure on the wrong draft.

Now ranked fable > opus > sonnet > v3 > haiku, and a collision is REPORTED, never
resolved silently:
  [gater] main:func_800241C0 drafted by fable/sonnet — staging the fable draft
2026-08-31 16:26:23 -06:00
Drew T f03deb6c99 feat(decomp): -O0 whale bank — ov_SC02_037 + ov_SC03_107, 4 fns / 1,698 ins, zero agent tokens
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).
2026-08-31 16:23:01 -06:00
Drew T 7a89bc4fb4 feat(config): -O0 whale carve step 1 — ov_SC02_037 + ov_SC03_107
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.
2026-08-31 16:22:41 -06:00
Drew T 61335ad18e feat(decomp): -O0 whale bank — ov_MAIN_012 func_80144B9C (770) + func_801457A4 (79), BINARY COMPLETE
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.
2026-08-31 16:21:13 -06:00
Drew T ff524cd07e feat(config): -O0 whale carve step 1 — split 0x80144B9C..0x801458E0 out of ov_MAIN_012_jr_8013F350
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).
2026-08-31 16:20:38 -06:00