Two same-seed runs now produce bit-equal eval_summary.json, alpha_rl_train_summary.json,
and diag.jsonl (modulo wall-clock elapsed_s). The 5-phase falsification chain landed:
Phase 2 PER tree-rebuild: __threadfence is NOT a grid-wide barrier; multiple blocks
raced across sum-tree levels. Fix: Grid=(1) Block=(1024) + __syncthreads
in rl_per_tree_rebuild.cu.
Phase 2.3 cuBLAS GEMM_DFALT + TF32 default-math allowed split-K non-deterministic
accumulation at 3 sites. New crates/ml-alpha/src/cublas_determinism.rs
applies CUBLAS_PEDANTIC_MATH via FOXHUNT_DETERMINISTIC env toggle
(0=TF32 prod, 1=PEDANTIC dev default, 2=DEFAULT_MATH control).
Phase 2.6 Two bugs surfaced sequentially in the backward kernel chain:
(1) rl_iqn_tau_cos_features had a multi-block r/w race on prng_state[batch]
— all N_TAU=32 blocks read seed; only tau_idx==0 wrote back; no
inter-block barrier. Fix: split into READ-ONLY rl_iqn_tau_cos_features
+ new sibling rl_iqn_advance_prng_state launched on same stream
(kernel-launch ordering = grid-wide barrier).
(2) OutcomeHead::new called near_zero_xavier without scoped_init_seed,
falling back to time+thread-id RNG. Stayed dormant until first done
event activated non-sentinel labels and divergent weights flowed via
grad_h_t_outcome into encoder gradient. Fix: add seed param + install
scoped_init_seed(dqn_seed.wrapping_add(0x0CE0)) guard.
Validation (./scripts/determinism-check.sh --quick, RTX 3050, b=128, 200+50 steps):
- All 200 rows of checksums.* leaves match (rel-tol 1e-5, abs-tol 1e-7)
- eval_summary.json, alpha_rl_train_summary.json byte-equal between runs
- diag.jsonl byte-equal modulo elapsed_s
- Eval pnl identical run-A vs run-B at seed 42
Pre-fix baseline (Phase 2.5 measurement): same-seed eval pnl spread $450k
($187k vs -$261k). Post-fix: $0 spread.
Speed cost: ~1.5ms/step amortised; ~10-15% slower than TF32 production
(PEDANTIC tax — acceptable in dev, toggle to FOXHUNT_DETERMINISTIC=0 for prod).
Mapped-pinned discipline: all 11 NEW memcpy_dtoh sites in diagnostic dump methods
+ per-step checksum readback use a new pub(crate) helper
read_slice_d_into<T: Copy>(stream, src, dst) — MappedRecordBuffer + raw
memcpy_dtod_async + raw_stream_sync + volatile read. Generic over T (f32, f64,
i32, u32, u8). Satisfies feedback_no_htod_htoh_only_mapped_pinned + hook guard.
Bundled Tier 1.5 fast-dev-cycle infrastructure (spec
docs/superpowers/specs/2026-06-02-fast-dev-cycle.md):
- scripts/local-mid-smoke.sh b=128, 2000+500, ~10min on RTX 3050
- scripts/determinism-check.sh runs mid-smoke twice, diffs checksums
- scripts/tier1_5_verdict.py behavioral kill verdict
- AdamW checkpoint save/load (crates/ml-alpha/src/trainer/optim.rs)
- IntegratedTrainer checkpoint save/load (resume from checkpoint)
- 15 Phase 1 checksum leaves in build_diag_value
- Env-gated dump methods (FOXHUNT_DETERMINISM_DEBUG_PER/MAMBA2/RL/BACKWARD)
for future divergence-chasing — never run in production
Documentation:
- docs/superpowers/specs/2026-06-02-determinism-foundation.md
- docs/superpowers/specs/2026-06-02-fast-dev-cycle.md
- docs/superpowers/plans/2026-06-02-determinism-foundation-implementation.md
- docs/superpowers/notes/2026-06-02-determinism-phase{1,2,2.2,2.5,2.6}-*.md
- Adjacent specs/plans/notes from the analytical chain that surfaced determinism
as the load-bearing blocker (eval-summary, eval-boundary, regime-observer,
multi-head policy, regime-invariance, Phase 3 IQN-complement post-mortem)
Unlocks: every controller / architecture / reward-shaping A/B from this commit
onward attributes outcome differences to the change, not random-init kernel-race
drift cascading through training x eval LOB-sim trajectories. The eval-collapse
investigation (pearl_reward_signal_anti_aligned_with_pnl, multi-head spec,
regime-invariance spec) is now testable with trustworthy verdicts.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
21 KiB
21 KiB
Regime-Invariance — Implementation Plan for the Four Interventions
Date: 2026-06-02
Status: Plan (task list only; no implementation)
Branch: ml-alpha-regime-observer (HEAD 63fc16f17)
Source spec: 2026-06-02-regime-invariance-four-interventions.md
Notes for the executor
- Interventions ship in series, one cluster smoke per intervention. Do NOT bundle.
- Intervention #1 (R-multiple) is the foundational rip-and-replace. The other three build on top.
- Per
feedback_no_feature_flags: the surfer-scaffold infra is deleted in the same commit that lands R-multiple. No parallel paths. - Per
feedback_single_source_of_truth_no_duplicates: nov2_/version suffixes anywhere. - Per
feedback_local_smoke_before_cluster: every.cu/ trait / buffer change runs the 1k-step local smoke on RTX 3050 Ti before any Argo submit. - Spec inaccuracy correction: §1.1 of the spec asserts "Read ATR from existing risk-stack signals (already computed for Kelly position sizing)". Verified false against
crates/ml-alpha/cuda/rl_kelly_fraction_controller.cu: Kelly consumeswin_rate × avg_win/avg_lossin USD, not ATR. There is no existing ATR EMA slot. Intervention #1 must therefore allocate and produce a fresh ATR EMA. The plan reflects this; the spec §7 open question #1 is settled by this finding.
Intervention #1 — R-multiple reward (ships first, single commit)
Phase A — ISV layout (RL bus surgery)
Task 1.1 — Retire surfer-scaffold slots 753-756
- File:
crates/ml-alpha/src/rl/isv_slots.rs - Action: Delete
RL_SURFER_SCAFFOLD_WEIGHT_INDEX(753),RL_SURFER_BREAK_EVEN_WR_INDEX(754),RL_SURFER_K_SHARPNESS_INDEX(755),RL_SURFER_WARMUP_TRADES_INDEX(756). Remove the §"Adaptive Surfer-Scaffold Shaping" header block (lines 1740-1779). RenumberRL_SLOTS_ENDfrom 757 → 753. - LOC: −45
Task 1.2 — Allocate R-multiple slots
- File:
crates/ml-alpha/src/rl/isv_slots.rs - Action: Reuse the freed 753-756 range with new semantics (no renumbering needed beyond
RL_SLOTS_END). Allocate:RL_ATR_EMA_INDEX = 753— per-batch ATR EMA in USD over closed-trade |Δrealized_pnl| (Wiener-α blend, half-life ~50 trades).RL_ATR_EMA_ALPHA_INDEX = 754— config, default 0.02.RL_ATR_FLOOR_INDEX = 755— config; floor in USD ≈ 1 ES tick × multiplier × min_size = $12.50.RL_R_MULTIPLE_CLAMP_INDEX = 756— config; symmetric clamp on R-multiple reward to ±N×ATR, default 5.0.
- Set
RL_SLOTS_END = 757. Add a §"R-multiple reward" header documenting the design + linkage to spec. - LOC: +50
Task 1.3 — Per-account ATR-at-entry CUDA buffer
- File:
crates/ml-alpha/src/trainer/integrated.rs - Action: Add
unit_atr_at_entry_d: CudaSlice<f32>(sizedb_size). Allocated inIntegratedRlTrainer::new, zero-init alongsideunit_entry_step_d. Listed in struct, zeroed byreset_session_stateperpearl_adaptive_carryover_discipline. - LOC: +20
Phase B — CUDA kernel changes
Task 1.4 — Delete rl_surfer_scaffold_controller.cu
- File:
crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu - Action:
rm. Perfeedback_no_feature_flags, the kernel + its 753-weight consumer go atomically. - LOC: −102
Task 1.5 — Rewrite Phase 5 of rl_fused_reward_pipeline.cu for R-multiple
- File:
crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu - Action: Replace Phase 5 / 5b entirely (lines 217-272). New structure:
- Capture
unit_atr_at_entry[b]on flat→positioned transition. Bootstrap =max(isv[RL_ATR_EMA_INDEX], isv[RL_ATR_FLOOR_INDEX]). - At close (
done > 0.5):r = clamp(realized_pnl_delta / max(unit_atr_at_entry[b], floor), ±R_MULTIPLE_CLAMP). - Drop entry-cost / short-hold-penalty / long-ride / per-step hold-bonus / inventory-penalty.
- Drop
RL_SURFER_SCAFFOLD_WEIGHT_INDEXread and allw *multipliers. - Drop
#defines forRL_SURFER_SCAFFOLD_WEIGHT_INDEX,RL_ENTRY_COST_INDEX,RL_SHORT_HOLD_MIN_STEPS_INDEX,RL_SHORT_HOLD_PENALTY_INDEX,RL_HOLD_BONUS_INDEX,RL_INVENTORY_PENALTY_BETA_INDEX. - Add
#defines forRL_ATR_EMA_INDEX,RL_ATR_FLOOR_INDEX,RL_R_MULTIPLE_CLAMP_INDEX. - Add new kernel arg
float* unit_atr_at_entry(afterunit_active).
- Capture
- LOC: −80 / +40 (net −40)
Task 1.6 — New ATR EMA producer kernel
- File:
crates/ml-alpha/cuda/rl_atr_ema_update.cu(NEW) - Action: Single-thread (1,1,1)/(1,1,1) launch. On done, read
|realized_pnl_delta|from the per-batch close, average across the closed-trade batch (single-block tree reduce, no atomics perfeedback_no_atomicadd), Wiener-α blend intoRL_ATR_EMA_INDEXwithRL_ATR_EMA_ALPHA_INDEX. Bootstraps from first-observation perpearl_first_observation_bootstrap. - LOC: +90
Phase C — Trainer wiring
Task 1.7 — Drop scaffold controller from trainer
- File:
crates/ml-alpha/src/trainer/integrated.rs - Action: Delete the four
rl_surfer_scaffold_controller-related blocks:- Constant
RL_SURFER_SCAFFOLD_CONTROLLER_CUBIN(line 299-300). - Struct fields
_rl_surfer_scaffold_controller_module+rl_surfer_scaffold_controller_fn(839-840). load_cubin/load_functionblocks (1807-1812).- Field assignment in struct literal (2959-2960).
- Method
launch_rl_surfer_scaffold_controller(4261-4275). - Call site in step pipeline (7757-7762).
- Constant
- LOC: −60
Task 1.8 — Wire ATR EMA kernel + buffer into reward pipeline launch
- File:
crates/ml-alpha/src/trainer/integrated.rs - Action:
- Add
RL_ATR_EMA_UPDATE_CUBINinclude + module/function load alongside other reward kernels. - Add
unit_atr_at_entry_dto therl_fused_reward_pipelineargs block (afterunit_active_d). - Add a
launch_rl_atr_ema_updatemethod. - Call it immediately AFTER the fused reward pipeline kernel (so the new ATR observation propagates the next time entry capture fires).
- Update
reset_session_stateto zerounit_atr_at_entry_dandRL_ATR_EMA_INDEXperpearl_adaptive_carryover_discipline.
- Add
- LOC: +90
Task 1.9 — ISV bootstrap entries
- File:
crates/ml-alpha/src/trainer/integrated.rs - Action: In the init
isv_seedtable (~line 3919), remove the 4 surfer entries and add R-multiple bootstrap entries:(RL_ATR_EMA_INDEX, 0.0_f32)(sentinel — first-observation bootstrap)(RL_ATR_EMA_ALPHA_INDEX, 0.02_f32)(RL_ATR_FLOOR_INDEX, 12.50_f32)(1 ES tick = $12.50)(RL_R_MULTIPLE_CLAMP_INDEX, 5.0_f32)
- Also remove the 4 surfer entries from
reset_session_state's seed list (~line 3646 area). - LOC: +12 / −10
Task 1.10 — Drop scaffold diag emission
- File:
crates/ml-alpha/src/trainer/integrated.rs - Action: Replace the
"surfer_scaffold_weight"JSONL field (~line 10053) with"atr_ema_usd"readingisv[RL_ATR_EMA_INDEX]and"r_multiple_last"(per-step max abs R-multiple if cheap; otherwise just ATR). - LOC: +5 / −5
Phase D — Build registration
Task 1.11 — Update build.rs kernel list
- File:
crates/ml-alpha/build.rs - Action: Remove
"rl_surfer_scaffold_controller"(line 118). Add"rl_atr_ema_update"in its place. - LOC: 0 (1 line swap)
Phase E — Tests + cubin sanity
Task 1.12 — GPU-oracle test for the new fused reward pipeline
- File:
crates/ml-alpha/tests/reward_alignment_invariants.rs(extend existing) - Action: Add a deterministic-seed sub-test asserting:
- After 1000 sim steps,
Pearson(rewards.sum, Δpnl) ≥ 0.7perpearl_reward_signal_anti_aligned_with_pnl. - On every close event, observed
reward == clamp(Δpnl / max(atr_at_entry, floor), ±5). unit_atr_at_entry_d[b]is overwritten on every flat→positioned transition and unchanged otherwise.
- After 1000 sim steps,
- Per
feedback_no_cpu_test_fallbacks: oracle is GPU-resident; test reads back via mapped-pinned only. - LOC: +120
Task 1.13 — ISV invariants test
- File:
crates/ml-alpha/tests/risk_stack_invariants.rs(extend) - Action: Assert
RL_SLOTS_END == 757and the four R-multiple slots resolve to 753/754/755/756. - LOC: +15
Task 1.14 — Audit-wiring + audit-isv dry run
- Files: scripts
scripts/audit-wiring.sh,scripts/audit-isv.sh - Action: Run both; verify no orphan references to
surfer_scaffold_*survive (consumers, configs, JSONL keys). No new orphans introduced by ATR slots. - LOC: 0 (script run only)
Phase F — Local validation
Task 1.15 — Local 1k-step smoke (RTX 3050 Ti)
- Command:
FOXHUNT_TEST_DATA=test_data/futures-baseline \ SQLX_OFFLINE=true \ cargo test -p ml-alpha --lib --release -- smoke_tests --ignored --nocapture - Pass gate: zero kernel asserts, no NaNs in diag JSONL,
atr_ema_usd > 0after the first close event,Pearson(reward, Δpnl)fromreward_alignment_invariantstest ≥ 0.7. - LOC: 0
Task 1.16 — Local 5k-step extended smoke
- Command: same as 1.15 but with
--n-steps 5000. Validates fold-1 train wr ≥ 0.30 trend before cluster commit. - LOC: 0
Phase G — Cluster smoke
Task 1.17 — Cluster smoke fold-1 b=1024 20k+5k
- Command:
./scripts/argo-alpha-rl.sh \ --sha <COMMIT_SHA> \ --branch ml-alpha-regime-observer \ --gpu-pool ci-training-l40s \ --batch-size 1024 \ --n-steps 20000 \ --n-eval-steps 5000 \ --n-folds 9 --fold-idx 0 - Pass gate: spec §1.3 — train wr ≥ 0.30 by step 5k AND eval wr ≥ 0.28 AND eval pnl > 0.
- Fail gate: eval pnl < −$5M → kill and pivot to Intervention #3.
- Note: per
pearl_argo_branch_ref_trap_strikes_again, pin the exact SHA in the script invocation. - LOC: 0
Intervention #1 total LOC: ~+250 / −300 = net −50 (matches spec §1.1 "Net code REDUCTION").
Intervention #2 — Wire regime_observer features into policy network input
Defer until #1 result known. Spec §2 says best deployed AFTER or WITH #3.
Task 2.1 — Confirm encoder input dim path
- File:
crates/ml-alpha/src/trainer/integrated.rs(read-only audit) - Action: Locate the encoder input projection used by
rl_encoder_context_broadcast(build.rs line 94). Identify the current K-dim count it broadcasts (encoder input[B, K, 56]per existing comment). - LOC: 0 (research)
Task 2.2 — Extend rl_encoder_context_broadcast.cu to inject 5 regime features
- File:
crates/ml-alpha/cuda/rl_encoder_context_broadcast.cu - Action: Add a 5-slot tail (cols 56-60) populated from ISV:
RL_REGIME_DEAD_ZONE_FLAG_INDEX(696)RL_REGIME_SESSION_PNL_VARIANCE_EMA_INDEX(700)RL_REGIME_RECOVERY_FACTOR_INDEX(699)RL_REGIME_TAIL_EVENT_RECENCY_INDEX(701) (normalize byRL_IQN_TAU_TAIL_BOOST_N_WINDOW_INDEX= 712)RL_EDGE_PH_MEAN_INDEX(750) (normalize againstRL_EDGE_PH_THRESHOLD_INDEX= 748)
- Each broadcast as a constant across the K-axis (same value all timesteps in the encoder window).
- LOC: +40
Task 2.3 — Bump encoder input projection from 56 → 61 dims
- File:
crates/ml-alpha/src/trainer/integrated.rs+ anyHIDDEN_INPUT_DIMconst - Action: Identify the constant carrying
56, replace with61(or named constENCODER_INPUT_DIM). Perfeedback_use_consts_not_literals_for_structural_dims, all consumers must reference the const not the literal. - LOC: +10 / −0
Task 2.4 — Expand projection weight matrix [in, hidden]
- File:
crates/ml-alpha/src/trainer/perception.rs(encoder init) - Action: Grow input-projection weight matrix from
[56, hidden]to[61, hidden]. New columns Xavier-init small. Update checkpoint format version bump constant. - LOC: +25
Task 2.5 — Local 1k-step smoke + 5k extended smoke
- Command: same as Task 1.15-1.16. Verifies the wider encoder runs cleanly and doesn't explode action entropy.
- LOC: 0
Task 2.6 — Cluster smoke
- Same as Task 1.17. Pass gate per spec §5 (eval pnl > 0). Compare action-entropy curve against #1 baseline.
- LOC: 0
Intervention #2 total LOC: ~+75
Intervention #3 — Cross-fold training (domain randomization)
Task 3.1 — Audit walk-forward sampler
- File:
crates/ml-alpha/examples/alpha_rl_train.rs(lines 314-340) - Action: Current sampler splits
n_filesinton_foldsblocks then pickstrain = 0..=fold_idx,eval = fold_idx+1. Map this to a per-batch fold-randomization variant. - LOC: 0 (research)
Task 3.2 — Add --cross-fold-training CLI flag
- File:
crates/ml-alpha/examples/alpha_rl_train.rs - Action: Add boolean flag
cross_fold_training: booldefaultingfalse. When set, each rollout batch samples a random training fold uniformly from0..=fold_idx; eval stays onfold_idx + 1(held-out). - LOC: +15
Task 3.3 — Per-batch-element fold sampling in rollout loop
- File:
crates/ml-alpha/examples/alpha_rl_train.rs+ downstream sampler inservices/ml_training_service - Action: When cross-fold mode is active, the per-batch GPU sample-and-gather path (
gpu_sample_and_gatherkernel) takes a randomly-chosen fold's file list per batch element. Verifygpu_sample_and_gatheralready supports per-batch file selection (it does — the dataset gather is per-snapshot-anchor). If not, extend. - LOC: +30
Task 3.4 — Verify all training folds load cleanly
- File: deployment-time check (no code change)
- Action: Per
pearl_mbp10_data_is_structurally_brokencorrection (cluster PVC has 148GB coherent 24-month ES MBP-10), confirm 9 quarterly files load without size/shape mismatch. Add an assertion inalpha_rl_train.rsthat alltrain_filesparse before training begins (and abort early if not). - LOC: +10
Task 3.5 — Local 1k-step smoke
- Command: same as Task 1.15 with
--cross-fold-trainingflag. - Pass gate: trade rate doesn't crash to zero; reward Pearson ≥ 0.7 preserved.
- LOC: 0
Task 3.6 — Cluster smoke
- Same as Task 1.17 with
--cross-fold-trainingand--n-folds 9 --fold-idx 7(train on folds 0-6, eval on fold 7, held-out tail). - LOC: 0
Intervention #3 total LOC: ~+55
Intervention #4 — Mixture-of-experts gated by regime (deferred)
Spec §4.2 marks this as 2-day, 250-LOC effort, defer until #1-#3 validated. Plan stays at task-level only.
Task 4.1 — Expert architecture design doc
- File:
docs/superpowers/specs/2026-06-XX-moe-regime-gating.md(CREATE only if Intervention #1-#3 fails) - Action: Capture expert-A / expert-B / gating-net dimensions, per-expert reward shaping plan, training schedule.
- LOC: ~150 markdown
Task 4.2 — Two-expert MLP heads (parallel to current PPO policy head)
- File:
crates/ml-alpha/src/rl/ppo.rs+ newcrates/ml-alpha/cuda/rl_moe_policy_head.cu - Action: 2 separate
[hidden, N_ACTIONS]weight matrices, forward + backward kernels. Gating net is a 2-layer MLP on regime features feeding softmax(K=2). - LOC: +250
Task 4.3 — Per-expert reward shaping
- File:
crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu - Action: Add per-expert reward path — expert A uses R-multiple-by-hold-duration, expert B uses R-multiple-of-inverse-hold (mean-revert specialty). Gated by
expert_assignment[b](sampled from gate softmax at trade open). - LOC: +40
Task 4.4 — Trainer wiring + 6 ISV slots
- File:
crates/ml-alpha/src/rl/isv_slots.rs,crates/ml-alpha/src/trainer/integrated.rs - Action: 6 new slots (gate temp, K=2 gate weights × 2 layers' hyperparams, per-expert KL trackers). Bumps
RL_SLOTS_ENDfrom 757 → 763. - LOC: +80
Task 4.5 — Cluster smoke
- Same as Task 1.17. Spec §4 falsification: eval pnl > 0.
- LOC: 0
Intervention #4 total LOC: ~+370 (excluding spec doc)
Cross-cutting tasks (apply to all interventions)
Task X.1 — Run scripts/audit-wiring.sh and scripts/audit-isv.sh after each intervention
- Action: Confirms no orphan kernel registrations, no dead ISV slots, no hardcoded literals where ISV reads should be used.
- LOC: 0
Task X.2 — Memory pearl update on cluster verdict
- File:
/home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/ - Action: After each cluster smoke, append a pearl-style entry to
MEMORY.mdtopic file with the verdict (PASS / MARGINAL / FAIL with eval pnl, wr, action entropy). Perfeedback_trust_code_not_docs, the cluster log is ground truth; pearl summarizes what was measured. - LOC: ~5 per intervention
Task X.3 — Pre-commit precommit zen + local smoke
- Action: Run
mcp__zen__precommitor equivalent +feedback_local_smoke_before_cluster1k-step smoke before each cluster submission. No exceptions. - LOC: 0
Sequencing decision tree (recap from spec §5)
[Ship #1] ──→ cluster smoke ──→ eval pnl > 0? ──yes──→ DONE, deploy
│
no
↓
[Ship #3] ──→ cluster smoke ──→ eval pnl > 0? ──yes──→ DONE
│
no
↓
[Ship #2] ──→ cluster smoke ──→ eval pnl > 0? ──yes──→ DONE
│
no
↓
[Ship #4] ──→ cluster smoke ──→ verdict
│
no
↓
Termination clause §5
Open questions resolved by this plan
- ATR source (spec §7 Q1): No existing Kelly ATR. Allocate fresh
RL_ATR_EMA_INDEXslot 753 + newrl_atr_ema_update.cuproducer kernel. - ATR bootstrap (Q2): Per
pearl_first_observation_bootstrap+ sentinel-zero protocol. Usemax(atr_ema, atr_floor=$12.50). - Eval-boundary discipline (Q3): Per
pearl_adaptive_carryover_discipline, zeroRL_ATR_EMA_INDEXandunit_atr_at_entry_dinreset_session_state. R-multiple denominator falls back toatr_floorfor the first eval trade — preferred over carrying train-end calibration into a regime-shifted eval window. - Fold sampling weight (Q4): Equal-weight (uniform) in Intervention #3 v1. Active domain randomization (regime-novelty weighting) is a future #3.1 if Plain #3 underperforms.
Estimated total LOC by intervention
| # | Intervention | Net LOC | Files touched | Cluster smokes |
|---|---|---|---|---|
| 1 | R-multiple reward | −50 | 8 | 1 |
| 2 | Regime features → encoder | +75 | 3 | 1 |
| 3 | Cross-fold training | +55 | 2 | 1 |
| 4 | Mixture-of-experts (deferred) | +370 | 5 | 1+ |
Files this plan touches (canonical list)
Intervention #1:
crates/ml-alpha/src/rl/isv_slots.rscrates/ml-alpha/cuda/rl_fused_reward_pipeline.cucrates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu(DELETE)crates/ml-alpha/cuda/rl_atr_ema_update.cu(CREATE)crates/ml-alpha/src/trainer/integrated.rscrates/ml-alpha/build.rscrates/ml-alpha/tests/reward_alignment_invariants.rscrates/ml-alpha/tests/risk_stack_invariants.rs
Intervention #2:
crates/ml-alpha/cuda/rl_encoder_context_broadcast.cucrates/ml-alpha/src/trainer/integrated.rscrates/ml-alpha/src/trainer/perception.rs
Intervention #3:
crates/ml-alpha/examples/alpha_rl_train.rsservices/ml_training_service/src/k8s_dispatcher.rs(if any K8s arg surface needs--cross-fold-training)
Intervention #4 (deferred):
docs/superpowers/specs/2026-06-XX-moe-regime-gating.md(CREATE)crates/ml-alpha/src/rl/ppo.rscrates/ml-alpha/cuda/rl_moe_policy_head.cu(CREATE)crates/ml-alpha/cuda/rl_fused_reward_pipeline.cucrates/ml-alpha/src/rl/isv_slots.rscrates/ml-alpha/src/trainer/integrated.rs