/* h_s2_aux_rms_ema — GPU-driven RMS(h_s2_aux) step observation into
 * `ISV[H_S2_AUX_RMS_EMA_INDEX=449]` for Pearls A+D consumption.
 *
 * SP14 Layer C Phase C.6 (2026-05-08): mirrors `h_s2_rms_ema_kernel.cu`
 * (slot 96, Q's GRN trunk output) for the separate auxiliary trunk output.
 * Input: `h_s2_aux [B, SH2]` (aux trunk final output, no activation).
 * Output: writes directly to `ISV[isv_target_idx]` (slot 449).
 *
 * GPU-drives-CPU-reads (spec §4.C.6) and
 * pearl_cold_path_no_exception_to_gpu_drives.md compliant.
 *
 * Single-block kernel (256 threads, shmem-reduction). No atomicAdd
 * (per `feedback_no_atomicadd.md`); the in-block tree reduction is
 * standard `s >>= 1` with `__syncthreads()`. Only thread 0 writes the
 * step observation.
 *
 * Pearl-A first-observation bootstrap: sentinel = 0.0; first valid
 * observation REPLACES directly (no blend). Subsequent observations
 * use fixed α=0.05 EMA blend (per `pearl_first_observation_bootstrap.md`).
 * ISV slot 449 is outside the SP4/SP5 wiener_state_buf linear span so
 * Pearl-D Wiener-optimal adaptive α is not available — fixed α=0.05 is
 * the correct per-step cadence blend rate consistent with the aux trunk
 * producer launch frequency.
 *
 * Args:
 *   h_s2_aux      — [B, SH2] row-major f32 device ptr.
 *   B             — batch size.
 *   SH2           — aux trunk output width (AUX_HIDDEN_DIM = 256).
 *   isv           — [ISV_TOTAL_DIM] f32 device ptr (mapped-pinned ISV bus).
 *   isv_target_idx — H_S2_AUX_RMS_EMA_INDEX (449).
 *   alpha         — EMA blend rate (0.05 for per-step producer cadence).
 *
 * Cost: one launch per collector step (same cadence as aux_trunk_forward).
 * 256 threads × ceil(B*SH2/256) strided loads + log2(256)=8 reduce steps
 * + 1 global write.
 */

extern "C" __global__ void h_s2_aux_rms_ema_update(
    const float* __restrict__ h_s2_aux,   /* [B, SH2] row-major */
    int          B,
    int          SH2,
    float* __restrict__ isv,              /* [ISV_TOTAL_DIM] mapped-pinned */
    int          isv_target_idx,          /* H_S2_AUX_RMS_EMA_INDEX (449) */
    float        alpha                    /* EMA blend rate (0.05) */
) {
    /* Single-block contract — no atomicAdd; only block 0 runs. */
    if (blockIdx.x != 0) return;

    extern __shared__ float smem[];
    const int tid   = (int)threadIdx.x;
    const int block = (int)blockDim.x;
    const int N     = B * SH2;

    /* Per-thread sum of squares over a strided slice. */
    float local_sumsq = 0.0f;
    for (int i = tid; i < N; i += block) {
        const float v = h_s2_aux[i];
        local_sumsq += v * v;
    }
    smem[tid] = local_sumsq;
    __syncthreads();

    /* In-block tree reduction (no atomicAdd). */
    for (int s = block / 2; s > 0; s >>= 1) {
        if (tid < s) smem[tid] += smem[tid + s];
        __syncthreads();
    }

    /* Thread 0 writes the EMA-blended step observation.
     * B > 0 and SH2 > 0 are constructor-guaranteed invariants; N = B*SH2 > 0
     * always holds. No defensive ternary — a zero N would produce NaN which
     * propagates loudly through downstream consumers.
     *
     * Pearl-A first-observation bootstrap: sentinel 0.0 → REPLACE directly.
     * Steady-state: EMA blend with caller-supplied α. */
    if (tid == 0) {
        const float rms     = sqrtf(smem[0] / (float)N);
        const float current = isv[isv_target_idx];

        float next;
        if (current <= 0.0f) {
            /* Pearl-A: sentinel or cold-start zero — replace outright. */
            next = rms;
        } else {
            next = (1.0f - alpha) * current + alpha * rms;
        }
        isv[isv_target_idx] = next;
        __threadfence_system();   /* PCIe-visible write for mapped pinned host_ptr */
    }
}
