Root cause of L40S segfault (train-tgdlq workflow, commit 0d639dfe9):
1. F4 (IB) and F5 (barrier) gradient kernels indexed cql_d_adv_logits and
on_b_logits_buf with stride b0_size (4) instead of total_actions (13).
The buffer is sized [B, total_actions, num_atoms]. Writes for batch i>0
landed in other actions'/batches' gradient slots — corrupting CQL gradient
for every batch beyond the first. After cuBLAS backward, weights diverged
in undefined ways; validation forward then segfaulted reading NaN-laced
parameters in GpuBacktestEvaluator init.
Fix: add `total_actions` kernel parameter (int), use it as the full stride
for adv_row and d_adv_a pointer arithmetic; keep b0_size as the loop
bound (direction-branch-only update). All three launch sites updated:
inlined F5 launch in apply_cql_gradient, inlined F4 launch alongside,
and the standalone inject_barrier_into_cql_d_logits method.
2. D6 ensemble oracle fired at epoch 0 because per-branch Q-gaps are all 0
at random init (range = 0 → score = 1.0 → plasticity trigger).
Shrink-and-perturb ran immediately, then again next epoch, etc. Gate
behind `learning_health.epoch > 5` (3 warmup + 2 buffer epochs for Q to
move) so the oracle only fires on post-warmup real collapse, not
untrained networks.
Both bugs are regressions from today's work — F4/F5 introduced yesterday,
D6 became live after the ens_disagreement real-signal fix in d9d35b6fa.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>