From a741d2b6b5a6862ca67a0dbd246320e166e08e4d Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sat, 20 Jun 2026 15:05:52 -0600 Subject: [PATCH] feat(phase-18): compiler-quirk research -> the matching TOOLKIT, taught to waves (v1.17.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RESEARCH (T0-T7): read gcc-2.7.2 source (gcc-papermario) + mined Xenogears (our exact compiler) -> per-class byte-gated verdicts. VERDICT REVERSAL (Drew "hand-match everything"): the call-crossing register-ORDER class is STEERABLE via `register __asm__` pins + a scheduling barrier (func_8012B8E4, the flagship "unsteerable" fn, byte-gated) -- not unsteerable. - TOOLKIT (cookbook §17): pins+barrier (register swap), array-decay (hoist-vs-remat), for-loop/statement-order (structural), sig_unify-mandatory; narrow-param = honest stub. §16 reconciled (§10-vs-§16). - TAUGHT TO WAVES (§17a): calibration 9/16=56% whole-binary (vs 33%); Step-1 (embedded canonical callee sigs + call-site-cast/re-validate) 28/31=90% match_one, 22 verified. ~31 reach-134 matches banked, each propagated x134. fleet 55.51 -> 56.64% (+1.13%). - FINDING: the match_one->gate gap is declaration plumbing (call-site casts not redeclaration; stale M2C_UNK sibling-decl reconcile; implicit-int caller class) -- recovered deterministically. - NEW residual classes: per-file -O0 (~18 fns, needs an -O0 split file like src/boot.c -> high ROI) + gcc loop-guard operand-order (irreducible). W1: build-divergence ruled out by proxy. - make check-all 136/136 byte-identical (R22); dedup 1450 validated/0 failed; 0 NON_MATCHING (G4). - worklog -> phase-ends/logs/Phase18.md (R19); bumps 1.16.0 -> 1.17.0 --- phase-ends/PhaseEnd_Phase18.md | 223 ++++++++++++++++++ .../{CURRENT_PHASE.md => logs/Phase18.md} | 0 2 files changed, 223 insertions(+) create mode 100644 phase-ends/PhaseEnd_Phase18.md rename phase-ends/{CURRENT_PHASE.md => logs/Phase18.md} (100%) diff --git a/phase-ends/PhaseEnd_Phase18.md b/phase-ends/PhaseEnd_Phase18.md new file mode 100644 index 000000000..ef9c939ed --- /dev/null +++ b/phase-ends/PhaseEnd_Phase18.md @@ -0,0 +1,223 @@ +# PhaseEnd — Phase 18: Compiler-quirk research → the matching TOOLKIT, taught to waves at scale +**Date:** 2026-06-20 · **Project Version:** 1.17.0 · **Phase Status:** Complete (milestone confirmed by Drew, gate 2) · **Generation:** Gen2 (10th phase) + +> Gen2 phase 10 of the arc (8→9→10→11→12→13→15→16→17→**18**; 14 deferred to Gen3+). Ran across **multiple +> sessions**; the granular per-task/per-session trail (T0–T7, the verdict reversal, the 5-avenue ceiling test, +> the Step-3b calibration, the Step-1 wave + recovery diagnostics) is preserved on-demand at +> **`phase-ends/logs/Phase18.md`** (R19 — NOT auto-loaded; consult only when researching a mechanism). This +> file is the synthesis. Owner decisions (Drew): pursue the research → on the "unsteerable" finding, **hand-match +> everything** (verdict reversal) → teach the toolkit to a calibration wave → **bounded ~40 Step-1 wave** +> (full tractable-247 sweep deferred to Phase 19) → close with a full Phase-19 backlog. + +## Build Log + +**Files created/changed and complete — do not recreate:** + +*The research + the matching TOOLKIT (T0–T7 + verdict reversal):* +- `tools/reference/` (gitignored, SETUP §5.6) — cloned **gcc-papermario** (`a6afc2af`, the PSX gcc-2.7.2 lineage + source) + **xenogears-decomp** (`f27c0768`, Square Oct-1998, our EXACT compiler). The primary-source corpus. +- `tools/wall_taxonomy.py` + `docs/wall-taxonomy.md` — census/size every residual class (carried from P17 T1). +- `tools/ram_probe.py` + `docs/actor-struct.md` — the actor/arg0 struct, recovered + live-verified (R10). + **Re-confirmed byte-NEUTRAL for matching** (matching reads access width off the asm opcode, not a struct def). +- `tools/ghidra_scripts/DecompileFunctions.java` — headless Ghidra-C batch pre-pass (no /mcp); feeds the + `.run/ghidra_c/` cache (300 fns) the waves draft from. +- `tools/census_conflict_callees.py` + `tools/derive_canonical_sigs.py` — the **canonical-sig layer** builders + (one byte-neutral `s32 func_X(s32…)` per conflict callee; arity from Ghidra-C + asm read-before-write $a0–$a3). + The 20-extern block at the top of `src/ov_SC01_077/ov_SC01_077.c` (LOCAL; reach-1 names differ per overlay). +- `docs/hand-matching-process.md` — §1 loop / §2 idioms / §3a 5-move signature playbook / §7 wave / §8 the + canonical-sig-layer finding / §8e Phase-18 outcome (corrected to the register-pinning toolkit). +- `docs/matching-cookbook.md` **§17** — **the matching TOOLKIT** (the phase's durable deliverable): the + call-crossing register-ORDER class is **STEERABLE with `register __asm__` PINS + a scheduling barrier** + (corrected from a wrong mid-phase "unsteerable" verdict); **array-decay** forces rematerialization; + **for-loop / statement-order** control scheduling; the **`sig_unify`-MANDATORY** gate gotcha; the + **narrow-param loose-typing** wall (stub). §16 reconciled (§10-vs-§16 tension resolved). +- `docs/matching-cookbook.md` **§17a (THIS SESSION)** — the toolkit **at wave scale** (measured close-rates) + + the **pipeline-integration recovery** techniques (call-site casts not redeclaration + re-validate; stale + sibling `M2C_UNK` decl reconcile; the implicit-int caller class) + **two NEW residual classes** (per-file + `-O0`; gcc loop-guard operand-order). +- `src/shared/engine_core.h` — the worked-example macros `DEFINE_func_8012B8E4` (pins + barrier) and + `DEFINE_func_801399A8` (for-loop/structural) + **~33 new Phase-18 reach-134 macros** (the wave harvest). +- `config/dedup.us.yaml` — **1450 validated cross-binary groups** (was ~1424 at phase start; +the Phase-18 + propagations). 0 failed. +- `src/ov_SC01_077/ov_SC01_077.c` + the **134 overlay `.c` files** — the harvest source + the ×134 + propagations; 2 recovery reconciles (func_8012A418 stale-sibling-decl; func_8012F68C parse-fix). + +*The Step-3b/Step-1 wave harness (gitignored under `.run/`, regenerable; the recipe is cookbook §17/§17a):* +- `.run/harvest_wave_3b.js` (calibration, 16 targets) + `.run/harvest_wave_p18s1.js` (Step-1, 31 targets, + +embedded canonical callee sigs +call-site-cast/re-validate). `tools/gen_harvest_targets.py` generates the + per-target callee-sig/data-decl manifest the Step-1 prompt embeds. +- `phase-ends/CURRENT_PHASE.md` → **`phase-ends/logs/Phase18.md`** (R19 archive). `phase-ends/PhaseEnd_Phase18.md` — this file. + +**Local artifacts (gitignored / regenerable):** `.run/ghidra_c/*` (300 cached Ghidra-C), `.run/drafts-*` +(wave drafts + gate-staging dirs), `.run/p18/*` (T1/T2-T3 analyses, exemplar scratch), `.run/harvest_*.js`, +`.run/*_targets*.json`, `build/`, `expected/`, `asm/`, `assets/`. `tools/reference/` (the cloned gcc/Xenogears +source). The 134 overlay Ghidra programs are script-reproducible (DB-commit deferred as regenerable). + +**Tools/packages installed:** None — the whole phase used the existing Phase-4/6/10/11/12/13 toolchain + venv. +(Wine deliberately NOT installed — W1 ruled build-divergence out by proxy; SETUP §4.8 unchanged.) + +**Verification results (literal) — the milestone proof:** +- **`make check-all` → 136 passed, 0 failed of 136** (main `143dbb89…`, resident `8e17e02f…`, all 134 overlays + byte-identical; ov_SC01_077 `d19c9580…`), reproduced from clean after every step (R22). +- **`make report`:** FLEET byte-identical **194,839 / 344,010 = 56.64%** (phase start 55.51%); **dedup-check + 1450 validated, 0 failed**; **0 NON_MATCHING** in any default build (G4). +- **The TOOLKIT, byte-gated per class:** register-ORDER → `func_8012B8E4` MATCH via pins+barrier (the flagship + "unsteerable" fn); array-decay → `func_8012B4B8` 88→52, `func_80149544/584` MATCH; structural/for-loop → + `func_801399A8` MATCH. Honest stubs: narrow-param (`func_80146A6C`), loop-guard order (`func_8012C2D0`), + §10 sched (`func_8014F2E0`), per-file -O0 (`func_8013C360`+~17). +- **The toolkit at WAVE scale (the bounded demonstration):** Step-3b calibration **9/16 = 56% whole-binary** + (vs the Phase-17 prompt's 33%); Step-1 (prompt v2: +embedded sigs +call-site-cast) **28/31 = 90% `match_one`, + 22 verified**. **Session fleet arc 55.58% → 56.64% (+1.06%, ~31 reach-134 matches; phase total 55.51→56.64 = + +1.13%).** +- `git status`: only `config/`/`tools/`/`src/`/`docs/`/`phase-ends/` tracked; **zero ROM-derived/generated bulk + staged**. **No Ghidra DB change** this phase (matching used cached Ghidra-C + asm; the `db.*.gbf` churn is + SessionStart-restart noise — R23 no-op, do NOT stage). + +**Milestone achieved (confirmed by Drew, gate 2):** the knowledge-gated milestone — a per-class byte-gated +verdict (validated C idiom OR honest "unsteerable" verdict naming the exact gcc pass), §10-vs-§16 reconciled, +≥1 quirk-tail function newly matched, 136 binaries byte-identical throughout — is **met and exceeded**. The +phase's headline reversal: the highest-reach **call-crossing register-ORDER class is STEERABLE** (`register +__asm__` pins + a scheduling barrier), not "unsteerable" as a mid-phase draft wrongly concluded. The toolkit +was then **taught to a parallel Ultracode wave** and demonstrated at scale (close-rate 33% → 56% → 90% +`match_one`), banking ~31 reach-134 matches (fleet +1.13%). + +**Next:** **Phase 19 — Scale the toolkit (the wave-harvest at scale) + the cheap front-loaded levers.** A fresh +session, **plan mode** (Tier-1 Phase-Start). See the **Phase-19 Backlog** below for the full prioritized brief +(the -O0 split-file fix first, recovery-tooling, then 50-target waves scaling to 100+, with an optional bounded +gcc-research spike on the last residual classes). The infra is BUILT and staged. + +## Deviations +| Item | Plan | Actual | Reason | +|---|---|---|---| +| Register-ORDER verdict | T1–T5 concluded "UNSTEERABLE from C" | **REVERSED — STEERABLE via `register __asm__` pins** | I'd skipped the most direct lever; Drew's "we hand-match everything" pushed it; `func_8012B8E4` byte-gated (P9/R14 self-correction). The "brute waves won't help" point stood; the HAND pin lever does. | +| Phase scope | research + a bounded quirk-tail demo (gate-1) | research **+ a calibration wave + a bounded Step-1 wave** | the reversal made the toolkit teachable to waves; demonstrating it at scale (33%→56%→90%) is the real proof it raises the ceiling | +| Step-1 size | "scale the wave on the tail" | **bounded 31 targets** (Drew chose ~40; full tractable-247 → Phase 19) | gate-1 deferred the full sweep to Phase 19; Step 1 = a bounded demonstration of the taught wave | +| Wave close-rate gap | (not anticipated) | `match_one` MATCH ≫ whole-binary verified (12→9, 28→22) | the gap is **declaration plumbing** (call-site casts the agents skip + `sig_unify` narrowing the extern), NOT codegen — recovered deterministically (cookbook §17a) | +| W1 Wine native-compiler check | bounded Wine run | **ruled out by proxy, Wine dropped** | 700+ matches prove our cc1's regalloc is faithful; the real compiler emits the same bytes → confirms, doesn't crack; heavy install not worth it (plan escape clause) | +| Effort | Max planning, xHigh/Ultracode execution | Max/xHigh research + debugging; **Ultracode for the waves** (Drew toggled); xHigh for this close | breadth-shaped waves under Ultracode (R26/R27); deep synthesis at xHigh (Drew's saved default) | +| New residual classes | (not anticipated) | **per-file -O0** + **gcc loop-guard operand-order** surfaced at scale | the waves exposed classes the single-function research hadn't; the -O0 one is build-infra (high ROI), not a wall | + +## Commit Message +``` +(Phase 18 landed as 16 commits commit:0141..commit:0156 + this PhaseEnd.) + +feat(phase-18): compiler-quirk research -> the matching TOOLKIT, taught to waves (v1.17.0) + +- RESEARCH (T0-T7): read gcc-2.7.2 source (gcc-papermario) + mined Xenogears (our exact + compiler) -> per-class byte-gated verdicts. VERDICT REVERSAL (Drew "hand-match everything"): + the call-crossing register-ORDER class is STEERABLE via `register __asm__` pins + a scheduling + barrier (func_8012B8E4, the flagship "unsteerable" fn, byte-gated) -- not unsteerable. +- TOOLKIT (cookbook §17): pins+barrier (register swap), array-decay (hoist-vs-remat), + for-loop/statement-order (structural), sig_unify-mandatory; narrow-param = honest stub. + §16 reconciled (§10-vs-§16). +- TAUGHT TO WAVES (§17a): calibration 9/16=56% whole-binary (vs 33%); Step-1 (embedded + canonical callee sigs + call-site-cast/re-validate) 28/31=90% match_one, 22 verified. + ~31 reach-134 matches banked, each propagated x134. fleet 55.51 -> 56.64% (+1.13%). +- FINDING: the match_one->gate gap is declaration plumbing (call-site casts not redeclaration; + stale M2C_UNK sibling-decl reconcile; implicit-int caller class) -- recovered deterministically. +- NEW residual classes: per-file -O0 (~18 fns, needs an -O0 split file like src/boot.c -> high + ROI) + gcc loop-guard operand-order (irreducible). W1: build-divergence ruled out by proxy. +- make check-all 136/136 byte-identical (R22); dedup 1450 validated/0 failed; 0 NON_MATCHING (G4). +- worklog -> phase-ends/logs/Phase18.md (R19); bumps 1.16.0 -> 1.17.0 +``` + +## Rules Added This Phase +| Rule | Reason | +|---|---| +| **None (governance).** Phase 18's lessons are *findings + techniques*, recorded where the Phase-8/11/13/15/16/17 precedent puts them (cookbook, not rules): the **register-pinning toolkit** + the **call-site-cast/re-validate** + the **sibling-decl/implicit-int recovery** + the **per-file -O0 and loop-guard residual classes** → cookbook §17/§17a + hand-matching §8e. The **verdict-reversal lesson** ("never conclude *unsteerable* before trying `register __asm__` pins; an intermediate metric is not the gate") is already governed by **G3/P9/R14** and the `dont-conclude-unsteerable-try-register-pins` memory. The **probe-before-investing / bounded-calibration-before-scale** discipline is already cookbook (Phase 15) + effort-map. No new norm of conduct emerged. | A research→toolkit→demonstrate phase produces knowledge, not governance. Existing rules covered the ground and the phase reinforced them (esp. R14: the "unsteerable" over-claim was an R14 failure mode, self-corrected). | + +## PhaseEnd Changelog +**v1.16.0 → v1.17.0 — Phase 18 complete (Gen2 phase 10).** Set out to "understand gcc-2.7.2 to raise the +match-% ceiling," and delivered a **matching TOOLKIT** that does exactly that. Reading the real gcc-2.7.2 source ++ mining Xenogears (our exact compiler) produced per-class byte-gated verdicts — and a **decisive reversal** +(Drew's "hand-match everything"): the highest-reach **call-crossing register-allocation-ORDER class is +STEERABLE** with `register __asm__` pins + a scheduling barrier (the flagship `func_8012B8E4`, byte-gated), +*not* "unsteerable" as a mid-phase draft wrongly concluded (a clean R14 self-correction). The toolkit +(pins+barrier · array-decay · for-loop/statement-order · sig_unify-mandatory; narrow-param = honest stub) was +then **taught to a parallel Ultracode harvest wave** and demonstrated at scale: whole-binary close-rate **33% → +56%** (Step-3b calibration) **→ 90% `match_one`** (Step-1, after embedding each callee's canonical signature +and teaching call-site casts). **~31 reach-134 functions matched** (each propagated ×134), driving the fleet +**55.51% → 56.64%**, all 136 binaries byte-identical, 0 NON_MATCHING. The phase's reusable yield: the toolkit +itself (cookbook §17), and the **pipeline-integration recovery** insight (§17a) — the `match_one`→gate gap is +**declaration plumbing** (call-site casts, stale-sibling-decl reconcile, implicit-int callers), recoverable for +~0 agent tokens, not a codegen wall. Two new residual classes surfaced at scale: the **per-file -O0 class** +(~18 byte-exact-at-O0 functions needing their own -O0 split file — a high-ROI build-infra fix, not a wall) and +the **gcc loop-guard operand-order** residual (irreducible). W1 ruled build-divergence out by proxy (Wine +dropped). No new governance rules (techniques → cookbook). The harvest infra is BUILT and staged for Phase 19's +scale-up. + +## Phase-19 Backlog — "Scale the toolkit" (Drew's research + wave recommendations, full context) + +> Phase 19 = **scale the proven toolkit over the tractable reach-134 tail**, front-loaded with two cheaper +> levers, run as a flywheel. Detailed plan is a fresh-session Tier-1 Phase-Start task; this is the prioritized +> brief, by ROI. (Decided with Drew at the Phase-18 close.) + +1. **[HIGHEST ROI — DO FIRST] The per-file `-O0` split-file fix (build-infra, no research, no agents).** ~18 + functions in ov_SC01_077 were built **-O0** (prologue sig `21F0A003` = `addu $fp,$sp,$zero`; args spilled to + frame; load-delay nops; redundant `addu rd,rs,$zero`). Their correct C is **byte-exact at -O0** but the + overlay TU compiles `-O2`, and **gcc-2.7.2 has no per-function optimize pragma** (opt is per-file, Makefile). + Fix = give them their own `-O0`-compiled split file with a target-specific `CC1FLAGS := -O0 …` (the + **`src/boot.c` precedent**, Phase 7). ~18 fns × reach-134 for an afternoon of Makefile work. Members: + func_8013C360 + func_8013B568/B598/B6A0/B7AC/B7F4/B83C/BC7C/BCDC/BD34/BD74, func_8013C08C/C0F8/C414/C938/C964, + func_80144B9C, func_801457A4. (One agent already produced verified-correct -O0 C for func_8013C360 — see logs.) +2. **[COMPOUNDING — DO SECOND] Recovery-tooling investment between batches.** This session's deterministic + recoveries (call-site arity/void casts; stale `M2C_UNK` sibling-decl reconcile) banked matches for **~0 agent + tokens**. Fold them into the gate so the close-rate rises mechanically: teach `sig_unify` (or a post-pass) to + **auto-apply call-site arity/void casts** when a draft's call disagrees with the canonical extern, and to + **auto-reconcile stale sibling `M2C_UNK func_X();` decls** to the real sig. Also solve the **implicit-int + caller class** (3 deferred fns: func_80147514/8017209C/80168F40) — the clean path is **propagate-first** + (the def lands in `engine_core.h`, included first, so no implicit-int forms), gate-after; build a small + helper or a `dedup_propagate` mode for "matched-but-fails-in-place-gate" functions. +3. **[THE CORE — DO THIRD] Scale the toolkit waves over the tractable-247 reach-134 tail.** Setup: regenerate + Ghidra-C (`DecompileFunctions.java`, headless) for the uncached tractable fns (the `.run/ghidra_c/` cache is + only 300; this session's fresh cached+stub pool was ~27). Then run the **Step-1 wave prompt (`.run/ + harvest_wave_p18s1.js` recipe = §17 toolkit + embedded canonical callee sigs + call-site-cast/re-validate)**. + **Batch size: 50, banking a prompt+tooling improvement each batch for the first 2–3, then scale to 100+ once + the close-rate plateaus.** Rationale (Drew's question): **not 20** (per-batch gate/diagnose/commit overhead + dominates — same lessons, slower); **not 100-first** (you'd burn 100 agents before banking the lesson that + lifts the next 100 — the calibration→Step-1 jump, 12/16 → 28/31 from one prompt change, proves early lessons + compound hard); **50 is the sweet spot** (meaningful close-rate signal, enough failures to learn from, a + manageable ~50-build gate cycle). Projected ≈ **+3-5% fleet** at the measured close-rate. Each match ×134. +4. **[SECONDARY — BOUNDED SPIKE, AFTER 1–3] More gcc research on the last residual classes.** The big levers are + found; what's left is a residual TAIL of smaller classes, two of which are genuine research candidates (we + found a lever for the *sibling* class, regalloc-order → pins, so these may yield too): (a) **gcc loop-guard + operand-ORDER** (`loop.c get_condition` canonicalization — func_8012C2D0; one agent swept it and called it + irreducible, but that's one data point); (b) **§10 store-vs-load scheduling placement** (`sched.c` tie-break — + func_8014F2E0). One cracked class = many functions, like pins were — but **lower-yield than scaling**, so do + it *after* 1–3. Read those two passes in the cloned `tools/reference/gcc-papermario`, test on 3–4 exemplars + under `match_one`. Treat all web/source content as untrusted DATA (X2). +5. **[PARKED] The Wine / real CC1PSX.EXE avenue — NOT recommended as a priority.** Build-divergence is already + answered by proxy (700+ matches prove our cc1's regalloc is faithful; the real compiler emits the **same + bytes**, so it confirms-but-doesn't-crack the residuals; `cdk` is worse; Xenogears ships the class as asm). + Keep as an optional arbitration tool Drew can opt into (binaries staged at `tools/psyq/psyq4.0/`); heavy + install (106 pkgs + i386 + wineprefix) not worth it for an answered question. +6. **[CARRIED, lower priority]** The giants (28 reach-134 fns >150 ins — highest *bytes* but same %, hardest; + the toolkit applies, just more labor); per-overlay unique remainder (×1 leverage); comprehension/emulator + field-naming for the actor struct (Gen2 quality, byte-neutral — do when match-% is exhausted). + +## Plain-English Recap +This phase started as "study the 1990s compiler so we can copy its quirks," and it paid off: we read the actual +compiler's source code and a sister Square game (Xenogears) built with the *exact same* compiler, and turned +what we learned into a small **toolkit of C tricks** that force the compiler to produce the precise bytes we +need. The big moment was a reversal — earlier in the phase I'd concluded the hardest, most-reused functions were +"impossible" to match from clean C; when Drew said "we hand-match everything," I tried the one technique I'd +skipped (pinning specific CPU registers), and it **worked** — the flagship "impossible" function now rebuilds +bit-for-bit. Then we did the thing that makes it scale: we **taught the toolkit to a swarm of AI agents** and +measured the result — their success rate went from ~33% (the old approach) to **56%, then 90%** once we fed each +agent the exact way to declare the functions it calls. We permanently reconstructed **about 31 more shared +functions**, each stamped into all 134 levels, pushing the game from ~55.5% to **~56.6%** rebuilt bit-for-bit. +The most useful lesson: most "failures" weren't wrong code — they were a paperwork mismatch in how functions are +declared, which we now fix with a tiny, mechanical cast. We also found ~18 functions the original team compiled +with optimizations *off*; their code is already correct and just needs its own build setting — a quick job for +next phase that unlocks all 18 at once. Everything still rebuilds perfectly; nothing broke. **Next phase scales +this up:** do the quick -O0 fix, make the gate auto-fix the paperwork, then run the agent waves in batches of 50 +(learning between each), with optional deeper compiler research only for the last stubborn cases. The Wine +route stays parked — we already have the answer it would give. + +## 🛑 Stop Here +PhaseEnd written; `CURRENT_PHASE.md` archived → `phase-ends/logs/Phase18.md` (R19). **Drew commits AND pushes** +this PhaseEnd + any uncommitted Phase-18 work (R6/R8 — the matching commits `commit:0152`..`commit:0156` are already +landed; this PhaseEnd + the archived log are the close). No Ghidra DB change this phase (R23 no-op — the +`db.*.gbf` churn is restart-noise, do NOT stage it). Gen2 continues — do **NOT** start Phase 19 here. Start a +**fresh session** (plan mode) for **Phase 19 — Scale the toolkit** (the backlog above; -O0 fix first, then +recovery-tooling, then the 50-target waves). Keep this file forever. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/logs/Phase18.md similarity index 100% rename from phase-ends/CURRENT_PHASE.md rename to phase-ends/logs/Phase18.md