diff --git a/crates/ml/src/trainers/dqn/trainer/training_loop.rs b/crates/ml/src/trainers/dqn/trainer/training_loop.rs index a797b3f02..ea561d425 100644 --- a/crates/ml/src/trainers/dqn/trainer/training_loop.rs +++ b/crates/ml/src/trainers/dqn/trainer/training_loop.rs @@ -1900,6 +1900,16 @@ impl DQNTrainer { self.last_distill_active = Some(fused.last_distill_active()); } + // D2/N2: Q-gap barrier constraint (scalar-only — informational loss). + // min_required = 0.05 × health; barrier_loss = 0.5 × max(0, min_required − q_gap)². + // Drives no gradient in this task; surfaces as HEALTH_DIAG `barrier=...` for visibility. + { + let q_gap_current = self.health_ema.q_gap_ema; + let min_required = 0.05_f32 * health_value; + let barrier = (min_required - q_gap_current).max(0.0); + self.last_barrier_loss = Some(0.5 * barrier * barrier); + } + // HEALTH_DIAG: components are [0, 1] normalized. effective = hyperparams after health-adaptation. novels = mechanism states. tracing::info!( "HEALTH_DIAG[{}]: health={:.2} components [q_gap={:.2} q_var={:.2} atoms={:.2} grad_stable={:.2} ens_agree={:.2} grad_cos={:.2} spectral={:.2}] effective [cql_alpha={:.4} iqn_budget={:.2} cql_budget={:.2} c51_budget={:.2} tau={:.5} sarsa_tau={:.2} gamma={:.3} cf_ratio={:.2}] novels [distill={} barrier={:.3} plasticity={} ib={:.3} ensemble_collapse={:.2} contrarian={} meta_q_pred={:.2}]",