diag(rl): emit v9 eval_warmup state to JSONL + cleanup lints
Diag: surfaces `risk_stack.eval_warmup.{remaining,active,blend,
floor_*,target_*}` so the v9 defensive-warmup window is observable
in diag.jsonl. `remaining` is the counter; `blend` is the
defensive-vs-normal mix coefficient (1.0 = full defensive, 0.0 =
normal); `floor_*` reflect the LIVE override values (read AFTER the
warmup kernel ran). Pre-warmup the kernel is a no-op (remaining=-1),
so v9 train-phase diag is bit-identical to v8.
Cleanup: tightens unreachable_pub items in tests/behavioral/* and
tests/sp5_producer_unit_tests.rs (pub → pub(crate)), removes
unused_mut on 6 sp5 scratch buffers, renames unused `step` loop
counter in alpha_baseline example, and explicitly discards an
intentionally-no-op `Command::assert` in cli_integration_test.
Reduces lint count by ~25; remaining 3 dead_code warnings flag
SP15 Phase 2A behavioral scaffolding (Phase 2B never landed —
deliberate signal, not noise).
Pre-existing pearl per feedback_no_hiding: do NOT suppress these
with #[allow]; the warnings ARE the design call surface.
This commit is contained in:
@@ -215,10 +215,11 @@ fn test_tune_status_invalid_uuid() {
|
||||
#[ignore = "Ignored because it tries to launch terminal UI"]
|
||||
fn test_dashboard_command() {
|
||||
let mut cmd = Command::cargo_bin("fxt").unwrap();
|
||||
cmd.arg("dashboard")
|
||||
let _ = cmd.arg("dashboard")
|
||||
.timeout(std::time::Duration::from_secs(2))
|
||||
.assert();
|
||||
// Will timeout or fail trying to connect, but that's expected
|
||||
// Will timeout or fail trying to connect, but that's expected — assert result
|
||||
// intentionally discarded; this test verifies arg parsing reaches launch.
|
||||
}
|
||||
|
||||
/// Test version flag
|
||||
|
||||
@@ -110,6 +110,15 @@ use ml_alpha::rl::isv_slots::{
|
||||
RL_KELLY_SAFETY_FRAC_INDEX, RL_KELLY_MIN_TRADES_FOR_RELEASE_INDEX,
|
||||
RL_TRAIL_TIGHTEN_FACTOR_INDEX, RL_TRAIL_LOOSEN_FACTOR_INDEX,
|
||||
RL_CUMULATIVE_DONES_INDEX,
|
||||
// v9 defensive eval-boundary calibration (spec
|
||||
// 2026-05-31-v9-defensive-eval-boundary-calibration.md).
|
||||
RL_EVAL_WARMUP_REMAINING_INDEX, RL_EVAL_WARMUP_STEPS_CONFIG_INDEX,
|
||||
RL_EVAL_WARMUP_DECAY_STEPS_CONFIG_INDEX,
|
||||
RL_EVAL_KELLY_SAFETY_DEFENSIVE_INDEX, RL_EVAL_IQN_TAU_MIN_DEFENSIVE_INDEX,
|
||||
RL_EVAL_ENTROPY_COEF_MIN_DEFENSIVE_INDEX, RL_EVAL_PPO_CLIP_EPS_MIN_DEFENSIVE_INDEX,
|
||||
RL_EVAL_KELLY_SAFETY_NORMAL_INDEX, RL_EVAL_IQN_TAU_MIN_NORMAL_INDEX,
|
||||
RL_EVAL_ENTROPY_COEF_MIN_NORMAL_INDEX, RL_EVAL_PPO_CLIP_EPS_MIN_NORMAL_INDEX,
|
||||
RL_ENTROPY_COEF_MIN_INDEX, RL_PPO_CLIP_EPS_MIN_INDEX,
|
||||
};
|
||||
use ml_alpha::trainer::diag_staging::DiagStaging;
|
||||
use ml_alpha::trainer::integrated::{
|
||||
@@ -760,6 +769,22 @@ fn main() -> Result<()> {
|
||||
})
|
||||
};
|
||||
|
||||
// v9 eval_warmup pre-compute (json! macro can't take Rust blocks/if-else).
|
||||
let eval_warmup_remaining = isv[RL_EVAL_WARMUP_REMAINING_INDEX];
|
||||
let eval_warmup_decay_steps = isv[RL_EVAL_WARMUP_DECAY_STEPS_CONFIG_INDEX];
|
||||
let eval_warmup_active = if eval_warmup_remaining > 0.0 { 1.0_f32 } else { 0.0_f32 };
|
||||
let eval_warmup_blend = if eval_warmup_remaining < 0.0 {
|
||||
0.0_f32
|
||||
} else if eval_warmup_remaining > eval_warmup_decay_steps {
|
||||
1.0_f32
|
||||
} else if eval_warmup_decay_steps > 0.0 {
|
||||
eval_warmup_remaining / eval_warmup_decay_steps
|
||||
} else if eval_warmup_remaining > 0.0 {
|
||||
1.0_f32
|
||||
} else {
|
||||
0.0_f32
|
||||
};
|
||||
|
||||
let record = json!({
|
||||
"step": step,
|
||||
"elapsed_s": t_start.elapsed().as_secs_f32(),
|
||||
@@ -886,6 +911,36 @@ fn main() -> Result<()> {
|
||||
"tighten": isv[RL_TRAIL_TIGHTEN_FACTOR_INDEX],
|
||||
"loosen": isv[RL_TRAIL_LOOSEN_FACTOR_INDEX],
|
||||
},
|
||||
// v9 — defensive eval-boundary calibration ([[pearl_adaptive_carryover_discipline]]).
|
||||
// `remaining` is the per-step counter: ≥ decay_steps → full
|
||||
// defensive blend=1.0; (0, decay_steps] → linear decay to 0;
|
||||
// 0 → final boundary write of normal values; -1 → kernel
|
||||
// no-op (post-warmup steady state). The four `floor_*`
|
||||
// entries are the LIVE values (read AFTER the warmup
|
||||
// kernel ran this step, so they reflect any override).
|
||||
"eval_warmup": {
|
||||
"remaining": eval_warmup_remaining,
|
||||
"active": eval_warmup_active,
|
||||
"blend": eval_warmup_blend,
|
||||
"warmup_steps_config": isv[RL_EVAL_WARMUP_STEPS_CONFIG_INDEX],
|
||||
"decay_steps_config": eval_warmup_decay_steps,
|
||||
"floor_kelly_safety": isv[RL_KELLY_SAFETY_FRAC_INDEX],
|
||||
"floor_iqn_tau_min": isv[RL_IQN_ACTION_TAU_MIN_INDEX],
|
||||
"floor_entropy_min": isv[RL_ENTROPY_COEF_MIN_INDEX],
|
||||
"floor_ppo_eps_min": isv[RL_PPO_CLIP_EPS_MIN_INDEX],
|
||||
"target_defensive": {
|
||||
"kelly_safety": isv[RL_EVAL_KELLY_SAFETY_DEFENSIVE_INDEX],
|
||||
"iqn_tau_min": isv[RL_EVAL_IQN_TAU_MIN_DEFENSIVE_INDEX],
|
||||
"entropy_min": isv[RL_EVAL_ENTROPY_COEF_MIN_DEFENSIVE_INDEX],
|
||||
"ppo_eps_min": isv[RL_EVAL_PPO_CLIP_EPS_MIN_DEFENSIVE_INDEX],
|
||||
},
|
||||
"target_normal": {
|
||||
"kelly_safety": isv[RL_EVAL_KELLY_SAFETY_NORMAL_INDEX],
|
||||
"iqn_tau_min": isv[RL_EVAL_IQN_TAU_MIN_NORMAL_INDEX],
|
||||
"entropy_min": isv[RL_EVAL_ENTROPY_COEF_MIN_NORMAL_INDEX],
|
||||
"ppo_eps_min": isv[RL_EVAL_PPO_CLIP_EPS_MIN_NORMAL_INDEX],
|
||||
},
|
||||
},
|
||||
// Calibration diagnostics — atom-span vs. Bellman dynamic
|
||||
// bound. Per docs/superpowers/specs/2026-05-31-c51-atom-
|
||||
// span-math-validation.md: the structural constraint is
|
||||
|
||||
@@ -907,7 +907,7 @@ fn main() -> Result<()> {
|
||||
.context("reset vol_obs min slot")?;
|
||||
}
|
||||
// Lockstep step loop.
|
||||
for step in 0..cli.horizon {
|
||||
for _ in 0..cli.horizon {
|
||||
// Gather current states (CPU; <100μs for N=500).
|
||||
let mut batched_states_host = vec![0.0_f32; n_par * STATE_DIM];
|
||||
for i in 0..n_par {
|
||||
|
||||
@@ -9,7 +9,7 @@ use ml::cuda_pipeline::lob_bar::LobBar;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BehavioralResult {
|
||||
pub(crate) struct BehavioralResult {
|
||||
/// Action distribution keys: "hold", "flat", "long_quarter",
|
||||
/// "long_half", "long_full", "short_quarter", "short_half", "short_full".
|
||||
pub action_dist: HashMap<&'static str, f32>,
|
||||
@@ -27,7 +27,7 @@ pub struct BehavioralResult {
|
||||
/// each Phase 2B test rewrites the body to call the trainer methods it
|
||||
/// requires. The signature here is the load-bearing piece — keep it stable.
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn evaluate_policy_on_market(
|
||||
pub(crate) fn evaluate_policy_on_market(
|
||||
// Phase 2B swaps `&mut ()` for `&mut GpuDqnTrainer` once the per-test
|
||||
// trainer methods land. Until then the unit param keeps the signature
|
||||
// callable in tests that don't yet exercise the trainer path.
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
use ml::cuda_pipeline::lob_bar::LobBar;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MagBucket { Quarter, Half, Full }
|
||||
pub(crate) enum MagBucket { Quarter, Half, Full }
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum OracleAction {
|
||||
pub(crate) enum OracleAction {
|
||||
Hold,
|
||||
Long(MagBucket),
|
||||
Short(MagBucket),
|
||||
@@ -16,12 +16,12 @@ pub enum OracleAction {
|
||||
}
|
||||
|
||||
/// Oracle for flat markets: always Hold.
|
||||
pub fn flat_market_oracle(bars: &[LobBar]) -> Vec<OracleAction> {
|
||||
pub(crate) fn flat_market_oracle(bars: &[LobBar]) -> Vec<OracleAction> {
|
||||
vec![OracleAction::Hold; bars.len()]
|
||||
}
|
||||
|
||||
/// Oracle for drift markets: take direction-of-trend, mag = Half.
|
||||
pub fn drift_market_oracle(bars: &[LobBar]) -> Vec<OracleAction> {
|
||||
pub(crate) fn drift_market_oracle(bars: &[LobBar]) -> Vec<OracleAction> {
|
||||
let mut actions = Vec::with_capacity(bars.len());
|
||||
actions.push(OracleAction::Hold); // first bar: no prior
|
||||
for w in bars.windows(2) {
|
||||
@@ -38,7 +38,7 @@ pub fn drift_market_oracle(bars: &[LobBar]) -> Vec<OracleAction> {
|
||||
}
|
||||
|
||||
/// Oracle for OU markets: reverse at ±2σ extremes.
|
||||
pub fn ou_market_oracle(bars: &[LobBar], mu: f32, sigma: f32) -> Vec<OracleAction> {
|
||||
pub(crate) fn ou_market_oracle(bars: &[LobBar], mu: f32, sigma: f32) -> Vec<OracleAction> {
|
||||
bars.iter().map(|b| {
|
||||
let z = (b.price - mu) / sigma.max(1e-6);
|
||||
if z > 2.0 { OracleAction::Short(MagBucket::Half) }
|
||||
|
||||
@@ -6,7 +6,7 @@ use rand::{Rng, SeedableRng};
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
/// Flat market: constant base price + Gaussian noise.
|
||||
pub fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> Vec<LobBar> {
|
||||
pub(crate) fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> Vec<LobBar> {
|
||||
let mut rng = StdRng::seed_from_u64(seed);
|
||||
(0..n).map(|_| {
|
||||
let noise: f32 = rng.gen_range(-1.0..1.0) * noise_sigma;
|
||||
@@ -18,7 +18,7 @@ pub fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> V
|
||||
}
|
||||
|
||||
/// Drift market: drift μ + noise σ.
|
||||
pub fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64) -> Vec<LobBar> {
|
||||
pub(crate) fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64) -> Vec<LobBar> {
|
||||
let mut rng = StdRng::seed_from_u64(seed);
|
||||
let mut price: f32 = 4500.0;
|
||||
(0..n).map(|_| {
|
||||
@@ -31,7 +31,7 @@ pub fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64)
|
||||
}
|
||||
|
||||
/// OU process: mean-reverting around equilibrium.
|
||||
pub fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32,
|
||||
pub(crate) fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32,
|
||||
mean_spread: f32, seed: u64) -> Vec<LobBar> {
|
||||
let mut rng = StdRng::seed_from_u64(seed);
|
||||
let mut price: f32 = mu;
|
||||
@@ -45,7 +45,7 @@ pub fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32,
|
||||
}
|
||||
|
||||
/// Regime-switch: Markov chain over `n_regimes`, each with (mu, sigma).
|
||||
pub fn regime_switch_market(
|
||||
pub(crate) fn regime_switch_market(
|
||||
n: usize,
|
||||
regime_params: &[(f32, f32)],
|
||||
transition_matrix: &[Vec<f32>],
|
||||
|
||||
@@ -1980,7 +1980,7 @@ fn pearl_1_ext_num_atoms_threshold_cascade() {
|
||||
|
||||
let scratch_size: usize = 4;
|
||||
let base: i32 = 0;
|
||||
let mut scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc");
|
||||
let scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc");
|
||||
|
||||
let isv_dev = isv_buf.dev_ptr;
|
||||
let scr_dev = scratch.dev_ptr;
|
||||
@@ -2048,7 +2048,7 @@ fn pearl_1_ext_num_atoms_exact_threshold_falls_to_next_branch() {
|
||||
|
||||
let scratch_size: usize = 4;
|
||||
let base: i32 = 0;
|
||||
let mut scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc");
|
||||
let scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc");
|
||||
|
||||
let isv_dev = isv_buf.dev_ptr;
|
||||
let scr_dev = scratch.dev_ptr;
|
||||
|
||||
Reference in New Issue
Block a user