- byte-proven: 0 drafts declare an extern struct/union data conflict; the lone apparent
DATA-conflict (func_8016A8FC/D_800AE620) was a typedef-REDEFINITION (Blk20 inline vs
engine_types.h, same type) masking the def-sig loose-typing wall. Stripping the inline
typedef compiles but still byte-mismatches. data-cast = code for 0 cases -> not built.
- cookbook §20 DATA-conflict note corrected; fleet unchanged 58.82%.
- byte-proven: only 1/134 overlays shares ov_SC01_077's -O0 cluster bytes; the 6 matched
-O0 fns reference per-overlay data addresses (func_8013B568 -> D_80187270 in ov_SC01_077
but 0x80182B04 in ov_SC01_005), so they're overlay-LOCAL, not ×134-shareable. The
Phase-18/19 'reach-134' label conflated function-present with byte-identical (R14).
- the §18 split infra (rollout tool + Makefile %_o0.o pattern + PROVIDE .ld) was built and
byte-validated to compile/link a 2nd overlay; the whole-binary gate then exposed the
per-overlay-data wall -> reverted (the finding is the deliverable, P9).
- -O0 rollout = ×1 per-overlay-unique re-matching (~0.2-0.3%), NOT +0.6% free -> SKIP.
cookbook §18 corrected; fleet unchanged 58.82%, 136/136 byte-identical.
- tools/cast_call_sites.py: per draft, callee whose canonical TU sig != draft's
intended sig -> decl->canonical (kills in-TU conflicting types, keeps symbol in
scope) + call-site cast ((ret(*)(args))func_X)(args) (codegen-neutral: gcc folds
the cast of a known symbol -> direct jal). Pure --in/--out; whole-binary gate is
the arbiter (G3/P9). Pipeline: canon_resident_calls -> cast_call_sites ->
sig_unify -> harvest_verify --chunk 1
- recovered 6 of T6 batch-1's 33 gate-fails byte-identical (func_80153C44/8015CF58/
801711FC/80161BE0/801683D8/8015F948); 5 propagated x134 + 1 local; check-all 136/136
- R14 CORRECTION of cookbook §20's '~33' projection (byte-proven): the batch is NOT
mostly callee-conflicts — ~6 callee (cast-recovered), ~18 DEF-side loose-typing wall
(def's matchable sig conflicts with the unchangeable caller-canonical; caller-side
fix BLOCKED because INCLUDE_ASM declares no symbol -> a shared macro's internal
extern is load-bearing in the 133 stub overlays), + a small DATA tail. The def-side
needs re-drafting under the caller-canonical sig (future wave), not a text transform
- cookbook §20 RESOLUTION + SETUP tool inventory (R16/R21/R30); CURRENT_PHASE log (R28)
- fleet 58.63% -> 58.82%, dedup 1501 -> 1508, 0 NON_MATCHING (G4)
- Drew (2026-06-21): cookbook entries / byte-verified findings / distillations / doc corrections
must be written DURING the producing session (fresh sessions get only summaries, lose the detail).
Defer only mechanical/continuable work. Extends R16. Memory: capture-knowledge-before-fresh-session.
- byte-verified: the 33 wave drafts that match_one-MATCH but fail the whole-binary gate are the
loose-typing wall in the CALL GRAPH (a callee declared with conflicting types across overlay
sites -> draft extern hits in-TU 'conflicting types'); match_one over-predicts (standalone +
masks jal/hi/lo). All recovery levers fail (sig_unify/canon_draft_decls/fix_arity/no-proto/
strip-externs); the only fix is per-site fn-ptr casts (SS17a) = Phase-21 recovery tooling
- diagnostic lesson: a failed in-TU build leaves a STALE .o (trust the whole-binary SHA, not objdump)
- build_engine_types.py --strip: lifted 48 named types -> src/shared/engine_types.h
(byte-neutral, ov_SC01_077 d19c9580); unblocked +4 struct-using matches -> propagated ×134
(incl. func_80147514, a T2 match freed by the lift)
- check-all 136/136 BYTE-IDENTICAL (R22); fleet 57.93% -> 58.00%; phase total 56.64% -> 58.00%
- KEY FINDING: propagation (not matching) now caps the fleet-% yield — ~16 banked matches stay
ov_SC01_077-local because their bodies use typedef'd/anonymous/sibling local types that the
named-struct-only build_engine_types can't lift (dedup_propagate compiles_standalone rejects).
Phase-20 lever: extend the type-lift to typedefs -> recovers those ×134 for ~0 agent tokens
- R14 finding: the 3 Phase-18 "implicit-int / propagate-first" near-misses are NOT
implicit-int (reproduced each through the byte-gate). Real classes:
(a) resident-callee LINK-miss: draft calls func_<ADDR> but the addr has a curated
name (0x8004CFEC = ratan2) -> "undefined reference to func_8004CFEC". NEW
tools/canon_resident_calls.py rewrites func_<ADDR> -> curated name (pure draft
text, body bytes unchanged) -> run FIRST in the recovery pipeline.
(b) shared-caller ARITY conflict: an engine_core.h caller macro declares the callee
file-scope `extern void func_X(void)` then calls func_X(); the real def takes an
arg -> "conflicting types". FIX = caller extern -> no-proto `extern void func_X();`
(byte-neutral for the caller; compatible with promotion-safe params int/long/ptr).
- 3 matches banked in ov_SC01_077: func_8017209C (ratan2 rewrite), func_80147514 (s32),
func_80168F40 (void*); 2 no-proto lines in src/shared/engine_core.h
- recovery pipeline: draft -> canon_resident_calls -> sig_unify -> harvest_verify --chunk 1
- make check-all 136/136 BYTE-IDENTICAL (full extract, R22); dedup 1450 validated/0 failed;
0 NON_MATCHING in any default build (G4); fleet 56.64% (the 3 are ov_SC01_077-local,
x134 propagation rides T3/Phase-20)
- cookbook §17a-3 corrected (R14/R16); SETUP tool inventory (R21); CURRENT_PHASE T2 done
- config/splat.ov_SC01_077.yaml: 3-object code split (ov_SC01_077_a before /
ov_SC01_077_o0 -O0 cluster / ov_SC01_077 after). A single object's .text cannot be
split around a middle object, so before/after are distinct objects; the after-region
keeps the ov_SC01_077 name (bulk matched C + asm paths unchanged).
- Makefile: target-specific CC1FLAGS:=-O0 for ov_SC01_077_o0.o (src/boot.c precedent).
- src/ov_SC01_077/ov_SC01_077_a.c (new before-region) + ov_SC01_077.c (after-region) +
ov_SC01_077_o0.c (new -O0 cluster): 6/16 -O0 fns matched byte-perfect
(func_8013B568/B7F4/BD34/C360/C938/C964).
- DEFERRED to Phase 20 (Drew, on the discovered difficulty): the 10 remaining -O0 fns hit
an indexed-global %lo-folding codegen quirk (gcc-source research, R17) + the x134
per-overlay rollout (engine_core.h compiles -O2, cannot carry -O0 fns). Corrects the
Phase-18 backlog premise (afternoon/free-x134) per R14. Documented in ov_SC01_077_o0.c.
- verified clean: main 143dbb89, resident 8e17e02f, ov_SC01_077 d19c9580 + 2 overlays.
- 🚨 top banner: T0-T5 'unsteerable' framing SUPERSEDED; resume 3->1->2 at Step 3b
with /effort ultracode ON; read cookbook §17 (the corrected TOOLKIT) first
- ▶ STEP-3b RUNBOOK: the §17 toolkit summary (reconstruct/array-decay/PINS/barrier +
the MANDATORY canonical-retype gate gotcha + stub-only narrow-param), the exact gate
pipeline + commands (match_one/harvest_verify/dedup_propagate, ov_SC01_077 good-sha
d19c9580), the durable worked examples (engine_core.h DEFINE_func_8012B8E4/801399A8),
the staged assets (harvest_wave_s4.js, probe_targets_s4.json, .run/ghidra_c/), fleet
baseline 55.58%/136-of-136, and the Step 1->2 close path
- everything a fresh full-context session needs to author the upgraded wave + run it
- §17 rewritten: the call-crossing register-ORDER class is MATCHABLE with register
__asm__ pins + a scheduling barrier (byte-proven func_8012B8E4), not unsteerable.
The wall was a missing lever (the pins), not an impossibility. Recipe documented as
a TOOLKIT: triage with match_one -> reconstruct / array-decay / PINS / barrier.
- §16 cross-refs + hand-matching §8e corrected to match. The ONLY genuine stub-and-skip
left is the narrow-param loose-typing conflict (func_80146A6C).
- self-correction (P9/R14): I concluded 'unsteerable' before trying the pins; Drew's
hand-match-everything directive exposed the gap. Recorded honestly.
The 'unsteerable' regalloc-order class is NOT unsteerable. Drew's directive (hand-match
everything) exposed the lever I'd skipped: explicit register pinning.
- func_8012B8E4 (the flagship circular reach-134 'unsteerable' fn): 21 -> MATCH via
* register pins: register s32 d __asm__("$16"); register s32 s1ang __asm__("$17")
-> FORCES the $s0/$s1 global allocation gcc otherwise density-orders the other way
* branch-polarity invert (§3-T4): 24->21
* explicit clamp temps (t=u6+0x1000; iVar4=u5-t): fixes the reassociation, 7->3
* scheduling barrier __asm__ __volatile__("" : : "r"(u5)): anchors the last sll, 3->MATCH
- byte-gated (harvest_verify, G3/P9) + propagated x134 (per-overlay gate); 134 byte-identical
- recipe = §10-family hand technique (pins + barriers); labor-intensive (per-fn register map
from the asm) but it WORKS -> the circular tail IS hand-matchable, not asm-only
- supersedes the T1-T5 'unsteerable' verdict; cookbook §17 + PhaseEnd to be revised accordingly
- the build-divergence escape is closed without the heavy Wine/CC1PSX run:
* gcc-2.7.2-psx byte-matches ~700 fns (incl. call-crossing callee-saved values)
-> its global allocation IS faithful to the original compiler -> real CC1PSX
would emit the SAME unsteerable swap
* cdk cc1 tested directly: diverges but WORSE (32 vs 21) -> cc1-switch doesn't crack it
* Xenogears (real-era toolchain) ships the class as asm
- Wine is a heavy install here (106 pkgs + i386 + wineprefix); low marginal value ->
deferred per the plan escape clause; binaries staged at tools/psyq/psyq4.0/ if wanted
- R22: clean-rebuild of ov_SC01_077 with the match = d19c9580 BYTE-IDENTICAL
- all Phase-18 tasks complete; ready for gate-2 milestone confirmation
- T6 2nd attempt (func_80146A6C): 18=18 ins, 8 mismatched, blocked by the Phase-16
LOOSE-TYPING wall (target loads a4 as s16/lhu; canonical engine_core.h sig declares
a4 s32/lw -> no consistent type). So STRUCTURAL_MISS is mixed: some close, some walled.
- T4: per-class idiom verdicts set (for-loop+statement-order+sig_unify byte-gated to a
full match; branch-polarity + array-decay-remat = real codegen fixes via match_one;
regalloc-order byte-proven unsteerable)
- CURRENT_PHASE updated (crash-recovery log)
- T2 (Xenogears mine, 2 bg agents): independent decomp on IDENTICAL gcc-2.7.2-psx -O2
has NO C lever for the call-crossing $s0/$s1 ORDER class; ships it as INCLUDE_ASM
(1174 nonmatch). Their idioms: variable-split, width/cast, per-file compiler-switch.
- flag/cc1 sweep (func_8012B8E4): -funsigned-char/-fpcc-struct-return/-fpeephole/
-fcaller-saves all 21 (no change); cdk worse (32); -fno-schedule-insns worse (71)
- T3 verdict: regalloc-ORDER class = UNSTEERABLE from C (global.c density ordering;
exhaustive empirics + independent corroboration)
- NEW IDIOM found on func_8012B4B8 (remat class): array-decay forces rematerialization
(s32 mtx[8] passed as decay -> remat not hoist; fixes 88->52 incl. the hard regalloc).
&struct/.w/*(M8*) all hoist. Bankable; this fn has a struct-copy conflict so partial.
- synthesis: .run/p18/T2_T3_synthesis.md; reconciles §10-vs-§16 for T5
- strategic redirect: high-reach circular tail largely unmatchable from C -> match-%
lever is the tractable-247 wave (Phase 19) + accept the tail as asm (Xenogears policy)
- replayed §10 levers on func_8012B8E4 under match_one (the floor-free oracle):
- §3-T4 branch-polarity invert: 24 -> 21 mismatched (a real §10-family win the
permuter's floor-polluted score could not measure in Phase 17)
- statement-order: no effect; coupling s1ang+u6: regressed to 71; -O3: identical alloc
- ROOT pinned via the cloned gcc-2.7.2 source: the residual $s0<->$s1 swap is two
ratan2-call-crossing pseudos allocated by global.c:allocno_compare (density =
log2(n_refs)*n_refs/live_length), NOT local-alloc; short-lived s1ang wins $s0
- GATE = NO branch (§10 helps but doesn't close) -> T3 sharply scoped + mechanism-pinned;
func_8012B4B8 (hoist-remat+struct) baselined (88 mismatched), deferred to T3/T4
- CURRENT_PHASE updated; findings in .run/p18/ (gitignored scratch)
- canonical-sig layer (session 4): tools/census_conflict_callees.py + derive_canonical_sigs.py
-> a 20-extern byte-neutral block atop ov_SC01_077.c (LOCAL, not engine_core.h); census
conflict callees 20->0, blocked targets 24->0; gate pipeline now draft -> sig_unify (MANDATORY)
-> harvest_verify --chunk 1; fleet 136/136 byte-identical (R22), 55.51% (no regression)
- FINDING (R14/P9): the conflict wall is 7%-reach not ~2x; the 4 reach-134 circular targets are
ALL gcc-quirk/regalloc/layout-bound (0 banked); the high-reach core IS the quirk tail; struct
types are byte-neutral for matching (the wall is gcc codegen, not knowledge)
- leverage analysis: fleet % is function-count-weighted (size adds no %); "unblock many" = the
layer (declaration, not matching); reach is the lever (already reach-sorted); 247 tractable
reach-134 stubs ~ +3-4% projected
- GO/NO-GO: NO-GO on brute waves at the current ceiling; GO on a compiler-quirk research phase
(read gcc-2.7.2 source + Xenogears + the §10/regalloc classes, R17) -> then resume the wave
- docs: cookbook §16 corrected + hand-matching-process.md §8 (the layer + the finding + handoff)
- worklog archived -> phase-ends/logs/Phase17.md (R19); bumps 1.15.0 -> 1.16.0
- CURRENT_PHASE.md: new session-4 START HERE (build the canonical-sig layer, then
scale the wave); old demo-protocol START HERE marked superseded
- hand-matching-process.md S7: the Ultracode harvest wave (pipeline, calibration
result 60% match_one / 33% whole-binary / +0.47%) + THE CANONICAL-SIG WALL (the
60->33 gap is 100% sig conflicts; surgical per-callee canonicalization = ~2x lever)
- matching-cookbook.md S16: guided-hand-matching breakthrough + new idioms (mask-local,
shared-ret0 goto, v0/v1 coalescing) + the wave/canonical-sig finding (R16 flywheel)
- SETUP.md: DecompileFunctions.java (headless batch Ghidra-C pre-pass) in the inventory (R21)
Capture the strategic pivot (Drew, 2026-06-19) and everything the fresh session needs:
- NO-GO on the 5-day brute force; GO on guided hand-matching (whole-binary-gated, ×134/match)
- the demo protocol (prove the loop on a medium struct-using fn, then a giant)
- THE CRUX: gate whole-binary, not match_one (isolated masks relocations -> doesn't transfer)
- the actor struct (base 0x80078E00 + confirmed fields + files) as the hand-matching asset
- tooling map, candidate targets, fleet-extract note
- deferred & decided (CUDA/ML = research wildcard not the plan; emulator naming paused)
- effort=Max for hand-matching (re-set, session-only); /mcp after the SessionStart restart
After the demo + PhaseEnd: plan-mode a whole-binary-gated permuter driven by Ghidra-C.
The permuter closes isolated near-misses (3-4/8 in <=75s; converted winners match_one
MATCH 17/53/54 ins) but 0/4 survive the whole-binary gate. Root cause (R17): the isolated
compile context differs from the whole-binary TU (callee inlining in the one-big-file
077.c), so match_one's "isolated == whole-binary" assumption is false for these and the
permuter optimizes the wrong target. This is the mechanism behind Phase 16's "42% mirage".
All 5 avenues tested: T2=0, T3=+0.52% (banked), T4/T5 byte-neutral, T6=0. The harness
ceiling did not rise to eureka. NEXT: T7 go/no-go (Tier-1/Max).
- recovered the engine actor/player struct cold via PCSX-Redux (R10/R11): base 0x80078E00,
~154 fields over 0x24C bytes, 32 pointer fields; HP/BP/gold/day/hour/position/BP-drain all
live-verified and anchored to the Phase-3 player state + the walk diff. -> docs/actor-struct.md
- tools/ram_probe.py: PCSX-Redux RAM capture/diff/read/ptr-scan workhorse for field-typing
- FINDING (the matching verdict): feeding the struct to m2c as --context is BYTE-NEUTRAL.
m2c adopts it (arg0->f_X) but structural-miss sample scores 0 better / 10 same / 2 worse vs
m2c-alone (full code+RAM pointer set). The misses are 50-98% mismatched = m2c decompiler
limits, not missing types. T4 fn-ptr context unblocks compilation but functions stay structural.
- conclusion: rich context (struct/type recovery) is a comprehension / decomp.me win, NOT a
byte-match lever -> Phase-16 wall re-confirmed from the type angle. struct-core-pivot.md updated.
- emulator field-naming pass deferred (comprehension, off the matching path); NEXT: T6 permuter
validation on the 146 genuine near-misses, then T7 go/no-go.
T4 in progress: proved the m2c --context fn-ptr-table mechanism, mapped byte-safe
shared-header integration (163 ground-truth + 186 used tables), found the fn-ptr fix
unblocks compilation but the byte-lever is T5's actor struct -> build T4 generator +
T5 struct together. No code artifact yet (probes in .run scratch). Paused for Drew's
PCSX-Redux emulator field-typing (T5) scheduling. Tree clean; all binaries byte-identical.
- banked 42 gate-proven SIG_FIXABLE_KR matches in ov_SC01_077 (of 48 predicted; 6 honestly
reverted by the whole-binary byte-gate, P9) + propagated 13 h_exact-shared x134 via
dedup_propagate (one body -> 134 overlays, each byte-gated, fail-closed)
- fleet byte-identical-from-source 54.48% -> 55.00% (+0.52%); REAL +1,778; dedup groups
1395 -> 1408; make check-all 136/136 byte-identical (R22); dedup-check 1408 validated/0 failed
- wall probe (R14): raw drafts 0/99 + sig_unify fails -> the K&R/sig_unify avenue is exhausted;
the residual conflict-class is ARITY_WALL (the §14e param-count dead-end) or structural (T4/T5)
- FIX a T1 classifier bug surfaced by verifying the wall vs the bytes: tu_compile_error captured
the baseline's pre-existing byte-NEUTRAL warnings ("makes integer from pointer", cc1 rc=0) and
mislabeled 99 fns LOOSE_TYPING_WALL; now keeps only non-warning errors -> there is NO loose-typing
wall, the real residual wall is ARITY_WALL=74. docs/wall-taxonomy.md amended with the correction.
- 134 overlay .c files carry the propagated DEFINE_func instantiations; a one-time full-fleet
make extract was required for the per-overlay propagation gate + check-all
- PhaseEnd_Phase16.md: the loose-typing wall (byte-proven), the kept harness fixes, the unattended
driver (built/not-deployed), and the new research directions (emulator -> Ghidra type propagation).
- CURRENT_PHASE.md archived -> logs/Phase16.md (R19).
- Negative-but-decisive result (Phase-3.5 precedent); bumps 1.14.0 -> 1.15.0.
- docs/struct-core-pivot.md: findings + decision + new research directions. Root cause = the
original engine is LOOSELY TYPED (K&R; same fn called with int/ptr, arg/no-arg across sites),
so no single canonical signature exists -> m2c guesses inconsistently, permuter can't fix
semantics, byte-gate (correctly) rejects. Yields ~3%, not the crack. New plan: emulator-recover
the actor struct/types -> Ghidra global type propagation -> Ghidra-C -> permuter+gate.
- harness bug-fixes (REAL, kept): p16_permute output-0-only match (killed the false '42%'),
base.c keeps callee externs, winner_to_draft line-strip; sig_unify canonicalizes m2c's
no-extern prototypes; gen_engine_decls.py (documents why a global canonical header breaks
loose-typed matches).
- a few byte-gated leaf matches banked in ov_SC01_077.c.