# ADR-0020 — Phase 5 / Rust Parity Sequencing **Status:** Accepted (2026-05-16) **Date:** 2026-05-16 **Authors:** Joshua Shay **Depends on:** ADR-0016 (Capability Roadmap), ADR-0019 (Exact Vault Recall Acceleration), `docs/PROGRESS.md` Phase 4 exit memo. ## Context Phase 4 exited 2026-05-16 with three lanes shipped (sample_efficiency, long_context_cost, multi_agent_composition) and ADR-0019 Stage 1 vectorising vault recall. Two non-trivial axes are now unblocked: - **Phase 5 — Curriculum Era.** Open-ended domain acquisition (English fluency v5 OOD, Hebrew, Koine Greek, elementary mathematics, foundational physics/biology, classical literature). Stresses the runtime *as it stands today* on semantic breadth and pack scale. - **Rust backend parity port.** CLAUDE.md sequencing rule 5: *"Add Rust backend parity only after Python semantics are locked by tests."* Phase 4 just locked vault recall semantics with bit-identity tests; the prior Phase 1–3 work locked algebra closure, intent classification, articulation, teaching, and trace hashing. The blocker is dissolved. The question is **what order to take these on**. Three positions are credible: ### Option A — Phase 5 first, Rust parity later Open Phase 5 now. Drive curriculum work on the Python runtime. Defer Rust until Phase 5 surfaces a concrete bottleneck that indexing/vectorisation cannot dissolve. - **Pro:** Maximum focus on capability expansion. Phase 5 is where CORE proves its end-goal claim (listen → comprehend → recall → think → articulate → learn → replay) on real domains. Every additional language / domain pack is a load-bearing capability bet. - **Pro:** Python is currently fast enough. Stage 1 vault recall is ~20 ms at N=10⁵. No measured Python bottleneck blocks Phase 5 work today. - **Con:** Phase 5 will balloon the test surface. Re-running Phase 1–4 lanes on every release (per the roadmap) will get slow. CI feedback latency directly governs willingness to refactor — a slow loop encourages unsafe shortcuts that CLAUDE.md explicitly warns against. - **Con:** Each new pack ships with Python-only semantics. When Rust parity lands later, every pack's behaviour will need re-verification against the Rust path — more locked surface to re-lock. ### Option B — Rust parity first, then Phase 5 Port the Python backend surfaces to Rust before opening Phase 5. Lock parity with bit-identity tests at every ported surface. - **Pro:** Phase 5 then starts on a faster substrate. Every curriculum eval re-run is cheaper. Faster feedback supports the eval-driven discipline the project rests on. - **Pro:** Locks parity at the smallest possible surface area. Today the locked Python surface is finite and bounded; after Phase 5 it will have grown by every pack, every curriculum, every new operator. Porting later means porting more. - **Con:** Rust port is itself a non-trivial project. Done poorly it introduces a parallel backend whose drift from Python is a constant source of incident. CLAUDE.md already treats Rust as opt-in for a reason — `CORE_BACKEND=rust` must remain explicit and the Python path must remain the deterministic default. - **Con:** Delays the capability story. No new curricula ship until parity is done. ### Option C — Parallel: Phase 5 curriculum + Rust parity in independent tracks Open Phase 5 on the Python runtime. In parallel, port one backend surface at a time to Rust, gated by bit-identity tests, without making Rust the default. Phase 5 curricula run on Python until Rust parity is proven per-surface. - **Pro:** Both axes progress. Phase 5 capability bets land on schedule. Rust parity grows incrementally, surface by surface. - **Pro:** Bit-identity gating means a Rust regression cannot silently corrupt Python-validated runtime behaviour. The Rust path is purely an acceleration; the Python path remains the source of truth. - **Con:** Two contexts to hold. Demands discipline about which surface is being touched at any given time. - **Con:** Mid-Phase-5 backend swap (per-surface enablement of Rust path) is a real operational complexity that needs careful tooling to keep replay determinism intact. ## Recommendation **Option C — parallel, with explicit ordering.** Concretely: 1. **Open Phase 5.1 (English fluency v5 OOD) immediately.** This is the natural successor to Phase 3 v2 grammatical- coverage and to ADR-0018's articulation operators. It does not depend on Rust. 2. **In parallel, open a Rust-parity track.** First port: `vault_recall` — the surface ADR-0019 Stage 1 just locked with bit-identity tests. Port is gated on byte-equal scores and identical top-k ordering against the Python path on a wide fixture vault. No Rust enablement on `main` until the bit-identity test passes under `CORE_BACKEND=rust`. 3. **Second port:** `geometric_product` and `versor_apply`. These are the hottest algebra paths; bit-identity is testable against the existing Python closure. Locked by Phase 1–3 algebra suite. 4. **Third port:** `cga_inner` (drop-in replacement now that the diagonal-metric kernel is the source of truth). 5. **Defer:** propagation, teaching, trace hashing — these are Python-shaped semantics with relatively low computational weight; port only if Phase 5 evidence demands it. Phase 5 may also unlock the deferred scope decisions in PROGRESS.md ("Code generation" before Phase 5, "Embodiment" during Phase 5). Those are separate ADRs; this one only governs sequencing. ## Decision **Option C — parallel, with explicit ordering.** Confirmed 2026-05-16. ## Consequences - `docs/PROGRESS.md` opens Phase 5 with "Status: IN PROGRESS" on the same date this ADR is accepted. - A new ADR (numbered after this one) opens to document the Rust parity contract per-surface (test discipline, parity gate, default-off enablement, replay determinism preservation). - The Rust track produces no new runtime behaviour — only faster execution of behaviour that the Python path already validates. Any divergence is a test failure, not a feature request. ## Parity status (live) | Surface | Gate file | Status | Dispatch | |----------------------|---------------------------------------------------------|-------------------|-------------------------| | `vault_recall` | `tests/test_vault_recall_rust_parity.py` | passing | enabled | | `cga_inner` | `tests/test_cga_inner_rust_parity.py` | passing | enabled | | `geometric_product` | `tests/test_geometric_product_rust_parity.py` | passing | enabled | | `versor_condition` | `tests/test_versor_condition_rust_parity.py` | passing (after f64 fold fix) | enabled | | `versor_apply` | `tests/test_versor_apply_rust_parity.py` | passing (f64 port, 29× over Python) | enabled | ### `versor_apply` f64 parity port — landed The Rust `versor_apply_closed` diverged from Python on two axes caught by the bit-identity gate: 1. **Precision** — Rust folded the sandwich (V·F·rev(V)) in f32; Python in f64. 2. **Closure structure** — Rust had a null-vector early branch and no post-unitize `versor_condition < 1e-6` recheck; Python is the inverse (no null branch; recheck + seed-rotor fallback). Resolution: a new `versor_apply_closed_f64` in `core-rs/src/versor.rs` performs the full sandwich + closure in f64, mirroring Python's `_close_applied_versor` exactly (no null branch, post-unitize condition recheck, seed-rotor fallback). Exposed through PyO3 as `versor_apply_with_closure_f64`; `algebra/backend.py::versor_apply` routes through it under `CORE_BACKEND=rust`. Parity gate re-enabled — 8/8 bit-identical. Bench: **29× over Python** (213µs → 7.4µs per call on the runtime hot path). ## What this ADR does NOT decide - Which Phase 5 curriculum to open *second* (Hebrew vs. mathematics vs. physics). Separate scope call once 5.1 ships. - The Rust crate layout / dependency choice. That belongs in the per-surface Rust parity ADR, not here. - Whether to invest in a third backend (e.g., GPU / JAX). Out of scope until both Python and Rust paths are mature.