Low-priority cleanup. The Candle attention convergence (#146) rewrote `wave_attention` in `candle_attention.rs` to route through `common::attn::wave_attention_forward` via `WaveAttentionCustomOp`. That covers the main-forward path.
But `CandleWaveModel::forward_with_monitors` in `candle_forward.rs` has a SECOND implementation of attention inline (around lines 310-380 in the current file). It still uses the old dense causal attention math — no phase-hashing, no content projection, and doesn't go through the CustomOp so it doesn't use the common code.
Why it didn't cause J10 failures: `forward_with_monitors` is only called at health-check intervals during training, not on the hot path. J10 runs `model.forward()` (the main path), which was fixed. So the divergence is confined to diagnostic output.
Impact: any monitor that runs during training reads attention weights/statistics from this parallel implementation. If someone reads per-head entropy or attention coherence from training logs, those numbers don't match what the real training forward computed.
Fix: extract the monitor logic (capture attention weights, compute head entropy/max-weight) into an optional return path on the main `wave_attention` wrapper, or make `forward_with_monitors` call `wave_attention` and pull stats from the CustomOp cache after the call. No new math, just routing.
Effort: small. ~30 min. No research gating — pure hygiene.
Low-priority cleanup. The Candle attention convergence (#146) rewrote `wave_attention` in `candle_attention.rs` to route through `common::attn::wave_attention_forward` via `WaveAttentionCustomOp`. That covers the main-forward path.
But `CandleWaveModel::forward_with_monitors` in `candle_forward.rs` has a SECOND implementation of attention inline (around lines 310-380 in the current file). It still uses the old dense causal attention math — no phase-hashing, no content projection, and doesn't go through the CustomOp so it doesn't use the common code.
Why it didn't cause J10 failures: `forward_with_monitors` is only called at health-check intervals during training, not on the hot path. J10 runs `model.forward()` (the main path), which was fixed. So the divergence is confined to diagnostic output.
Impact: any monitor that runs during training reads attention weights/statistics from this parallel implementation. If someone reads per-head entropy or attention coherence from training logs, those numbers don't match what the real training forward computed.
Fix: extract the monitor logic (capture attention weights, compute head entropy/max-weight) into an optional return path on the main `wave_attention` wrapper, or make `forward_with_monitors` call `wave_attention` and pull stats from the CustomOp cache after the call. No new math, just routing.
Effort: small. ~30 min. No research gating — pure hygiene.