Commit Graph

3673 Commits

Author SHA1 Message Date
Drew T 94ca16e007 docs(accelerators): #20 — set TU boundaries at the rodata island's jtbl spans, at segmentation time
Found P31 S72; COULD have been found 2026-06-15 (Phase 7, commit:0025), which wrote the
island's contents by hand and named loadDestPtrTable as the divider. The signal needs no
matching progress — it is a property of the retail image.

The number that matters is the cost curve: at Phase 6 src/800.c had 13 externs and 0
typedefs and the split was a yaml edit; at S72 it had 2,378 externs and 175 typedefs and
cost 57 crossing declarations, a shared header and 4 stale consumers. Plus a session of
wrong conclusions (11 'PROVEN gate-rejects', 10 of which banked once the carve existed).

PREREQ recorded honestly: the binding constraint came from the OVERLAY work two phases
later, so this is knowledge that never got carried back to main — not carelessness.

General principle: segmentation is the exception to probe-before-investing. When a
decision is evidenced at t=0, cheap now, and strictly more expensive later, make it early
even though its payoff is unproven.
2026-09-02 13:49:37 -06:00
Drew T 8e8521da22 fix+docs: make every consumer aware of main's new TU layout (R36)
The split created two new TUs and a shared header; four consumers still described main's
game code as one file:

* tools/reconcile_slate.py — HARDCODED open('src/800.c'), so after the split it saw a
  THIRD of main's typedefs while reporting success (silently-narrowed scope, R32).
  Measured: 133 visible before the fix, 187 after, 0 lost. Now globs
  corpus.src_files('main') + src/800_shared.h, so a future split is already handled.
* docs/wave-playbook.md 1c — still said spans B/C/D were NOT drawable and that drawing
  one is an R45 violation. That is now false and would have STOPPED a future session
  from drawing the very targets this work unlocked.
* cookbook §426 — its 'the remaining spans need src/800.c split' paragraph now records
  that it was done the same session, and points at §431 for the method.
* config/dedup.us.yaml + src/shared/clearTbl40.h — both said dedup group I0 is
  instantiated 'at both sites in src/800.c'; both sites are above 0x80035270 and are now
  in src/800_c.c.

Byte-neutral: dedup.us.yaml parses, gate_main --assert-baseline BYTE-IDENTICAL.
SETUP.md gains a row describing the layout and the rule it implies: never hardcode
src/800.c, glob corpus.src_files('main').
2026-09-02 13:48:00 -06:00
Drew T 430f40576a docs(cookbook): §431 — widen the gate-reverts-src rule from declaration edits to BANKS
I wrote the rule for declaration edits and then lost two byte-proven banks to the same
mechanism an hour later. gate_main's opening 'git checkout -- src/*.c' destroys anything
uncommitted in src/, banks included. R42 is 'commit before the next command that can
touch src/', not 'commit at a good stopping point' — and count banks from the SOURCE,
which is the only oracle that caught it.
2026-09-02 13:43:11 -06:00
Drew T 8c72831177 fix(main): recover func_8002EED8 + func_8002F248 — I destroyed them with my own R42 violation
Both banked byte-identical earlier this session ('BANKED 2 of 3'), then sat UNCOMMITTED
while my very next action was another gate. gate_main.try_batch's first step is
`git checkout -- src/*.c`, which reverted them; the following commit captured only the
third function. I reported 14 banks; the source said 12.

Caught by counting banks from the SOURCE (the INCLUDE_ASM stub's absence) rather than
from my own account of what I had done — the oracle the project already mandates.

I had written this exact hazard into cookbook §431 an hour earlier, for DECLARATION
edits, and did not apply the same reasoning to BANKS. R42 is not 'commit at a good
stopping point', it is 'commit before the next command that can touch src/'.
2026-09-02 13:42:30 -06:00
Drew T 468c3751c3 docs(decision-log): S72 addendum — the split decision, and the effort estimate I got wrong
Quoted '2,318 scattered externs' as measured-not-guessed. It was measured, and it
measured the wrong quantity: what a split costs is declarations used OUTSIDE their
region (57 of 1,247), not the total. R41's denominator discipline applied to an EFFORT
estimate. Also records the correction to the '1998' premise: the spans prove at least
three TU boundaries, not that the devs' files were exactly these three.
2026-09-02 13:37:38 -06:00
Drew T 3e8138819a docs(cookbook): §431 — splitting a 27k-line TU at its original boundaries
Where to split: the jtbl spans (tables pack tight within a TU, separated across TUs), and
that is also the MINIMUM — a TU with no switch emits no table and is invisible, so what
you recover is a lower bound on the original structure, not the structure.

What crosses: ask the compiler. 2,318 externs is the scary number and the wrong one; only
57 of 1,247 declared names cross a boundary, 19 of them typedefs with one definition each
and zero shape conflicts. Fix TYPES first — a missing typedef cascades into dozens of
parse errors that all evaporate at once.

Plus the general defect it exposed (a typedef stripper must read the destination's
includes) and the operational rule it cost twice (gate_main reverts src/*.c first, so
commit alignment edits before gating).
2026-09-02 13:37:11 -06:00
Drew T 40bbd07c45 feat(main): func_80036260 banked — 10 of the 11 'PROVEN gate-rejects' are now banked
Its blocker was an extern the DRAFT carried for a different symbol
(func_8004355C declared (s32, void*) against the TU's (s32, u8*)). Adopting the TU's
spelling verbatim — the documented §376 recovery — banked it byte-identical in 10s.

Only func_800316F8 of the eleven remains, and it is a TABLE REJECT: .text
byte-identical, 18 bytes wrong in its own jump table (§405-A).
2026-09-02 13:36:20 -06:00
Drew T 00e5bfe57c feat(main_diff_locate): TABLE REJECT — the third verdict class, and the one this session is about
func_800316F8's .text was BYTE-IDENTICAL and all 18 differing bytes were its own jump
table; the tool called it a PLUMBING REJECT and routed it to the §376 declaration chain,
advice that would never have fixed it. classify() now separates a .rodata-only
divergence and names it §405-A: match_one compares .text ONLY, so a draft sits at
closeness 0 while emitting a wrong table — gcc emits case BODIES in source order while
entry i points at case i, so case value and case order are independent and only the
order is pinned by .text.

Attribution reads one symbol LOW for a cc1-emitted table (once the function is C its
table is a $L label, not a jtbl_ data symbol, so the bytes land in the PRECEDING table's
extent) — the OBJECT name is what identifies it, not the symbol name. Shared by
gate_main so both report the same four verdicts.

Controls: self-test PASS, green build IDENTICAL, and the known func_800316F8 case now
classifies TABLE REJECT.
2026-09-02 13:35:17 -06:00
Drew T 9de9514249 feat(main): 3 more banked via the §376 alignment — func_8003602C, func_80038FFC, func_80039C70
Byte-identical. 13 main functions banked this session.

func_800316F8 rejected with a precise, new-class verdict: its .text is BYTE-IDENTICAL
and all 18 differing bytes are its own jump table at 0x800730C4 (attributed to the
preceding jtbl_800730AC because the table is now a cc1 $L label, not a data symbol).
That is §405-A in the flesh — match_one compares .text ONLY, so a draft can sit at
closeness 0 while emitting a wrong table. Not a body reject and not plumbing either.
2026-09-02 13:34:26 -06:00
Drew T 11dda014ee fix(main): align 5 forward declarations with their definitions (§376), byte-neutral
Each of these five had a TU forward declaration that contradicted the real signature,
which is what made gate_main drop their byte-correct drafts:

  func_800316F8  void f(void*)            -> void f(s32)        + cast at the one call
  func_8003602C  void f(void)             -> void f(s32)          (use is address-taken)
  func_80036260  void f(void)             -> int  f(void)         (use is address-taken)
  func_80038FFC  void f(u8**)             -> s32  f(s32*)       + cast at the one call
  func_80039C70  void f(void*,s16,u8)     -> void f(u8*,s16,s16)

Verified byte-identical with NO draft substituted, so any later gate failure is
attributable to the draft and not to this edit.

OPERATIONAL NOTE: gate_main's first action is , so an
UNCOMMITTED declaration edit is silently discarded and the gate then judges the drafts
against the old declarations. Commit alignment work before gating (R42's shape, seen
from the tool's side).
2026-09-02 13:32:32 -06:00
Drew T 483e2514a3 feat(main): 3 span-B functions banked; gate_main now sees header-provided typedefs
func_8002EED8 · func_8002F248 · func_80031988 — byte-identical, the first banks that
span B's carve made possible.

gate_main defect the split exposed: defs_above scans the destination .c ALONE, so a
typedef the TU gets through #include is invisible to strip_dup_typedefs and every draft
carrying its own copy dies with 'redefinition of X'. Latent until src/800.c's split moved
19 shared typedefs into src/800_shared.h, at which point func_80031988 — byte-correct,
and one of the eleven — failed to compile for that reason alone. header_defs() now walks
the destination file's quoted includes transitively and seeds defs_above with what they
provide, so an identical copy is stripped and a different shape is renamed, exactly as
for in-file definitions.

func_80031988 had TWO stacked blockers: this one, and the struct-tag false conflict in
typesig fixed earlier today. Neither was a property of the function.
2026-09-02 13:30:20 -06:00
Drew T 7df4895e7b feat(main): split src/800.c at the jtbl-span TU boundaries — spans B and C now carve
BYTE-IDENTICAL with NO function banked (gate_main --assert-baseline, clean rebuild),
which is the whole point: the structure lands first and proves neutral, then drafts bank
against it.

One code object contributes exactly ONE contiguous .rodata run, and 800.o's is span A,
so spans B and C each needed their own object:

  800    vram 0x800123F0-0x8002B0B4  -> .rodata span A (0x80072A38-0x80072C70)
  800_b  vram 0x8002B0B4-0x80035270  -> .rodata span B (0x80072E44-0x80073140)
  800_c  vram 0x80035270-0x8003A444  -> .rodata span C (0x800732A0-0x8007344C)

The span owners' address ranges are disjoint and ordered — tables pack tight WITHIN a
TU and are separated by other data ACROSS TUs — so these are (at least some of) the
original translation-unit boundaries. Splitting here is both the fix and the minimum;
any extra split would be speculation.

main's island is now a 7-piece data->rodata sandwich, so ld_interleave moves from
--front/--tail to --order.

THE SPLIT WAS CHEAP, AND MY FIRST ESTIMATE WAS WRONG. I costed it at '2,318 scattered
extern lines' — that is the TOTAL; what matters is how many CROSS a boundary, and that
is 57 of 1,247 declared names (4.6%), of which 19 are typedefs with exactly one
definition each and zero shape conflicts. Zero file-local statics. src/800_shared.h
carries exactly those, derived from the COMPILER's own errors rather than a regex model
of C (R33), and each typedef was MOVED, never copied.

Unlocks 17 functions / 4,471 instructions = 39% of what is left in main, incl.
SaveLoadRoutine (1139) and func_8003388C (663).
2026-09-02 13:27:29 -06:00
Drew T f0148e6822 docs(phase-31): S72 CLOSE — R22 green 213/213, 7 main banked, main past 51.8%, frontier 140
The 11 'PROVEN gate-rejects' were one missing rodata carve, not bad bodies. Next
session starts at the span B/C carve: 18 jtbl functions left in main's frontier holding
most of its remaining instruction mass, blocked on splitting src/800.c at 0x8002B0B4
and 0x80035270 — the original TU boundaries the jtbl spans reveal.
2026-09-02 12:58:44 -06:00
Drew T 174f7aaf7a docs(cookbook): §430 — a shared tail is a late cross-jump merge, not a source goto
Counterweight to §3-B, with a precise discriminator. When two arms converge on a shared
block, that block is usually a late cross_jump merge of per-arm DUPLICATED statements
(§298). Spelling it as a real goto is not equivalent when the label sits INSIDE a loop:
the goto becomes a jump into the loop body, jump.c's mark_loop_jump marks it
loop_invalid, cc1 -dL prints 'Loop at N ignored due to multiple entry points', and
loop.c silently drops invariant hoisting — measured: the 1/0x80 constant hoist into
$a0/$a1 vanished, 2 insns plus a spurious andi.

Discriminator: shared tail outside every loop -> fold it (§3-B, and you may free a hard
ABI register). Shared tail inside a loop body -> duplicate per arm and let cross_jump
merge. A -dL line is a free oracle for this.

Also records CdReadSectorReadyCB's three remaining residual clusters as pack fuel so the
next attempt starts from the draft, not from the .s.
2026-09-02 12:53:19 -06:00
Drew T 8c40fa3ebd feat(main): 4 more span-A switch functions banked byte-identical (wave S72m_1)
CdReadStateMachine (385 ins) · func_80024448 (362) · func_80026D64 (189) ·
func_8001B0D4 (86). One clean rebuild, 10.8 s, 4 of 4 accepted.

Wave shape: 5 targets, one agent per workflow, 5 concurrent. 4 MATCH / 1 NEAR.
Every agent verified its jump table and reloc stream past match_one's .text-only blind
spot (§405-A) because the packs carried the §426 carve note.

Three new laws banked from their notes: §428 (zero-byte cross-jump barrier), §429
(every held pointer needs its own local), §428a (two residuals moving in opposite
directions share one starved resource — which refuted my own prediction).

func_80024448 was recovered from disk after its Fable agent was killed by a rate limit
and the workflow reported NO-DRAFT; match_one on that file: closeness 0 (playbook §5b).
2026-09-02 12:52:39 -06:00
Drew T c0724cb59e docs(cookbook): §428a REWRITTEN — my prediction was refuted; the real law is better
I predicted §428's UID barrier would resolve func_8001B0D4's fence<->over-merge
coupling, reasoning that it changes no liveness. The escalation that tested it did not
use §428 at all. §3-B did it: seven in-block 'return 0;' -> 'goto L_ret0;' to one shared
tail removed the priority-1 hard-$v0 sets, freeing $v0 for the D_800747E4 reload and
$v1 for CdQueueBusy's result, AND fired all three cross-jumps (92->86). One edit, both
residuals.

The real law is the opposite of my framing: two residuals moving in opposite directions
under every lever are usually not in tension — they are two symptoms of ONE starved
resource, and every lever so far was paying for one with the other. Ask what they are
both competing for, and inventory the hard register sets the source forces. A repeated
'return <const>;' in switch arms is the commonest way to pin $v0 many times over.

The wrong prediction is kept in the section as the refutation (R14).
2026-09-02 12:38:49 -06:00
Drew T 4001ff0c6f docs(cookbook): §429 — every held pointer needs its own local; negative-displacement byte stores need one too
From main/CdReadStateMachine (MATCH 385/385, opus). One root: gcc-2.7.2 canonicalises
(mem (reg)) back to a symbol when the pseudo has a single reachable set, so reusing a
pointer local changes the addressing mode downstream. p[-0x10] re-folds to
lui %hi(sym-0x10) unless the offset pointer gets its own single-set local; and a
multi-set pseudo defeats the canonicalisation for every use, costing a load-delay nop.
Same mechanism as §421 read from the source side.

Also confirms the §333 frame dial and the merged-tail label pin (gcc's cross_jump picks
the other end of a merge than you expect) as the cheap alternative to a §5a fence.
2026-09-02 12:33:10 -06:00
Drew T 92c001ea8c docs(cookbook): §428a — the fence<->over-merge coupling (agent-measured, flagged not-yet-byte-proven)
A volatile __asm__ barrier is a scheduling AND allocation event, so on a function whose
residual is allocation-shaped every fence that kills the double-hop re-enables a
cross-jump over-merge. Two residuals in tension, which is why ~20 variants across two
attempts never converged. §428's UID-based barrier is the predicted resolution because
it changes no liveness; the escalation now running is the test.

Flagged explicitly as one agent's report, not a byte-proof (R14).
2026-09-02 12:26:01 -06:00
Drew T c6ab5ff455 docs(cookbook): §428 — a zero-byte cross-jump barrier (advance the pointer inside each switch arm)
Sharpens §5a/§336, supplies the missing precondition to §162. Writing the cursor
advance inside each switch arm instead of a shared temp makes the converging addu a
label CREATED by cross_jump (get_label_before), so its INSN_UID >= max_uid and
jump.c:1988's guard stops the minimum=2 jump-to-jump search from ever running — only
minimum=1 survives, which is exactly the target's single merge. Killed a -23
LENGTH-DRIFT (5 spurious tail merges) in one edit, with no volatile asm.

Found by the S72 main wave on func_80026D64 (MATCH in 2 compiles). The agent also
verified the .rodata table against jtbl_80072BFC past match_one's .text-only blind
spot — because the pack carried the §426 carve note telling it to.
2026-09-02 12:17:05 -06:00
Drew T 7bfc543d76 docs(playbook): 5b — gate the directory, never the verdict list
A wave verdict is a claim about what the AGENTS returned, not what is on disk.
claude_wave_draft.js has no filesystem access, so its .catch() reports NO-DRAFT for
any agent that dies — including one that already wrote a finished body.

S72: a Fable agent on main/func_80024448 spent 133k tokens / 17 tool calls / 10 min,
wrote its draft, then died on 'You've reached your Fable limit'. The workflow returned
NO-DRAFT with an empty draft_path; match_one on that same file returns
{status: match, closeness: 0, nins: 362}. A complete byte-perfect body reported as no
work at all.
2026-09-02 12:13:56 -06:00
Drew T 8edd0f61e0 docs(phase-31): S72 progress — main's jtbl carve debugged, fixed, 3 banked; span B/C is the next lever 2026-09-02 12:09:06 -06:00
Drew T ab5d1e3188 fix(gate_main): a tag keyword is not a type this model can see (R39 over-refusal)
typesig() keeps only tokens in TYPES, which DISCARDS every typedef name — so
'Ent30D80 *' and 'Rec14 *' both reduce to '*' and the model has always been blind to
what a pointer points at. But 'struct' was in TYPES, so 'struct Ent30D80 *' reduced to
'struct*' and conflicted with 'Ent30D80 *', its own typedef.

src/800.c declares func_80031988 BOTH ways and compiles today — gcc, the arbiter,
agrees they are one type — yet the checker DROPPED the byte-verified draft, and S71
recorded it among the eleven 'PROVEN gate-rejects'. Dropping struct/union/enum from
the param token set loosens nothing the typedef path had not already loosened.

R39 control over the already-succeeded population (gate_main.typesig is imported by
pregate_check and four other tools, so this reaches overlay slates too): 452 drafts
across 54 binaries, 178 drops before / 177 after — ZERO new refusals, exactly one
removed: main:func_80031988.
2026-09-02 12:06:59 -06:00
Drew T e94bb20833 docs: bank the S72 main carve finding — cookbook §426/§427, playbook 1c, SETUP, decision log
§426 — main's switch functions were never a codegen wall: one .rodata carve had been
missing since Phase 7, so a drafted switch double-emitted its jump table (+28/+52/+76/+84
image growth, 238 symbols shifted, first mover jtbl_80072A4C every time). Includes the
derived name-address overlay that names a layout shift with no reference build, the span
table, and why spans B-D need src/800.c split at the original TU boundaries the spans reveal.

§427 — a hash is a correctness oracle with zero diagnostic content; preserve the red
artifact before anything rebuilds over it, attribute per byte, and negative-control the
localizer in both directions.

playbook 1c — which main jtbl functions are drawable (R45), and the gate note: main is not
in the parallel lane, read the BODY/PLUMBING/MIXED verdict before recording a main reject.
2026-09-02 12:05:01 -06:00
Drew T 69f3f22112 fix(journal_notes): a pack with one old note could never receive a newer one
The idempotency check was 'heading present -> skip', and claude_wave_packs.py calls
this tool at pack-build time, so EVERY pack that had any history was sealed against
evidence recorded later. Measured on wave S72m_1: 4 of 5 targets took a
freshly-recorded carve finding and the fifth silently did not, because it alone
carried a prior note. Now idempotent by REPLACEMENT — the generated block is always
the tail of the file, so truncate at the heading and re-render the current row set.
Re-running is byte-identical (verified) and never duplicates the heading.

Also records the S72 main carve unlock in .run/journal_notes_local.jsonl so it
reaches every future main switch-function pack through the same one code path.
2026-09-02 12:02:23 -06:00
Drew T cbf5bae043 feat(main): unblock main's switch functions — the rodata span carve + derived jtbl pads
main's gate could only ever say "got X want Y". S71 read 7 such verdicts as body
rejects and recorded 11 functions as "PROVEN gate-rejects, §376 in its purest form".
They are not: all 11 are switch functions, and the blocker is that main has had
exactly ONE rodata carve since Phase 7 (LZSS's jtbl_80072A38). Every other main jump
table stayed raw in the tail data, so a drafted switch DOUBLE-EMITTED its table, the
image grew (+28/+52/+76/+84 measured), and all 238 symbols above 0x80072A4C shifted.

* tools/main_diff_locate.py (NEW) — turns a red image into a named list of divergent
  symbols via the linker map; per-byte attribution, self-test flips a byte at a known
  address and asserts the containing symbol (plus the identical-pair direction).
* gate_main.py — PRESERVES the red image + map before the R40 baseline control
  rebuilds over it, and auto-localizes: BODY REJECT vs PLUMBING REJECT vs MIXED. Also
  -j on the build (was single-threaded) and the §376 drop list written to
  .run/gate_main_dropped.json with the reconciliation chain.
* splat.us.exe.yaml — the .rodata carve extends from the LZSS table alone to the whole
  contiguous game-jtbl span 0x80072A38-0x80072C70 (12 tables, one 800.o run).
  Byte-neutral with no drafts substituted (probed first).
* jtbl_rodata_pads.py — --derive now works for main: one file-0-vram expression makes
  both address->bytes and yaml-piece->address correct for the EXE's 0x800 header and
  leaves flat overlays unchanged. Makefile arms it for BINARY=main.

Banked byte-identical: func_8001A114, func_8001AAD0, func_8001AF34 — three of the
eleven. 25 of main's 59 frontier functions (6,215 of 12,912 instructions) are in this
class; the remaining spans need src/800.c split at the TU boundaries the spans reveal.
2026-09-02 11:56:35 -06:00
Drew T 6c904ebd0c docs: close the S71 documentation gaps - SETUP tools table, playbook steps, decision log, gate ledger
Audit found real gaps rather than assumed coverage:
* SETUP.md (R21) had NONE of the five tools written this session. Added a table for
  journal_notes / launch_check / gate_triage / restage_matching / weave_sweep, each with
  when you need it, plus the two gating rules now enforced in code (parallel_gate refuses
  main; gate_main refuses a no-op draft and counts banks from the source).
* wave-playbook: launch_check as step 4c (payloads go stale while gates run - 3 of 27
  wave-2 targets were already banked) and gate_triage as step 6b with the measured
  blocker census.
* decision-log (R31) held only the §406 pivot. Added the two strategic entries this
  session actually turned on: gating main with a tool documented as unable to gate it
  (false PASS, caught only by R22), and the drafting pool running dry while the lever
  was an exclude list nobody re-probed after a tool fix.
* CURRENT_PHASE: the per-gate ledger for all 14 cycles plus the carve/rebase/main gates.
* Two memories: gate-main-only-with-gate-main, reprobe-exclude-lists-after-tool-fixes.
2026-09-02 11:19:49 -06:00
Drew T 9631949339 docs(phase-31): S71 CLOSE — R22 green 213/213, frontier 147, 63 banked, main past 50% 2026-09-02 11:15:21 -06:00
Drew T 9c7b1e053b feat(decomp): parallel gate — 2 fns across 2 binaries (10 workers)
ov_SC01_084    func_80180E74
  ov_SC07_006    func_801890FC
2026-09-02 11:09:14 -06:00
Drew T 853cab2a97 docs(cookbook): §425 - sb aliases scalar globals (true_dependence's QImode carve-out) + 3 more
Four byte-verified findings from md_MAIN_003/func_800CF3E8 (467/469 ins), read off
-dS/-dR/-dl/-dr:

1. sched.c true_dependence guards on GET_MODE(mem) != QImode, so a `sb` store aliases
   scalar global loads while sh/sw struct stores are exempt - that is what pins the
   colour lbu after `sb 0xb` and gives it a mandatory filler slot. A property of the
   store's MODE, not of what it points at.
2. An OT-index re-read must be a MEM_IN_STRUCT_P FIXED-address lhu to depend on its
   store; expand's memory_address() forces the constant into a reg (la+lhu 0(reg))
   unless you copy the whole struct into a 2-byte register local. This was the 468/469
   wall for every prior attempt.
3. A `la` above an idx*24 chain needs a single-set base pseudo emitted before the
   multiply: block-scope `{ Sprt24 *b_ = ARR; p = &b_[idx]; }`.
4. Recorded as NOT reachable from C: $v1-vs-$a0 for the last prim pointer is
   local-alloc's pointer-vs-temp priority tie. Permuter or a §419 density manoeuvre.
2026-09-02 11:03:52 -06:00
Drew T 1e314e7208 docs(cookbook): §424 - equal-priority stores emit REVERSED (sched1 LUID tie picks the last statement)
Byte-proven on ov_SC07_006/func_801890FC (387 ins). For two stores at equal priority,
sched1's LUID tie picks the LAST statement's store first; regalloc then gives the other
load $v0, where it sits behind its own $v0 predecessors, and sched2 pins that chain last.
Each equal-priority pair therefore appears reversed: source FE;E-=0x40;A-=0xEE;88;8A;8C
emits as E,88,A,8C,8A.

This is why "statement order is inert" is such a common wrong conclusion here - the
mapping is not identity, so trying orders at random explores the wrong space. Compute the
intended emission order, then invert each equal-priority pair.

Companion (§193-E): the prior draft left no different-address store between `sh 0xA` and
its re-read, so cse forwarded it as `andi 0xffff`. A store to a different address between
the two re-seeds cse - §416's "re-read the store" seen from the failure side.
2026-09-02 10:22:16 -06:00
Drew T 9a94d0593d feat(decomp): main +1 - func_8005FA94 via §265 verbatim asm (gate_main, BYTE-IDENTICAL)
A §332 delay-slot wall the pinned triple cannot emit from C; banked the verbatim-asm way,
verified by one clean rebuild at 143dbb89f34491258bbc27810d0a12ec8b43a8dd. Bank confirmed
from the source (stub gone), not from the tool's own count.
2026-09-02 10:16:01 -06:00
Drew T 225f7d4ee9 docs(phase-31): frontier 150, 60 banked; main lane reopened; two gate_main defects fixed 2026-09-02 10:15:20 -06:00
Drew T b5f4bff5b2 feat(decomp): parallel gate — 4 fns across 4 binaries (10 workers)
ov_SC04_000    func_80180020
  ov_SC05_010    func_8017F15C
  ov_SC07_002    func_80183458
  ov_SC06_029    func_8017F330
2026-09-02 10:14:15 -06:00
Drew T 38a039d121 fix(gate_main): refuse a draft that contains its own INCLUDE_ASM (R43)
Substituting such a draft puts the stub straight back: nothing changes, the clean build
is trivially byte-identical, and the function is reported banked while its stub is still
in src/. That is how func_8002B0B4 was counted in this morning's "BANKED 5 of 6" when
only 4 had applied.

Refused at slate load so it fires in every mode including dry run. Negative-controlled
both directions: the no-op slate is refused by name, a real verbatim draft still passes.
Scope measured before generalising: 5 of 2,749 stored drafts, all one of two functions -
rare, but silent, which is why it is a refusal and not a warning.
2026-09-02 10:10:39 -06:00
Drew T ce671f7e14 fix(gate_main): count banks from the SOURCE, not from the slate
len(good) is "what we decided to keep", not "what was substituted". A draft whose stub
pattern does not match is a SILENT NO-OP: nothing changes, the build is trivially
byte-identical, the batch passes, and the function is reported banked while its
INCLUDE_ASM is still in src/.

Measured here: the bisect printed "BANKED 5 of 6" and func_8002B0B4's stub was still in
src/800.c - four real banks. The stub's absence is the bank oracle everywhere else in
this project; gate_main now uses it too, and names any accepted draft that never applied
instead of counting it.
2026-09-02 10:06:38 -06:00
Drew T e167c9c3cc feat(decomp): main +5 via gate_main - four §265 verbatim-asm walls and one ordinary body
The main lane, run the ONLY way main can be gated (§414): substitute the whole slate ->
make extract -> make build -> compare SHA1, with a bisect when the batch fails.
Baseline asserted green first (143dbb89...), batch of 6 failed, bisect isolated
func_8002C410 in 7 rebuilds, and the remaining 5 banked BYTE-IDENTICAL.

Four of the five are §265 verbatim-asm bodies for functions on the §332/§188 toolchain
wall list - the accepted route for a function the pinned triple cannot emit from C, the
same way func_800D0B1C banked overnight. Candidates came from scoring every stored draft
for main's 53 non-rejected open functions with match_one: 6 of 53 at closeness 0.
2026-09-02 10:05:39 -06:00
Drew T 38faa55a56 feat(decomp): parallel gate — 2 fns across 2 binaries (10 workers)
ov_SC03_105    func_801803A0
  ov_SC06_033    func_80190E64
2026-09-02 10:00:36 -06:00
Drew T a2b0a6b98f tune(draw): opus up to 340 ins, not 150 - Fable for difficulty, not length (Drew)
Drew, 2026-09-02: "use opus mainly and only escalate the difficult ones to fable".
The S69 table that set the old 150 line actually puts opus's cliff at ~350:

    m1  opus  191-347 ins   10/15 MATCH   1,291 tok/matched-ins   <-- best measured
    m2  opus  347-670 ins    1/9  MATCH   7,158                   <-- the cliff

So 150 was handing opus's STRONGEST band to Fable. Fable is now reserved for >340
instructions and for arm_from_history's compiler-internal residual signal (§413) at any
size. On the current pool that moves three functions back to opus while two stay on
Fable because their history names a scheduling/regalloc residual - escalation by
difficulty rather than length, which is the point.

Retries default back to opus: one failure is not evidence of a wall, and the history
signal lifts a target on its own if the notes justify it.
2026-09-02 09:58:03 -06:00
Drew T 1baa5ec405 feat(decomp): parallel gate — 2 fns across 2 binaries (10 workers)
ov_SC03_092    func_8017FA74
  ov_SC03_028    func_80181000
2026-09-02 09:35:36 -06:00
Drew T 11862e3c22 docs(phase-31): gate 10 (+3), frontier 162, 48 banked; §420-§423 harvested 2026-09-02 05:51:08 -06:00
Drew T f778dd3f6a feat(decomp): parallel gate — 3 fns across 3 binaries (10 workers)
md_SC07_003    func_801A293C
  ov_SC01_001    func_8017FEE0
  ov_SC03_105    func_80187A30
2026-09-02 05:50:05 -06:00
Drew T 5ce3704e21 docs(cookbook): §422 QImode arithmetic + flag^1 temp; §423 the "cause not determined" typedef collision
§422 (resident/func_800D06E8, 344 ins): mips.h has no PROMOTE_MODE, so a u8 local is
really QImode - `(u8)(c-3) < 2` gives widen_operand a paradoxical subreg (raw $a0) and
combine.c:9246 drops the truncation before the sltiu, while `(u32)(c-3)` re-widens and
loses it. And `s1 = (cmp) ^ 1` puts the comparison in the destination pseudo; the flag
needs its own temp.

§423 (ov_SC03_092/func_8017FA74): match_one closeness 0 + gate reject + "cause not
determined" has a checkable cause. harvest_verify splices the draft where the
INCLUDE_ASM stub was; a file-scope typedef the TU also defines (five lines below, here)
makes gcc-2.7.2 exit 33 on a C89 redefinition, the TU fails, the gate reverts, and
nothing names the typedef. Fix is block scope, proven with a three-way control. Mirror
of §409 law 2 - together they cover collisions above and below the splice point.

Tool trap: a scratch TU copy needs -Isrc/<overlay> or cpp exits 1 on the relative
engine_core.h include and EVERY variant passes identically.
2026-09-02 05:49:58 -06:00
Drew T a10613fa4a docs(cookbook): §421 - a la $tN + addiu pair can be a RELOAD artifact no C spelling reaches
Byte-proven on md_SC07_003/func_801A293C (313 ins, 6 -> 0 in 23 compiles). $t0 there is
reload's scratch (regalloc.md L80: first wholly-unused call-clobbered reg), not an
allocation: a launder output goes to $v0 via local-alloc, and any $8 pin evicts every
mfhi/reload to $t1 (measured 3x). The reach is §385's single-instruction non-volatile
asm with the bare symbol as an INPUT.

Also: spelling an addPrim RMW as a libgpu P_TAG 24-bit addr bitfield makes
store_fixed_bit_field mask the value first, hoisting 0xFFFFFF before 0xFF000000 and
closing both the lui/ori and lw-swap clusters at once (6 -> 3); and an in-place `and $s0`
needs `s0 = s0 & 0xFFFFFF` written out, not the compound form.

The law: before spending iterations on a register you cannot get, ask whether it belongs
to regalloc at all - reload runs after every dial C gives you.
2026-09-02 05:42:42 -06:00
Drew T 1c932c61b8 docs(phase-31): +4 via multi-cluster symbol rebase; frontier 165, 45 banked 2026-09-02 05:38:45 -06:00
Drew T 2cedd19aaa fix(symfix): key the slate by (binary, fn); §420 multi-cluster rebase banks 4 in 57s
aprop_symfix deduped its slate by BARE FUNCTION NAME, so a four-row slate for
func_8016AB6C across ov_SC03_107/ov_SC07_007/010/011 reported "1 drafts audited" - and
the three dropped rows each needed a DIFFERENT rebase, because each overlay has its own
target symbols. Same root as reloc_filter's binof and gate_lane's homonym staging: three
tools, one R48/§238 defect.

With all four visible, the structure is two uniform delta clusters of two, identical in
shape across all four overlays - one seed body's two data clusters each moving as a
block. STALE-DELTA only admits ONE cluster, so it refused all four as AMBIGUOUS. §420
records the safe generalisation (runs of constant delta, every run >= 2 members, D_
symbols only) and the verification step.

Rebased by hand under that rule, all four still MATCH at closeness 0, and the gate
banked 4/4 in 57 seconds with no drafting (commit:3629). Frontier 165, 45 banked.
2026-09-02 05:38:28 -06:00
Drew T 24d5017039 feat(decomp): parallel gate — 4 fns across 4 binaries (4 workers)
ov_SC03_107    func_8016AB6C
  ov_SC07_011    func_8016AB6C
  ov_SC07_010    func_8016AB6C
  ov_SC07_007    func_8016AB6C
2026-09-02 05:37:23 -06:00
Drew T f31f56ef17 docs(phase-31): the drafting pool emptied; the carve lane replaced it (+5, frontier 169)
Wave 3 drew 1 target - 0 left in pool. Of 174 open: 64 main, and of 110 non-main, 41
drafted this session, 68 excluded, 2 walls, ZERO undrawn. Re-probing the 68 with
jtbl_carve --probe found 17 now reporting `tail`, because tonight's jr_isolate_all
fixes changed their overlays. All 17 already had drafts; 10 scored closeness 0 with no
drafting. The gate banked 5, and they are exactly the five overlays jr-isolated tonight.

An exclude list is a snapshot of what the TOOLING could not do and goes stale the moment
the tooling improves - re-probe it after every tool fix.
2026-09-02 05:34:38 -06:00
Drew T c0e8731947 feat(decomp): parallel gate — 5 fns across 5 binaries (10 workers)
ov_SC03_010    func_8017F6C0
  ov_SC03_029    func_80186A34
  ov_SC03_013    func_801806F8
  ov_SC03_092    func_8017FE88
  ov_SC07_000    func_8017F8B8
2026-09-02 05:33:39 -06:00
Drew T 9bb43dd426 docs(phase-31): S71 gate 9 (+2, frontier 174, 36 banked); §419 density lever recorded 2026-09-02 05:19:10 -06:00
Drew T ca813cb5ab feat(decomp): parallel gate — 2 fns across 2 binaries (10 workers)
ov_SC01_000    func_8017DD04
  ov_SC03_028    func_80180B44
2026-09-02 05:18:24 -06:00