diff --git a/crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu b/crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu index 3d0214102..8ac96af80 100644 --- a/crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu +++ b/crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu @@ -22,12 +22,16 @@ // Math: // competence = confidence(n_trades) × sigmoid(k_sharp × max(0, wr_ema − break_even)) // w_competence = 1 − competence -// w_decay = min(1, 2 × frac_alerted) +// w_decay = max(0, 2 × frac_alerted − 1) // FIX v5.1: only fires above 50% // w = clamp(max(w_competence, w_decay), 0, 1) // // At training start (n_trades=0, wr_ema=0): competence ≈ 0, w_competence ≈ 1 → w=1 (full scaffold) // After warmup and wr_ema crossing break-even: competence → 1, w → 0 (pure pnl) -// If edge-decay PH alerts on >50% of batches: w_decay forces scaffold back up +// If edge-decay PH alerts on >50% of batches: w_decay re-engages PROPORTIONAL to +// excess above 50% baseline (alpha-rl-zf6s5 step 5719 verdict 2026-06-02 showed +// train-time PH inherently alerts on 75-95% of batches, so the v5 original +// `min(1, 2×frac)` pinned w=1 forever even when wr crossed break-even; the new +// `max(0, 2×frac − 1)` only fires above 50% and reaches 1.0 only at 100% alert). // // Per `feedback_no_atomicadd`: single-thread launch (1×1×1), no atomic ops. // Per `feedback_cpu_is_read_only`: pure device-side. @@ -76,8 +80,12 @@ extern "C" __global__ void rl_surfer_scaffold_controller(float* isv) { // Inverse: scaffold weight from competence. const float w_competence = 1.0f - competence; - // Edge-decay re-engagement: if >50% of batches are PH-alerted, force w back up. - const float w_decay = fminf(1.0f, 2.0f * frac_decay); + // Edge-decay re-engagement: only fires ABOVE 50% baseline. Train-time PH + // naturally alerts on a steady fraction of batches even during healthy + // learning (pearl_edge_decay_detector_phase1_validated_train_also_decays); + // re-engagement must measure EXCESS alertedness, not absolute level, or it + // pins the scaffold at w=1 forever (alpha-rl-zf6s5 step 5719 failure mode). + const float w_decay = fmaxf(0.0f, 2.0f * frac_decay - 1.0f); const float w_raw = fmaxf(w_competence, w_decay); const float w = fmaxf(0.0f, fminf(1.0f, w_raw));