//! Alpha trading system ISV slot block.
//!
//! Reserves indices 539..=553 (15 slots, contiguous) for durable
//! infrastructure of the alpha trading system: stacker/execution-policy
//! diagnostics, controller anchors, and population baselines. Unlike the
//! SP4..SP22 slot files (which were per-iteration milestone-scoped), this
//! block is named for the *system* — these slots are intended to outlive
//! any individual Phase E/F/G milestone.
//!
//! Consumers read via the standard sentinel-bootstrap pattern:
//! `if isv[idx] <= ALPHA_SENTINEL + ALPHA_SENTINEL_EPS { fallback }`.
//!
//! Slot layout:
//!
//! | Index | Name                         | Role         | Update cadence  | Producer kernel                          |
//! |-------|------------------------------|--------------|-----------------|------------------------------------------|
//! | 539   | Q_SPREAD_EMA_INDEX           | Diagnostic   | per-rollout-step| alpha_kill_criteria.cu                 |
//! | 540   | ACTION_ENTROPY_EMA_INDEX     | Diagnostic   | per-rollout-step| alpha_kill_criteria.cu                 |
//! | 541   | RETURN_VS_RANDOM_EMA_INDEX   | Diagnostic   | per-trade-close | alpha_kill_criteria.cu                 |
//! | 542   | EARLY_Q_MOVEMENT_EMA_INDEX   | Diagnostic   | per-epoch       | alpha_kill_criteria.cu                 |
//! | 543   | STACKER_THRESHOLD_INDEX      | Controller   | per-rollout-step| stacker_threshold_controller.cu          |
//! | 544   | TRADE_RATE_TARGET_INDEX      | Anchor       | static          | host init                                |
//! | 545   | TRADE_RATE_OBSERVED_EMA_INDEX| Diagnostic   | per-rollout-step| stacker_threshold_controller.cu          |
//! | 546   | STACKER_KELLY_ATTENUATION_IX | Controller   | per-trade-close | stacker_threshold_controller.cu          |
//! | 547   | RANDOM_BASELINE_MEAN_INDEX   | Anchor       | per-fold        | host computes once per fold              |
//! | 548   | RANDOM_BASELINE_STD_INDEX    | Anchor       | per-fold        | host computes once per fold              |
//! | 549   | REGIME_VOL_EMA_INDEX         | Diagnostic   | per-inference   | alpha_regime_vol_update.cu             |
//! | 550   | REGIME_VOL_REF_INDEX         | Controller   | per-inference   | alpha_regime_vol_update.cu             |
//! | 551   | REGIME_VOL_REF_SAMPLES_INDEX | Counter      | per-inference   | alpha_regime_vol_update.cu             |
//! | 552   | REGIME_VOL_REF_FLOOR_INDEX   | Anchor       | TrainingPersist | host seed + stacker_threshold_controller |
//! | 553   | REGIME_VOL_OBS_MIN_INDEX     | Diagnostic   | per-inference   | alpha_regime_vol_update.cu             |
//!
//! Reservation discipline: this block had two spares at first-commit
//! (549, 550) for absorbing growth. T16 consumed those (vol_ema +
//! vol_ref) and added one more for the bootstrap-sample counter,
//! bumping the block from 12 to 13 slots. Add new alpha-system slots
//! HERE first, not by extending past 551 ad-hoc.

pub const ALPHA_ISV_BLOCK_LO: usize = 539;
pub const ALPHA_ISV_BLOCK_HI: usize = 553; // inclusive upper

// Diagnostics (read-only for circuit breakers; not controllers)
pub const Q_SPREAD_EMA_INDEX: usize = 539;
pub const ACTION_ENTROPY_EMA_INDEX: usize = 540;
pub const RETURN_VS_RANDOM_EMA_INDEX: usize = 541;
pub const EARLY_Q_MOVEMENT_EMA_INDEX: usize = 542;

// Stacker-threshold controller (engagement-rate self-correction)
pub const STACKER_THRESHOLD_INDEX: usize = 543;
pub const TRADE_RATE_TARGET_INDEX: usize = 544;
pub const TRADE_RATE_OBSERVED_EMA_INDEX: usize = 545;
pub const STACKER_KELLY_ATTENUATION_INDEX: usize = 546;

// Random-uniform baseline (computed once per fold; used by kill criterion 541)
pub const RANDOM_BASELINE_MEAN_INDEX: usize = 547;
pub const RANDOM_BASELINE_STD_INDEX: usize = 548;

// Phase E.4.A T16 regime detection (vol-EMA pre-emptive Kelly defense).
//
// Slot 549 holds the Wiener-α-smoothed estimate of recent squared
// log-return magnitude across the parallel envs. Slot 550 holds a slow-
// tracking "reference" vol (windowed Welford-style median proxy) that
// represents the regime the policy was trained on. The ratio
// `vol_ref / max(vol_ema, ε)` becomes a multiplicative Kelly scale —
// when realized vol blows past the trained reference, Kelly attenuates
// PRE-EMPTIVELY (before realized P&L losses trigger the existing
// STACKER_KELLY_ATTENUATION reactive response).
//
// Producer kernel: `alpha_regime_vol_update.cu` (per inference step,
// reads B mid-prices, updates both slots in one kernel via Pearl A
// bootstrap + Wiener-α with floor 0.4 per pearl_wiener_alpha_floor_for_nonstationary).
// Consumer: `stacker_threshold_controller.cu` reads slot 549/550 when
// computing kelly_attenuation update.
pub const REGIME_VOL_EMA_INDEX: usize = 549;
pub const REGIME_VOL_REF_INDEX: usize = 550;
// Sample counter that gates the vol_ref bootstrap. While count <
// `REGIME_VOL_REF_BOOTSTRAP_SAMPLES` the kernel updates vol_ref as a
// running mean over the observed vol_ema values; once the counter
// crosses the threshold, vol_ref switches to slow β-tracking. Without
// this, the very first observation drives vol_ref permanently noisy,
// and short-training-window walk-forward folds get pessimistic Kelly
// attenuation immediately (regression confirmed 2026-05-16 against
// the previous pre-defense walk-forward Sharpe).
pub const REGIME_VOL_REF_SAMPLES_INDEX: usize = 551;
// Cross-fold-persistent floor on REGIME_VOL_REF (per
// `pearl_kelly_cap_signal_driven_floors` and
// `pearl_blend_formulas_must_have_permanent_floor`). Prevents the
// deadband-deadlock where vol_ref → 0 during calm market stretches
// then traps Kelly at the regime_scale floor once any vol resumes.
// TrainingPersist semantics: NOT cleared by fold reset; survives
// across cells of a 2D sweep so the first cell's bootstrap statistics
// inform subsequent cells. Hardcoded sub-floor of 1e-12 inside the
// kernel guards against the slot itself collapsing to zero.
// Sentinel value 0 → kernel uses the hardcoded sub-floor only.
pub const REGIME_VOL_REF_FLOOR_INDEX: usize = 552;
// Running min of vol_obs observed within the current eval cell.
// Updated by `alpha_regime_vol_update_kernel` per inference step, but
// filtered to skip vol_obs <= 1e-12 (artifact-zero observations from
// stationary snapshots where curr == prev). Reset by host to a large
// sentinel at the start of each cell so the next cell's min
// accumulates cleanly. Consumed by the floor-update branch of the
// stacker controller at cell-end. Sentinel "large value" is 1e10
// (anything above any realistic vol_obs).
pub const REGIME_VOL_OBS_MIN_INDEX: usize = 553;
pub const REGIME_VOL_OBS_MIN_SENTINEL: f32 = 1.0e10;

// Sentinels: zero by convention. Fallbacks use the first-observation
// bootstrap pattern (see pearl_first_observation_bootstrap.md).
pub const ALPHA_SENTINEL: f32 = 0.0;
pub const ALPHA_SENTINEL_EPS: f32 = 1e-9;

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

    #[test]
    fn test_block_bounds_consistent() {
        assert!(ALPHA_ISV_BLOCK_HI >= ALPHA_ISV_BLOCK_LO);
        let block_size = ALPHA_ISV_BLOCK_HI - ALPHA_ISV_BLOCK_LO + 1;
        assert_eq!(block_size, 15, "alpha-system reserved 15 contiguous slots");
    }

    #[test]
    fn test_all_indices_inside_block() {
        let indices = [
            Q_SPREAD_EMA_INDEX,
            ACTION_ENTROPY_EMA_INDEX,
            RETURN_VS_RANDOM_EMA_INDEX,
            EARLY_Q_MOVEMENT_EMA_INDEX,
            STACKER_THRESHOLD_INDEX,
            TRADE_RATE_TARGET_INDEX,
            TRADE_RATE_OBSERVED_EMA_INDEX,
            STACKER_KELLY_ATTENUATION_INDEX,
            RANDOM_BASELINE_MEAN_INDEX,
            RANDOM_BASELINE_STD_INDEX,
            REGIME_VOL_EMA_INDEX,
            REGIME_VOL_REF_INDEX,
            REGIME_VOL_REF_SAMPLES_INDEX,
            REGIME_VOL_REF_FLOOR_INDEX,
            REGIME_VOL_OBS_MIN_INDEX,
        ];
        for &i in &indices {
            assert!(i >= ALPHA_ISV_BLOCK_LO && i <= ALPHA_ISV_BLOCK_HI,
                "slot {i} out of alpha-system block");
        }
    }

    #[test]
    fn test_indices_unique() {
        let indices = [
            Q_SPREAD_EMA_INDEX,
            ACTION_ENTROPY_EMA_INDEX,
            RETURN_VS_RANDOM_EMA_INDEX,
            EARLY_Q_MOVEMENT_EMA_INDEX,
            STACKER_THRESHOLD_INDEX,
            TRADE_RATE_TARGET_INDEX,
            TRADE_RATE_OBSERVED_EMA_INDEX,
            STACKER_KELLY_ATTENUATION_INDEX,
            RANDOM_BASELINE_MEAN_INDEX,
            RANDOM_BASELINE_STD_INDEX,
            REGIME_VOL_EMA_INDEX,
            REGIME_VOL_REF_INDEX,
            REGIME_VOL_REF_SAMPLES_INDEX,
            REGIME_VOL_REF_FLOOR_INDEX,
            REGIME_VOL_OBS_MIN_INDEX,
        ];
        let mut sorted: Vec<usize> = indices.to_vec();
        sorted.sort();
        sorted.dedup();
        assert_eq!(sorted.len(), indices.len(), "duplicate slot index");
    }

    #[test]
    fn test_total_dim_covers_block() {
        // The parent ISV_TOTAL_DIM must be at least ALPHA_ISV_BLOCK_HI + 1.
        use crate::cuda_pipeline::gpu_dqn_trainer::ISV_TOTAL_DIM;
        assert!(ISV_TOTAL_DIM > ALPHA_ISV_BLOCK_HI,
            "ISV_TOTAL_DIM ({ISV_TOTAL_DIM}) must exceed ALPHA_ISV_BLOCK_HI ({ALPHA_ISV_BLOCK_HI})");
    }
}
