Files
foxhunt/crates/ml-alpha
jgrusewski 8b2ac1e577 fix(per-horizon-cfc): ALPHA — skip reorder, channels_in_bucket lookup, Adam moment masking
URGENT correctness fix surfaced by checkpoint deep-dive after Smoke 2 failed
the WIN gate (mean_run_len ratio = 1.0× vs target ≥10×; all 5 horizons
uniformly ~2.4 events).

THREE INDEXING BUGS identified:

1. tau_reorder produces bucket-grouped tau_all_d, but Controller B's
   tau_clamp_kernel reads bucket_id_per_channel[c] where c is the
   POSITION in the reordered buffer (not the original channel index)
   → wrong bucket-IQR lookup → τ never constrained → buckets collapse
   (deep-dive showed all 5 buckets had nearly identical τ ranges
   [0.07, 74] except bucket 4 reaching 878).

2. heads_w_skip grad mask ran but Adam (m, v) momentum from BEFORE the
   transition re-introduced gradient signal across the transition →
   off-bucket positions stayed nonzero throughout training
   (deep-dive: 512/512 = 100% of off-bucket positions nonzero in
   trunk_best_h6000.bin).

3. per-branch CfC kernel read w_in[c * HIDDEN_DIM + k] with c =
   bucket-grouped position, but W_in rows are indexed by ORIGINAL
   channel → kernel read wrong rows for each output → outputs were
   essentially random per-channel.

ALPHA FIX: skip the reorder entirely, use bucket-filter throughout:
- Removed tau_reorder_kernel; cfc.tau_d stays in original-channel layout.
- Added channels_in_bucket_kernel that populates a
  [N_HORIZONS × MAX_BUCKET_DIM] lookup (original channel index per
  (bucket, within-bucket-position)).
- Per-branch CfC fwd+bwd now reads channels_in_bucket[branch][tid]
  → original_c, then uses original_c for w_in/w_rec indexing. All
  weights stay in original layout consistently.
- Controller B's tau_clamp_kernel now correctly operates on original-
  channel cfc.tau_d with bucket_id_per_channel[c] lookup (no position-
  vs-channel confusion).
- Added zero_off_bucket_kernel + three-layer defense for heads_w_skip
  block-diagonal invariant:
    (a) At transition: zero off-bucket params + zero Adam (m, v)
        moments via opt_heads_w_skip.m_mut() / v_mut() accessors.
    (b) Per-step: heads_w_skip_grad_mask_apply_kernel zeros off-bucket
        gradients before Adam step (unchanged from prior follow-up).
    (c) Per-step: zero_off_bucket_kernel zeros off-bucket params after
        Adam step, catching any drift from Adam's ε denominator or
        weight decay.

New AdamW::m_mut()/v_mut() accessors enable the projection at transition.

GPU oracle tests: 19 total (16 in bucket_transition + 3 in cfc_step_per_branch).
New tests verify:
- channels_in_bucket_kernel correctness under non-contiguous bucket assignment
- zero_off_bucket maintains invariant after many mock Adam steps
- fwd kernel writes only to bucket-assigned channels under arbitrary mapping

Per `feedback_no_partial_refactor`: all 3 indexing bugs + Adam momentum
defense land in one commit.

ml-alpha lib: 33 passed.
GPU oracle tests on RTX 3050 sm_86: 19 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:43:51 +02:00
..