Root cause of all NaN/garbage: 12 branch logit pointer offsets used
hardcoded * 4 (sizeof f32) instead of * sizeof(bf16) = 2.
This caused mse_loss_batched and c51_loss_batched to read 2× past
the end of branch logit buffers — 3719 out-of-bounds reads per step
(detected by compute-sanitizer). The out-of-bounds reads produced
NaN gradients → Adam propagated NaN to params → all downstream
reads returned garbage.
Fix: replace * 4 with * std::mem::size_of::<half::bf16>() (= 2).
Smoke test: Q-values now VALID (1.18, 1.91), Sharpe +8.98,
val_loss=-4.04. Training completes 3 epochs without divergence.
Remaining: train_loss/grad_norm readback still 0 (training_guard).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>