diff --git a/crates/ml/src/trainers/dqn/trainer/training_loop.rs b/crates/ml/src/trainers/dqn/trainer/training_loop.rs index c9ee3e54f..cfbecf691 100644 --- a/crates/ml/src/trainers/dqn/trainer/training_loop.rs +++ b/crates/ml/src/trainers/dqn/trainer/training_loop.rs @@ -5070,6 +5070,66 @@ impl DQNTrainer { ); } + // SP14 Layer B Task B.12 (2026-05-05): per-epoch EGF pearl diagnostic. + // Reads the 13 SP14 ISV slots that the EGF pearl populates each step + // (producers in experience_kernels.cu launched by the B.1–B.11 chain). + // gate1/gate2 sigmoid outputs are intentionally omitted — recomputing + // sigmoid host-side would violate feedback_no_cpu_compute_strict; the + // inputs (k_aux, k_q, q_dis_short, var_*) are sufficient for a reader + // to infer the sigmoid outputs and diagnose gradient-hacking events. + { + use crate::cuda_pipeline::sp14_isv_slots::{ + ALPHA_GRAD_SMOOTHED_INDEX, ALPHA_GRAD_RAW_INDEX, + BETA_RATE_LIMITER_ADAPTIVE_INDEX, + K_AUX_ADAPTIVE_INDEX, K_Q_ADAPTIVE_INDEX, + AUX_DIR_ACC_VARIANCE_EMA_INDEX, Q_DISAGREEMENT_VARIANCE_EMA_INDEX, + ALPHA_GRAD_RAW_VARIANCE_EMA_INDEX, + Q_DISAGREEMENT_SHORT_EMA_INDEX, Q_DISAGREEMENT_LONG_EMA_INDEX, + GATE1_OPEN_STATE_INDEX, AUX_DIR_ACC_POST_OPEN_MIN_INDEX, + GRADIENT_HACK_LOCKOUT_REMAINING_INDEX, + }; + let ( + alpha_smoothed, alpha_raw, beta_adaptive, + k_aux_curr, k_q_curr, + var_aux, var_q, var_alpha, + q_dis_short, q_dis_long, + gate1_state, post_open_min, lockout, + ) = if let Some(ref fused) = self.fused_ctx { + let trainer = fused.trainer(); + ( + trainer.read_isv_signal_at(ALPHA_GRAD_SMOOTHED_INDEX), + trainer.read_isv_signal_at(ALPHA_GRAD_RAW_INDEX), + trainer.read_isv_signal_at(BETA_RATE_LIMITER_ADAPTIVE_INDEX), + trainer.read_isv_signal_at(K_AUX_ADAPTIVE_INDEX), + trainer.read_isv_signal_at(K_Q_ADAPTIVE_INDEX), + trainer.read_isv_signal_at(AUX_DIR_ACC_VARIANCE_EMA_INDEX), + trainer.read_isv_signal_at(Q_DISAGREEMENT_VARIANCE_EMA_INDEX), + trainer.read_isv_signal_at(ALPHA_GRAD_RAW_VARIANCE_EMA_INDEX), + trainer.read_isv_signal_at(Q_DISAGREEMENT_SHORT_EMA_INDEX), + trainer.read_isv_signal_at(Q_DISAGREEMENT_LONG_EMA_INDEX), + trainer.read_isv_signal_at(GATE1_OPEN_STATE_INDEX), + trainer.read_isv_signal_at(AUX_DIR_ACC_POST_OPEN_MIN_INDEX), + trainer.read_isv_signal_at(GRADIENT_HACK_LOCKOUT_REMAINING_INDEX), + ) + } else { + (0.0, 0.0, 0.5, 20.0, 15.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 1.0, 0.0) + }; + tracing::info!( + "HEALTH_DIAG[{}]: pearl_egf_diag α_smoothed={:.4} α_raw={:.4} β={:.3} \ + k_aux={:.2} k_q={:.2} \ + var_aux={:.5} var_q={:.5} var_α={:.5} \ + q_dis_s={:.4} q_dis_l={:.4} \ + gate1={} post_open_min={:.3} lockout={}", + epoch, + alpha_smoothed, alpha_raw, beta_adaptive, + k_aux_curr, k_q_curr, + var_aux, var_q, var_alpha, + q_dis_short, q_dis_long, + if gate1_state == 1.0 { "open" } else { "closed" }, + post_open_min, lockout as u32, + ); + } + // C1/P1: propagate health to GPU replay buffer for diversity-weighted priorities. { let mut agent = self.agent.write().await; diff --git a/docs/isv-slots.md b/docs/isv-slots.md index 90b7e9780..cb0a51146 100644 --- a/docs/isv-slots.md +++ b/docs/isv-slots.md @@ -388,5 +388,10 @@ Producer + consumer wiring lands in B.3-B.12; B.1 is additive infrastructure (slot constants), B.2 wires the 11 fold-reset registry entries + dispatch arms — both are additive only (no behavior change). +B.12 (2026-05-05) adds the per-epoch `HEALTH_DIAG[{epoch}]: pearl_egf_diag` +emit in `training_loop.rs` reading all 13 slots. gate1/gate2 sigmoid output +values are omitted from the emit (recomputing host-side would violate +`feedback_no_cpu_compute_strict`); the inputs are sufficient for inference. + **Spec:** `docs/superpowers/specs/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md` **Plan:** `docs/superpowers/plans/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md`