//! Formal classification of every piece of training-time state by reset lifecycle.
//! See `docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md` §4.A.1.

/// Reset lifecycle category for a piece of training-time state.
///
/// See `docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md` §4.A.1.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResetCategory {
    /// Cleared at fold boundary.
    FoldReset,
    /// Cleared at validation-window boundary.
    WindowReset,
    /// Anneals toward bootstrap value over `decay_bars` bars at fold boundary.
    SoftReset { decay_bars: u32 },
    /// Survives across folds (learned weights).
    TrainingPersist,
    /// Structurally fixed across runs; fail-fast on mismatch. No migration path exists.
    SchemaContract,
}

/// One entry in the reset registry.
#[derive(Debug, Clone, Copy)]
pub struct RegistryEntry {
    pub name: &'static str,
    pub category: ResetCategory,
    /// Short description — what this state represents. Used in diagnostic dumps.
    pub description: &'static str,
}

/// Typed registry classifying every piece of training-time state.
///
/// Populated at construction; read-only thereafter. Adding new state requires
/// adding a new entry here in the same commit that introduces the state
/// (per Invariant 2 Wire-It-Up).
#[derive(Debug)]
pub struct StateResetRegistry {
    entries: Vec<RegistryEntry>,
}

impl StateResetRegistry {
    /// Build the registry with all currently-known training state.
    pub fn new() -> Self {
        let entries = vec![
            // ───── Fold-reset state ─────────────────────────────────────
            RegistryEntry {
                name: "kelly_stats",
                category: ResetCategory::FoldReset,
                description: "Per-window Kelly win/loss counters (ps[14..18])",
            },
            RegistryEntry {
                name: "eval_q_mean_ema",
                category: ResetCategory::FoldReset,
                description: "Per-branch Q-mean EMAs in update_eval_v_range",
            },
            RegistryEntry {
                name: "eval_q_std_ema",
                category: ResetCategory::FoldReset,
                description: "Per-branch Q-std EMAs in update_eval_v_range",
            },
            RegistryEntry {
                name: "isv_v_range_slots",
                category: ResetCategory::FoldReset,
                description: "ISV[23..31) — v_center/v_half per branch",
            },
            RegistryEntry {
                name: "isv_learning_health",
                category: ResetCategory::FoldReset,
                description: "ISV[12] — learning_health EMA",
            },
            RegistryEntry {
                name: "isv_sharpe_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[22] — training Sharpe EMA",
            },
            RegistryEntry {
                name: "isv_q_means",
                category: ResetCategory::FoldReset,
                description: "ISV[13..22) — per-direction/magnitude Q-mean EMAs + |Q| refs",
            },
            // ───── IQN readiness gauge (Fix 3 of 3 fold-boundary audit) ─────
            // Three coupled scalars on `GpuDqnTrainer`:
            //   `iqn_loss_initial`: anchor captured on first `update_iqn_readiness`
            //   `iqn_loss_ema`: streaming smoothed loss
            //   `iqn_readiness`: (initial - ema)/initial gauge ∈ [0,1]
            //   plus the pinned-host-mapped device slot read by c51_loss
            //   kernel as CVaR α + by gating consumers.
            // Without fold-boundary reset the anchor stays pinned to the
            // fold-0 epoch-1 IQN loss for the entire run, so the gauge in
            // fold N+1 is computed against a stale baseline. All three names
            // dispatch to the same `reset_iqn_readiness_state` helper.
            RegistryEntry {
                name: "isv_iqn_loss_initial",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.iqn_loss_initial_pinned — fold-anchor for IQN improvement gauge stored in a mapped-pinned scalar; producer is `update_iqn_readiness_kernel` (SP4 Layer C close-out C2, 2026-05-01 — migrated from host-side `f32` field per `feedback_no_cpu_compute_strict`); cold-start sentinel `prev < 1e-12 ⇒ bootstrap` keeps the deleted host formula's semantics. Reset to 0.0 at fold boundary so the new fold's first kernel launch re-enters the bootstrap branch.",
            },
            RegistryEntry {
                name: "isv_iqn_loss_ema",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.iqn_loss_ema_pinned — streaming smoothed IQN loss in a mapped-pinned scalar; producer is `update_iqn_readiness_kernel` (SP4 Layer C close-out C2, 2026-05-01 — migrated from host-side `f32` field per `feedback_no_cpu_compute_strict`); coupled to isv_iqn_loss_initial in the readiness fraction `(initial - ema) / max(initial, 1e-6)`. Reset to 0.0 at fold boundary alongside the anchor so the new fold's first kernel launch reseeds via bootstrap.",
            },
            RegistryEntry {
                name: "isv_iqn_readiness",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.iqn_readiness_pinned (mapped-pinned device slot read by c51_loss_kernel as CVaR α and by IQN gradient-weight gating); produced GPU-side by `update_iqn_readiness_kernel` per training step (SP4 Layer C close-out C2, 2026-05-01) — replaces the prior host-side `(1-α) × prev + α × loss` arithmetic in `update_iqn_readiness` per `feedback_no_cpu_compute_strict`. Reset to 0.0 at fold boundary so the new fold's first `update_iqn_readiness` recaptures the bootstrap anchor.",
            },
            // ───── Window-reset state ───────────────────────────────────
            RegistryEntry {
                name: "portfolio_state",
                category: ResetCategory::WindowReset,
                description: "Per-window portfolio (ps[0..8]) — reset at window start",
            },
            RegistryEntry {
                name: "hold_time",
                category: ResetCategory::WindowReset,
                description: "ps[5] / ps[10] — consecutive bars in position",
            },
            RegistryEntry {
                name: "entry_price",
                category: ResetCategory::WindowReset,
                description: "ps[3] — entry price of current trade",
            },
            // ───── Soft-reset state (anneals across folds) ──────────────
            RegistryEntry {
                name: "isv_grad_balance_targets",
                category: ResetCategory::SoftReset { decay_bars: 500 },
                description: "ISV[31..35) — per-branch grad-norm targets",
            },
            RegistryEntry {
                name: "isv_grad_scale_limit",
                category: ResetCategory::SoftReset { decay_bars: 500 },
                description: "ISV[35] — scale-clamp limit EMA",
            },
            // ───── Training-persist state ───────────────────────────────
            RegistryEntry {
                name: "network_params",
                category: ResetCategory::TrainingPersist,
                description: "Online network weights (all tensors)",
            },
            RegistryEntry {
                name: "target_network",
                category: ResetCategory::TrainingPersist,
                description: "Target network weights — Polyak EMA of online",
            },
            RegistryEntry {
                name: "adam_momentum",
                category: ResetCategory::TrainingPersist,
                description: "Adam optimizer first moment (m)",
            },
            RegistryEntry {
                name: "adam_variance",
                category: ResetCategory::TrainingPersist,
                description: "Adam optimizer second moment (v)",
            },
            RegistryEntry {
                name: "adam_step",
                category: ResetCategory::TrainingPersist,
                description: "Adam step counter t",
            },
            RegistryEntry {
                name: "replay_buffer",
                category: ResetCategory::TrainingPersist,
                description: "PER experience buffer — persists across folds",
            },
            // ───── Schema-contract state ────────────────────────────────
            RegistryEntry {
                name: "ISV_LAYOUT_FINGERPRINT",
                category: ResetCategory::SchemaContract,
                description: "ISV[55..57) — compile-time structural hash of slot layout; fail-fast only, no migration path",
            },
            RegistryEntry {
                name: "isv_iql_branch_scale_floor",
                category: ResetCategory::SchemaContract,
                description: "ISV[36] — IQL branch_scales floor (safety bound, bootstrap-only)",
            },
            RegistryEntry {
                name: "isv_total_epochs",
                category: ResetCategory::SchemaContract,
                description: "ISV[TOTAL_EPOCHS_INDEX=40] — total epoch count for this run; constant after construction",
            },
            RegistryEntry {
                name: "isv_cql_alpha",
                category: ResetCategory::FoldReset,
                description: "ISV[CQL_ALPHA_INDEX=48] — CQL pessimism base coefficient; producer upgraded SchemaContract → FoldReset by Plan 3 Task 9 C.5 (GPU `cql_alpha_seed_update` kernel ramps slot 48 toward `config.cql_alpha × (1 - seed_frac)`); cold-start config.cql_alpha reapplied at fold boundary",
            },
            RegistryEntry {
                name: "isv_plan_threshold",
                category: ResetCategory::FoldReset,
                description: "ISV[PLAN_THRESHOLD_INDEX=49] — plan-MLP activation threshold; producer upgraded to GPU plan_threshold_update kernel (Plan 3 B.4); cold-start 0.5 reapplied at fold boundary",
            },
            // ───── Fold-reset state (pre-allocated GPU-written slots) ───
            RegistryEntry {
                name: "isv_q_quantiles",
                category: ResetCategory::FoldReset,
                description: "ISV[50..58) — per-branch Q P5/P95 EMAs (Plan 2 C.1); reset to v_min/v_max bootstrap at fold boundary",
            },
            RegistryEntry {
                name: "isv_epoch_idx",
                category: ResetCategory::FoldReset,
                description: "ISV[EPOCH_IDX_INDEX=39] — current epoch index; CPU writes at epoch boundary (Plan 1 Task 13)",
            },
            RegistryEntry {
                name: "isv_epsilon_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[EPSILON_EFF_INDEX=41] — effective epsilon; GPU epsilon_update kernel fills (Plan 1 Task 14)",
            },
            RegistryEntry {
                name: "isv_tau_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[TAU_EFF_INDEX=42] — effective tau; GPU tau_update kernel fills (Plan 1 Task 13)",
            },
            RegistryEntry {
                name: "isv_gamma_dir_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[GAMMA_DIR_EFF_INDEX=43] — direction-branch gamma; GPU per_branch_gamma_update fills (Plan 2 Task 3 D.2)",
            },
            RegistryEntry {
                name: "isv_gamma_mag_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[GAMMA_MAG_EFF_INDEX=44] — magnitude-branch gamma; GPU per_branch_gamma_update fills (Plan 2 Task 3 D.2)",
            },
            RegistryEntry {
                name: "isv_gamma_ord_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[GAMMA_ORD_EFF_INDEX=45] — order-branch gamma; GPU per_branch_gamma_update fills (Plan 2 Task 3 D.2)",
            },
            RegistryEntry {
                name: "isv_gamma_urg_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[GAMMA_URG_EFF_INDEX=46] — urgency-branch gamma; GPU per_branch_gamma_update fills (Plan 2 Task 3 D.2)",
            },
            RegistryEntry {
                name: "isv_kelly_cap_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[KELLY_CAP_EFF_INDEX=47] — effective Kelly cap; GPU kelly_cap_update kernel aggregates ps[14..18] across envs",
            },
            // ───── C.2 Reward-component attribution EMAs (Plan 3 Task 1) ───
            RegistryEntry {
                name: "isv_reward_component_emas",
                category: ResetCategory::FoldReset,
                description: "ISV[63..69) — per-component |reward| EMAs [popart,cf,trail,micro,opp_cost,bonus]; GPU reward_component_ema kernel fills (Plan 3 C.2)",
            },
            // ───── B.2 ISV-driven trade-attempt novelty bonus (Plan 3 Task 3) ─
            RegistryEntry {
                name: "isv_trade_attempt_rate_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[71] — Flat→Positioned transition rate EMA; GPU trade_rate_ema_update kernel fills (Plan 3 B.2)",
            },
            RegistryEntry {
                name: "isv_trade_target_rate",
                category: ResetCategory::FoldReset,
                description: "ISV[72] — reference rate for novelty, frozen at epoch 5 from measured EMA (Plan 3 B.2)",
            },
            // ───── B.4 Adaptive plan threshold readiness EMA (Plan 3 Task 4) ─
            RegistryEntry {
                name: "isv_readiness_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[75] — batch-mean readiness EMA; GPU plan_threshold_update kernel fills; drives PLAN_THRESHOLD_INDEX=49 derivation (Plan 3 B.4)",
            },
            // ───── C.3 Train-vs-val state-distribution KL (Plan 3 Task 7) ───
            RegistryEntry {
                name: "isv_state_kl_train_val_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[78] — train-vs-val moment-match KL EMA on OFI dims; GPU state_kl_moment_match kernel fills (Plan 3 C.3)",
            },
            RegistryEntry {
                name: "isv_state_kl_amplification",
                category: ResetCategory::FoldReset,
                description: "ISV[79] — Flat-trap escape amplifier ∈ [1, 2] driven by KL ratio; consumed by B.1 opp_cost + B.2 bonus (Plan 3 C.3)",
            },
            // ───── B.3 GPU-only seeded warm-start (Plan 3 Task 8) ───
            RegistryEntry {
                name: "isv_seed_steps_target",
                category: ResetCategory::SchemaContract,
                description: "ISV[SEED_STEPS_TARGET_INDEX=82] — config replay_seed_steps; constant for the run, written once at constructor (Plan 3 B.3)",
            },
            RegistryEntry {
                name: "isv_seed_steps_done",
                category: ResetCategory::FoldReset,
                description: "ISV[SEED_STEPS_DONE_INDEX=83] — cumulative seed-phase steps; GPU-incremented each collect_experiences (Plan 3 B.3)",
            },
            RegistryEntry {
                name: "isv_seed_frac_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[SEED_FRAC_EMA_INDEX=84] — seed-fraction EMA, Task 9 CQL ramp consumer; cold-start 1.0 (Plan 3 B.3)",
            },
            // ───── E.5 Attention-focus interpretability (Plan 4 Task 5 Mode A) ──
            RegistryEntry {
                name: "isv_vsn_mag_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MAG_EMA_INDEX=87] — magnitude-branch VSN-weight magnitude EMA; GPU attention_focus_ema_update kernel fills (Plan 4 E.5 Mode A)",
            },
            RegistryEntry {
                name: "isv_vsn_dir_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_DIR_EMA_INDEX=88] — direction-branch VSN-weight magnitude EMA; GPU attention_focus_ema_update kernel fills (Plan 4 E.5 Mode A)",
            },
            RegistryEntry {
                name: "isv_mamba2_retention_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[MAMBA2_RETENTION_EMA_INDEX=89] — Mamba2 state-transition magnitude EMA, retention proxy; GPU attention_focus_ema_update kernel fills (Plan 4 E.5 Mode A)",
            },
            // ───── Plan 4 Task 2c.3c.5: trunk-RMS EMA producer ─────────────
            RegistryEntry {
                name: "isv_h_s2_rms_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[H_S2_RMS_EMA_INDEX=96] — per-batch RMS(save_h_s2) EMA (α=0.05); GPU h_s2_rms_ema_update kernel fills; producer-only in 2c.3c.5, consumer wired in 2c.3c.6 (mag_concat_qdir adaptive-scale). SP4 Task A13.0 retrofit — Pearl A sentinel 0.0 reapplied at fold boundary (paired with sp4_wiener_state reset zeroing state.x_lag in lockstep so the new fold's first producer launch triggers Pearl A's first-observation replacement)",
            },
            // ───── Plan 4 Task 3 (E.3): IQN multi-quantile diagnostic EMAs ──
            RegistryEntry {
                name: "isv_iqn_q_p05_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[IQN_Q_P05_EMA_INDEX=99] — mean |Q| at IQN τ=0.05 EMA; GPU iqn_quantile_ema_update kernel fills (Plan 4 Task 3 E.3). Cold-start 0.0 reapplied at fold boundary. Diagnostic only — no consumer kernel reads slot 99 in this commit",
            },
            RegistryEntry {
                name: "isv_iqn_q_p25_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[IQN_Q_P25_EMA_INDEX=100] — mean |Q| at IQN τ=0.25 EMA; GPU iqn_quantile_ema_update kernel fills (Plan 4 Task 3 E.3). Cold-start 0.0 reapplied at fold boundary. Diagnostic only",
            },
            RegistryEntry {
                name: "isv_iqn_q_p75_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[IQN_Q_P75_EMA_INDEX=101] — mean |Q| at IQN τ=0.75 EMA; GPU iqn_quantile_ema_update kernel fills (Plan 4 Task 3 E.3). Cold-start 0.0 reapplied at fold boundary. Diagnostic only",
            },
            RegistryEntry {
                name: "isv_iqn_q_p95_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[IQN_Q_P95_EMA_INDEX=102] — mean |Q| at IQN τ=0.95 EMA; GPU iqn_quantile_ema_update kernel fills (Plan 4 Task 3 E.3). Cold-start 0.0 reapplied at fold boundary. Diagnostic only",
            },
            // ───── Plan 4 Task 1B-ii: VSN per-group mask EMAs ──────────
            RegistryEntry {
                name: "isv_vsn_mask_g0_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_0_EMA_INDEX=105] — `market` group importance mask EMA (α=0.05); GPU vsn_mask_ema_update kernel fills (Plan 4 Task 1B-i kernel; launch site wired in 1B-iii). SP4 Task A13.3 retrofit — Pearl A sentinel 0.0 reapplied at fold boundary (paired with sp4_wiener_state reset zeroing state.x_lag in lockstep so the new fold's first producer launch triggers Pearl A's first-observation replacement)",
            },
            RegistryEntry {
                name: "isv_vsn_mask_g1_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_1_EMA_INDEX=106] — `ofi` group importance mask EMA; same producer/contract as g0; SP4 Task A13.3 sentinel 0.0",
            },
            RegistryEntry {
                name: "isv_vsn_mask_g2_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_2_EMA_INDEX=107] — `tlob` group importance mask EMA; same producer/contract as g0; SP4 Task A13.3 sentinel 0.0",
            },
            RegistryEntry {
                name: "isv_vsn_mask_g3_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_3_EMA_INDEX=108] — `mtf` group importance mask EMA; same producer/contract as g0; SP4 Task A13.3 sentinel 0.0",
            },
            RegistryEntry {
                name: "isv_vsn_mask_g4_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_4_EMA_INDEX=109] — `portfolio` group importance mask EMA; same producer/contract as g0; SP4 Task A13.3 sentinel 0.0",
            },
            RegistryEntry {
                name: "isv_vsn_mask_g5_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VSN_MASK_GROUP_5_EMA_INDEX=110] — `plan_isv` group importance mask EMA; same producer/contract as g0; SP4 Task A13.3 sentinel 0.0",
            },
            // ───── Plan 4 Task 6 Commit A: aux-head loss EMAs ─────────
            RegistryEntry {
                name: "isv_aux_next_bar_mse_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_NEXT_BAR_MSE_EMA_INDEX=113] — next-bar regression head batch-mean MSE EMA (α=0.05); GPU aux_heads_loss_ema_update kernel fills (Plan 4 Task 6 Commit A; producer kernel landed in Commit A, launch site wires in Commit B alongside launch_h_s2_rms_ema). Cold-start 0.0 (no aux forwards have happened yet) reapplied at fold boundary. Diagnostic only — no GPU consumer kernel reads slot 113 in either commit",
            },
            RegistryEntry {
                name: "isv_aux_regime_ce_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_REGIME_CE_EMA_INDEX=114] — regime classification head batch-mean cross-entropy EMA; same producer/contract as next_bar_mse_ema; cold-start 0.0",
            },
            // SP13 B1.0 (2026-05-05): the former
            // `isv_aux_label_scale_ema` registry entry (FoldReset for ISV
            // slot 117) was retired together with the slot itself when
            // the next-bar MSE flipped to scale-free. No replacement
            // entry is needed — the slot is no longer written by any
            // producer, so there is nothing to reset at fold boundary.
            // Mixture-of-Experts per-expert utilization (slots 118..126).
            // SP4 Task A13.2 retrofit — Pearl A sentinel 0.0 at fold boundary.
            RegistryEntry {
                name: "isv_moe_expert_util_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[MOE_EXPERT_UTIL_EMA_BASE..MOE_EXPERT_UTIL_EMA_BASE+8) — per-expert gate-weight EMA (α=0.05); GPU moe_expert_util_ema_update kernel fills (Phase 2 task 2.4); SP4 Task A13.2 retrofit — Pearl A sentinel 0.0 reapplied at fold boundary (paired with sp4_wiener_state reset)",
            },
            // MoE gate-distribution entropy EMA (slot 126).
            // SP4 Task A13.2 retrofit — Pearl A sentinel 0.0 at fold boundary.
            RegistryEntry {
                name: "isv_moe_gate_entropy_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[MOE_GATE_ENTROPY_EMA_INDEX=126] — entropy of batch-mean gate distribution, EMA α=0.05; SP4 Task A13.2 retrofit — Pearl A sentinel 0.0 reapplied at fold boundary (paired with sp4_wiener_state reset); GPU moe_gate_entropy_ema_update kernel fills (Phase 2 task 2.4)",
            },
            // MoE adaptive load-balance λ controller (slot 128).
            // Reset to moe_lambda_floor (= old static 0.01 default) at fold
            // boundary so the new fold starts from the permanent minimum
            // and rises adaptively as the entropy EMA decays under it.
            // Per `pearl_blend_formulas_must_have_permanent_floor.md`, the
            // floor is a permanent minimum — λ_eff never drops below it.
            RegistryEntry {
                name: "isv_moe_lambda_eff",
                category: ResetCategory::FoldReset,
                description: "ISV[MOE_LAMBDA_EFF_INDEX=128] — adaptive load-balance λ from `moe_lambda_eff_update` controller; reset to config.moe_lambda_floor at fold boundary (gate uniform-init ⇒ deficit=0 ⇒ λ_floor); per-step kernel raises λ_eff up to (floor + max_extra) when gate-entropy EMA decays below entropy_target_frac × ln(K)",
            },
            // Plan C Phase 2 follow-up A.2 (2026-04-29): adaptive Polyak-tau
            // drift signal (slot 129). Reset to 0.0 at fold boundary so the
            // new fold starts with no inherited drift dampening — the first
            // epoch's tau passes through unchanged (drift_factor = 1) and
            // the producer kernel `q_drift_rate_ema_update` only re-fires
            // once `DQNTrainer::prev_epoch_q_mean` has graduated above 1e-6
            // (gated in `training_loop.rs`). Companion to A.1's
            // prev_epoch_q_mean reset in `reset_for_fold` — both ensure
            // cross-fold drift state cannot leak into the new fold's
            // tau-dampening computation.
            RegistryEntry {
                name: "isv_q_drift_rate",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_DRIFT_RATE_INDEX=129] — adaptive Polyak-tau drift signal ∈ [0, 4]; GPU q_drift_rate_ema_update kernel fills (Plan C A.2). Cold-start 0.0 (no-op dampening factor) reapplied at fold boundary; consumer tau_update_kernel.cu multiplies tau_eff by 1/(1+ISV[129]) so dampening is monotone — tau only ever decreases under drift, healthy runs unaffected",
            },
            // Plan C Phase 2 follow-up K (2026-04-29): adaptive fold-boundary
            // warmup signal (slot 130). Reset to 0.0 at fold boundary so the
            // new fold's first steps see heavy lr/clip damping; the kernel
            // raises the factor toward 1.0 as the fast grad-norm EMA catches
            // up to the slow steady-state EMA (which persists across folds).
            // Companion to A.1+A.2+A.3 fold-boundary state-reset family.
            // Per `pearl_blend_formulas_must_have_permanent_floor.md`, the
            // damping floor (lr_floor=0.05, clip_floor=0.1) is a permanent
            // minimum — even at factor=0 the consumers never reach zero lr/clip.
            RegistryEntry {
                name: "isv_fold_warmup_factor",
                category: ResetCategory::FoldReset,
                description: "ISV[FOLD_WARMUP_FACTOR_INDEX=130] — adaptive fold-boundary warmup factor ∈ [0, 1]; GPU fold_warmup_factor_update kernel fills (Plan C K). Cold-start 0.0 (heavy damping at fold start) reapplied at fold boundary; consumers in training_loop.rs derive lr_eff = lr_base × max(0.05, factor) and clip_eff = clip_base × (0.1 + 0.9 × factor) — both monotone (only dampen). Companion FoldReset entry `isv_grad_norm_fast_ema` resets the kernel's numerator EMA so the ratio starts at 0",
            },
            // Plan C Phase 2 follow-up K (2026-04-29): fast grad-norm EMA
            // mapped-pinned scalar feeding `fold_warmup_factor_update`'s
            // numerator. Companion to `isv_fold_warmup_factor` — both must
            // reset together so the ratio (fast/slow) starts at 0 → factor=0
            // → heavy damping. The slow EMA does NOT reset (cross-fold
            // steady-state baseline). Per `feedback_no_partial_refactor.md`,
            // both halves of the warmup-factor input contract are reset in
            // lockstep.
            RegistryEntry {
                name: "isv_grad_norm_fast_ema",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.grad_norm_fast_ema_pinned — fast (α=0.1, ~10-step horizon) EMA of `gr.raw_grad_norm`, host-side mapped-pinned scalar updated GPU-side by `update_grad_norm_emas_kernel` (chained on the producer's stream after `launch_grad_norm_finalize`) and consumed by `fold_warmup_factor_update` (Plan C K). SP4 Layer C close-out C1 redesigned (2026-05-01) migrated the prior host-side EMA arithmetic block in `update_adaptive_clip` to GPU per `feedback_no_cpu_compute_strict`. Reset to 0.0 at fold boundary so the warmup-factor ratio starts at 0 → consumer-derived lr_eff/clip_eff start at their permanent-floor values; recovers to slow-EMA baseline as gradients stabilise. Slow EMA companion (grad_norm_slow_ema_pinned) deliberately persists across folds — it tracks the cross-fold steady-state grad scale that the warmup factor compares against.",
            },
            // ───── SP4 Layer A Task A12 (2026-04-30): adaptive bound +
            // Wiener-state + Pearl C engagement-counter fold-boundary resets.
            //
            // Pearl A's first-observation replacement requires both
            // `prev_x_mean` (the ISV bound slot) AND `state.x_lag` (Wiener
            // triple offset 2) to be exactly 0 when a producer first fires
            // in a new fold. Without resetting them at the fold boundary
            // the new fold's first producer launch wouldn't see the sentinel
            // and would EMA the new step-observation against a stale fold-N
            // estimate — exactly the cross-fold anchor staleness Mech 8's
            // reverted slow_ema reset was trying to fix imperfectly.
            //
            // Pearl C's engagement-rate Wiener-EMA is per-(group, block);
            // its 2816 i32 counters likewise must zero at fold boundary so
            // post-clamp saturation detection starts each fold from a clean
            // baseline. Per `feedback_no_partial_refactor.md` — every half
            // of the SP4 fold-reset contract (40 bound slots + 207-float
            // Wiener triples + 2816-int engagement counters) migrates in
            // the same commit.
            //
            // Each entry below is one logical group; the reset dispatch arm
            // in `reset_named_state` writes 0.0 to every slot in the group.
            // ──────────────────────────────────────────────────────────────
            RegistryEntry {
                name: "sp4_target_q_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[TARGET_Q_BOUND_INDEX=131] — Mech 1 target_q clamp bound; SP4 Pearl A sentinel 0 at fold boundary so the new fold's first `target_q_p99_update` launch fires the first-observation replacement (Task A12)",
            },
            RegistryEntry {
                name: "sp4_atom_pos_bounds",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_POS_BOUND_BASE..+SP4_BRANCH_COUNT) = ISV[132..136) — Mech 2 atom-position clamp bounds per branch; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_weight_bounds",
                category: ResetCategory::FoldReset,
                description: "ISV[WEIGHT_BOUND_BASE..+SP4_PARAM_GROUP_COUNT) = ISV[136..144) — Mech 9 post-Adam weight clamp bounds per param group; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_adam_m_bounds",
                category: ResetCategory::FoldReset,
                description: "ISV[ADAM_M_BOUND_BASE..+SP4_PARAM_GROUP_COUNT) = ISV[144..152) — Mech 5 Adam momentum diagnostic per param group; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_adam_v_bounds",
                category: ResetCategory::FoldReset,
                description: "ISV[ADAM_V_BOUND_BASE..+SP4_PARAM_GROUP_COUNT) = ISV[152..160) — Mech 5 Adam variance diagnostic per param group; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_wd_rate_bounds",
                category: ResetCategory::FoldReset,
                description: "ISV[WD_RATE_BASE..+SP4_PARAM_GROUP_COUNT) = ISV[160..168) — AdamW weight_decay rate per param group; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_grad_clip_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[GRAD_CLIP_BOUND_INDEX=168] — Mech 6 adaptive_clip upper bound; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_h_s2_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[H_S2_BOUND_INDEX=169] — Mech 10 h_s2 clamp bound; SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_l1_lambda_trunk",
                category: ResetCategory::FoldReset,
                description: "ISV[L1_LAMBDA_TRUNK_INDEX=170] — trunk L1 lambda (group-0-only); SP4 Pearl A sentinel 0 at fold boundary (Task A12)",
            },
            RegistryEntry {
                name: "sp4_bw_d_h_s2_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[BW_D_H_S2_BOUND_INDEX=171] — Layer C #260 backward-grad sanitiser bound for `bw_d_h_s2`. SP4 Pearl A sentinel 0 at fold boundary so the new fold's first `launch_sp4_bw_d_h_s2_p99` observation replaces directly (no stale prev_x_mean). Companion to sp4_wiener_state's slot-69 triple (offset 120..123) — both halves reset together per `feedback_no_partial_refactor.md`",
            },
            RegistryEntry {
                name: "sp4_q_dir_grad_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_DIR_GRAD_BOUND_INDEX=172] — Layer C #260 backward-grad sanitiser bound for `d_value_logits ∪ d_adv_logits` (multi-sub-buffer p99). SP4 Pearl A sentinel 0 at fold boundary so the new fold's first `launch_sp4_q_dir_grad_p99` observation replaces directly. Companion to sp4_wiener_state's slot-70 triple (offset 123..126) — both halves reset together per `feedback_no_partial_refactor.md`",
            },
            RegistryEntry {
                name: "sp4_wiener_state",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.wiener_state_buf [(71 + SP5_PRODUCER_COUNT) × 3 floats; total = (SP4 + SP5/SP6/SP7/SP8/SP9/SP10/SP11 producers) × {sample_var, diff_var, x_lag}] — SP4 Pearl D Wiener-EMA state for SP4 block [0..213) (40 SP4 + 29 Task-A13 retrofit + 2 Layer-C-#260 producers). Mapped-pinned f32; bulk write_bytes(0) at fold boundary covers the full buffer [0..(71 + SP5_PRODUCER_COUNT) × 3) so Pearl A's sentinel branch fires on the new fold's first producer launch for ALL SP4 and SP5+ producers (`prev_x_mean == 0 AND state.x_lag == 0` → first-observation replacement). Companion to the 11 SP4 ISV bound entries above — both halves of the sentinel contract reset together per `feedback_no_partial_refactor.md`. Buffer size derives from SP5_PRODUCER_COUNT (formula form, not a hardcoded literal) to avoid drift on each subsequent SP.",
            },
            RegistryEntry {
                name: "sp4_clamp_engage_counters",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.clamp_engage_per_block_buf [2816 ints = (SP4_PARAM_GROUP_COUNT + 3 curiosity sub-launches) × MAX_BLOCKS_PER_ADAM = 11 × 256] — SP4 Pearl C engagement counter (per-(group, Adam-launch-block) clamp-trigger count). Mapped-pinned i32; bulk write_bytes(0) at fold boundary so the new fold's engagement-rate tracking restarts from a clean 0 baseline. Buffer length grew 2048→2816 in Task A14/A15 to give Curiosity's 4 sub-launches (W1/b1/W2/b2) distinct offset slots avoiding per-block index collisions (Task A12 + A14/A15)",
            },
            RegistryEntry {
                name: "sp4_pearl_c_rate_deficit_state",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.pearl_c_rate_deficit_state_buf [24 floats = SP4_PARAM_GROUP_COUNT × {sample_var, diff_var, x_lag}] — SP4 Pearl C rate-deficit Wiener-state (per param group). Mapped-pinned f32; bulk write_bytes(0) at fold boundary so the new fold's engagement-rate-deficit EMA restarts from Pearl A sentinel (`prev_x_mean == 0 AND state.x_lag == 0` → first-observation replacement). Paired with sp4_pearl_c_rate_deficit_ema — both reset together (Task A15)",
            },
            RegistryEntry {
                name: "sp4_pearl_c_rate_deficit_ema",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.pearl_c_rate_deficit_ema [8 floats, host-only [f32; SP4_PARAM_GROUP_COUNT]] — SP4 Pearl C rate-deficit EMA per param group (the `prev_x_mean` surrogate read by `pearls_ad_update`). Reset to all-zeros at fold boundary so the new fold's deficit tracking restarts from the Pearl A sentinel. Companion to sp4_pearl_c_rate_deficit_state (Task A15)",
            },
            // ───── SP5 Task A1: Pearl 1 atom-span + shared-signal ISV entries ──
            RegistryEntry {
                name: "sp5_atom_v_center",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_V_CENTER_BASE=174..178) — per-branch C51 atom-span center (v_center). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first `pearl_1_atom_update` launch fires the first-observation replacement (Task A1).",
            },
            RegistryEntry {
                name: "sp5_atom_v_half",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_V_HALF_BASE=178..182) — per-branch C51 atom half-span (v_half = max_abs_dev × headroom). SP5 Pearl A sentinel 0 at fold boundary (Task A1).",
            },
            RegistryEntry {
                name: "sp5_atom_headroom",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_HEADROOM_BASE=182..186) — per-branch C51 atom headroom multiplier (init 6.0 via Pearl A bootstrap). SP5 Pearl A sentinel 0 at fold boundary so the headroom controller re-bootstraps to 6.0 at the new fold's first step (Task A1).",
            },
            RegistryEntry {
                name: "sp5_atom_clip_rate",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_CLIP_RATE_BASE=186..190) — per-branch atom clip rate (atoms_clip_count / (batch_size*13)). SP5 Pearl A sentinel 0 at fold boundary (Task A1).",
            },
            RegistryEntry {
                name: "sp5_budget_c51",
                category: ResetCategory::FoldReset,
                description: "ISV[BUDGET_C51_BASE=190..194) — per-branch C51 loss budget share. SP7 (2026-05-03): will be driven by the loss-balance controller using flatness-modulated target ratio (C51/IQN ≤ 1.0×flatness). Pearl A sentinel 0 at fold boundary; consumer-side floor currently 0.05 (`fused_training.rs:3384`). (Pearl 2 will stop writing this slot in SP7 T6; T7 replaces the consumer floor with sentinel-aware bootstrap.)",
            },
            RegistryEntry {
                name: "sp5_budget_iqn",
                category: ResetCategory::FoldReset,
                description: "ISV[BUDGET_IQN_BASE=194..198) — per-branch IQN loss budget share (BASE_IQN + (1-flatness)×(1-BASE_IQN), dominates when Q is flat relative to noise σ). SP5 Pearl A sentinel 0 at fold boundary (Task A3).",
            },
            RegistryEntry {
                name: "sp5_budget_cql",
                category: ResetCategory::FoldReset,
                description: "ISV[BUDGET_CQL_BASE=198..202) — per-branch CQL loss budget share. SP7 (2026-05-03): will be driven by the loss-balance controller (`loss_balance_controller_kernel`) using flatness-modulated target ratio (CQL/IQN ≤ 2.0×(1−flatness)). Pearl A sentinel 0 at fold boundary; consumer-side floor currently 0.02 (`fused_training.rs:3386`). (Pearl 2 will stop writing this slot in SP7 T6; T7 replaces the consumer floor with sentinel-aware bootstrap.)",
            },
            RegistryEntry {
                name: "sp5_budget_ens",
                category: ResetCategory::FoldReset,
                description: "ISV[BUDGET_ENS_BASE=202..206) — per-branch ensemble loss budget share. SP7 (2026-05-03): consumer-side floor currently 0.02 (`fused_training.rs:3387`); Ens is currently not actively driven (no loss-balance target ratio defined for Ens vs IQN). Pearl A sentinel 0 at fold boundary. (Pearl 2 will stop writing this slot in SP7 T6; T7 replaces the consumer floor with sentinel-aware bootstrap.)",
            },
            RegistryEntry {
                name: "sp5_flatness",
                category: ResetCategory::FoldReset,
                description: "ISV[FLATNESS_BASE=206..210) — per-branch flatness signal (var(Q[b]) / (σ[b]² + EPS_DIV), clamped to [0, 1]). Drives Pearl 2's per-branch IQN/C51 split. SP5 Pearl A sentinel 0 at fold boundary (Task A3).",
            },
            RegistryEntry {
                name: "sp5_noisy_sigma",
                category: ResetCategory::FoldReset,
                description: "ISV[NOISY_SIGMA_BASE=210..214) — per-branch NoisyNet sigma (= sigma_fraction x v_half from Pearl 1). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first pearl_3_sigma_update launch fires the first-observation replacement (Task A2).",
            },
            RegistryEntry {
                name: "sp5_sigma_fraction",
                category: ResetCategory::FoldReset,
                description: "ISV[SIGMA_FRACTION_BASE=214..218) — per-branch sigma fraction (entropy-deficit controller, init 0.1 via Pearl A bootstrap, envelope [0.001, 0.5]). SP5 Pearl A sentinel 0 at fold boundary so the controller re-bootstraps to 0.1 at the new fold's first step (Task A2).",
            },
            RegistryEntry {
                name: "sp5_branch_entropy",
                category: ResetCategory::FoldReset,
                description: "ISV[BRANCH_ENTROPY_BASE=218..222) — per-branch action entropy (mean over batch, from q_branch_stats softmax). SP5 Pearl A sentinel 0 at fold boundary (Task A1).",
            },
            RegistryEntry {
                name: "sp5_q_var_per_branch",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_VAR_PER_BRANCH_BASE=222..226) — per-branch Q-value variance (mean over batch×branch-actions). SP5 Pearl A sentinel 0 at fold boundary (Task A1).",
            },
            // SP7 (2026-05-03): loss-balance controller per-branch
            // Wiener-state EMAs. Both halves of the Pearl A contract
            // (sentinel 0 at fold boundary so the controller's
            // `old_budget < EPS_DIV` cold-start branch fires).
            RegistryEntry {
                name: "sp7_lb_diff_var_cql",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_DIFF_VAR_CQL_BASE=297..301) — per-branch EMA of (candidate_cql − old_cql)² used as Wiener α numerator for the CQL budget controller. SP7 Pearl A sentinel 0 at fold boundary so α resets cleanly when budgets re-bootstrap.",
            },
            RegistryEntry {
                name: "sp7_lb_sample_var_cql",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_SAMPLE_VAR_CQL_BASE=301..305) — per-branch EMA of (cql_norm)² used as Wiener α denominator for the CQL budget controller. SP7 Pearl A sentinel 0 at fold boundary.",
            },
            RegistryEntry {
                name: "sp7_lb_diff_var_c51",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_DIFF_VAR_C51_BASE=305..309) — per-branch EMA of (candidate_c51 − old_c51)² used as Wiener α numerator for the C51 budget controller. SP7 Pearl A sentinel 0 at fold boundary.",
            },
            RegistryEntry {
                name: "sp7_lb_sample_var_c51",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_SAMPLE_VAR_C51_BASE=309..313) — per-branch EMA of (c51_norm)² used as Wiener α denominator for the C51 budget controller. SP7 Pearl A sentinel 0 at fold boundary.",
            },
            // SP7 activation-flag fix (2026-05-03): per-(head, branch)
            // activation flags. Monotonic per fold (kernel writes 1.0 once,
            // never 0; apply_pearls_ad short-circuits 0 writes). FoldReset
            // to 0 forces the new fold to re-activate from scratch as
            // gradients populate, so the consumer's bootstrap covers the
            // fold's first few steps and the controller's first real
            // budget update lands without the indistinguishable-state
            // pathology that existed pre-fix (kernel cold_start_basis
            // numerically equaled consumer's bootstrap).
            RegistryEntry {
                name: "sp7_lb_cql_active",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_CQL_ACTIVE_BASE=313..317) — SP7 controller activation flag for CQL — fold-boundary reset to 0 forces fresh activation as grads populate in the new fold.",
            },
            RegistryEntry {
                name: "sp7_lb_c51_active",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_C51_ACTIVE_BASE=317..321) — SP7 controller activation flag for C51 — fold-boundary reset to 0 forces fresh activation as grads populate in the new fold.",
            },
            // SP8 (Fix 36, 2026-05-03): ISV-driven adaptive MAX_BUDGET cap.
            // Replaces the hardcoded `MAX_BUDGET=1.0f` constant in
            // `loss_balance_controller_kernel.cu` per
            // `pearl_controller_anchors_isv_driven.md` and
            // `feedback_isv_for_adaptive_bounds.md`. Three FoldReset entries
            // (canary + per-head cap blocks) — sentinel 0 at fold boundary
            // so Pearl A's first-observation replacement fires on the new
            // fold's first producer launch. Wiener-state companion is reset
            // by the existing bulk memset of `wiener_state_buf` covered by
            // the `sp4_wiener_state` registry entry's dispatch arm
            // (SP5_PRODUCER_COUNT linear span grew 147 → 156, the buffer
            // expanded in lockstep).
            RegistryEntry {
                name: "sp8_train_active_frac",
                category: ResetCategory::FoldReset,
                description: "ISV[TRAIN_ACTIVE_FRAC_INDEX=321] — SP8 Fix 36 canary signal. EMA-smoothed per-step train-rollout active fraction `(Short + Long) / total`, produced by `train_active_frac_compute_kernel` from `monitoring_summary[5..17)`. Drives `loss_balance_max_budget_compute_kernel` (the adaptive cap). FoldReset sentinel 0 at fold boundary so Pearl A's first-observation replacement fires on the new fold's first producer launch (per `pearl_first_observation_bootstrap.md`). Replaces the host-side compute previously at `training_loop.rs:3392-3396` per `feedback_no_cpu_compute_strict.md`.",
            },
            RegistryEntry {
                name: "sp8_lb_max_budget_cql",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_MAX_BUDGET_CQL_BASE=322..326) — SP8 Fix 36 per-branch CQL MAX_BUDGET cap. Produced by `loss_balance_max_budget_compute_kernel` from the train_active_frac canary via linear interpolation `FLOOR + active_frac × (CEIL − FLOOR)` (FLOOR=0.01, CEIL=1.0). Read by `loss_balance_controller_kernel` to clamp the per-branch CQL budget — replaces the prior hardcoded `MAX_BUDGET=1.0f` regime-encoded constant. FoldReset sentinel 0 at fold boundary; Pearl A bootstraps the cap from the producer's first observation.",
            },
            RegistryEntry {
                name: "sp8_lb_max_budget_c51",
                category: ResetCategory::FoldReset,
                description: "ISV[LB_MAX_BUDGET_C51_BASE=326..330) — SP8 Fix 36 per-branch C51 MAX_BUDGET cap. Produced by `loss_balance_max_budget_compute_kernel` (same producer as the CQL block) from the same `train_active_frac` canary; per-(head, branch) layout is forward-compatible with future divergent caps. Read by `loss_balance_controller_kernel` to clamp the per-branch C51 budget. FoldReset sentinel 0 at fold boundary.",
            },
            // SP9 (Fix 37, 2026-05-03): Kelly cold-start warmup floor —
            // fully ISV-driven. Replaces the prior implicit cold-start
            // exit (single-axis statistical via `total_trades >= 10`) with
            // OR'd statistical / behavioral / temporal confidence per
            // `pearl_cold_start_exit_signal_or.md`. Co-located eval_dist
            // GPU migration deletes the DtoH host helper per
            // `feedback_no_cpu_compute_strict.md`. Nine FoldReset entries
            // (warmup_floor + q_var_mag_ema + divergence + 3 EMA targets +
            // 3 eval_dist mag bins) — sentinel 0 at fold boundary so
            // Pearl A's first-observation replacement fires on the new
            // fold's first producer launch. Wiener-state companion is
            // reset by the existing bulk memset of `wiener_state_buf`
            // covered by the `sp4_wiener_state` registry entry's dispatch
            // arm (SP5_PRODUCER_COUNT linear span grew 156 → 165, the
            // buffer expanded in lockstep).
            RegistryEntry {
                name: "sp9_kelly_warmup_floor",
                category: ResetCategory::FoldReset,
                description: "ISV[KELLY_WARMUP_FLOOR_INDEX=330] — SP9 Fix 37 adaptive Kelly cold-start floor. Produced by `kelly_warmup_floor_compute_kernel` from base_floor (q_var_mag self-relative ratio) × (1 − OR'd statistical/behavioral/temporal confidence). Consumed in `unified_env_step_core` (`trade_physics.cuh`) as an additional max-floor on `health_safety_sp5`. FoldReset sentinel 0 at fold boundary so the cap re-engages for new-fold learning until OR'd confidence accumulates. Per `pearl_cold_start_exit_signal_or.md`.",
            },
            RegistryEntry {
                name: "sp9_q_var_mag_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_VAR_MAG_EMA_INDEX=331] — SP9 Fix 37 EMA-tracked baseline of magnitude-branch Q-variance (`ISV[FLATNESS_BASE+1]`). Drives the self-relative `base_floor` ratio in the warmup-floor formula per `pearl_controller_anchors_isv_driven.md` (eliminates the prior 0.5 hardcoded half-Kelly constant). Produced by `q_var_mag_ema_compute_kernel`; smoothed by Pearl D Wiener-α; FoldReset sentinel 0 → Pearl A bootstraps from the new fold's first observation.",
            },
            RegistryEntry {
                name: "sp9_intent_eval_divergence",
                category: ResetCategory::FoldReset,
                description: "ISV[INTENT_EVAL_DIVERGENCE_INDEX=332] — SP9 Fix 37 intent vs eval Full-magnitude divergence ratio `intent_f / eval_f`. Behavioral-axis numerator of the OR'd cold-start exit. Produced by `intent_eval_divergence_compute_kernel` from `monitoring_summary[5..17)` + `ISV[EVAL_DIST_F_INDEX]`. Under healthy training the ratio ≈ 1; under val-Flat-collapse it rises sharply, dropping behavioral_conf to 0 and keeping the cap closed. FoldReset sentinel 0 so Pearl A bootstraps from the new fold's first observation.",
            },
            RegistryEntry {
                name: "sp9_kelly_sample_count_target",
                category: ResetCategory::FoldReset,
                description: "ISV[KELLY_SAMPLE_COUNT_TARGET_INDEX=333] — SP9 Fix 37.1 (2026-05-03) Invariant-1 numerical anchor: minimum trade samples (100.0) for statistical-confidence release of the OR'd cold-start exit. Originally Fix 37 routed this through `kelly_sample_count_target_ema_kernel` + Pearl A/D smoothing, but smoke-test-wrwkz revealed sentinel-bootstrap saturated the EMA to the first observation in epoch 1, making `current/target = 1.0` and defeating the cold-start floor. Now constructor-written ONCE per `feedback_isv_for_adaptive_bounds.md`. FoldReset rewrites the same Invariant-1 anchor (NOT sentinel 0) since this is a constant, not a stateful EMA. The consumer's `kelly_sample_count / sample_count_target` ratio yields a self-normalised `statistical_conf` in [0, 1] that rises monotonically as samples accumulate.",
            },
            RegistryEntry {
                name: "sp9_kelly_divergence_target",
                category: ResetCategory::FoldReset,
                description: "ISV[KELLY_DIVERGENCE_TARGET_INDEX=334] — SP9 Fix 37.1 (2026-05-03) Invariant-1 numerical anchor: divergence ratio (2.0) above which intent-vs-eval is considered diverged enough to keep the cap closed (behavioral-axis denominator of the OR'd cold-start exit). Originally Fix 37 routed this through `kelly_divergence_target_ema_kernel` + Pearl A/D smoothing, but smoke-test-wrwkz revealed sentinel-bootstrap saturated the EMA to the first observation in epoch 1 (`div_tgt=70 005`), making the consumer's ratio collapse to 1.0 and defeating the cold-start floor. Now constructor-written ONCE per `feedback_isv_for_adaptive_bounds.md`. FoldReset rewrites the same Invariant-1 anchor (NOT sentinel 0). The consumer's `1 − divergence / divergence_target` ratio yields a self-normalised `behavioral_conf` in [0, 1] that drops sharply when divergence exceeds the threshold.",
            },
            RegistryEntry {
                name: "sp9_kelly_temporal_target",
                category: ResetCategory::FoldReset,
                description: "ISV[KELLY_TEMPORAL_TARGET_INDEX=335] — SP9 Fix 37.1 (2026-05-03) Invariant-1 numerical anchor: minimum epochs in fold (5.0) before temporal-confidence release of the OR'd cold-start exit (the safety net per Risk 3 in the SP9 spec — always fires once epoch_idx ≥ target if statistical and behavioral both stay dormant). Originally Fix 37 routed this through `kelly_temporal_target_ema_kernel` + Pearl A/D smoothing, but smoke-test-wrwkz revealed sentinel-bootstrap saturated the EMA to the first observation in epoch 1 (`temp_tgt=1.0`), making temporal_conf trivially `epoch_idx/1.0 ≥ 1` from epoch 1 onwards. Now constructor-written ONCE per `feedback_isv_for_adaptive_bounds.md`. FoldReset rewrites the same Invariant-1 anchor (NOT sentinel 0). The consumer's `epoch_idx_in_fold / temporal_target` ratio yields a self-normalised `temporal_conf` in [0, 1] that rises linearly with fold-time.",
            },
            RegistryEntry {
                name: "sp9_eval_dist_q",
                category: ResetCategory::FoldReset,
                description: "ISV[EVAL_DIST_Q_INDEX=336] — SP9 Fix 37 GPU-resident eval-side Quarter mag fraction. Produced by `eval_intent_dist_compute_kernel` reading `intent_mag_buf` directly on the GPU; smoothed by Pearl D Wiener-α. Replaces the prior DtoH `read_eval_intent_magnitude_distribution()[0]` per `feedback_no_cpu_compute_strict.md`. FoldReset sentinel 0; Pearl A bootstraps from the first val window of the new fold.",
            },
            RegistryEntry {
                name: "sp9_eval_dist_h",
                category: ResetCategory::FoldReset,
                description: "ISV[EVAL_DIST_H_INDEX=337] — SP9 Fix 37 GPU-resident eval-side Half mag fraction. Same producer as Q/F. FoldReset sentinel 0.",
            },
            RegistryEntry {
                name: "sp9_eval_dist_f",
                category: ResetCategory::FoldReset,
                description: "ISV[EVAL_DIST_F_INDEX=338] — SP9 Fix 37 GPU-resident eval-side Full mag fraction. Same producer as Q/H. ALSO read by the `intent_eval_divergence_compute_kernel` each training step (cross-window persistent within a fold). FoldReset sentinel 0; Pearl A bootstraps from the first val window's first non-empty intent_mag_buf reduction.",
            },
            // SP10 (Fix 38, 2026-05-03): eval Thompson selector temperature.
            // Per `pearl_thompson_for_distributional_action_selection.md`
            // (amended) + `pearl_controller_anchors_isv_driven.md` +
            // `pearl_blend_formulas_must_have_permanent_floor.md`: the
            // rollout selector is unconditional Thompson; argmax is
            // reserved for the Bellman target. The temperature is a side-
            // output of `intent_eval_divergence_compute_kernel` clamped to
            // [0.5, 2.0]. FoldReset sentinel 0 so Pearl A's first-
            // observation replacement fires on the new fold's first
            // producer launch — the consumer's defensive
            // `if (!(temp > 0.5f)) temp = 0.5f` pre-bootstrap clamp keeps
            // the selector at its permanent-stochasticity floor before the
            // producer fires (read-after-reset in the same fold step).
            RegistryEntry {
                name: "sp10_eval_thompson_temp",
                category: ResetCategory::FoldReset,
                description: "ISV[EVAL_THOMPSON_TEMP_INDEX=339] — SP10 Fix 38 eval Thompson selector temperature. Produced as a side-output of `intent_eval_divergence_compute_kernel` (`temp = clamp(divergence/divergence_target, 0.5, 2.0)`). Consumed in `experience_action_select` direction-branch unconditionally (training AND eval) for the temperature-blended Thompson sample `q_eff[d] = E[Q][d] + temp · (q_sample[d] − E[Q][d])`. MIN_TEMP=0.5 enforces permanent stochasticity per `pearl_blend_formulas_must_have_permanent_floor.md`. Eliminates the val-Flat-collapse pathology (T10 train-multi-seed-khr7c on `8a25b330f`: dir_entropy=0, trade_count=1 in 214k bars) caused by the prior `eval_mode ⇒ argmax(E[Q])` selector branch. FoldReset sentinel 0; Pearl A bootstraps from the first observation; the consumer's defensive clamp keeps the selector at MIN_TEMP between fold-reset and first producer launch.",
            },
            // ── SP11 (Fix 39, 2026-05-04): reward-subsystem controller ─────
            //
            // Per `pearl_reward_as_controlled_subsystem.md` +
            // `pearl_controller_anchors_isv_driven.md`: 20 new ISV slots at
            // [340..360) populated by the SP11 controller chain — 6 component
            // weights + 4 controller scalar outputs + 10 canaries (val-sharpe
            // delta/var, 6 mag-ratios, saboteur engagement, PnL EMA). All
            // slots are FoldReset sentinel 0; Pearl A bootstraps from the
            // first producer launch on the new fold (per
            // `pearl_first_observation_bootstrap.md`). Producers and
            // consumers are introduced in subsequent A1/A2/B tasks; A0 is
            // additive — no consumer reads any of these slots yet, so the
            // trainer continues to behave identically.
            //
            // The 20 entries below mirror the registry layout established by
            // the SP9/SP10 entries above (one entry per uniquely-zeroed slot
            // / per-related-block). The 6 mag-ratio canaries occupy a
            // contiguous 6-slot block at [352..358) and share a single
            // entry — the dispatch arm zeroes the whole range with one loop,
            // matching the `sp5_pnl_aggregation`/`sp5_health_composition`
            // pattern.
            //
            // Wiener-state companion: the bulk memset of `wiener_state_buf`
            // covered by the `sp4_wiener_state` registry entry's dispatch
            // arm includes the new SP11 triples (the buffer linear span
            // grew from `(SP4_PRODUCER_COUNT + 166) × 3` to
            // `(SP4_PRODUCER_COUNT + 186) × 3` = +60 floats). Both halves
            // of the Pearl A sentinel contract reset together per
            // `feedback_no_partial_refactor.md`.
            //
            // SP11 Task A2 (2026-05-04): novelty visit-count hash table
            // reset arm — closes the A0 deferral. The buffer is a 1M-slot
            // f32 mapped-pinned table on `GpuDqnTrainer.novelty_hash_buf`;
            // the dispatch arm (`reset_named_state` in `training_loop.rs`)
            // zero-fills the host-side mapped slice via
            // `host_slice_mut().fill(0.0)` — the kernel reading via
            // `dev_ptr` observes zeros after the next stream-sync barrier.
            //
            // Cross-fold persistence is wrong here: the policy at fold N+1
            // is a different model than fold N, so visit counts from N
            // would mis-classify N+1's state-action distribution as
            // already-explored. FoldReset is the correct semantics — the
            // hash function (the projection matrix) stays frozen for
            // reproducibility, but the visit-count table re-cold-starts.
            //
            // The 42×16 SimHash projection matrix on `novelty_simhash_proj`
            // is intentionally NOT in this registry — it's the hash
            // function, not state, and must outlive every fold for
            // reproducibility (different runs with same seed get same
            // hash function).
            RegistryEntry {
                name: "sp11_novelty_hash",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.novelty_hash_buf — SP11 Fix 39 SimHash novelty visit-count hash table (1M slots × f32 = 4 MB mapped-pinned). Cleared at fold boundary so the new fold's policy starts with a clean visit-count → 1.0 novelty signal everywhere → maximal exploration pressure during the fold's cold-start window. The dispatch arm zero-fills via `host_slice_mut().fill(0.0)` — CPU writing a literal zero (NOT compute) per `feedback_no_htod_htoh_only_mapped_pinned.md`. The companion 42×16 SimHash projection matrix at `novelty_simhash_proj` is NOT in this registry — it's the hash function (frozen at trainer init from `SP11_PROJ_SEED_SALT`) and must outlive every fold for reproducibility.",
            },
            RegistryEntry {
                name: "sp11_reward_popart_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_POPART_WEIGHT_INDEX=340] — SP11 Fix 39 PopArt reward component weight. Produced by `reward_subsystem_controller_kernel` from improvement_z + per-component mag-ratio canaries (winner/diversifier blend). Consumed in unified-reward composition (Layer B). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit (per `pearl_first_observation_bootstrap.md`). Spec: docs/superpowers/specs/2026-05-04-sp11-reward-as-controlled-subsystem.md §3.3.",
            },
            RegistryEntry {
                name: "sp11_reward_cf_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_CF_WEIGHT_INDEX=341] — SP11 Fix 39 counterfactual (CF) reward component weight. Produced by `reward_subsystem_controller_kernel`. Consumed in `mse_loss_kernel` and `c51_loss_kernel` (Layer B replaces the hardcoded `cf_weight=0.3f`). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit (per `pearl_first_observation_bootstrap.md`). Spec §3.3.",
            },
            RegistryEntry {
                name: "sp11_reward_trail_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_TRAIL_WEIGHT_INDEX=342] — SP11 Fix 39 trail-stop reward component weight. Produced by `reward_subsystem_controller_kernel`. Consumed in trail-shaping reward path (Layer B). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit. Spec §3.3.",
            },
            RegistryEntry {
                name: "sp11_reward_micro_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_MICRO_WEIGHT_INDEX=343] — SP11 Fix 39 microstructure reward component weight. Produced by `reward_subsystem_controller_kernel`. Consumed in microstructure-shaping reward path (Layer B). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit. Spec §3.3.",
            },
            RegistryEntry {
                name: "sp11_reward_opp_cost_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_OPP_COST_WEIGHT_INDEX=344] — SP11 Fix 39 opportunity-cost reward component weight. Produced by `reward_subsystem_controller_kernel`. Consumed in opportunity-cost shaping reward path (Layer B). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit. Spec §3.3.",
            },
            RegistryEntry {
                name: "sp11_reward_bonus_weight",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_BONUS_WEIGHT_INDEX=345] — SP11 Fix 39 exploration-bonus reward component weight. Produced by `reward_subsystem_controller_kernel`. Consumed in bonus-shaping reward path (Layer B). FoldReset sentinel 0; Pearl A bootstraps from the first controller emit. Spec §3.3.",
            },
            RegistryEntry {
                name: "sp11_curiosity_pressure",
                category: ResetCategory::FoldReset,
                description: "ISV[CURIOSITY_PRESSURE_INDEX=346] — SP11 Fix 39 curiosity-pressure scalar. Produced by `reward_subsystem_controller_kernel` from val-sharpe trend + mag-ratio dispersion. Consumed at REPLAY-time (not collect-time) as `r_with_curiosity = r_base + curiosity_pressure · novelty_signal(state, action)`. Curiosity-bound capped at `SP11_CURIOSITY_BOUND_FRACTION=0.3 × pnl_reward_magnitude_ema`, with a permanent-fraction floor of `SP11_CURIOSITY_PERMANENT_FRACTION=0.2 × bound` per `pearl_blend_formulas_must_have_permanent_floor.md`. FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3, §3.4.2.",
            },
            RegistryEntry {
                name: "sp11_saboteur_intensity_mult",
                category: ResetCategory::FoldReset,
                description: "ISV[SABOTEUR_INTENSITY_MULT_INDEX=347] — SP11 Fix 39 saboteur curriculum intensity multiplier. Produced by `reward_subsystem_controller_kernel` from saboteur_engagement_rate canary. Consumed in `gpu_experience_collector` (Layer B multiplies `saboteur_perturbation_scale` by this slot). Clamped to `[SP11_SABOTEUR_MIN=0.5, SP11_SABOTEUR_MAX=2.0]` (Invariant-1 rate-limiter anchor per `feedback_isv_for_adaptive_bounds.md` §3.4.2). FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3, §3.4.2.",
            },
            RegistryEntry {
                name: "sp11_reward_weight_floor",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_WEIGHT_FLOOR_INDEX=348] — SP11 Fix 39 controller-output minimum-weight floor (`SP11_WEIGHT_FLOOR_FRACTION=0.5 × hard_floor`). Produced by `reward_subsystem_controller_kernel`. Consumed by the controller itself (re-read on next step) to enforce that no reward component drops below `SP11_WEIGHT_HARD_FLOOR=0.01` after blending — Invariant-1 rate-limiter floor preventing winner-take-all collapse. FoldReset sentinel 0; Pearl A bootstraps. Spec §3.4.2.",
            },
            RegistryEntry {
                name: "sp11_curiosity_bound",
                category: ResetCategory::FoldReset,
                description: "ISV[CURIOSITY_BOUND_INDEX=349] — SP11 Fix 39 curiosity-pressure structural cap (`SP11_CURIOSITY_BOUND_FRACTION=0.3 × pnl_reward_magnitude_ema`). Produced by `reward_subsystem_controller_kernel`. Consumed at the curiosity-pressure clamp — keeps curiosity scaled to the run's PnL magnitude rather than a tuned constant per `feedback_isv_for_adaptive_bounds.md`. FoldReset sentinel 0; Pearl A bootstraps. Spec §3.4.2.",
            },
            RegistryEntry {
                name: "sp11_val_sharpe_delta_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VAL_SHARPE_DELTA_EMA_INDEX=350] — SP11 Fix 39 val-sharpe trend canary. Produced by `val_sharpe_delta_compute_kernel` (two-pass delta_ema + delta_var_ema, A1). Smoothed by Pearls A+D via chained `apply_pearls_ad_kernel` per `feedback_no_cpu_compute_strict.md`. Consumed by the SP11 controller as the improvement_z numerator. FoldReset sentinel 0; Pearl A bootstraps from the first val-sharpe sample of the new fold. Spec §3.3.1.",
            },
            RegistryEntry {
                name: "sp11_val_sharpe_var_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[VAL_SHARPE_VAR_EMA_INDEX=351] — SP11 Fix 39 val-sharpe variance canary (squared-deviation EMA against the running delta_ema). Produced alongside `sp11_val_sharpe_delta_ema` by `val_sharpe_delta_compute_kernel` (A1). Smoothed by Pearls A+D. Consumed by the SP11 controller as the improvement_z denominator (`improvement_z = delta_ema / sqrt(var_ema + SP11_EPS_DIV)`). FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3.1.",
            },
            RegistryEntry {
                name: "sp11_reward_component_mag_ratios",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_COMPONENT_MAG_RATIO_BASE=352..358) — SP11 Fix 39 per-component reward-magnitude ratios (6 contiguous slots: popart, cf, trail, micro, opp_cost, bonus). Produced by `reward_component_mag_ratio_compute_kernel` (A1) reading existing per-component reward-magnitude EMAs (`REWARD_POPART_EMA_INDEX..+6`) and normalising to a unit simplex. Smoothed by Pearls A+D via chained `apply_pearls_ad_kernel`. Consumed by the SP11 controller for the winner_weight / diversifier_weight blend. The single registry entry covers the whole 6-slot block — the dispatch arm zeroes all 6 with one loop, matching the `sp5_pnl_aggregation` block-reset pattern. Per `feedback_trust_code_not_docs.md`: spec §3.3.1 said `grad EMA` but the SP4 grad-balancer is per-branch (4 slots), not per-reward-component — code wins, terminology corrected to `mag-ratio` (semantics for the controller are equivalent). FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3.1.",
            },
            RegistryEntry {
                name: "sp11_saboteur_engagement_rate",
                category: ResetCategory::FoldReset,
                description: "ISV[SABOTEUR_ENGAGEMENT_RATE_INDEX=358] — SP11 Fix 39 saboteur engagement-rate canary (per-bar fraction of bars where the saboteur curriculum was actively perturbing experience tuples). Produced by `saboteur_engagement_compute_kernel` (A1) — block tree-reduce over the per-bar engagement flag, then EMA. Smoothed by Pearls A+D. Consumed by the SP11 controller to drive `saboteur_intensity_mult` toward the engagement floor (`SP11_ENGAGEMENT_FLOOR=0.1`). FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3.1.",
            },
            RegistryEntry {
                name: "sp11_pnl_reward_magnitude_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[PNL_REWARD_MAGNITUDE_EMA_INDEX=359] — SP11 Fix 39 PnL-reward magnitude EMA (running `|reward_total|` smoothed with Wiener-α). Produced by the unified-reward kernel chain (existing producer; SP11 wires the EMA output to this slot in A1 alongside the val-sharpe and engagement canaries). Consumed by the SP11 controller as the curiosity-bound scale factor (`curiosity_bound = SP11_CURIOSITY_BOUND_FRACTION=0.3 × this slot`) per `feedback_isv_for_adaptive_bounds.md` §3.4.2. FoldReset sentinel 0; Pearl A bootstraps. Spec §3.3.1, §3.4.2.",
            },
            RegistryEntry {
                name: "sp11_popart_component_mag_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[POPART_COMPONENT_MAG_EMA_INDEX=360] — SP11 Fix 39 B1b fix-up popart-component-specific magnitude EMA. Produced by `popart_component_ema_kernel` (block tree-reduce over the per-bar `popart_component_per_sample` GPU buffer populated by `experience_env_step` at the `r_popart = capped_pnl;` voluntary-exit branch); smoothed by Pearls A+D via chained `apply_pearls_ad_kernel` (n_slots=1). Consumed by the SP11 mag-ratio canary as the popart axis input — replaces ISV[REWARD_POPART_EMA_INDEX=63]'s overloaded role as 'popart's signal' with a popart-component-specific value. Pre-fix-up slot 63 was overloaded (PopArt's normalisation input AND popart-component magnitude collapsed to the same value because reward composition was inline accumulation); B1b's structural decomposition surfaced the contamination — controller redistributed weight toward popart (w_pop ≈ 2.0) based on a contaminated ratio → 10× sharpe drop in B1b smoke. Slot 63 unchanged (PopArt's input = total reward magnitude, pre-SP11 invariant preserved). FoldReset sentinel 0; Pearl A bootstraps from the first observation. Spec §4 amendment 'Why slot 360'.",
            },
            RegistryEntry {
                name: "sp11_reward_component_var_emas",
                category: ResetCategory::FoldReset,
                description: "ISV[REWARD_COMPONENT_VAR_EMA_BASE=361..367) — SP11 Fix 39 B1b smoke-recovery (2026-05-04, spec §4 amendment 'Why z-score' lines 564-619) per-reward-component variance EMAs (6 contiguous slots: popart at 361, then cf 362, trail 363, micro 364, opp_cost 365, bonus 366). Slot 361 is produced alongside the popart-component magnitude EMA by the extended `popart_component_ema_kernel` via single-pass two-tree-reduce Welford (mean → var); slots 362..367 are produced alongside the non-popart magnitudes by the extended `reward_component_ema_kernel` via per-thread Welford (component 0/popart variance is split off because slot 63 is the pre-SP11 total-reward magnitude EMA, not popart-component magnitude — see slot-360 fix-up rationale). All 6 variances are smoothed by Pearls A+D via chained `apply_pearls_ad_kernel` (n_slots=1 for popart variance at slot 361, n_slots=5 for the contiguous block at slots 362..367). Consumed by `reward_component_mag_ratio_compute_kernel` for z-score normalisation: `z[c] = mag[c] / max(sqrt(var[c]), EPS_DIV)` then `ratio[c] = z[c] / sum_z`. Replaces the linear `winner_weight = mag_ratio` formula that amplified popart's intrinsic O(100) magnitude over the other components' O(0.1-2) — w_pop saturated to MAX_WEIGHT, curiosity_b exploded, sharpe collapsed in B1b smoke `smoke-test-6wd2c` on commit `61b2fa962`. The single registry entry covers the whole 6-slot block — the dispatch arm zeroes all 6 with one loop, matching the `sp11_reward_component_mag_ratios` block-reset pattern. FoldReset sentinel 0; Pearl A bootstraps the variance from the first observation alongside its paired magnitude.",
            },
            // ── SP13 (2026-05-04): redefine success for predictive skill ─────
            //
            // Phase 0a Task T1: register the two dir-acc EMA slots so the
            // fold-boundary path resets them to the random-guessing sentinel
            // 0.5 (NOT 0.0 — directional accuracy has a meaningful baseline at
            // 50%, and Pearl A's first-observation replacement fires on the
            // first epoch of the new fold to track the new fold's signal
            // without contamination from the previous fold's terminal value).
            //
            // Slots 372 (TARGET_DIR_ACC), 376..380 (DIR_SKILL_BONUS_ALPHA/BETA,
            // LUCK_WIN_DISCOUNT, SKILL_BONUS_CAP_RATIO) are constructor-
            // initialised Invariant-1 anchors and need NO registry entry —
            // they must never reach sentinel 0 between folds, so the
            // constructor-write path keeps them populated with their defaults
            // for the lifetime of the trainer. Slot 375 (AUX_DIR_PREDICTION)
            // is per-bar overwritten by the aux head and needs no reset
            // entry either. Spec: docs/superpowers/specs/2026-05-04-sp13-redefine-success-for-predictive-skill.md
            RegistryEntry {
                name: "sp13_aux_dir_acc_short_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373] — SP13 Phase 0a fast EMA of directional accuracy (α=0.3). Produced by `apply_fixed_alpha_ema_kernel` chained off `aux_dir_acc_reduce_kernel` (the fixed-α applicator preserves the short/long timescale split that Wiener-optimal `apply_pearls_ad_kernel` would collapse). Consumed by the Phase 0b aux-w controller as the deficit numerator (`deficit = max(0, target − short)`) and the stagnation comparator. FoldReset sentinel 0.5 (random-guessing baseline) so Pearl A bootstraps from the new fold's first observation per `pearl_first_observation_bootstrap.md` — the EMA tracks the new fold's signal without bias from the previous fold's terminal accuracy. Spec §implementation phases.",
            },
            RegistryEntry {
                name: "sp13_aux_dir_acc_long_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_DIR_ACC_LONG_EMA_INDEX=374] — SP13 Phase 0a slow EMA of directional accuracy (α=0.05) feeding the stagnation detector. Produced by `apply_fixed_alpha_ema_kernel` chained off `aux_dir_acc_reduce_kernel` (the fixed-α applicator preserves the short/long timescale split that Wiener-optimal `apply_pearls_ad_kernel` would collapse). Consumed by the Phase 0b aux-w controller's stagnation term (`stagnation = max(0, 1 − (short − long) / max(0.005, deficit))`) so a stalled short EMA decays aux_w back toward base — the deliberate 'we tried, it didn't help' signal that distinguishes Phase 0b's controller from SP11's inverted formula. FoldReset sentinel 0.5 (random-guessing baseline) so Pearl A bootstraps from the new fold's first observation per `pearl_first_observation_bootstrap.md`. Spec §implementation phases.",
            },
            // SP13 v3 P0a.T3 (2026-05-04): Hold-pricing observed-rate EMA.
            // Slots 380 (HOLD_COST) and 381 (HOLD_RATE_TARGET) are
            // constructor-initialised Invariant-1 anchors and need NO
            // registry entry — slot 380 is rewritten every step by the
            // host-side controller in `training_loop.rs`, and slot 381 is
            // a static default (0.20) that must never reach sentinel 0
            // between folds. Slot 382 is the per-fold observed-rate EMA
            // produced by `apply_fixed_alpha_ema_kernel` chained off
            // `hold_rate_observer_kernel`; sentinel 0.0 lets Pearl A
            // bootstrap from the new fold's first observation per
            // `pearl_first_observation_bootstrap.md` so the EMA tracks
            // the new fold's Hold-rate without bias from the previous
            // fold's terminal value.
            RegistryEntry {
                name: "sp13_hold_rate_observed_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[HOLD_RATE_OBSERVED_EMA_INDEX=382] — SP13 v3 P0a.T3 per-fold EMA of the per-step Hold-pick rate `count(action_dir == DIR_HOLD) / batch_size`. Produced by `apply_fixed_alpha_ema_kernel` chained off `hold_rate_observer_kernel` (added in P0a.T3). Consumed by the host-side Hold-cost controller in `training_loop.rs`: `excess = max(0, observed − target); hold_cost = HOLD_COST_BASE × (1 + 5 × excess)` clamped to `[0.5×base, 5.0×base]`, written to ISV[HOLD_COST_INDEX=380]. The reward-composition site in `experience_kernels.cu` then subtracts the cost on every Hold-action bar — pricing the action so the policy uses Hold deliberately rather than as a free CQL-bias-anchored default. FoldReset sentinel 0.0 so Pearl A bootstraps from the new fold's first observation; the controller relaxes back to baseline cost (HOLD_COST_BASE × 0.5 floor) when observed ≤ target. Spec §'Change 1: Price Hold (replaces v2's Eliminate Hold)'.",
            },
            // ── SP14 fold-reset slots [383..396) ─────────────────────────────
            // 11 of the 13 allocated slots reset at fold boundary; the two
            // exceptions are:
            //   - ALPHA_GRAD_RAW_INDEX=392: recomputed every step from the
            //     three Welford variance EMAs (388, 389, 390); no stale value
            //     persists past the first step of a new fold.
            //   - GRADIENT_HACK_LOCKOUT_REMAINING_INDEX=395: epoch-boundary
            //     decay; the lockout counter naturally reaches 0 at the start
            //     of any new epoch/fold and must NOT be reset here (doing so
            //     would silently abort an active lockout mid-fold).
            // Both naturally re-initialise without explicit reset.
            // Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md
            RegistryEntry {
                name: "sp14_q_disagreement_short_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_DISAGREEMENT_SHORT_EMA_INDEX=383] — SP14 EGF Gate 2 fast EMA (α=0.3) of per-step K=4↔K=2 mapped argmax disagreement with Hold/Flat masked. Produced by `q_disagreement_update_kernel` (B.3) every training step. Consumed by `alpha_grad_compute_kernel` (B.4) as the Gate 2 sigmoid driver. FoldReset sentinel SENTINEL_Q_DISAGREEMENT=0.5 (analytic random alignment under masking) so Pearl A bootstraps from the new fold's first observation per `pearl_first_observation_bootstrap.md` — the EMA tracks the new fold's Q-head signal without bias from the previous fold's terminal value. Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_q_disagreement_long_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_DISAGREEMENT_LONG_EMA_INDEX=384] — SP14 EGF Gate 2 slow EMA (α=0.05) of per-step K=4↔K=2 mapped argmax disagreement with Hold/Flat masked. Produced by `q_disagreement_update_kernel` (B.3) every training step. Used alongside the short EMA for stagnation detection in the Gate 2 controller (persistent high disagreement despite training ↔ Q head stuck, not just noisy). FoldReset sentinel SENTINEL_Q_DISAGREEMENT=0.5 so Pearl A bootstraps from the new fold's first observation per `pearl_first_observation_bootstrap.md`. Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_k_aux_adaptive",
                category: ResetCategory::FoldReset,
                description: "ISV[K_AUX_ADAPTIVE_INDEX=385] — SP14 ISV-driven adaptive sigmoid steepness for Gate 1 (aux competence). Driven by AUX_DIR_ACC_VARIANCE_EMA_INDEX=388 via `k_aux = K_BASE_AUX × exp(-variance / VARIANCE_REF_AUX)` clamped to [K_MIN, K_BASE_AUX]; high aux variance → flatter sigmoid → more cautious gate opening. FoldReset sentinel K_BASE_AUX=20.0 (Invariant-1 anchor: start each fold with maximum steepness, decay only if the new fold's signal is noisy). Produced by `alpha_grad_compute_kernel` (B.4); consumed by Gate 1 sigmoid. Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_k_q_adaptive",
                category: ResetCategory::FoldReset,
                description: "ISV[K_Q_ADAPTIVE_INDEX=386] — SP14 ISV-driven adaptive sigmoid steepness for Gate 2 (Q-head disagreement). Driven by Q_DISAGREEMENT_VARIANCE_EMA_INDEX=389 via `k_q = K_BASE_Q × exp(-variance / VARIANCE_REF_Q)` clamped to [K_MIN, K_BASE_Q]; high Q-disagreement variance → flatter sigmoid → more cautious gate modulation. FoldReset sentinel K_BASE_Q=15.0 (Invariant-1 anchor: start each fold with maximum steepness). Produced by `alpha_grad_compute_kernel` (B.4); consumed by Gate 2 sigmoid. Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_beta_rate_limiter_adaptive",
                category: ResetCategory::FoldReset,
                description: "ISV[BETA_RATE_LIMITER_ADAPTIVE_INDEX=387] — SP14 ISV-driven adaptive rate-limiter β for the α_grad_smoothed EMA. Driven by ALPHA_GRAD_RAW_VARIANCE_EMA_INDEX=390 via `β = BETA_BASE + (BETA_MAX − BETA_BASE) × (1 − exp(-variance / VARIANCE_REF_ALPHA))` so volatile α_grad_raw → heavier smoothing (β → BETA_MAX=0.95) and stable signal → light smoothing (β → BETA_BASE=0.5). FoldReset sentinel BETA_BASE=0.5 (Invariant-1 anchor: start each fold with light smoothing). Produced by `alpha_grad_compute_kernel` (B.4); consumed by the α_grad_smoothed EMA update. Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_aux_dir_acc_variance_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_DIR_ACC_VARIANCE_EMA_INDEX=388] — SP14 Welford variance EMA of per-step aux directional accuracy (Welford one-pass, α=0.05). Drives K_AUX_ADAPTIVE_INDEX=385: low variance → high k_aux (confident gate), high variance → low k_aux (cautious gate). FoldReset sentinel SENTINEL_VARIANCE=0.0 so that k_aux initialises at K_BASE_AUX=20.0 (maximum steepness) at the start of each fold and decays only if the new fold's aux-acc signal is genuinely noisy. Produced by `aux_variance_update_kernel` (B.3). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_q_disagreement_variance_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[Q_DISAGREEMENT_VARIANCE_EMA_INDEX=389] — SP14 Welford variance EMA of per-step Q-disagreement signal (Welford one-pass, α=0.05). Drives K_Q_ADAPTIVE_INDEX=386: low variance → high k_q (confident Gate 2 modulation), high variance → low k_q (cautious). FoldReset sentinel SENTINEL_VARIANCE=0.0 so that k_q initialises at K_BASE_Q=15.0 at the start of each fold. Produced by `q_disagreement_update_kernel` (B.3). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_alpha_grad_raw_variance_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[ALPHA_GRAD_RAW_VARIANCE_EMA_INDEX=390] — SP14 Welford variance EMA of per-step α_grad_raw (Welford one-pass, α=0.05). Drives BETA_RATE_LIMITER_ADAPTIVE_INDEX=387: low α_grad_raw variance → light smoothing (β → BETA_BASE=0.5, responsive), high variance → heavy smoothing (β → BETA_MAX=0.95, stable). FoldReset sentinel SENTINEL_VARIANCE=0.0 so that β initialises at BETA_BASE=0.5 at the start of each fold. Produced by `alpha_grad_compute_kernel` (B.4). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_gate1_open_state",
                category: ResetCategory::FoldReset,
                description: "ISV[GATE1_OPEN_STATE_INDEX=391] — SP14 Schmitt-trigger persistent state for Gate 1 (0.0=closed, 1.0=open). Hysteresis: open when aux_dir_acc crosses target+SCHMITT_BAND=0.03, close when it drops below target-SCHMITT_BAND. Prevents rapid open/close cycling near the threshold. FoldReset sentinel SENTINEL_GATE1_CLOSED=0.0 (gate starts closed at each fold boundary; Gate 1 must re-earn the right to open by demonstrating competence on new fold data before α_grad can be non-zero). Produced and consumed by `alpha_grad_compute_kernel` (B.4). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_alpha_grad_smoothed",
                category: ResetCategory::FoldReset,
                description: "ISV[ALPHA_GRAD_SMOOTHED_INDEX=393] — SP14 rate-limited (β-smoothed) α_grad, the actual scalar consumed by the backward path to modulate aux→Q gradient flow. Updated each step as `α_smoothed = β × α_smoothed + (1−β) × α_raw` where β = BETA_RATE_LIMITER_ADAPTIVE_INDEX=387. FoldReset sentinel SENTINEL_ALPHA_GRAD=0.0 (no gradient flow at fold start; the smoothed gate must re-ramp from zero so the policy's early fold exploration is not contaminated by the previous fold's terminal gate state). Produced by `alpha_grad_compute_kernel` (B.4); consumed by backward-pass gradient scaling (B.10). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            RegistryEntry {
                name: "sp14_aux_dir_acc_post_open_min",
                category: ResetCategory::FoldReset,
                description: "ISV[AUX_DIR_ACC_POST_OPEN_MIN_INDEX=394] — SP14 anti-gradient-hacking circuit breaker: running minimum of aux_dir_acc since Gate 1 last opened. Initialised to 1.0 (no minimum observed) when Gate 1 opens; updated each step post-open as `min = fminf(min, current_aux_dir_acc)`. If `open_min − current_aux_dir_acc > LOCKOUT_TRIGGER_DROP=0.05` while q_disagreement also rises by >LOCKOUT_TRIGGER_DIS_RISE=0.10, the lockout circuit fires (B.5) and writes LOCKOUT_EPOCHS=2.0 to GRADIENT_HACK_LOCKOUT_REMAINING_INDEX=395. FoldReset sentinel SENTINEL_POST_OPEN_MIN=1.0 (no minimum observed at fold start; the circuit breaker must accumulate post-open observations before it can fire). Produced by `alpha_grad_compute_kernel` (B.4). Spec: docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md.",
            },
            // ── SP15 Phase 1.2 (2026-05-06): cost-net sharpe slots ────────────
            // Two ISV slots [407, 408]:
            //   - OFI_IMPACT_LAMBDA_INDEX=407: Invariant-1 anchor (NOT a
            //     stateful EMA). Constructor-seeded to 2.0e-4 per spec §6.2;
            //     FoldReset rewrites the same anchor (NOT sentinel 0) so the
            //     cost kernel functions correctly on the new fold's first
            //     launch. Per-fold ISV refit may overwrite this in later
            //     phases; the FoldReset arm restores the default to break
            //     cold-start of each new fold (must never reach sentinel 0,
            //     which would silently disable the OFI-impact term).
            //   - COST_PER_BAR_AVG_INDEX=408: stateful kernel output
            //     (mean(cost_t)). FoldReset sentinel 0; Pearl A bootstraps
            //     from the new fold's first kernel launch per
            //     `pearl_first_observation_bootstrap.md`.
            // Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md.
            RegistryEntry {
                name: "sp15_ofi_impact_lambda",
                category: ResetCategory::FoldReset,
                description: "ISV[OFI_IMPACT_LAMBDA_INDEX=407] — SP15 Phase 1.2 (2026-05-06) OFI-impact lambda for the cost-net sharpe kernel (per spec §6.2). Per-side cost: `λ × |position| × |ofi| × side_ind`. Constant Invariant-1 anchor (2.0e-4) per `feedback_isv_for_adaptive_bounds.md` — NOT a stateful EMA. Constructor-seeded; FoldReset rewrites the same default so the cost kernel functions correctly on the new fold's first launch (must never reach sentinel 0, which would silently disable the OFI-impact term). Per-fold ISV refit may overwrite this in later phases; the reset path restores the default to break cold-start of each new fold. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.2.",
            },
            RegistryEntry {
                name: "sp15_cost_per_bar_avg",
                category: ResetCategory::FoldReset,
                description: "ISV[COST_PER_BAR_AVG_INDEX=408] — SP15 Phase 1.2 (2026-05-06) mean cost-per-bar emitted by the cost-net sharpe kernel (`sum(cost_t) / n` over the sharpe window). Stateful kernel output (NOT cross-fold persistent); FoldReset sentinel 0 so the new fold's first `launch_sp15_cost_net_sharpe` writes the new fold's true mean. Per `pearl_first_observation_bootstrap.md`. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.2.",
            },
            // ── SP15 Phase 1.3 (2026-05-06): drawdown reporting slots ─────────
            // Six ISV slots [401..407) emitted by `dd_state_kernel`:
            //   - DD_CURRENT (401): per-step current drawdown ratio in [0, 1].
            //     FoldReset sentinel 0 (no DD at fold start; first kernel
            //     launch overwrites with the true value).
            //   - DD_MAX (402): running max of DD_CURRENT across the fold.
            //     FoldReset sentinel 0 (each fold's max-DD is independent;
            //     cross-fold persistence would silently drag the calmar
            //     denominator floor between folds).
            //   - DD_RECOVERY_BARS (403): bars since last new high-water mark.
            //     FoldReset sentinel 0 (new fold = fresh recovery counter).
            //   - DD_PERSISTENCE (404): twin counter to DD_RECOVERY_BARS in
            //     this phase (split-lifetime semantics may diverge in later
            //     phases). FoldReset sentinel 0 same rationale.
            //   - CALMAR (405): floored max-DD denominator for the host
            //     composer (`calmar = mean_pnl / value-here`). Kernel writes
            //     `max(dd_max, 1e-4)`; the floor eliminates the saturation-
            //     at-100 artifact seen in train-dd4xl HEALTH_DIAG. FoldReset
            //     sentinel 1e-4 so the very first calmar division at fold
            //     start uses the floor, not a sentinel 0 (which would yield
            //     ±inf and immediately re-trip the saturation guard).
            //   - DD_PCT (406): `clip(current_dd / max(dd_budget, 1e-4), 0, 1)`.
            //     FoldReset sentinel 0 (cold-start before first kernel
            //     launch).
            // Per spec §6.3. Reads PS_PEAK_EQUITY (slot 7) and PS_PREV_EQUITY
            // (slot 9) from the existing position state buffer — NO new ISV
            // equity slot (invariant verified during plan v2 critical
            // review).
            RegistryEntry {
                name: "sp15_dd_current",
                category: ResetCategory::FoldReset,
                description: "ISV[DD_CURRENT_INDEX=401] — SP15 Phase 1.3 (2026-05-06) per-step current drawdown ratio max(0, (peak − equity) / peak). Stateful kernel output (NOT cross-fold persistent); FoldReset sentinel 0 so the new fold's first `launch_sp15_dd_state` writes the new fold's true value (no DD at fold start by definition). Per `pearl_first_observation_bootstrap.md`. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            RegistryEntry {
                name: "sp15_dd_max",
                category: ResetCategory::FoldReset,
                description: "ISV[DD_MAX_INDEX=402] — SP15 Phase 1.3 (2026-05-06) running max of DD_CURRENT across the current fold. Stateful kernel output; FoldReset sentinel 0 since each fold's max-DD is independent — cross-fold persistence would silently drag the calmar denominator floor between folds and break the invariant that calmar reports the worst draw-down OF THIS FOLD. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            RegistryEntry {
                name: "sp15_dd_recovery_bars",
                category: ResetCategory::FoldReset,
                description: "ISV[DD_RECOVERY_BARS_INDEX=403] — SP15 Phase 1.3 (2026-05-06) bars since last new high-water mark in the current fold. Stateful counter; FoldReset sentinel 0 (new fold = fresh recovery counter; the kernel zeros this slot in-flight whenever a new peak fires, but cold-start of the new fold also requires the slot to be 0 so the very first below-peak step starts the counter from 0 → 1). Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            RegistryEntry {
                name: "sp15_dd_persistence",
                category: ResetCategory::FoldReset,
                description: "ISV[DD_PERSISTENCE_INDEX=404] — SP15 Phase 1.3 (2026-05-06) twin counter to DD_RECOVERY_BARS in this phase (split-lifetime semantics may diverge in a later phase, e.g. recovery resets at any fresh peak while persistence resets only above a hysteresis band). FoldReset sentinel 0 same rationale as DD_RECOVERY_BARS. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            RegistryEntry {
                name: "sp15_calmar",
                category: ResetCategory::FoldReset,
                description: "ISV[CALMAR_INDEX=405] — SP15 Phase 1.3 (2026-05-06) floored max-DD denominator for the host composer's `calmar = mean_pnl / value-here`. Kernel writes `max(dd_max, 1e-4)` so the floor eliminates the saturation-at-100 artifact seen in train-dd4xl HEALTH_DIAG when max-DD ≈ 0. FoldReset sentinel 1e-4 (the SAME floor value, NOT 0) so the very first calmar division at fold start uses the floor — sentinel 0 would yield ±inf and immediately re-trip the saturation guard before the first kernel launch overwrites the slot. Per `feedback_isv_for_adaptive_bounds.md` (1e-4 is an Invariant-1 anchor — the floor value is fixed structural protection, not an adaptive EMA). Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            RegistryEntry {
                name: "sp15_dd_pct",
                category: ResetCategory::FoldReset,
                description: "ISV[DD_PCT_INDEX=406] — SP15 Phase 1.3 (2026-05-06) `clip(current_dd / max(dd_budget, 1e-4), 0, 1)` ∈ [0, 1]. Stateful kernel output; FoldReset sentinel 0 (cold-start at fold boundary; the kernel overwrites with the true value on the new fold's first launch). Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §6.3.",
            },
            // ── SP15 Phase 3.1 (2026-05-06): r_quality + r_discipline split ───
            // Three ISV slots [417..420) + one mapped-pinned scratch buffer
            // (`sp15_alpha_warm_count`):
            //   - ALPHA_SPLIT_INDEX=417: Invariant-1 sentinel anchor (NOT a
            //     stateful EMA). Constructor-seeded to 0.5 per spec §8.2 (3.1)
            //     post-amendment-2 fix; FoldReset rewrites the same 0.5 anchor
            //     so the composer's cold-start absorber re-engages on each new
            //     fold (the warm-count goes back to 0; the producer kernel
            //     becomes a no-op until `*warm_count >= N_WARM=100` again).
            //     Per `feedback_isv_for_adaptive_bounds.md` — the structural
            //     0.5 anchor is the cold-start absorber, the runtime value is
            //     signal-driven once warmup completes.
            //   - GRAD_NORM_QUALITY_INDEX=418: stateful EMA of the r_quality
            //     gradient L2 norm. FoldReset sentinel 0 so Pearl A
            //     bootstraps from the new fold's first observation per
            //     `pearl_first_observation_bootstrap.md`.
            //   - GRAD_NORM_DISCIPLINE_INDEX=419: stateful EMA of the
            //     r_discipline gradient L2 norm. FoldReset sentinel 0 same
            //     rationale as quality.
            //   - sp15_alpha_warm_count (NOT an ISV slot — single-element
            //     mapped-pinned scratch buffer on the trainer struct):
            //     FoldReset sentinel 0.0 via `host_slice_mut().fill(0.0)`
            //     so the new fold's first composer launch re-enters the
            //     warm-up window from step 0 and the producer-kernel gate
            //     re-engages. Mirrors the `sp11_novelty_hash` pattern of
            //     resetting a non-ISV mapped-pinned buffer at fold boundary.
            // Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §8.2 (3.1).
            RegistryEntry {
                name: "sp15_alpha_split",
                category: ResetCategory::FoldReset,
                description: "ISV[ALPHA_SPLIT_INDEX=417] — SP15 Phase 3.1 (2026-05-06) blend coefficient for `r_total = α × r_quality + (1 − α) × r_discipline`. Constant Invariant-1 anchor (0.5) per `feedback_isv_for_adaptive_bounds.md` — NOT a stateful EMA. Constructor-seeded to 0.5; FoldReset rewrites the same anchor so the composer's cold-start absorber re-engages on each new fold (per spec §8.2 (3.1) post-amendment-2 fix: deriving α from `grad_norm_q / (grad_norm_q + grad_norm_d + ε)` at boot would produce 0/0=0 from zero grad-norm EMAs). The companion `alpha_split_producer_kernel` is gated on `sp15_alpha_warm_count` and stays a silent no-op until `*warm_count >= N_WARM=100`, at which point it activates and writes the formula's clamped output here on each step. Per-fold ISV refit may overwrite this in later phases. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §8.2 (3.1).",
            },
            RegistryEntry {
                name: "sp15_grad_norm_quality",
                category: ResetCategory::FoldReset,
                description: "ISV[GRAD_NORM_QUALITY_INDEX=418] — SP15 Phase 3.1 (2026-05-06) stateful EMA of the r_quality gradient L2 norm. Read by `alpha_split_producer_kernel` (post-warmup) to compute `α = grad_norm_q / (grad_norm_q + grad_norm_d + ε)`. FoldReset sentinel 0 so Pearl A bootstraps from the new fold's first observation per `pearl_first_observation_bootstrap.md` (also drives the warm-count gate: `alpha_split_producer_kernel` will re-enter no-op state at fold boundary because the companion `sp15_alpha_warm_count` resets in the same arm cluster). Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §8.2 (3.1).",
            },
            RegistryEntry {
                name: "sp15_grad_norm_discipline",
                category: ResetCategory::FoldReset,
                description: "ISV[GRAD_NORM_DISCIPLINE_INDEX=419] — SP15 Phase 3.1 (2026-05-06) stateful EMA of the r_discipline gradient L2 norm. Read by `alpha_split_producer_kernel` (post-warmup) as the denominator partner of `sp15_grad_norm_quality`. FoldReset sentinel 0 same rationale as quality. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §8.2 (3.1).",
            },
            RegistryEntry {
                name: "sp15_alpha_warm_count",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.sp15_alpha_warm_count [1] f32 mapped-pinned scratch buffer — SP15 Phase 3.1 (2026-05-06) warm-up counter for the r_quality + r_discipline split composer (per spec §8.2 (3.1) post-amendment-2 fix). Initialised to 0.0 in the constructor; incremented by 1 each step inside `r_quality_discipline_split_kernel`; once `*count >= N_WARM=100` the composer stops overriding α to 0.5 and `alpha_split_producer_kernel` activates from no-op state. FoldReset sentinel 0.0 via `host_slice_mut().fill(0.0)` (mirrors the `sp11_novelty_hash` pattern of resetting a non-ISV mapped-pinned buffer at fold boundary) — the new fold's first composer launch must re-enter the warm-up window from step 0 so the cold-start absorber holds α at 0.5 long enough for the new fold's grad-norm EMAs to accumulate ≥100 non-zero observations before the producer kernel takes over. Spec: docs/superpowers/specs/2026-05-06-sp15-honest-numbers.md §8.2 (3.1).",
            },
            // SP5 Task A1: Wiener-state companion reset. The wiener_state_buf
            // covers SP4 (SP4_PRODUCER_COUNT=71 producers × 3 = 213 floats) +
            // SP5 (SP5_PRODUCER_COUNT × 3 floats) = (71 + SP5_PRODUCER_COUNT) × 3
            // floats total. The SP5 triples start at offset 213 and span
            // SP5_PRODUCER_COUNT × 3 floats; the runtime constant lives in
            // `cuda_pipeline::sp5_isv_slots::SP5_PRODUCER_COUNT` and grows as
            // each subsequent SP adds producers (B1b fix-up landed slot 360 →
            // SP5_PRODUCER_COUNT=187 → buffer = 774 floats, SP5 block
            // [213..774)). Always derive sizes via the formula
            // `(71 + SP5_PRODUCER_COUNT) × 3` rather than committing the literal.
            // Both halves of the Pearl A sentinel contract must reset together per
            // `feedback_no_partial_refactor.md`.
            RegistryEntry {
                name: "sp5_wiener_state",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.wiener_state_buf SP5 block at offsets [SP4_PRODUCER_COUNT × 3 .. (SP4_PRODUCER_COUNT + SP5_PRODUCER_COUNT) × 3) = [213..(213 + SP5_PRODUCER_COUNT × 3)). The total buffer is sized via `SP5_WIENER_TOTAL_FLOATS = (SP4_PRODUCER_COUNT=71 + SP5_PRODUCER_COUNT) × 3` floats; refer to the runtime `SP5_PRODUCER_COUNT` (`cuda_pipeline::sp5_isv_slots`) for the current value rather than hardcoding a literal here — it grows on each SP follow-up (e.g. B1b fix-up landed slot 360 → SP5_PRODUCER_COUNT=187 → buffer=774 floats, SP5 block [213..774)). Each producer triple = {sample_var, diff_var, x_lag}. SP5 Pearl D Wiener-EMA state for ALL SP5 producers (Pearls 1, 2, 3, 4, 5, Layer D D1/D2/D3, SP7 T1, SP7 activation-flag fix, SP8 Fix 36, SP9 Kelly cold-start, SP10 eval Thompson temperature, SP11 reward-subsystem controller). Bulk write_bytes(0) at fold boundary covers the full wiener_state_buf [0..SP5_WIENER_TOTAL_FLOATS) so Pearl A's sentinel branch fires on the new fold's first producer launch. Pearl 6 (slots 280..286) does NOT use this buffer — Pearl 6 is cross-fold-persistent and uses in-kernel EWMA + cumulative-via-max(); the wiener slots [525..543) that Pearl 6 would map to under naive offset arithmetic (213 + (280-174)*3 = 525) are intentionally unused. Both SP4 and SP5 [non-Pearl-6] blocks reset atomically.",
            },
            // SP5 Task A6: Pearl 6 cross-fold-persistent Kelly cap signals.
            // ISV[280..286) are EXPLICITLY EXEMPT from this registry.
            // Adding fold-reset entries for these slots would defeat the entire
            // design purpose: these slots must survive across fold and window
            // boundaries to break the cold-start Kelly cap pathology.
            //
            // Investigation finding: portfolio_state has ResetCategory::WindowReset
            // (not FoldReset) — it resets at every WINDOW boundary. This makes the
            // cross-fold-persistent design even more critical: without Pearl 6, the
            // Kelly cold-start re-engages after every window, not just every fold.
            //
            // The pearl_6_kelly_kernel uses in-kernel slow EWMA (alpha=0.01) and
            // cumulative-via-max() for sample_count. No apply_pearls call; no wiener
            // state entries needed. ISV[280..286) are never zeroed by the fold-reset
            // path. This is the intended, documented behavior for Task A6.
            // SP5 Task A4: per-group Adam β1/β2/ε ISV slots (Pearl 4).
            // ISV[226..250) = 24 floats (8 groups × 3 hyperparams).
            // Pearl A sentinel 0 at fold boundary → first step fires first-observation
            // replacement (cosine_sim=0 → low-stability envelope midpoints).
            RegistryEntry {
                name: "sp5_adam_beta1",
                category: ResetCategory::FoldReset,
                description: "ISV[ADAM_BETA1_BASE=226..234) — per-group Adam β1 (gradient direction stability). Pearl 4 producer: cosine_sim→[0,1] → β1 = 0.85 + 0.10×stability ∈ [0.85, 0.95]. SP5 Pearl A sentinel 0 at fold boundary (Task A4).",
            },
            RegistryEntry {
                name: "sp5_adam_beta2",
                category: ResetCategory::FoldReset,
                description: "ISV[ADAM_BETA2_BASE=234..242) — per-group Adam β2 (gradient direction stability). Pearl 4 producer: cosine_sim→[0,1] → β2 = 0.99 + 0.0095×stability ∈ [0.99, 0.9995]. SP5 Pearl A sentinel 0 at fold boundary (Task A4).",
            },
            RegistryEntry {
                name: "sp5_adam_eps",
                category: ResetCategory::FoldReset,
                description: "ISV[ADAM_EPS_BASE=242..250) — per-group Adam ε (grad L2 norm scaled). Pearl 4 producer: ε = clamp(grad_norm×1e-7, 1e-10, 1e-6). SP5 Pearl A sentinel 0 at fold boundary (Task A4).",
            },
            RegistryEntry {
                name: "sp5_grad_prev_buf",
                category: ResetCategory::FoldReset,
                description: "GpuDqnTrainer.grad_prev_buf_per_group [total_params f32s] — previous-step gradient direction for cosine similarity computation (Pearl 4). Zero at fold boundary so the new fold's first grad_cosine_sim_update sees cosine_sim=0 (low-stability bootstrap) and Pearl A fires first-observation replacement (Task A4).",
            },
            // SP5 Task A5: per-branch IQN τ schedule (Pearl 5).
            // ISV[IQN_TAU_BASE=250..270) = 20 floats (4 branches × 5 quantiles).
            // Pearl A sentinel 0 at fold boundary → first step fires first-observation
            // replacement: zero skew → default symmetric schedule {0.05,0.25,0.5,0.75,0.95}.
            RegistryEntry {
                name: "sp5_iqn_tau",
                category: ResetCategory::FoldReset,
                description: "ISV[IQN_TAU_BASE=250..270) — per-branch IQN τ schedule (4 branches × 5 quantiles). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first pearl_5_iqn_tau_update launch fires the first-observation replacement back to the symmetric default {0.05, 0.25, 0.5, 0.75, 0.95} at zero skew (Task A5).",
            },
            // SP5 Task A7: per-direction trail-stop distance (Pearl 8).
            // ISV[TRAIL_DIST_PER_DIR_BASE=270..274) = 4 floats (Short, Hold, Long, Flat).
            // Pearl A sentinel 0 at fold boundary → first pearl_8_trail_update launch
            // fires the first-observation replacement with the new fold's ATR value.
            RegistryEntry {
                name: "sp5_trail_dist_per_dir",
                category: ResetCategory::FoldReset,
                description: "ISV[TRAIL_DIST_PER_DIR_BASE=270..274) — per-direction trail-stop distance. Short[270] and Long[272]: atr_abs × 2.0; Hold[271] and Flat[273]: 1.0. SP5 Pearl A sentinel 0 at fold boundary so the first pearl_8_trail_update launch fires first-observation replacement with the new fold's ATR (Task A7).",
            },
            // SP5 Task A8: per-branch C51 num_atoms (Pearl 1-ext).
            // ISV[ATOM_NUM_ATOMS_BASE=274..278) = 4 floats (one per branch).
            // Pearl A sentinel 0 at fold boundary → first pearl_1_ext_num_atoms_update launch
            // fires the first-observation replacement with the new fold's v_half-derived count.
            RegistryEntry {
                name: "sp5_atom_num_atoms",
                category: ResetCategory::FoldReset,
                description: "ISV[ATOM_NUM_ATOMS_BASE=274..278) — per-branch C51 num_atoms (discrete {16, 32, 64} mapped from v_half thresholds, smoothed via Pearls A+D so transition-region values may be fractional; Layer B's atoms_update consumer rounds at consume time). SP5 Pearl A sentinel 0 at fold boundary triggers first-observation replacement (Task A8).",
            },
            // SP5 Layer D Task D1: PnL aggregation outputs (4 ISV slots).
            // ISV[286..290) — Layer D D1 PnL summary written by
            // pnl_aggregation_update producer kernel. NOT cross-fold persistent
            // (unlike Pearl 6's Kelly slots) — PnL aggregation is per-fold by
            // construction, so the standard Pearl A sentinel-bootstrap path
            // applies. Both halves of the contract reset together: ISV slot
            // zeroed AND wiener_state_buf SP5 block (offsets [213..555)) bulk
            // memset(0) at fold boundary covers the four new triples too,
            // so the new fold's first launcher fires Pearl A's first-observation
            // replacement (per `pearl_first_observation_bootstrap.md`).
            RegistryEntry {
                name: "sp5_pnl_aggregation",
                category: ResetCategory::FoldReset,
                description: "ISV[PNL_TOTAL_INDEX=286..PNL_MAX_DD_INDEX+1=290) — Layer D D1 PnL aggregation outputs (total/mean/population variance/max drawdown). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first `launch_sp5_pnl_aggregation` fires the first-observation replacement (Task D1, 2026-05-02). Producer is `pnl_aggregation_kernel.cu`; consumer wires atomically in D4 (per `feedback_no_partial_refactor.md`). PnL aggregation is NOT cross-fold persistent — unlike Pearl 6's Kelly stats — so all four slots take the standard sentinel path. Wiener-state companion: bulk memset of the entire `wiener_state_buf[0..(71 + SP5_PRODUCER_COUNT) × 3)` at fold boundary covers the D1 triples at offsets [213+(286-174)*3 .. 213+(290-174)*3) = [549..561). reset_for_fold's existing bulk memset of the whole buffer length covers these triples atomically — no per-slot wiener entry is needed. Buffer size derives from SP5_PRODUCER_COUNT to avoid drift on subsequent SPs.",
            },
            // SP5 Layer D Task D2: Health composition outputs (4 ISV slots).
            // ISV[290..294) — Layer D D2 LearningHealth composed score plus
            // 3 normalised intermediates (q_gap_norm, q_var_norm,
            // grad_norm_norm). The underlying EMAs (q_gap_ema / q_var_ema /
            // grad_norm_ema in `health_ema`) reset at fold boundary, so the
            // composition outputs reset together via the standard Pearl A
            // sentinel-bootstrap path. Both halves of the contract reset
            // together: ISV slot zeroed AND wiener_state_buf SP5 block
            // bulk memset(0) at fold boundary covers the four new triples
            // at offsets [213+(290-174)*3 .. 213+(294-174)*3) = [561..573).
            RegistryEntry {
                name: "sp5_health_composition",
                category: ResetCategory::FoldReset,
                description: "ISV[HEALTH_SCORE_INDEX=290..GRAD_NORM_NORM_INDEX+1=294) — Layer D D2 LearningHealth composition outputs (composed score + q_gap_norm + q_var_norm + grad_norm_norm). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first `launch_health_composition` fires the first-observation replacement (Task D2, 2026-05-02). Producer is `health_composition_kernel.cu` — single-block 1-thread arithmetic kernel reproducing `learning_health.rs::NormalizedComponents::from_raw` + `compose()` bit-for-bit. Consumer wires atomically in D4 (per `feedback_no_partial_refactor.md`). The underlying EMAs (`HealthEmaTrackers.q_gap_ema` / `q_var_ema` / `grad_norm_ema`, plus the 4 non-EMA inputs sourced from per-branch Q-gap dispersion / atom utilisation / spectral gap / Adam m-flat cosine) reset at fold boundary so the composition outputs reset together. Wiener-state companion: bulk memset of the entire `wiener_state_buf[0..(71 + SP5_PRODUCER_COUNT) × 3)` at fold boundary covers the D2 triples at offsets [213+(290-174)*3 .. 213+(294-174)*3) = [561..573). reset_for_fold's existing bulk memset of the whole buffer length covers these triples atomically — no per-slot wiener entry is needed. Buffer size derives from SP5_PRODUCER_COUNT to avoid drift on subsequent SPs.",
            },
            // SP5 Layer D Task D3: Training metrics EMA outputs (3 ISV slots).
            // ISV[294..297) — Layer D D3 host-formula EMAs for
            // `training_sharpe_ema` / `max_dd_ema` / `low_dd_ratio` migrated
            // from the host-side adversarial-regime + adaptive-DSR block at
            // `training_loop.rs:5039-5113`. The corresponding host scalars
            // (`training_sharpe_ema` + `_initialized`, `max_dd_ema`,
            // `low_dd_ratio`) reset at fold boundary on the host side
            // (constructor.rs:587-588/710-712 sets them to zero on each
            // construction; D4 will route the per-fold reset through the
            // existing fold-reset codepath alongside this ISV-side
            // sentinel). All three ISV slots take the standard Pearl A
            // sentinel-bootstrap path. Both halves of the contract reset
            // together: ISV slot zeroed AND `wiener_state_buf` SP5 block
            // bulk memset(0) at fold boundary covers the three new triples
            // at offsets [213+(294-174)*3 .. 213+(297-174)*3) = [573..582).
            RegistryEntry {
                name: "sp5_training_metrics_ema",
                category: ResetCategory::FoldReset,
                description: "ISV[TRAINING_SHARPE_EMA_INDEX=294..LOW_DD_RATIO_INDEX+1=297) — Layer D D3 training-metrics EMA outputs (training_sharpe_ema / max_dd_ema / low_dd_ratio). SP5 Pearl A sentinel 0 at fold boundary so the new fold's first `launch_training_metrics_ema` fires the first-observation replacement (Task D3, 2026-05-02). Producer is `training_metrics_ema_kernel.cu` — single-block 3-thread arithmetic kernel (one thread per metric) reproducing the host-side adaptive-α Sharpe EMA + fixed-α=0.1 max_dd EMA + fixed-α=0.15 low_dd_ratio EMA from `training_loop.rs:5039-5113` bit-for-bit. Consumer wires atomically in D4 (per `feedback_no_partial_refactor.md`). The underlying host scalars (`training_sharpe_ema` + `_initialized`, `max_dd_ema`, `low_dd_ratio` on `DQNTrainer`) reset at fold boundary alongside the ISV slots — D4 ties the host-side fold-reset call into the same path. Wiener-state companion: bulk memset of the entire `wiener_state_buf[0..(71 + SP5_PRODUCER_COUNT) × 3)` at fold boundary covers the D3 triples at offsets [213+(294-174)*3 .. 213+(297-174)*3) = [573..582). reset_for_fold's existing bulk memset of the whole buffer length covers these triples atomically — no per-slot wiener entry is needed. Buffer size derives from SP5_PRODUCER_COUNT to avoid drift on subsequent SPs. Per `feedback_trust_code_not_docs.md` the third metric is `low_dd_ratio` (the binary low-drawdown indicator EMA at `training_loop.rs:5052`), not the speculative `gamma_blend` referenced in the SP5 plan §D Task D3 — that field does not exist in the codebase.",
            },
        ];
        Self { entries }
    }

    /// Look up a state's category by name. Returns `None` for unknown names
    /// (caller should assert Some before use to catch typos).
    pub fn category(&self, name: &str) -> Option<ResetCategory> {
        self.entries.iter().find(|e| e.name == name).map(|e| e.category)
    }

    /// Iterator over all entries with `FoldReset` category.
    /// Used by the fold-boundary reset logic.
    pub fn fold_reset_entries(&self) -> impl Iterator<Item = &RegistryEntry> {
        self.entries.iter().filter(|e| e.category == ResetCategory::FoldReset)
    }

    /// Iterator over all entries with `WindowReset` category.
    pub fn window_reset_entries(&self) -> impl Iterator<Item = &RegistryEntry> {
        self.entries.iter().filter(|e| e.category == ResetCategory::WindowReset)
    }

    /// Iterator over all entries with `SoftReset` category.
    pub fn soft_reset_entries(&self) -> impl Iterator<Item = &RegistryEntry> {
        self.entries.iter().filter(|e| matches!(e.category, ResetCategory::SoftReset { .. }))
    }

    /// All entries (debug/diagnostic dump).
    pub fn all(&self) -> &[RegistryEntry] {
        &self.entries
    }
}

impl Default for StateResetRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn registry_classifies_known_state() {
        let r = StateResetRegistry::new();
        assert_eq!(r.category("kelly_stats"), Some(ResetCategory::FoldReset));
        assert_eq!(r.category("isv_grad_balance_targets"), Some(ResetCategory::SoftReset { decay_bars: 500 }));
        assert_eq!(r.category("network_params"), Some(ResetCategory::TrainingPersist));
        assert_eq!(r.category("ISV_LAYOUT_FINGERPRINT"), Some(ResetCategory::SchemaContract));
    }

    #[test]
    fn registry_fold_reset_iterates_only_fold_reset_entries() {
        let r = StateResetRegistry::new();
        let names: Vec<&str> = r.fold_reset_entries().map(|e| e.name).collect();
        assert!(names.contains(&"kelly_stats"));
        assert!(!names.contains(&"network_params"));
    }

    #[test]
    fn registry_unknown_name_returns_none() {
        let r = StateResetRegistry::new();
        assert_eq!(r.category("nonexistent_state"), None);
    }

    /// Contract test: every FoldReset/SoftReset entry in StateResetRegistry::new()
    /// must have a corresponding match arm in reset_named_state. Catches the
    /// "add registry entry, forget dispatch arm → runtime panic at fold boundary"
    /// bug pattern (occurred twice: SP5 Layer A #281, SP7 T7 commit 6e479c55c).
    ///
    /// Source-introspection design — no production-code change. The test extracts
    /// the reset_named_state function body via brace-balance walk, then checks
    /// that each FoldReset/SoftReset entry name appears as a string literal inside
    /// that body.
    #[test]
    fn every_fold_and_soft_reset_entry_has_dispatch_arm() {
        let dispatch_src = include_str!("trainer/training_loop.rs");

        // Extract reset_named_state body: find fn signature, then match-block.
        let fn_start = dispatch_src
            .find("pub(crate) fn reset_named_state")
            .expect("reset_named_state not found in training_loop.rs");
        let after_fn = &dispatch_src[fn_start..];

        // Find `match name {` inside the function body.
        let match_rel = after_fn
            .find("match name {")
            .expect("match name { not found in reset_named_state");
        let match_start_abs = fn_start + match_rel;

        // Walk braces to extract the match block (from `match name {` to its
        // closing `}`), so we only search within the dispatch, not the rest of
        // the file.
        let match_src = &dispatch_src[match_start_abs..];
        let body = {
            let mut depth: i32 = 0;
            let mut end = match_src.len();
            for (i, ch) in match_src.char_indices() {
                match ch {
                    '{' => depth += 1,
                    '}' => {
                        depth -= 1;
                        if depth == 0 {
                            end = i + 1;
                            break;
                        }
                    }
                    _ => {}
                }
            }
            &match_src[..end]
        };

        let registry = StateResetRegistry::new();
        // FoldReset and SoftReset (any decay_bars) both dispatch through
        // reset_named_state. WindowReset/TrainingPersist/SchemaContract do not.
        let entries_needing_dispatch: Vec<&RegistryEntry> = registry
            .all()
            .iter()
            .filter(|e| matches!(
                e.category,
                ResetCategory::FoldReset | ResetCategory::SoftReset { .. }
            ))
            .collect();

        let mut missing: Vec<&str> = Vec::new();
        for entry in &entries_needing_dispatch {
            let needle = format!("\"{}\"", entry.name);
            if !body.contains(needle.as_str()) {
                missing.push(entry.name);
            }
        }

        assert!(
            missing.is_empty(),
            "Registry entries with no dispatch arm in reset_named_state ({} total, {} missing): {:?}\n\
             These will panic at fold-boundary with 'unknown name' MLError.\n\
             Add a match arm in trainer/training_loop.rs::reset_named_state.",
            entries_needing_dispatch.len(),
            missing.len(),
            missing
        );
    }
}
