Skip to main content

memra_engine/
kda.rs

1//! Kimi Delta Attention (KDA) — the glm5_next (GLM-5.3-Flash) linear-attention mixer.
2//!
3//! Arithmetic contract: `memra_reference::kimi_delta_net`, pinned by
4//! `kimi_delta_net_matches_hand_derived_three_token_recurrence`. Every step below cites the
5//! reference stage it reproduces; the GPU-vs-reference gate is
6//! `crates/memra-engine/tests/kda_fixture_gpu.rs`.
7//!
8//! Geometry (research/glm53-flash-bringup-20260827/CENSUS.md): 64 heads x 128, q/k/v all the
9//! same width, short conv kernel 4, forget-gate lower bound -5.0. Symmetric widths and no GQA
10//! repeat mean channel `c == h*head_dim + i` IS the (head, dim) pair, so every per-token tensor
11//! stays token-major end to end — there is no analogue of GDN's qkv_to_gdn_repack scatter here.
12//!
13//! PREFILL DISPATCH — SEQUENTIAL SCAN, not the chunked UT transform (deliberate).
14//! `memra_kda_scan_s128` runs prefill and decode alike, which is exactly the shipped
15//! GDN arrangement next door: `gdn_scan_s128` IS the default prefill path and the chunked WY
16//! kernels sit behind `MEMRA_GDN_CHUNKED`. One kernel for both also keeps the decode==verify
17//! dispatch identity that cu/hybrid.cu's headers require. A chunked twin exists but is
18//! SHELVED, ATTRIBUTED-NEGATIVE — it is not a pending tuning follow-up. It was built as L3
19//! of the prefill-gap plan (`MEMRA_KDA_CHUNKED`, unmerged branch lane/glm5-kda-chunk-scan),
20//! and the box prefill census then attributed the wall elsewhere: on a cold 4626-token prime
21//! the whole kda family is 221.6 GPU ms of 6598 (3.4%, "confirms L3's ATTRIBUTED-NEGATIVE:
22//! scan ~2.4%") while mla-prefill-attn owns 75.8% — receipts
23//! `research/glm53-flash-bringup-20260827/launch-diet-20260830/WINDOW-20260830.md` §4 and
24//! `box-receipts-20260830/census-analysis.txt`. No A/B is owed on the scan; a revival needs
25//! a new attribution first. The algebra stays banked for that day: it is NOT a transcription
26//! of the GDN K1-K5 chain — KDA's decay is per channel, so the chunk form needs a per-channel
27//! cumulative log gate `Gcum[t][i]` with `k` scaled by `exp(-Gcum)` and `q` by `exp(+Gcum)`
28//! (banked `chunk_kimi_delta_attention` in
29//! research/glm53-flash-bringup-20260827/modular_glm5_next-ref.py), where GDN gets away with
30//! one scalar `G` per (token, head).
31//!
32//! CONV FUSION — fused WEIGHTS and a fused RING, per-plane launches. The checkpoint ships three
33//! per-plane conv weights; they are concatenated once at load into one `[3*qkv, kernel]` f32
34//! buffer, because the plan already declares the state carrier fused (`StatePlan::Recurrent`
35//! `conv_width = 3*qkv`) and that makes a plane's weight offset and its ring offset the same
36//! `plane*qkv` arithmetic. The three PROJECTIONS stay separate: they are independently
37//! quantized tensors, and concatenating them would mean dequantizing to build one matmul.
38//! Applying each plane's taps to its own plane is the fused grouped conv exactly (the reference
39//! says so in-line), so nothing is approximated by the split.
40
41use crate::Engine;
42use crate::cache::{Cache, RecurLayer};
43use crate::model::GpuTensor;
44use cudarc::driver::{CudaSlice, LaunchConfig, PushKernelArg};
45use memra_gguf::model_plan::KimiDeltaNetPlan;
46use memra_gguf::source::TensorSource;
47use std::sync::atomic::{AtomicU64, Ordering};
48
49/// Engagement counter for the fused 6-way projection door (`MEMRA_KDA_FUSED_PROJ`), the
50/// grouped-prefill `moe_grouped_prefill_dispatches` precedent: gates and box A/B arms count
51/// dispatches at the arm's own call site instead of inferring engagement from a 200.
52pub static KDA_FUSED6_DISPATCHES: AtomicU64 = AtomicU64::new(0);
53pub static KDA_FUSED6_E4M3_DISPATCHES: AtomicU64 = AtomicU64::new(0);
54
55/// Same door, BF16 operand arm (`qmatvec_kda6_bf16f32`, lane/glm5-decode-diet lever 3).
56/// Counted separately so a box A/B on the serving recipe (MEMRA_BF16_MMV=1, where the q8 arm
57/// refuses by design) can attribute engagement to the arm that actually ran.
58pub static KDA_FUSED6_BF16_DISPATCHES: AtomicU64 = AtomicU64::new(0);
59
60/// Same door, W8-MIRROR arm (`qmatvec_kda6_q8f32_rp_v2`, lane/b200-gemv-hbm-20260902 round 3).
61/// Counted separately for the same reason the bf16 arm is: a box A/B on the serving recipe must
62/// be able to attribute engagement to the arm that actually ran.
63pub static KDA_FUSED6_Q8RP_DISPATCHES: AtomicU64 = AtomicU64::new(0);
64
65/// The only head width `memra_kda_scan_s128` is instantiated for, and the only one glm5_next
66/// ships (`linear_attn_config.head_dim = 128`).
67pub const KDA_HEAD_DIM: usize = 128;
68/// The conv kernels hold their window in a fixed register array; wider kernels would silently
69/// read past it, so the loader refuses them.
70const KDA_MAX_CONV_KERNEL: usize = 8;
71/// FLA l2norm epsilon. Fixed at 1e-6 and INSIDE the sqrt — independent of the layer's rms eps,
72/// which is a different constant used by the output norm below.
73const KDA_L2_EPS: f32 = 1e-6;
74
75/// One loaded KDA mixer. Field names follow the reference's tensor roles, not the HF spellings.
76pub struct KdaAttnLayer {
77    pub plan: KimiDeltaNetPlan,
78    /// q/k/v projections, `[qkv, hidden]` each.
79    pub wq: GpuTensor,
80    pub wk: GpuTensor,
81    pub wv: GpuTensor,
82    /// Forget gate low-rank pair: `f_a [head_dim, hidden]`, `f_b [qkv, head_dim]`.
83    pub f_a: GpuTensor,
84    pub f_b: GpuTensor,
85    /// Output gate low-rank pair, same shapes as the forget pair.
86    pub g_a: GpuTensor,
87    pub g_b: GpuTensor,
88    /// Per-head beta projection, `[heads, hidden]`.
89    pub b_proj: GpuTensor,
90    /// Output projection, `[hidden, qkv]`.
91    pub wo: GpuTensor,
92    /// The three per-plane conv weights concatenated into `[3*qkv, kernel]` (see module header).
93    pub conv: CudaSlice<f32>,
94    /// `A_log [heads]`, `dt_bias [qkv]` (per CHANNEL, unlike GDN's per-head bias),
95    /// `o_norm [head_dim]`.
96    pub a_log: GpuTensor,
97    pub dt_bias: GpuTensor,
98    pub o_norm: GpuTensor,
99    /// glm5 TP-2 sidecar (`MEMRA_GLM5_TP`, lane/glm5-tp2). `Some` means THIS layer struct is
100    /// the ROOT-RANK HEAD SHARD (heads/2) and the sidecar carries the peer shard + runtime.
101    /// Every plain entry point REFUSES a sharded layer by name — only the TP walk
102    /// (`glm5_tp::kda_tp_*`) may execute it. `None` everywhere else (zero cost, zero change).
103    pub tp: Option<Box<crate::glm5_tp::Glm5TpKda>>,
104}
105
106impl KdaAttnLayer {
107    pub fn heads(&self) -> usize {
108        self.plan.num_heads as usize
109    }
110    pub fn head_dim(&self) -> usize {
111        self.plan.head_dim as usize
112    }
113    pub fn qkv(&self) -> usize {
114        self.heads() * self.head_dim()
115    }
116    pub fn conv_kernel(&self) -> usize {
117        self.plan.conv_kernel as usize
118    }
119    /// Fused conv ring width, matching `StatePlan::Recurrent { conv_width }` for this layer.
120    pub fn conv_width(&self) -> usize {
121        3 * self.qkv()
122    }
123    /// Recurrent state elements, matching `StatePlan::Recurrent { state_width }`.
124    pub fn state_width(&self) -> usize {
125        self.heads() * self.head_dim() * self.head_dim()
126    }
127
128    /// Load block `il`'s KDA tensors. Names are the ggml-dialect contract names from
129    /// `memra_gguf::tensor_contract::add_kda`; the safetensors source translates them.
130    pub fn load(
131        e: &Engine,
132        src: &dyn TensorSource,
133        il: u32,
134        plan: &KimiDeltaNetPlan,
135    ) -> Result<Self, Box<dyn std::error::Error>> {
136        let heads = plan.num_heads as usize;
137        let head_dim = plan.head_dim as usize;
138        let kernel = plan.conv_kernel as usize;
139        if head_dim != KDA_HEAD_DIM {
140            return Err(format!(
141                "blk.{il}: KDA head_dim {head_dim} is not the {KDA_HEAD_DIM} the scan kernel is \
142                 instantiated for; a new memra_kda_scan_s<N> instantiation is required before \
143                 this geometry can serve"
144            )
145            .into());
146        }
147        if heads == 0 {
148            return Err(format!("blk.{il}: KDA num_heads must be positive").into());
149        }
150        if !(2..=KDA_MAX_CONV_KERNEL).contains(&kernel) {
151            return Err(format!(
152                "blk.{il}: KDA conv_kernel {kernel} outside the 2..={KDA_MAX_CONV_KERNEL} window \
153                 the conv kernels hold in registers"
154            )
155            .into());
156        }
157        let p = |s: &str| format!("blk.{il}.{s}");
158        let load = |name: String| GpuTensor::load_from_source(e, src, &name);
159
160        let qkv = heads * head_dim;
161        // Fuse the three per-plane conv weights into one [3*qkv, kernel] buffer (module header).
162        // Each source tensor is [qkv, kernel] channel-major, so the planes concatenate as whole
163        // row blocks and plane p lands at row p*qkv — the ring's own plane offset.
164        let mut conv = e.zeros(3 * qkv * kernel)?;
165        for (plane, name) in [
166            "kda_q_conv1d.weight",
167            "kda_k_conv1d.weight",
168            "kda_v_conv1d.weight",
169        ]
170        .into_iter()
171        .enumerate()
172        {
173            let w = load(p(name))?;
174            let src_data = w.float_data();
175            if src_data.len() != qkv * kernel {
176                return Err(format!(
177                    "blk.{il}.{name}: {} elements, contract requires {}",
178                    src_data.len(),
179                    qkv * kernel
180                )
181                .into());
182            }
183            e.copy_into(&mut conv, plane * qkv * kernel, src_data, qkv * kernel)?;
184        }
185
186        Ok(Self {
187            plan: *plan,
188            wq: load(p("kda_q.weight"))?,
189            wk: load(p("kda_k.weight"))?,
190            wv: load(p("kda_v.weight"))?,
191            f_a: load(p("kda_f_a.weight"))?,
192            f_b: load(p("kda_f_b.weight"))?,
193            g_a: load(p("kda_g_a.weight"))?,
194            g_b: load(p("kda_g_b.weight"))?,
195            b_proj: load(p("kda_b.weight"))?,
196            wo: load(p("kda_out.weight"))?,
197            conv,
198            a_log: load(p("kda_a_log"))?,
199            dt_bias: load(p("kda_dt.bias"))?,
200            o_norm: load(p("kda_o_norm.weight"))?,
201            tp: None,
202        })
203    }
204}
205
206/// Which conv arm a call takes. `Prefill` reads the ring as a left pad and rolls it afterwards;
207/// `Decode` fuses assemble+conv+roll for the single new row. The two produce bit-identical
208/// values at T=1 (same ascending tap order over the same window) — the split exists so decode
209/// and the spec verify keep one dispatch class, per the cu/hybrid.cu decode==verify law.
210#[derive(Clone, Copy, PartialEq, Eq)]
211pub(crate) enum ConvArm {
212    Prefill,
213    Decode,
214}
215
216/// The scan-input buffers of one KDA step, STOLEN from the step instead of dropped
217/// (lane/glm5-loop-port, port 3 — the module doc's named GdnStash/ReplaySSM diet): the
218/// glm5 verify walk's rollback checkpoint keeps these ~160 KB of already-allocated
219/// buffers per row per layer and retires the per-row 4 MiB recurrent-state clones
220/// (~0.95 GiB transient at K=7). Replaying `kda_scan` over them from a pre-round state
221/// snapshot rebuilds the post-row state EXACTLY: each replay is the ORIGINAL t=1 launch
222/// re-issued — same kernel, same inputs, same shape — so the rebuilt state is
223/// byte-identical to the clone it replaces by construction, not by a numeric argument.
224pub struct KdaScanInputs {
225    pub q: CudaSlice<f32>,
226    pub k: CudaSlice<f32>,
227    pub v: CudaSlice<f32>,
228    pub g: CudaSlice<f32>,
229    pub beta: CudaSlice<f32>,
230}
231
232/// The rollback stash of one BATCHED verify-rows KDA call (lane/glm5-verify-batch): the
233/// per-layer t=K+1 twin of the per-row [`KdaScanInputs`] steal. Everything here is either
234/// stolen from buffers the call allocated anyway (`raws`, `scan` — zero copies) or one
235/// small clone per layer per round (`ring_snap`, `3*qkv*(kernel-1)` floats ~ 96 KiB).
236///
237/// Rollback to `keep` rows rebuilds both state planes EXACTLY:
238///   * conv ring: restore `ring_snap`, then re-issue `kda_conv_ring_roll` per plane over
239///     `raws` at T=keep — the roll is pure placement (no arithmetic), so the rebuilt ring
240///     is the sequential chain's ring after row keep-1 byte-for-byte.
241///   * ssm state: ONE `kda_scan` replay at T=keep from the caller's pre-round snapshot
242///     over the batched `scan` inputs (the kernel walks rows 0..keep of the [t, ..]
243///     buffers) — the in-kernel T-loop IS the chained t=1 program (register-resident
244///     state, identical per-step order), held by the scan-chain bit-gate.
245pub struct KdaRowsStash {
246    /// The fused conv ring BEFORE this call's rolls (one clone per layer per round).
247    pub ring_snap: CudaSlice<f32>,
248    /// RAW (pre-conv) q/k/v projection rows `[t, qkv]`, stolen post-roll (plane order).
249    pub raws: [CudaSlice<f32>; 3],
250    /// Batched scan inputs `[t, ..]`, stolen post-scan.
251    pub scan: KdaScanInputs,
252    /// Row count of the call that filled this stash; rollback validates `keep` against it.
253    pub rows: usize,
254}
255
256/// What a `kda_core` call is asked to leave behind for rollback — and, for `Rows`, which
257/// matmul class the call rides (the decode-exact rows classes, `matmul_rows_exact`).
258pub(crate) enum KdaStash<'a> {
259    /// No rollback stash (prefill / plain decode).
260    None,
261    /// Per-row t=1 steal (loop-port 3, the per-row verify walk).
262    Decode(&'a mut Option<KdaScanInputs>),
263    /// BATCHED verify-rows steal (lane/glm5-verify-batch): scan inputs + raw conv rows +
264    /// a pre-call ring snapshot; every matmul rides `matmul_rows_exact` so each row is
265    /// bit-identical to the t=1 decode program per the decode-exact class contracts.
266    Rows(&'a mut Option<KdaRowsStash>),
267}
268
269/// `MEMRA_KDA_STEP_TRACE=1` (gate-harness instrument, default OFF, never a serving flag): after
270/// each sub-step of the KDA core, print the non-finite element count of every live buffer on one
271/// line. memra#131 cell 9 placed the graph door's poison inside `kda_decode_cached` at layer 4
272/// (finite input, finite state, all-NaN mixer output after a capture); this names the kernel.
273fn kda_trace_on() -> bool {
274    static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
275    *V.get_or_init(|| std::env::var("MEMRA_KDA_STEP_TRACE").as_deref() == Ok("1"))
276}
277fn kda_trace(e: &Engine, stage: &str, t: usize, bufs: &[(&str, &CudaSlice<f32>)]) {
278    if !kda_trace_on() {
279        return;
280    }
281    // A device-to-host copy is illegal inside an open CUDA graph capture (it invalidates the
282    // capture); box cell 11 (memra#131) hit exactly that on the session's first decode step,
283    // which the door captures. Print the stage with a note instead of counting.
284    if crate::glm5_graph_capture_open() {
285        eprintln!("[kda-step-trace] t={t} {stage}: (inside an open graph capture; not counted)");
286        return;
287    }
288    let mut line = format!("[kda-step-trace] t={t} {stage}:");
289    for (name, b) in bufs {
290        let n = match e.dtoh(b) {
291            Ok(v) => v.iter().filter(|x| !x.is_finite()).count(),
292            Err(_) => usize::MAX,
293        };
294        line.push_str(&format!(" {name}={n}/{}", b.len()));
295    }
296    eprintln!("{line}");
297}
298
299/// The whole mixer, stage for stage against `memra_reference::kimi_delta_net`.
300///
301/// `ring` is the fused `[3*qkv, kernel-1]` conv state (zeroed = fresh prefill's zero left pad)
302/// and is updated in place. `state_in`/`state_out` are the `[heads, 128, 128]` recurrent state
303/// in the kernel's transposed `M[col][i]` layout; they MUST be distinct buffers.
304#[allow(clippy::too_many_arguments)]
305// allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
306fn kda_core(
307    e: &Engine,
308    la: &KdaAttnLayer,
309    x: &CudaSlice<f32>,
310    t: usize,
311    eps: f32,
312    ring: &mut CudaSlice<f32>,
313    state_in: &CudaSlice<f32>,
314    state_out: &mut CudaSlice<f32>,
315    arm: ConvArm,
316    stash: KdaStash<'_>,
317    scan_clock: Option<&mut u64>,
318    pre_q8: KdaPreQ8<'_>,
319) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
320    // glm5 TP fail-closed choke point: every plain KDA entry (stateless, prime, decode,
321    // stash — INCLUDING the batched verify-rows walk, `kda_verify_rows_cached`) funnels
322    // through here. A TP-sharded layer holds heads/2 — running it on the plain path would
323    // compute a silently-halved mixer, so it refuses by name instead.
324    if la.tp.is_some() {
325        return Err(format!(
326            "KDA layer is glm5-TP-sharded (MEMRA_GLM5_TP): the plain mixer path is unwired \
327             for a head shard — only the TP decode/prime walk may execute it (t={t}, arm \
328             {})",
329            if arm == ConvArm::Decode {
330                "decode"
331            } else {
332                "prefill"
333            }
334        )
335        .into());
336    }
337    // Verify-batch wo seam (lane/glm5-verify-batch): the rows arm routes the output
338    // projection through the decode-exact classes, exactly like every projection inside
339    // the core — the wo dispatch moved into this wrapper with the TP split, its routing
340    // did not change.
341    let rows_exact = matches!(stash, KdaStash::Rows(_));
342    let gated = kda_core_gated(
343        e, la, x, t, eps, ring, state_in, state_out, arm, stash, scan_clock, pre_q8,
344    )?;
345    if rows_exact {
346        let y = e.matmul_rows_exact(&la.wo, &gated, t);
347        // Door W: gated's last reader was the wo matmul above.
348        e.vws_recycle(gated);
349        y
350    } else {
351        e.matmul(&la.wo, &gated, t)
352    }
353}
354
355/// [`kda_core`] up to (and excluding) the output projection: returns the gated `[t, qkv]`
356/// mixer output. Split out for the glm5 TP-2 seam, whose column-parallel `wo` runs over the
357/// cross-rank GATHERED gated tensor rather than this shard's slice — the plain path is
358/// `kda_core` above, byte-for-byte the pre-split body (the wo matmul and its rows-exact
359/// routing moved, nothing else). This body is the CURRENT doored/batched core: it carries
360/// the `MEMRA_KDA_FUSED_PROJ` door and the verify-batch rows arm; the TP decode/prime walk
361/// calls it with `KdaStash::None`, the spec x TP verify walk (lane/glm5-composition) with
362/// `KdaStash::Rows` per rank, and the TP load preflight refuses the fused-proj door by
363/// name (unproven composition on head shards — see the FLAGS.md composition matrix).
364#[allow(clippy::too_many_arguments)] // mirrors kda_core's own contract-shaped list
365pub(crate) fn kda_core_gated(
366    e: &Engine,
367    la: &KdaAttnLayer,
368    x: &CudaSlice<f32>,
369    t: usize,
370    eps: f32,
371    ring: &mut CudaSlice<f32>,
372    state_in: &CudaSlice<f32>,
373    state_out: &mut CudaSlice<f32>,
374    arm: ConvArm,
375    stash: KdaStash<'_>,
376    mut scan_clock: Option<&mut u64>,
377    pre_q8: KdaPreQ8<'_>,
378) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
379    let heads = la.heads();
380    let head_dim = la.head_dim();
381    let qkv = la.qkv();
382    let kernel = la.conv_kernel();
383    // The BATCHED verify-rows arm (lane/glm5-verify-batch): prefill conv dispatch (per-row
384    // bit-identical to the decode arm — same ascending taps over the same window values,
385    // held by the conv-arm bit-gate) + decode-exact matmul classes + the rows stash.
386    let rows_exact = matches!(stash, KdaStash::Rows(_));
387    if rows_exact && arm != ConvArm::Prefill {
388        return Err("KDA rows stash requires the prefill conv arm".into());
389    }
390    if arm == ConvArm::Decode && t != 1 {
391        return Err(format!("KDA decode arm requires t == 1, got {t}").into());
392    }
393    if ring.len() < la.conv_width() * (kernel - 1) {
394        return Err(format!(
395            "KDA conv ring holds {} floats, layer needs {}",
396            ring.len(),
397            la.conv_width() * (kernel - 1)
398        )
399        .into());
400    }
401    if state_in.len() < la.state_width() || state_out.len() < la.state_width() {
402        return Err(format!(
403            "KDA recurrent state holds {}/{} floats, layer needs {}",
404            state_in.len(),
405            state_out.len(),
406            la.state_width()
407        )
408        .into());
409    }
410
411    // Stage 1 — the six projections that read x directly. f_b/g_b are chained off their own
412    // down-projections below, exactly as the reference nests them.
413    //
414    // MEMRA_KDA_FUSED_PROJ=1 (default OFF): the six matvec calls collapse to one quantize +
415    // one `qmatvec_kda6_q8f32_mmvq` launch — the program shape both vLLM and SGLang ship for
416    // this trunk (ENGINE-SURVEY.md C1) and the step37 QKV_FUSED transfer (TRANSFER-MAP lever 1).
417    // `kda_proj_fused6` refuses (returns None) on any operand/env shape where its bit-identity
418    // claim would not hold, so the fall-through arm is always the unchanged program.
419    let mut g6 = match e.kda_proj_fused6_pre(la, x, t, pre_q8)? {
420        Some(outs) => outs,
421        None if rows_exact => {
422            // Verify-rows matmul class: per-weight decode-exact dispatch (the tcols /
423            // batched-MMVQ / per-token-linear classes — each row bit-identical to the
424            // t=1 program by the matmul_rows_exact contract).
425            [&la.wq, &la.wk, &la.wv, &la.f_a, &la.g_a, &la.b_proj]
426                .into_iter()
427                .map(|w| e.matmul_rows_exact(w, x, t))
428                .collect::<Result<Vec<_>, _>>()?
429        }
430        None => e.matmul_group(
431            &[&la.wq, &la.wk, &la.wv, &la.f_a, &la.g_a, &la.b_proj],
432            x,
433            t,
434        )?,
435    };
436    let beta_raw = g6.pop().unwrap(); // [T, heads]
437    let gate_down = g6.pop().unwrap(); // [T, head_dim]
438    let forget_down = g6.pop().unwrap(); // [T, head_dim]
439    if kda_trace_on() {
440        let mut v: Vec<(&str, &CudaSlice<f32>)> = vec![("x", x)];
441        let names = ["g6_0", "g6_1", "g6_2", "g6_3", "g6_4", "g6_5"];
442        for (k, b) in g6.iter().enumerate() {
443            v.push((names[k.min(5)], b));
444        }
445        v.push(("forget_down", &forget_down));
446        v.push(("gate_down", &gate_down));
447        v.push(("beta_raw", &beta_raw));
448        kda_trace(e, "proj", t, &v);
449    }
450    let v_raw = g6.pop().unwrap(); // [T, qkv]
451    let k_raw = g6.pop().unwrap();
452    let q_raw = g6.pop().unwrap();
453
454    // Rows stash: snapshot the ring BEFORE the rolls mutate it (one ~96 KiB clone per
455    // layer per round — the rollback's re-roll base). Door W: on the rows arm the snapshot
456    // (and every scratch below) is a pooled draw — vws_uninit == alloc_uninit with the
457    // door off, and the non-rows arms keep the plain allocs untouched.
458    let ring_snap = match &stash {
459        KdaStash::Rows(_) => {
460            let mut snap = e.vws_uninit(ring.len())?;
461            e.dtod_copy_into(ring, &mut snap, 0)?;
462            Some(snap)
463        }
464        _ => None,
465    };
466
467    // Stage 2 — per-plane causal short conv + SiLU. Planes are ordered q, k, v in both the fused
468    // weight buffer and the fused ring, which is the order the reference stores conv_state in.
469    let mut q_conv = if rows_exact {
470        e.vws_uninit(t * qkv)?
471    } else {
472        e.uninit(t * qkv)?
473    };
474    let mut k_conv = if rows_exact {
475        e.vws_uninit(t * qkv)?
476    } else {
477        e.uninit(t * qkv)?
478    };
479    let mut v_conv = if rows_exact {
480        e.vws_uninit(t * qkv)?
481    } else {
482        e.uninit(t * qkv)?
483    };
484    // MEMRA_KDA_CONV3 (lane/glm5-kda-conv3-20260904, default OFF): the decode arm's three
485    // per-plane launches as ONE (plane = blockIdx.y), bit-identical per channel; the prefill arm
486    // and the door-OFF decode keep the per-plane loop verbatim.
487    if arm == ConvArm::Decode && kda_conv3_on() && kernel <= 9 {
488        e.kda_conv_silu_decode3(
489            [&q_raw, &k_raw, &v_raw],
490            ring,
491            &la.conv,
492            [&mut q_conv, &mut k_conv, &mut v_conv],
493            qkv,
494            kernel,
495        )?;
496    } else {
497        for (plane, (raw, out)) in [
498            (&q_raw, &mut q_conv),
499            (&k_raw, &mut k_conv),
500            (&v_raw, &mut v_conv),
501        ]
502        .into_iter()
503        .enumerate()
504        {
505            match arm {
506                ConvArm::Prefill => {
507                    e.kda_conv_silu(raw, &la.conv, ring, out, qkv, t, kernel, plane)?
508                }
509                ConvArm::Decode => {
510                    e.kda_conv_silu_decode(raw, ring, &la.conv, out, qkv, kernel, plane)?
511                }
512            }
513        }
514    }
515    // The prefill arm reads the OLD ring for every token, so the roll runs only after all three
516    // planes have been convolved. The decode arm already rolled inside its fused kernel.
517    if arm == ConvArm::Prefill {
518        for (plane, raw) in [&q_raw, &k_raw, &v_raw].into_iter().enumerate() {
519            e.kda_conv_ring_roll(raw, ring, qkv, t, kernel, plane)?;
520        }
521    }
522
523    // Stage 3 — q/k L2 norm over head_dim (eps INSIDE the sqrt, fixed 1e-6). Rows of the
524    // token-major layout are contiguous head_dim runs, so no repack is needed.
525    let mut q_l2 = if rows_exact {
526        e.vws_uninit(t * qkv)?
527    } else {
528        e.uninit(t * qkv)?
529    };
530    let mut k_l2 = if rows_exact {
531        e.vws_uninit(t * qkv)?
532    } else {
533        e.uninit(t * qkv)?
534    };
535    e.l2_norm(&q_conv, &mut q_l2, head_dim, t * heads, KDA_L2_EPS)?;
536    e.l2_norm(&k_conv, &mut k_l2, head_dim, t * heads, KDA_L2_EPS)?;
537    kda_trace(
538        e,
539        "conv+l2",
540        t,
541        &[
542            ("q_conv", &q_conv),
543            ("k_conv", &k_conv),
544            ("v_conv", &v_conv),
545            ("q_l2", &q_l2),
546            ("k_l2", &k_l2),
547        ],
548    );
549    // Door W: the convs' last readers were the l2 norms (the ring rolls read the raws).
550    if rows_exact {
551        e.vws_recycle(q_conv);
552        e.vws_recycle(k_conv);
553    }
554
555    // Stage 4 — gates. forget: g = lower_bound * sigmoid(exp(A_log[h]) * (f_b(f_a(x)) + dt_bias)),
556    // emitted RAW (the scan applies expf). beta: per-head sigmoid of its own projection.
557    let forget = if rows_exact {
558        e.matmul_rows_exact(&la.f_b, &forget_down, t)?
559    } else {
560        e.matmul(&la.f_b, &forget_down, t)?
561    };
562    let mut g_log = if rows_exact {
563        e.vws_uninit(t * qkv)?
564    } else {
565        e.uninit(t * qkv)?
566    };
567    e.kda_gate(
568        &forget,
569        la.dt_bias.float_data(),
570        la.a_log.float_data(),
571        &mut g_log,
572        qkv,
573        t,
574        head_dim,
575        la.plan.gate_lower_bound,
576    )?;
577    let mut beta = if rows_exact {
578        e.vws_uninit(t * heads)?
579    } else {
580        e.uninit(t * heads)?
581    };
582    e.sigmoid(&beta_raw, &mut beta, t * heads)?;
583    kda_trace(e, "gates", t, &[("forget", &forget), ("beta", &beta)]);
584    // Door W: forget_down's last reader was the f_b matmul, forget's the gate kernel,
585    // beta_raw's the sigmoid.
586    if rows_exact {
587        e.vws_recycle(forget_down);
588        e.vws_recycle(forget);
589        e.vws_recycle(beta_raw);
590    }
591
592    // Stage 5 — the delta-rule recurrence. `scale` carries the reference's head_dim^-0.5 query
593    // scale: q feeds only the readout, never the state, so scaling the readout is exact.
594    // At t > 1 the kernel walks the T steps IN-KERNEL over register-resident state — the
595    // sequential chain preserved inside ONE launch (chained-t=1 identity by construction,
596    // held by the scan-chain bit-gate). `scan_clock` is the trace-level-2 instrument: it
597    // drains the stream around the launch so the sequential-class share lands in its own
598    // bucket (shares, never walls).
599    let scale = 1.0 / (head_dim as f32).sqrt();
600    let mut core = if rows_exact {
601        e.vws_uninit(t * qkv)?
602    } else {
603        e.uninit(t * qkv)?
604    };
605    let scan_t0 = scan_clock.as_ref().map(|_| {
606        let _ = e.stream().synchronize();
607        std::time::Instant::now()
608    });
609    e.kda_scan(
610        &q_l2, &k_l2, &v_conv, &g_log, &beta, state_in, state_out, &mut core, heads, t, scale,
611    )?;
612    kda_trace(
613        e,
614        "scan",
615        t,
616        &[
617            ("state_in", state_in),
618            ("core", &core),
619            ("state_out", state_out),
620        ],
621    );
622    if let (Some(ns), Some(t0)) = (scan_clock.take(), scan_t0) {
623        let _ = e.stream().synchronize();
624        *ns += t0.elapsed().as_nanos() as u64;
625    }
626
627    // Stage 6 — sigmoid-gated RMSNorm over head_dim (layer rms eps here, NOT the l2 eps), then
628    // the output projection.
629    let gate = if rows_exact {
630        e.matmul_rows_exact(&la.g_b, &gate_down, t)?
631    } else {
632        e.matmul(&la.g_b, &gate_down, t)?
633    };
634    let mut gated = if rows_exact {
635        e.vws_uninit(t * qkv)?
636    } else {
637        e.uninit(t * qkv)?
638    };
639    e.kda_gated_rmsnorm(
640        &core,
641        la.o_norm.float_data(),
642        &gate,
643        &mut gated,
644        head_dim,
645        t * heads,
646        eps,
647    )?;
648    kda_trace(e, "gated_norm", t, &[("gate", &gate), ("gated", &gated)]);
649    // Door W: gate_down's last reader was the g_b matmul; core's and gate's the
650    // gated-rmsnorm above.
651    if rows_exact {
652        e.vws_recycle(gate_down);
653        e.vws_recycle(gate);
654        e.vws_recycle(core);
655    }
656    // Steal the scan/conv inputs for the caller's rollback stash: stage 5 has consumed
657    // the scan inputs and the rolls were the raws' last readers — moving them out is
658    // free (no copy, no launch; the buffers were allocated this call either way).
659    match stash {
660        KdaStash::None => {}
661        KdaStash::Decode(s) => {
662            *s = Some(KdaScanInputs {
663                q: q_l2,
664                k: k_l2,
665                v: v_conv,
666                g: g_log,
667                beta,
668            });
669        }
670        KdaStash::Rows(s) => {
671            // Door W: the PREVIOUS round's stash dies here — its nine buffers restock
672            // the pool instead of falling to nine async frees (per layer per round).
673            if let Some(old) = s.take() {
674                e.vws_recycle(old.ring_snap);
675                for r in old.raws {
676                    e.vws_recycle(r);
677                }
678                e.vws_recycle(old.scan.q);
679                e.vws_recycle(old.scan.k);
680                e.vws_recycle(old.scan.v);
681                e.vws_recycle(old.scan.g);
682                e.vws_recycle(old.scan.beta);
683            }
684            *s = Some(KdaRowsStash {
685                ring_snap: ring_snap.expect("rows arm snapshotted the ring above"),
686                raws: [q_raw, k_raw, v_raw],
687                scan: KdaScanInputs {
688                    q: q_l2,
689                    k: k_l2,
690                    v: v_conv,
691                    g: g_log,
692                    beta,
693                },
694                rows: t,
695            });
696        }
697    }
698    Ok(gated)
699}
700
701/// STATELESS prefill from a zero conv ring and a zero recurrent state — the arm the logits-only
702/// forward paths take. Allocates and discards both state buffers.
703pub fn kda_attn(
704    e: &Engine,
705    la: &KdaAttnLayer,
706    x: &CudaSlice<f32>,
707    t: usize,
708    eps: f32,
709) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
710    let mut ring = e.zeros(la.conv_width() * (la.conv_kernel() - 1))?;
711    let state_in = e.zeros(la.state_width())?;
712    let mut state_out = e.zeros(la.state_width())?;
713    kda_core(
714        e,
715        la,
716        x,
717        t,
718        eps,
719        &mut ring,
720        &state_in,
721        &mut state_out,
722        ConvArm::Prefill,
723        KdaStash::None,
724        None,
725        None,
726    )
727}
728
729/// STATEFUL prefill: carries the ring forward and advances the recurrent state from `state_in`
730/// into `state_out`. Callers own the ping-pong; the two state buffers must be distinct.
731#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
732pub fn kda_attn_prime(
733    e: &Engine,
734    la: &KdaAttnLayer,
735    x: &CudaSlice<f32>,
736    t: usize,
737    eps: f32,
738    ring: &mut CudaSlice<f32>,
739    state_in: &CudaSlice<f32>,
740    state_out: &mut CudaSlice<f32>,
741) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
742    kda_core(
743        e,
744        la,
745        x,
746        t,
747        eps,
748        ring,
749        state_in,
750        state_out,
751        ConvArm::Prefill,
752        KdaStash::None,
753        None,
754        None,
755    )
756}
757
758/// T=1 decode step. Same math as a one-token prime; separate conv arm so the fused
759/// assemble+conv+roll kernel keeps decode and the spec verify on one dispatch class.
760pub fn kda_attn_decode(
761    e: &Engine,
762    la: &KdaAttnLayer,
763    x: &CudaSlice<f32>,
764    eps: f32,
765    ring: &mut CudaSlice<f32>,
766    state_in: &CudaSlice<f32>,
767    state_out: &mut CudaSlice<f32>,
768) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
769    kda_core(
770        e,
771        la,
772        x,
773        1,
774        eps,
775        ring,
776        state_in,
777        state_out,
778        ConvArm::Decode,
779        KdaStash::None,
780        None,
781        None,
782    )
783}
784
785/// Stateful KDA against the shared recurrent-state carrier, in the eager GDN discipline: the
786/// scan reads `ssm_state` and writes the spare `ssm_state_alt`, then the two OWNED resident
787/// buffers swap in place. Stable pointers, no per-step alloc/free — the per-step scratch this
788/// replaced churned the stream-ordered pool and made decode run-to-run nondeterministic
789/// (crates/memra-kv `RecurLayer::ssm_state_alt`). NOT capture-safe: a captured graph bakes
790/// capture-time pointers and never re-runs the host swap, which is why the capture loops refuse.
791#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
792fn kda_cached(
793    e: &Engine,
794    la: &KdaAttnLayer,
795    x: &CudaSlice<f32>,
796    t: usize,
797    eps: f32,
798    cache: &mut Cache,
799    il: usize,
800    arm: ConvArm,
801    stash: KdaStash<'_>,
802    scan_clock: Option<&mut u64>,
803    pre_q8: KdaPreQ8<'_>,
804) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
805    let rl = cache.recur[il].as_mut().ok_or_else(|| {
806        format!(
807            "blk.{il}: KDA layer has no recurrent state — the cache allocator saw a \
808                 non-Recurrent StatePlan for a KDA layer"
809        )
810    })?;
811    let out = {
812        let RecurLayer {
813            conv_state,
814            ssm_state,
815            ssm_state_alt,
816        } = rl;
817        kda_core(
818            e,
819            la,
820            x,
821            t,
822            eps,
823            conv_state,
824            ssm_state,
825            ssm_state_alt,
826            arm,
827            stash,
828            scan_clock,
829            pre_q8,
830        )?
831    };
832    std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
833    Ok(out)
834}
835
836/// Stateful prefill of `t` tokens through the cache's KDA state for layer `il`.
837pub fn kda_prime_cached(
838    e: &Engine,
839    la: &KdaAttnLayer,
840    x: &CudaSlice<f32>,
841    t: usize,
842    eps: f32,
843    cache: &mut Cache,
844    il: usize,
845) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
846    kda_cached(
847        e,
848        la,
849        x,
850        t,
851        eps,
852        cache,
853        il,
854        ConvArm::Prefill,
855        KdaStash::None,
856        None,
857        None,
858    )
859}
860
861/// One decode step through the cache's KDA state for layer `il`.
862/// `MEMRA_KDA_CONV3` (lane/glm5-kda-conv3-20260904; default ON on sm_100a builds since 2026-09-04,
863/// OFF elsewhere, `=0`/`=1` override): the T=1 KDA conv+SiLU runs its three planes in one launch. Read PER CALL. Why and receipts: the kernel header in
864/// cu/kda.cu and docs/FLAGS.md.
865pub(crate) fn kda_conv3_on() -> bool {
866    kda_conv3_on_from(
867        std::env::var("MEMRA_KDA_CONV3").ok().as_deref(),
868        env!("MEMRA_BUILT_CUDA_ARCH"),
869    )
870}
871
872/// The pure parse behind [`kda_conv3_on`]: `1` arms, `0` disarms, unset follows the BUILD ARCH
873/// (ON for `100a`, OFF otherwise): the fused launch carries a 2x B200 receipt (+1.82% at c1,
874/// darklanes research/glm5-b200-20260902/LANE.md, convab) and no SM120 one.
875pub fn kda_conv3_on_from(v: Option<&str>, built_arch: &str) -> bool {
876    match v.map(str::trim) {
877        Some("1") => true,
878        Some("0") => false,
879        _ => built_arch == "100a",
880    }
881}
882
883/// Engagement counter for `MEMRA_KDA_CONV3`; gates take a delta.
884pub static KDA_CONV3_DISPATCHES: std::sync::atomic::AtomicU64 =
885    std::sync::atomic::AtomicU64::new(0);
886
887/// Snapshot of [`KDA_CONV3_DISPATCHES`].
888pub fn kda_conv3_dispatches() -> u64 {
889    KDA_CONV3_DISPATCHES.load(std::sync::atomic::Ordering::Relaxed)
890}
891
892/// A pre-quantized q8_1 view of the mixer input (`(aq, ad)` from `rms_norm_zq8_f32`), or
893/// `None` for the launcher to quantize itself. Threaded from the walk to the fused
894/// six-projection launcher (`MEMRA_GLM5_Q8_FUSE_ATTN`, lane/glm5-attn-norm-zq8-20260904).
895pub type KdaPreQ8<'a> = Option<(&'a CudaSlice<i8>, &'a CudaSlice<f32>)>;
896
897/// [`kda_decode_cached`] with the mixer input's q8_1 view already emitted by the caller's norm
898/// (`MEMRA_GLM5_Q8_FUSE_ATTN`): identical launches minus the fused launcher's own quantize.
899pub fn kda_decode_cached_q8(
900    e: &Engine,
901    la: &KdaAttnLayer,
902    x: &CudaSlice<f32>,
903    pre_q8: KdaPreQ8<'_>,
904    eps: f32,
905    cache: &mut Cache,
906    il: usize,
907) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
908    kda_cached(
909        e,
910        la,
911        x,
912        1,
913        eps,
914        cache,
915        il,
916        ConvArm::Decode,
917        KdaStash::None,
918        None,
919        pre_q8,
920    )
921}
922
923pub fn kda_decode_cached(
924    e: &Engine,
925    la: &KdaAttnLayer,
926    x: &CudaSlice<f32>,
927    eps: f32,
928    cache: &mut Cache,
929    il: usize,
930) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
931    kda_cached(
932        e,
933        la,
934        x,
935        1,
936        eps,
937        cache,
938        il,
939        ConvArm::Decode,
940        KdaStash::None,
941        None,
942        None,
943    )
944}
945
946/// [`kda_decode_cached`] with the step's scan inputs STOLEN for a rollback stash
947/// (loop-port 3; doc on [`KdaScanInputs`]). Identical launches — the steal is a move of
948/// buffers the step allocated either way.
949pub fn kda_decode_cached_stash(
950    e: &Engine,
951    la: &KdaAttnLayer,
952    x: &CudaSlice<f32>,
953    eps: f32,
954    cache: &mut Cache,
955    il: usize,
956) -> Result<(CudaSlice<f32>, KdaScanInputs), Box<dyn std::error::Error>> {
957    let mut stash: Option<KdaScanInputs> = None;
958    let out = kda_cached(
959        e,
960        la,
961        x,
962        1,
963        eps,
964        cache,
965        il,
966        ConvArm::Decode,
967        KdaStash::Decode(&mut stash),
968        None,
969        None,
970    )?;
971    let stash = stash.ok_or("kda_core returned without filling the requested scan stash")?;
972    Ok((out, stash))
973}
974
975/// THE BATCHED VERIFY-ROWS KDA CALL (lane/glm5-verify-batch): one t=K+1 `kda_core` pass
976/// per layer per round, replacing t per-row [`kda_decode_cached_stash`] calls. Projections,
977/// gates and norms batch m=t through the decode-exact matmul classes (`matmul_rows_exact`);
978/// the conv takes the prefill dispatch (per-token bit-identical to the decode arm's taps);
979/// the recurrence stays SEQUENTIAL inside one `memra_kda_scan_s128` launch (the in-kernel
980/// T-loop over register-resident state == the chained t=1 program). Per-row bit-identity
981/// vs the t=1 chain is held by the walk gates (`glm5_tparallel_verify_gpu`) and the
982/// kernel bit-gates (`glm5_verify_batch_gpu`).
983///
984/// The caller owns the pre-round ssm snapshot (`Glm5VerifyCkpt::kda_ssm_snap`, cloned
985/// BEFORE this call); the returned [`KdaRowsStash`] carries everything else rollback
986/// needs. `scan_clock`: the trace-level-2 sequential-class bucket (ns accumulated around
987/// the scan launch with stream drains — an instrument, never a serving mode).
988#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kda_cached call contract plus the trace clock
989pub fn kda_verify_rows_cached(
990    e: &Engine,
991    la: &KdaAttnLayer,
992    x: &CudaSlice<f32>,
993    t: usize,
994    eps: f32,
995    cache: &mut Cache,
996    il: usize,
997    scan_clock: Option<&mut u64>,
998) -> Result<(CudaSlice<f32>, KdaRowsStash), Box<dyn std::error::Error>> {
999    let mut stash: Option<KdaRowsStash> = None;
1000    let out = kda_cached(
1001        e,
1002        la,
1003        x,
1004        t,
1005        eps,
1006        cache,
1007        il,
1008        ConvArm::Prefill,
1009        KdaStash::Rows(&mut stash),
1010        scan_clock,
1011        None,
1012    )?;
1013    let stash = stash.ok_or("kda_core returned without filling the requested rows stash")?;
1014    Ok((out, stash))
1015}
1016
1017/// Roll layer `il` back to "after row `keep-1`" from a BATCHED verify-rows round
1018/// (lane/glm5-verify-batch; the [`KdaRowsStash`] doc states the two-plane contract):
1019/// restore the pre-round conv ring and re-roll `keep` raw rows (pure placement), then
1020/// replay the scan ONCE at T=keep from the pre-round ssm snapshot over the batched
1021/// inputs. Full accept (`keep == rows`) never calls this — the resident state IS the
1022/// state after the last kept row.
1023pub fn kda_verify_rollback_rows(
1024    e: &Engine,
1025    la: &KdaAttnLayer,
1026    snap: &CudaSlice<f32>,
1027    stash: &KdaRowsStash,
1028    keep: usize,
1029    cache: &mut Cache,
1030    il: usize,
1031) -> Result<(), Box<dyn std::error::Error>> {
1032    let rl = cache.recur[il]
1033        .as_mut()
1034        .ok_or_else(|| format!("blk.{il}: KDA rows rollback on a layer with no recurrent state"))?;
1035    kda_verify_rollback_rows_on(e, la, snap, stash, keep, rl, il)
1036}
1037
1038/// [`kda_verify_rollback_rows`] over a CALLER-OWNED state plane — the glm5 spec x TP seam
1039/// (lane/glm5-composition): under `MEMRA_GLM5_TP` each rank's shard-geometry conv ring +
1040/// ssm ping-pong lives in `cache.glm5_tp_recur[il][rank]` on that rank's engine, so the
1041/// rollback restores per rank through this entry with the rank's own `(engine, shard,
1042/// snapshot, stash)` tuple. The cache wrapper above delegates here — one body, byte-for-byte
1043/// the pre-refactor walk on the plain path.
1044pub fn kda_verify_rollback_rows_on(
1045    e: &Engine,
1046    la: &KdaAttnLayer,
1047    snap: &CudaSlice<f32>,
1048    stash: &KdaRowsStash,
1049    keep: usize,
1050    rl: &mut RecurLayer,
1051    il: usize,
1052) -> Result<(), Box<dyn std::error::Error>> {
1053    if keep == 0 || keep >= stash.rows {
1054        return Err(format!(
1055            "blk.{il}: KDA rows rollback keep={keep} outside 1..{} (full accept keeps the \
1056             resident state and never replays)",
1057            stash.rows
1058        )
1059        .into());
1060    }
1061    let qkv = la.qkv();
1062    let kernel = la.conv_kernel();
1063    let heads = la.heads();
1064    let scale = 1.0 / (la.head_dim() as f32).sqrt();
1065    // Conv ring: pre-round snapshot back, then re-roll the kept raw rows per plane. The
1066    // roll kernel reads every old slot into registers before any store, so T=keep < pad
1067    // mixes snapshot slots and kept rows exactly as the sequential chain's rolls did.
1068    e.copy_into(
1069        &mut rl.conv_state,
1070        0,
1071        &stash.ring_snap,
1072        stash.ring_snap.len(),
1073    )?;
1074    for (plane, raw) in stash.raws.iter().enumerate() {
1075        e.kda_conv_ring_roll(raw, &mut rl.conv_state, qkv, keep, kernel, plane)?;
1076    }
1077    // Recurrent state: ONE T=keep replay from the snapshot over the batched scan inputs
1078    // (the kernel walks rows 0..keep of the [t, ..] buffers); readout discarded. The
1079    // ping-pong ends with the rebuilt state under the `ssm_state` name, matching
1080    // `kda_cached`'s swap discipline.
1081    let mut o = e.uninit(keep * qkv)?;
1082    {
1083        let RecurLayer {
1084            ssm_state: _,
1085            ssm_state_alt,
1086            ..
1087        } = rl;
1088        e.kda_scan(
1089            &stash.scan.q,
1090            &stash.scan.k,
1091            &stash.scan.v,
1092            &stash.scan.g,
1093            &stash.scan.beta,
1094            snap,
1095            ssm_state_alt,
1096            &mut o,
1097            heads,
1098            keep,
1099            scale,
1100        )?;
1101    }
1102    std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
1103    Ok(())
1104}
1105
1106/// Rebuild layer `il`'s recurrent state to "after row `inputs.len()-1`" by REPLAYING the
1107/// stashed scan inputs from the pre-round snapshot `snap` (loop-port 3, the module-doc
1108/// diet made concrete): each replay is the original t=1 `memra_kda_scan_s128` launch
1109/// re-issued over the very buffers that step consumed, so the rebuilt state is
1110/// byte-identical to the per-row clone it replaces BY CONSTRUCTION. The readout is
1111/// discarded; the conv ring is not touched (the walk still clones it per row — 288 KiB
1112/// against the 4 MiB ssm plane this retires). The ping-pong rides the resident pair and
1113/// ends with the rebuilt state under the `ssm_state` name, matching `kda_cached`'s own
1114/// swap discipline.
1115pub fn kda_scan_replay(
1116    e: &Engine,
1117    la: &KdaAttnLayer,
1118    snap: &CudaSlice<f32>,
1119    inputs: &[KdaScanInputs],
1120    cache: &mut Cache,
1121    il: usize,
1122) -> Result<(), Box<dyn std::error::Error>> {
1123    if inputs.is_empty() {
1124        return Err(format!(
1125            "blk.{il}: KDA replay needs at least one stashed row (rollback keep >= 1; a \
1126             restore TO the snapshot itself is a different contract)"
1127        )
1128        .into());
1129    }
1130    if la.tp.is_some() {
1131        return Err(format!(
1132            "blk.{il}: KDA scan replay (the PER-ROW rollback seam) is unwired for a \
1133             glm5-TP-sharded layer — the spec x TP composition requires the BATCHED \
1134             verify walk, whose rollback rides kda_verify_rollback_rows_on per rank"
1135        )
1136        .into());
1137    }
1138    let heads = la.heads();
1139    let scale = 1.0 / (la.head_dim() as f32).sqrt();
1140    let qkv = la.qkv();
1141    let rl = cache.recur[il]
1142        .as_mut()
1143        .ok_or_else(|| format!("blk.{il}: KDA replay on a layer with no recurrent state"))?;
1144    let mut o = e.uninit(qkv)?; // discarded readout scratch, reused across rows
1145    for (r, inp) in inputs.iter().enumerate() {
1146        {
1147            let RecurLayer {
1148                ssm_state,
1149                ssm_state_alt,
1150                ..
1151            } = rl;
1152            let state_in: &CudaSlice<f32> = if r == 0 { snap } else { ssm_state };
1153            e.kda_scan(
1154                &inp.q,
1155                &inp.k,
1156                &inp.v,
1157                &inp.g,
1158                &inp.beta,
1159                state_in,
1160                ssm_state_alt,
1161                &mut o,
1162                heads,
1163                1,
1164                scale,
1165            )?;
1166        }
1167        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
1168    }
1169    Ok(())
1170}
1171
1172impl Engine {
1173    /// Per-plane causal short conv + SiLU over a T-token chunk (cu/kda.cu).
1174    #[allow(clippy::too_many_arguments)]
1175    pub fn kda_conv_silu(
1176        &self,
1177        x_tm: &CudaSlice<f32>,
1178        w: &CudaSlice<f32>,
1179        ring: &CudaSlice<f32>,
1180        y_tm: &mut CudaSlice<f32>,
1181        qkv: usize,
1182        t: usize,
1183        kernel: usize,
1184        plane: usize,
1185    ) -> Result<(), Box<dyn std::error::Error>> {
1186        let f = self.func("memra_kda_conv_silu_f32");
1187        let cfg = LaunchConfig {
1188            grid_dim: (qkv.div_ceil(256) as u32, t as u32, 1),
1189            block_dim: (256, 1, 1),
1190            shared_mem_bytes: 0,
1191        };
1192        let (n, tt, k, p) = (qkv as i32, t as i32, kernel as i32, plane as i32);
1193        let stream = self.gpu.stream();
1194        let mut b = stream.launch_builder(&f);
1195        b.arg(x_tm)
1196            .arg(w)
1197            .arg(ring)
1198            .arg(&mut *y_tm)
1199            .arg(&n)
1200            .arg(&tt)
1201            .arg(&k)
1202            .arg(&p);
1203        unsafe { b.launch(cfg)? };
1204        Ok(())
1205    }
1206
1207    /// Roll one plane of the fused conv ring forward over a T-token chunk (cu/kda.cu).
1208    pub fn kda_conv_ring_roll(
1209        &self,
1210        x_tm: &CudaSlice<f32>,
1211        ring: &mut CudaSlice<f32>,
1212        qkv: usize,
1213        t: usize,
1214        kernel: usize,
1215        plane: usize,
1216    ) -> Result<(), Box<dyn std::error::Error>> {
1217        let f = self.func("memra_kda_conv_ring_roll_f32");
1218        let cfg = LaunchConfig {
1219            grid_dim: (qkv.div_ceil(256) as u32, 1, 1),
1220            block_dim: (256, 1, 1),
1221            shared_mem_bytes: 0,
1222        };
1223        let (n, tt, k, p) = (qkv as i32, t as i32, kernel as i32, plane as i32);
1224        let stream = self.gpu.stream();
1225        let mut b = stream.launch_builder(&f);
1226        b.arg(x_tm).arg(&mut *ring).arg(&n).arg(&tt).arg(&k).arg(&p);
1227        unsafe { b.launch(cfg)? };
1228        Ok(())
1229    }
1230
1231    /// T=1 fused assemble + conv + SiLU + ring roll for one plane (cu/kda.cu).
1232    #[allow(clippy::too_many_arguments)]
1233    /// The three-plane form of [`Engine::kda_conv_silu_decode`] (door `MEMRA_KDA_CONV3`,
1234    /// lane/glm5-kda-conv3-20260904): one launch with `plane = blockIdx.y` in place of the three
1235    /// per-plane launches; per channel the same body, so outputs and the ring are bit-identical
1236    /// (gate `tests/kda_conv3_gpu.rs`). `kernel` (K) must be at most 9 (the `win[8]` window).
1237    #[allow(clippy::too_many_arguments)]
1238    pub fn kda_conv_silu_decode3(
1239        &self,
1240        x: [&CudaSlice<f32>; 3],
1241        ring: &mut CudaSlice<f32>,
1242        w: &CudaSlice<f32>,
1243        y: [&mut CudaSlice<f32>; 3],
1244        qkv: usize,
1245        kernel: usize,
1246    ) -> Result<(), Box<dyn std::error::Error>> {
1247        if kernel == 0 || kernel > 9 {
1248            return Err("kda_conv_silu_decode3: kernel width outside the 8-wide window".into());
1249        }
1250        if KDA_CONV3_DISPATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed) == 0 {
1251            eprintln!(
1252                "[kda-conv3] engaged: the three KDA conv+SiLU decode planes run as one launch \
1253                 (MEMRA_KDA_CONV3=1)"
1254            );
1255        }
1256        let f = self.func("memra_kda_conv_silu_decode3_f32");
1257        let cfg = LaunchConfig {
1258            grid_dim: (qkv.div_ceil(256) as u32, 3, 1),
1259            block_dim: (256, 1, 1),
1260            shared_mem_bytes: 0,
1261        };
1262        let (n, k) = (qkv as i32, kernel as i32);
1263        let [x0, x1, x2] = x;
1264        let [y0, y1, y2] = y;
1265        let stream = self.gpu.stream();
1266        let mut b = stream.launch_builder(&f);
1267        b.arg(x0)
1268            .arg(x1)
1269            .arg(x2)
1270            .arg(&mut *ring)
1271            .arg(w)
1272            .arg(&mut *y0)
1273            .arg(&mut *y1)
1274            .arg(&mut *y2)
1275            .arg(&n)
1276            .arg(&k);
1277        unsafe { b.launch(cfg)? };
1278        Ok(())
1279    }
1280
1281    #[allow(clippy::too_many_arguments)]
1282    pub fn kda_conv_silu_decode(
1283        &self,
1284        x_new: &CudaSlice<f32>,
1285        ring: &mut CudaSlice<f32>,
1286        w: &CudaSlice<f32>,
1287        y: &mut CudaSlice<f32>,
1288        qkv: usize,
1289        kernel: usize,
1290        plane: usize,
1291    ) -> Result<(), Box<dyn std::error::Error>> {
1292        let f = self.func("memra_kda_conv_silu_decode_f32");
1293        let cfg = LaunchConfig {
1294            grid_dim: (qkv.div_ceil(256) as u32, 1, 1),
1295            block_dim: (256, 1, 1),
1296            shared_mem_bytes: 0,
1297        };
1298        let (n, k, p) = (qkv as i32, kernel as i32, plane as i32);
1299        let stream = self.gpu.stream();
1300        let mut b = stream.launch_builder(&f);
1301        b.arg(x_new)
1302            .arg(&mut *ring)
1303            .arg(w)
1304            .arg(&mut *y)
1305            .arg(&n)
1306            .arg(&k)
1307            .arg(&p);
1308        unsafe { b.launch(cfg)? };
1309        Ok(())
1310    }
1311
1312    /// Per-channel forget gate, emitted as the RAW log-gate (cu/kda.cu).
1313    #[allow(clippy::too_many_arguments)]
1314    pub fn kda_gate(
1315        &self,
1316        forget: &CudaSlice<f32>,
1317        dt_bias: &CudaSlice<f32>,
1318        a_log: &CudaSlice<f32>,
1319        g: &mut CudaSlice<f32>,
1320        qkv: usize,
1321        t: usize,
1322        head_dim: usize,
1323        lower_bound: f32,
1324    ) -> Result<(), Box<dyn std::error::Error>> {
1325        let f = self.func("memra_kda_gate_f32");
1326        let cfg = LaunchConfig {
1327            grid_dim: (qkv.div_ceil(256) as u32, t as u32, 1),
1328            block_dim: (256, 1, 1),
1329            shared_mem_bytes: 0,
1330        };
1331        let (n, tt, hd, lb) = (qkv as i32, t as i32, head_dim as i32, lower_bound);
1332        let stream = self.gpu.stream();
1333        let mut b = stream.launch_builder(&f);
1334        b.arg(forget)
1335            .arg(dt_bias)
1336            .arg(a_log)
1337            .arg(&mut *g)
1338            .arg(&n)
1339            .arg(&tt)
1340            .arg(&hd)
1341            .arg(&lb);
1342        unsafe { b.launch(cfg)? };
1343        Ok(())
1344    }
1345
1346    /// The per-channel-decay delta-rule scan (cu/kda.cu). One warp per output column.
1347    #[allow(clippy::too_many_arguments)]
1348    pub fn kda_scan(
1349        &self,
1350        q: &CudaSlice<f32>,
1351        k: &CudaSlice<f32>,
1352        v: &CudaSlice<f32>,
1353        g: &CudaSlice<f32>,
1354        beta: &CudaSlice<f32>,
1355        state_in: &CudaSlice<f32>,
1356        state_out: &mut CudaSlice<f32>,
1357        o: &mut CudaSlice<f32>,
1358        heads: usize,
1359        t: usize,
1360        scale: f32,
1361    ) -> Result<(), Box<dyn std::error::Error>> {
1362        // Four columns per block keeps one warp per column at 128 threads, the same shape
1363        // gdn_scan_s128 launches with.
1364        const COLS_PER_BLOCK: u32 = 4;
1365        let f = self.func("memra_kda_scan_s128");
1366        let cfg = LaunchConfig {
1367            grid_dim: (
1368                heads as u32,
1369                1,
1370                (KDA_HEAD_DIM as u32).div_ceil(COLS_PER_BLOCK),
1371            ),
1372            block_dim: (32, COLS_PER_BLOCK, 1),
1373            shared_mem_bytes: 0,
1374        };
1375        let (h, tt, s) = (heads as i32, t as i32, scale);
1376        let stream = self.gpu.stream();
1377        let mut b = stream.launch_builder(&f);
1378        b.arg(q)
1379            .arg(k)
1380            .arg(v)
1381            .arg(g)
1382            .arg(beta)
1383            .arg(state_in)
1384            .arg(&mut *state_out)
1385            .arg(&mut *o)
1386            .arg(&h)
1387            .arg(&tt)
1388            .arg(&s);
1389        unsafe { b.launch(cfg)? };
1390        Ok(())
1391    }
1392
1393    /// Sigmoid-gated fp32 RMSNorm over head_dim (cu/kda.cu). GDN's `gated_rmsnorm` gates with
1394    /// SiLU; KDA's Glm5NextTextRMSNormGated hardcodes sigmoid.
1395    #[allow(clippy::too_many_arguments)]
1396    pub fn kda_gated_rmsnorm(
1397        &self,
1398        core: &CudaSlice<f32>,
1399        w: &CudaSlice<f32>,
1400        gate: &CudaSlice<f32>,
1401        dst: &mut CudaSlice<f32>,
1402        ncols: usize,
1403        nrows: usize,
1404        eps: f32,
1405    ) -> Result<(), Box<dyn std::error::Error>> {
1406        let f = self.func("memra_kda_gated_rmsnorm_f32");
1407        let cfg = LaunchConfig {
1408            grid_dim: (nrows as u32, 1, 1),
1409            block_dim: (256, 1, 1),
1410            shared_mem_bytes: 0,
1411        };
1412        let (nc, ep) = (ncols as i32, eps);
1413        let stream = self.gpu.stream();
1414        let mut b = stream.launch_builder(&f);
1415        b.arg(core)
1416            .arg(w)
1417            .arg(gate)
1418            .arg(&mut *dst)
1419            .arg(&nc)
1420            .arg(&ep);
1421        unsafe { b.launch(cfg)? };
1422        Ok(())
1423    }
1424
1425    /// The `MEMRA_KDA_FUSED_PROJ` door: run the KDA stage-1 six-projection group as ONE
1426    /// `quantize_q8_1` + ONE `qmatvec_kda6_q8f32_mmvq` launch, or return `None` and let the
1427    /// caller take the unchanged `matmul_group` arm.
1428    ///
1429    /// ENGAGEMENT IS DELIBERATELY NARROW — every condition below exists so the door's numeric
1430    /// claim stays exactly what the gate proves (`tests/kda_fused_proj_gpu.rs`):
1431    ///  * wq/wk/wv must be plain-layout Q8_0 (`rp: false`, no `rp4` mirror, `scale == 1.0`) —
1432    ///    the fused kernel's per-(token,row) body is `qmatvec_q8_0_mmvq` VERBATIM, so those
1433    ///    rows are BIT-IDENTICAL to the unfused MMVQ/batched arm; a repacked layout would ride
1434    ///    the `_rp` twins instead and the claim would be against the wrong kernel.
1435    ///  * f_a/g_a/b_proj must be f32 `Float` — their fused rows replace cuBLASLt with a
1436    ///    deterministic warp tree: a reduction-order class change (the step37 QKV_FUSED class),
1437    ///    measured and pinned in the gate.
1438    ///  * t in 1..=15 (the batch cap), and the env classes under which the UNFUSED arm rides
1439    ///    the MMVQ-class per-row program: `MEMRA_FAST!=0`, `mmvq_supports(Q8_0)`,
1440    ///    `MEMRA_NO_BATCHED` unset for t>=2, `MEMRA_B8!=0` for t>=5. Outside those envs the
1441    ///    unfused arm is a different kernel class (dp4a / Stage-A), so the door refuses rather
1442    ///    than weakening its identity claim.
1443    ///
1444    /// The flag is read PER CALL (the `MEMRA_MOE_FUSED_EPI` rollback-seam precedent), so both
1445    /// arms alternate inside one process. Output order matches `matmul_group`'s:
1446    /// `[q, k, v, forget_down, gate_down, beta_raw]`.
1447    pub fn kda_proj_fused6(
1448        &self,
1449        la: &KdaAttnLayer,
1450        x: &CudaSlice<f32>,
1451        t: usize,
1452    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
1453        self.kda_proj_fused6_pre(la, x, t, None)
1454    }
1455
1456    /// [`Engine::kda_proj_fused6`] with an optional pre-quantized activation for the W8 arm
1457    /// (`MEMRA_GLM5_Q8_FUSE_ATTN`); every other arm ignores it (they quantize per projection or
1458    /// run bf16) and stays the program it was.
1459    pub fn kda_proj_fused6_pre(
1460        &self,
1461        la: &KdaAttnLayer,
1462        x: &CudaSlice<f32>,
1463        t: usize,
1464        pre_q8: KdaPreQ8<'_>,
1465    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
1466        if std::env::var("MEMRA_KDA_FUSED_PROJ").as_deref() != Ok("1") {
1467            return Ok(None);
1468        }
1469        // glm5 TP composition guard (#82 review): the load preflight refuses this door at
1470        // ARM time, but the flag is read PER CALL — a post-load `set` would otherwise
1471        // engage the fused six-projection group on head shards inside the TP walk, an
1472        // unproven composition (the door's gate ran on full-width projections). A shard
1473        // declines here and takes the caller's unchanged arm, announced once.
1474        if la.tp.is_some() {
1475            static TP_F6_DECLINE: std::sync::Once = std::sync::Once::new();
1476            TP_F6_DECLINE.call_once(|| {
1477                eprintln!(
1478                    "[kda-fused-proj] DECLINED on a glm5-TP head shard: the door is gated \
1479                     on full-width projections (the load preflight refuses the pair; this \
1480                     is the per-call twin for a post-load flag set)"
1481                );
1482            });
1483            return Ok(None);
1484        }
1485        if !(1..=15).contains(&t) {
1486            return Ok(None);
1487        }
1488        // E4M3 SIX-GROUP ARM (lane/glm5-b200-mint-consume, 2026-09-04) — the operand class the
1489        // GLM-5.3-Flash B200 hybrid mint actually ships. That mint quantizes ALL SIX KDA
1490        // projections to per-tensor e4m3, so under MEMRA_ST_E4M3 (default ON) every one of them
1491        // is QT_F8_E4M3-resident at 1.0 B/weight: cheaper than the bf16 serving recipe's 2.0 and
1492        // cheaper than the Q8_0 re-encode's 1.0625, with no lossy re-quant hop. The two arms
1493        // below cannot serve that shape — they require a FloatBf16 trio plus an f32 trio — so
1494        // without this arm the mint's cheapest operand would fall to SIX separate launches on
1495        // each of the 34 KDA layers, with six redundant broadcasts of the same activation.
1496        //
1497        // This is an operand arm of an EXISTING door, not a new one: it rides
1498        // MEMRA_KDA_FUSED_PROJ=1 exactly as the bf16 and q8rp arms do, and it additionally
1499        // declines wherever the unfused e4m3 program it claims bit-identity against would not
1500        // be the shipped one (MEMRA_FAST=0, no MMVQ support for the qtype, or the
1501        // MEMRA_E4M3_DUAL=0 rollback that restores per-tensor launches).
1502        //
1503        // m=1 ONLY. `qmatvec_e4m3_mmvq_fused6` pins the token index at 0, matching the
1504        // `e4m3_mmvq_row1` body it shares with the pair and triple. t>1 keeps the caller's arm.
1505        let e4m3 = |w: &GpuTensor| -> Option<(usize, usize, f32)> {
1506            match w {
1507                GpuTensor::Quant {
1508                    qtype: crate::QT_F8_E4M3,
1509                    row_bytes,
1510                    scale,
1511                    rp: false,
1512                    rp4: None,
1513                    blk: None,
1514                    ..
1515                } => Some((w.in_features(), *row_bytes, *scale)),
1516                _ => None,
1517            }
1518        };
1519        if let (Some(e_q), Some(e_k), Some(e_v), Some(e_fa), Some(e_ga), Some(e_b)) = (
1520            e4m3(&la.wq),
1521            e4m3(&la.wk),
1522            e4m3(&la.wv),
1523            e4m3(&la.f_a),
1524            e4m3(&la.g_a),
1525            e4m3(&la.b_proj),
1526        ) {
1527            let six = [e_q, e_k, e_v, e_fa, e_ga, e_b];
1528            let in_f = e_q.0;
1529            if t != 1
1530                || std::env::var("MEMRA_FAST").as_deref() == Ok("0")
1531                || !self.mmvq_supports(crate::QT_F8_E4M3)
1532                || !self.e4m3_dual_on()
1533                // Every range must share in_f and the q8_1 activation block, and an e4m3 row is
1534                // exactly in_f bytes — a row_bytes that disagrees means a padded or foreign
1535                // layout this kernel's single `row_bytes` cannot address.
1536                || six.iter().any(|&(i, rb, _)| i != in_f || rb != in_f)
1537                || !in_f.is_multiple_of(32)
1538                || x.len() < in_f
1539            {
1540                return Ok(None);
1541            }
1542            let dims = [
1543                la.wq.out_features(),
1544                la.wk.out_features(),
1545                la.wv.out_features(),
1546                la.f_a.out_features(),
1547                la.g_a.out_features(),
1548                la.b_proj.out_features(),
1549            ];
1550            let ws: [f32; 6] = std::array::from_fn(|i| six[i].2);
1551            fn e4m3_bytes(w: &GpuTensor) -> &CudaSlice<u8> {
1552                match w {
1553                    GpuTensor::Quant { bytes, .. } => bytes,
1554                    _ => unreachable!("e4m3() above only admits Quant"),
1555                }
1556            }
1557            let bytes = e4m3_bytes;
1558            let w = [
1559                bytes(&la.wq),
1560                bytes(&la.wk),
1561                bytes(&la.wv),
1562                bytes(&la.f_a),
1563                bytes(&la.g_a),
1564                bytes(&la.b_proj),
1565            ];
1566            // ONE activation quantize for all six ranges — the six-launch path pays this per
1567            // projection. `pre_q8` is the W8 posture's pre-quantized pair and does not apply to
1568            // this operand class, so the arm always quantizes here.
1569            let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
1570            let mut outs = [
1571                self.uninit(dims[0])?,
1572                self.uninit(dims[1])?,
1573                self.uninit(dims[2])?,
1574                self.uninit(dims[3])?,
1575                self.uninit(dims[4])?,
1576                self.uninit(dims[5])?,
1577            ];
1578            self.e4m3_fused6_into(w, &aq, &ad, in_f, dims, in_f, ws, &mut outs)?;
1579            if KDA_FUSED6_E4M3_DISPATCHES.fetch_add(1, Ordering::Relaxed) == 0 {
1580                eprintln!(
1581                    "[kda-fused6] engaged arm=e4m3 in_f={in_f} out={dims:?} t={t} (one launch \
1582                     replaces the six per-tensor e4m3 projections and their six redundant \
1583                     activation quantizes; MEMRA_KDA_FUSED_PROJ=1 MEMRA_ST_E4M3=1)"
1584                );
1585            }
1586            return Ok(Some(outs.into_iter().collect()));
1587        }
1588        // The f32 trio is common to both operand arms. Any mismatch = refuse; the caller's
1589        // arm is the shipped program.
1590        let f32w = |w: &GpuTensor| -> Option<usize> {
1591            match w {
1592                GpuTensor::Float { .. } => Some(w.in_features()),
1593                _ => None,
1594            }
1595        };
1596        let (Some(in_fa), Some(in_ga), Some(in_b)) =
1597            (f32w(&la.f_a), f32w(&la.g_a), f32w(&la.b_proj))
1598        else {
1599            return Ok(None);
1600        };
1601        // BF16 operand arm (lever 3 of the decode diet): the serving recipe (MEMRA_BF16_MMV=1)
1602        // admits wq/wk/wv to raw bf16 residency, where the Q8_0 arm below never binds. Its
1603        // bit-identity bar is against `matvec_bf16_f32acc_x4_rows` (matmul's FloatBf16
1604        // decode-tier arm), so it refuses wherever that arm would not be the unfused program:
1605        // MEMRA_BF16_MMV off (the chunked cuBLASLt GEMM class), the step37 W8 mirror doors on
1606        // (matvec_bf16_rows_into reroutes through the q8 mirror when BOTH are set), or
1607        // MEMRA_GLM5_W8 on (2026-09-02, lane/b200-glm5-w8: the SAME reroute, independent
1608        // door — this fused kernel's bit-identity claim is against the unmirrored bf16
1609        // program, so it must decline whichever door moved that program's target).
1610        let bf16 = |w: &GpuTensor| -> Option<usize> {
1611            match w {
1612                GpuTensor::FloatBf16 { .. } => Some(w.in_features()),
1613                _ => None,
1614            }
1615        };
1616        if let (Some(in_q), Some(in_k), Some(in_v)) = (bf16(&la.wq), bf16(&la.wk), bf16(&la.wv)) {
1617            // MEMRA_B200_BF16_GEMV_LT (lane/b200-gemv-hbm-20260902) reroutes the SAME
1618            // unfused target (`matvec_bf16_f32acc_x4_rows`) to a cuBLASLt reference GEMV, so
1619            // this fused arm declines for exactly the reason it declines for the W8 mirrors:
1620            // its bit-identity bar is against the unmirrored, unrerouted bf16 program. With
1621            // the door on, the three bf16 projections fall to the unfused group and each one
1622            // takes the library GEMV, which is what the reference door is there to measure.
1623            // W8 POSTURE FUSION (lane/b200-gemv-hbm-20260902 round 3). Under MEMRA_GLM5_W8 the
1624            // six projections each reroute through `matvec_bf16_via_q8_mirror`, so this group
1625            // runs as SIX separate launches plus six redundant quantizes of the same `x` — and
1626            // the bf16 fused arm below cannot serve it, because its bit-identity bar is against
1627            // the unmirrored bf16 program. `qmatvec_kda6_q8f32_rp_v2` is the fused twin for
1628            // that posture: three mirrored ranges on the rp v2 body (bit-identical to
1629            // `qmatvec_q8_0_mmvq_rp` per row) and three f32 ranges on the same deterministic
1630            // warp tree the q8 arm of this door already ships and has pinned. Gated on
1631            // MEMRA_B200_GEMV_V2 so it carries its own receipt; without that door W8 still
1632            // declines to the unfused path exactly as before.
1633            if crate::glm5_w8_on() && !(crate::step_tp_w8_on() && crate::w8_hybrid_on()) {
1634                if !Self::bf16_mmv_on() || !crate::b200_gemv_v2_on() {
1635                    return Ok(None);
1636                }
1637                let in_f = in_q;
1638                if [in_k, in_v, in_fa, in_ga, in_b].iter().any(|&i| i != in_f)
1639                    || !in_f.is_multiple_of(128)
1640                    || x.len() < t * in_f
1641                    || Engine::q8_v2_smem_bytes(in_f) > 48 * 1024
1642                {
1643                    return Ok(None);
1644                }
1645                let dims = [
1646                    la.wq.out_features(),
1647                    la.wk.out_features(),
1648                    la.wv.out_features(),
1649                    la.f_a.out_features(),
1650                    la.g_a.out_features(),
1651                    la.b_proj.out_features(),
1652                ];
1653                let (
1654                    GpuTensor::FloatBf16 { data: bq, .. },
1655                    GpuTensor::FloatBf16 { data: bk, .. },
1656                    GpuTensor::FloatBf16 { data: bv, .. },
1657                ) = (&la.wq, &la.wk, &la.wv)
1658                else {
1659                    unreachable!("bf16() above only admits FloatBf16");
1660                };
1661                let (
1662                    GpuTensor::Float { data: wfa, .. },
1663                    GpuTensor::Float { data: wga, .. },
1664                    GpuTensor::Float { data: wb, .. },
1665                ) = (&la.f_a, &la.g_a, &la.b_proj)
1666                else {
1667                    unreachable!("f32w() above only admits Float");
1668                };
1669                let mut outs = [
1670                    self.uninit(t * dims[0])?,
1671                    self.uninit(t * dims[1])?,
1672                    self.uninit(t * dims[2])?,
1673                    self.uninit(t * dims[3])?,
1674                    self.uninit(t * dims[4])?,
1675                    self.uninit(t * dims[5])?,
1676                ];
1677                self.kda_proj_fused6_q8rp_raw_pre(
1678                    bq,
1679                    bk,
1680                    bv,
1681                    wfa,
1682                    wga,
1683                    wb,
1684                    x,
1685                    &mut outs,
1686                    in_f,
1687                    dims,
1688                    t,
1689                    crate::q8_row_ilp_on(),
1690                    pre_q8,
1691                )?;
1692                if KDA_FUSED6_Q8RP_DISPATCHES.fetch_add(1, Ordering::Relaxed) == 0 {
1693                    eprintln!(
1694                        "[kda-fused6] engaged arm=q8rp_v2 in_f={in_f} out={dims:?} t={t} (one \
1695                         launch replaces the six W8-mirror projections and their six redundant \
1696                         activation quantizes; MEMRA_KDA_FUSED_PROJ=1 MEMRA_B200_GEMV_V2=1)"
1697                    );
1698                }
1699                return Ok(Some(outs.into_iter().collect()));
1700            }
1701            if !Self::bf16_mmv_on()
1702                || (crate::step_tp_w8_on() && crate::w8_hybrid_on())
1703                || crate::b200_bf16_gemv_lt_on()
1704            {
1705                return Ok(None);
1706            }
1707            let in_f = in_q;
1708            if [in_k, in_v, in_fa, in_ga, in_b].iter().any(|&i| i != in_f)
1709                || !in_f.is_multiple_of(128)
1710                || x.len() < t * in_f
1711            {
1712                return Ok(None);
1713            }
1714            let dims = [
1715                la.wq.out_features(),
1716                la.wk.out_features(),
1717                la.wv.out_features(),
1718                la.f_a.out_features(),
1719                la.g_a.out_features(),
1720                la.b_proj.out_features(),
1721            ];
1722            let (
1723                GpuTensor::FloatBf16 { data: bq, .. },
1724                GpuTensor::FloatBf16 { data: bk, .. },
1725                GpuTensor::FloatBf16 { data: bv, .. },
1726            ) = (&la.wq, &la.wk, &la.wv)
1727            else {
1728                unreachable!("bf16() above only admits FloatBf16");
1729            };
1730            let (
1731                GpuTensor::Float { data: wfa, .. },
1732                GpuTensor::Float { data: wga, .. },
1733                GpuTensor::Float { data: wb, .. },
1734            ) = (&la.f_a, &la.g_a, &la.b_proj)
1735            else {
1736                unreachable!("f32w() above only admits Float");
1737            };
1738            let mut outs = [
1739                self.uninit(t * dims[0])?,
1740                self.uninit(t * dims[1])?,
1741                self.uninit(t * dims[2])?,
1742                self.uninit(t * dims[3])?,
1743                self.uninit(t * dims[4])?,
1744                self.uninit(t * dims[5])?,
1745            ];
1746            self.kda_proj_fused6_bf16_raw(bq, bk, bv, wfa, wga, wb, x, &mut outs, in_f, dims, t)?;
1747            if KDA_FUSED6_BF16_DISPATCHES.fetch_add(1, Ordering::Relaxed) == 0 {
1748                eprintln!(
1749                    "[kda-fused6] engaged arm=bf16 in_f={in_f} out={dims:?} t={t} (one launch \
1750                     replaces the six-projection group on the bf16-resident serving recipe; \
1751                     MEMRA_KDA_FUSED_PROJ=1)"
1752                );
1753            }
1754            return Ok(Some(outs.into_iter().collect()));
1755        }
1756        // Dispatch-class envs: the bit-identity bar is against the MMVQ-class per-row program.
1757        if std::env::var("MEMRA_FAST").as_deref() == Ok("0")
1758            || !self.mmvq_supports(crate::QT_Q8_0)
1759            || (t >= 2 && std::env::var("MEMRA_NO_BATCHED").is_ok())
1760            || (t >= 5 && !Self::b8_enabled())
1761        {
1762            return Ok(None);
1763        }
1764        // Q8_0 operand classes (the non-BF16_MMV shapes).
1765        let q8 = |w: &GpuTensor| -> Option<(usize, usize)> {
1766            match w {
1767                GpuTensor::Quant {
1768                    qtype: crate::QT_Q8_0,
1769                    row_bytes,
1770                    scale,
1771                    rp: false,
1772                    rp4: None,
1773                    ..
1774                } if *scale == 1.0 => Some((w.in_features(), *row_bytes)),
1775                _ => None,
1776            }
1777        };
1778        let (Some((in_q, rb_q)), Some((in_k, rb_k)), Some((in_v, rb_v))) =
1779            (q8(&la.wq), q8(&la.wk), q8(&la.wv))
1780        else {
1781            return Ok(None);
1782        };
1783        let in_f = in_q;
1784        if [in_k, in_v, in_fa, in_ga, in_b].iter().any(|&i| i != in_f)
1785            || rb_k != rb_q
1786            || rb_v != rb_q
1787            || !in_f.is_multiple_of(128)
1788            || x.len() < t * in_f
1789        {
1790            return Ok(None);
1791        }
1792        let dims = [
1793            la.wq.out_features(),
1794            la.wk.out_features(),
1795            la.wv.out_features(),
1796            la.f_a.out_features(),
1797            la.g_a.out_features(),
1798            la.b_proj.out_features(),
1799        ];
1800        let (
1801            GpuTensor::Quant { bytes: bq, .. },
1802            GpuTensor::Quant { bytes: bk, .. },
1803            GpuTensor::Quant { bytes: bv, .. },
1804        ) = (&la.wq, &la.wk, &la.wv)
1805        else {
1806            unreachable!("q8() above only admits Quant");
1807        };
1808        let (
1809            GpuTensor::Float { data: wfa, .. },
1810            GpuTensor::Float { data: wga, .. },
1811            GpuTensor::Float { data: wb, .. },
1812        ) = (&la.f_a, &la.g_a, &la.b_proj)
1813        else {
1814            unreachable!("f32w() above only admits Float");
1815        };
1816
1817        let (aq, ad) = self.quantize_q8_1(x, t, in_f)?;
1818        let mut outs = [
1819            self.uninit(t * dims[0])?,
1820            self.uninit(t * dims[1])?,
1821            self.uninit(t * dims[2])?,
1822            self.uninit(t * dims[3])?,
1823            self.uninit(t * dims[4])?,
1824            self.uninit(t * dims[5])?,
1825        ];
1826        self.kda_proj_fused6_raw(
1827            bq, bk, bv, wfa, wga, wb, &aq, &ad, x, &mut outs, in_f, dims, t, rb_q,
1828        )?;
1829
1830        // Engagement receipt: counted at the arm's own call site, announced once per boot
1831        // (the [bf16-mmv] RESIDENT lesson: engagement lines are receipts, never inferred).
1832        if KDA_FUSED6_DISPATCHES.fetch_add(1, Ordering::Relaxed) == 0 {
1833            eprintln!(
1834                "[kda-fused6] engaged in_f={in_f} out={dims:?} t={t} (one launch replaces the \
1835                 six-projection group; MEMRA_KDA_FUSED_PROJ=1)"
1836            );
1837        }
1838        Ok(Some(outs.into_iter().collect()))
1839    }
1840
1841    /// The raw fused-6 launch (`qmatvec_kda6_q8f32_mmvq`): three Q8_0 weights + three f32
1842    /// weights, one q8_1 activation pair + the raw f32 activation, six outputs, t token rows.
1843    /// Geometry-checked but POLICY-FREE: the gate's red arms drive mutations (transposed slice
1844    /// data, dropped ranges via `dims[i] = 0`) through this entry, so the mutation reaches the
1845    /// exact program the door serves.
1846    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1847    pub fn kda_proj_fused6_raw(
1848        &self,
1849        wq: &CudaSlice<u8>,
1850        wk: &CudaSlice<u8>,
1851        wv: &CudaSlice<u8>,
1852        wfa: &CudaSlice<f32>,
1853        wga: &CudaSlice<f32>,
1854        wb: &CudaSlice<f32>,
1855        aq: &CudaSlice<i8>,
1856        ad: &CudaSlice<f32>,
1857        x: &CudaSlice<f32>,
1858        outs: &mut [CudaSlice<f32>; 6],
1859        in_f: usize,
1860        dims: [usize; 6],
1861        t: usize,
1862        row_bytes: usize,
1863    ) -> Result<(), Box<dyn std::error::Error>> {
1864        const ROWS_PER_BLOCK: usize = 4; // MEMRA_MMVQ_ROWS in qmatvec.cu
1865        if t == 0
1866            || !in_f.is_multiple_of(128)
1867            || x.len() < t * in_f
1868            || aq.len() < t * in_f
1869            || ad.len() < t * (in_f / 32)
1870        {
1871            return Err("kda_proj_fused6 geometry".into());
1872        }
1873        for (i, (w, want_rows)) in [(wq, dims[0]), (wk, dims[1]), (wv, dims[2])]
1874            .into_iter()
1875            .enumerate()
1876        {
1877            if w.len() < want_rows * row_bytes {
1878                return Err(format!(
1879                    "kda_proj_fused6: q8 weight {i} holds {} bytes, needs {}",
1880                    w.len(),
1881                    want_rows * row_bytes
1882                )
1883                .into());
1884            }
1885        }
1886        for (i, (w, want_rows)) in [(wfa, dims[3]), (wga, dims[4]), (wb, dims[5])]
1887            .into_iter()
1888            .enumerate()
1889        {
1890            if w.len() < want_rows * in_f {
1891                return Err(format!(
1892                    "kda_proj_fused6: f32 weight {} holds {} floats, needs {}",
1893                    i + 3,
1894                    w.len(),
1895                    want_rows * in_f
1896                )
1897                .into());
1898            }
1899        }
1900        for (i, (o, want)) in outs.iter().zip(dims).enumerate() {
1901            if o.len() < t * want {
1902                return Err(format!("kda_proj_fused6: output {i} too small").into());
1903            }
1904        }
1905        let blocks: usize = dims.iter().map(|d| d.div_ceil(ROWS_PER_BLOCK)).sum();
1906        let f = self.func("qmatvec_kda6_q8f32_mmvq");
1907        let cfg = LaunchConfig {
1908            grid_dim: (blocks as u32, t as u32, 1),
1909            block_dim: (32, ROWS_PER_BLOCK as u32, 1),
1910            shared_mem_bytes: 0,
1911        };
1912        let inf = in_f as i32;
1913        let d = dims.map(|v| v as i32);
1914        let (mi, rb) = (t as i32, row_bytes as i64);
1915        let [o0, o1, o2, o3, o4, o5] = outs;
1916        let stream = self.gpu.stream();
1917        let mut b = stream.launch_builder(&f);
1918        b.arg(wq)
1919            .arg(wk)
1920            .arg(wv)
1921            .arg(wfa)
1922            .arg(wga)
1923            .arg(wb)
1924            .arg(aq)
1925            .arg(ad)
1926            .arg(x)
1927            .arg(&mut *o0)
1928            .arg(&mut *o1)
1929            .arg(&mut *o2)
1930            .arg(&mut *o3)
1931            .arg(&mut *o4)
1932            .arg(&mut *o5)
1933            .arg(&inf)
1934            .arg(&d[0])
1935            .arg(&d[1])
1936            .arg(&d[2])
1937            .arg(&d[3])
1938            .arg(&d[4])
1939            .arg(&d[5])
1940            .arg(&mi)
1941            .arg(&rb);
1942        unsafe { b.launch(cfg)? };
1943        Ok(())
1944    }
1945
1946    /// The raw BF16-arm fused-6 launch (`qmatvec_kda6_bf16f32`): three bf16-resident weights
1947    /// (raw checkpoint u16 bytes, the `admit=bf16_mmv` residency) + three f32 weights, one raw
1948    /// f32 activation, six outputs, t token rows. Block = `mmv_block()` — the SAME blockDim
1949    /// `matvec_bf16_rows_into` pins, because the bf16 body's shared-tree reduction shape (and
1950    /// therefore its bits) is a function of blockDim. Geometry-checked but POLICY-FREE: the
1951    /// gate's red arms drive mutations through this entry, exactly like the q8 raw above.
1952    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1953    pub fn kda_proj_fused6_bf16_raw(
1954        &self,
1955        wq: &CudaSlice<u8>,
1956        wk: &CudaSlice<u8>,
1957        wv: &CudaSlice<u8>,
1958        wfa: &CudaSlice<f32>,
1959        wga: &CudaSlice<f32>,
1960        wb: &CudaSlice<f32>,
1961        x: &CudaSlice<f32>,
1962        outs: &mut [CudaSlice<f32>; 6],
1963        in_f: usize,
1964        dims: [usize; 6],
1965        t: usize,
1966    ) -> Result<(), Box<dyn std::error::Error>> {
1967        self.kda_proj_fused6_bf16_arm_raw(
1968            wq,
1969            wk,
1970            wv,
1971            wfa,
1972            wga,
1973            wb,
1974            x,
1975            outs,
1976            in_f,
1977            dims,
1978            t,
1979            crate::b200_gemv_v2_level(),
1980        )
1981    }
1982
1983    /// The same launch with the arm chosen EXPLICITLY instead of from the memoized
1984    /// `MEMRA_B200_GEMV_V2` door, so a bench or gate can drive every arm inside one process
1985    /// (`b200_matvec_bench`, the `_arm_raw` precedent).
1986    ///
1987    /// `arm`: `0` = the shipped `qmatvec_kda6_bf16f32`; `1` = `_v2`, whose three BF16 ranges take
1988    /// the eight-rows-per-block walk (activation loaded once and reused across the rows, ten
1989    /// 16 B loads in flight before the first fma, one barrier chain per block) instead of
1990    /// `kda6_bf16_rows4`'s four sequential rows; `2` = `_v3`, the same walk with its weight
1991    /// tiles staged through shared memory by `cp.async` so the in-flight budget stops being
1992    /// register-bound. `2` falls back to `1` when v3's dynamic smem would exceed the 48 KB
1993    /// default cap. Per row the arithmetic is unchanged in every arm, so all three are
1994    /// BIT-IDENTICAL to each other and to `matvec_bf16_f32acc_x4_rows`.
1995    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1996    pub fn kda_proj_fused6_bf16_arm_raw(
1997        &self,
1998        wq: &CudaSlice<u8>,
1999        wk: &CudaSlice<u8>,
2000        wv: &CudaSlice<u8>,
2001        wfa: &CudaSlice<f32>,
2002        wga: &CudaSlice<f32>,
2003        wb: &CudaSlice<f32>,
2004        x: &CudaSlice<f32>,
2005        outs: &mut [CudaSlice<f32>; 6],
2006        in_f: usize,
2007        dims: [usize; 6],
2008        t: usize,
2009        arm: u8,
2010    ) -> Result<(), Box<dyn std::error::Error>> {
2011        if t == 0 || !in_f.is_multiple_of(128) || x.len() < t * in_f {
2012            return Err("kda_proj_fused6_bf16 geometry".into());
2013        }
2014        for (i, (w, want_rows)) in [(wq, dims[0]), (wk, dims[1]), (wv, dims[2])]
2015            .into_iter()
2016            .enumerate()
2017        {
2018            if w.len() < want_rows * in_f * 2 {
2019                return Err(format!(
2020                    "kda_proj_fused6_bf16: bf16 weight {i} holds {} bytes, needs {}",
2021                    w.len(),
2022                    want_rows * in_f * 2
2023                )
2024                .into());
2025            }
2026        }
2027        for (i, (w, want_rows)) in [(wfa, dims[3]), (wga, dims[4]), (wb, dims[5])]
2028            .into_iter()
2029            .enumerate()
2030        {
2031            if w.len() < want_rows * in_f {
2032                return Err(format!(
2033                    "kda_proj_fused6_bf16: f32 weight {} holds {} floats, needs {}",
2034                    i + 3,
2035                    w.len(),
2036                    want_rows * in_f
2037                )
2038                .into());
2039            }
2040        }
2041        for (i, (o, want)) in outs.iter().zip(dims).enumerate() {
2042            if o.len() < t * want {
2043                return Err(format!("kda_proj_fused6_bf16: output {i} too small").into());
2044            }
2045        }
2046        // v3 declines to v2 when its staged tiles would not fit the 48 KB default dynamic
2047        // shared-memory cap (36 KB at the default mmv_block()=128, 72 KB at 256).
2048        let arm = if arm >= 2 && !crate::gemv_v3_fits() {
2049            1
2050        } else {
2051            arm
2052        };
2053        // Rows per block, and therefore the block partition of the six ranges: 4 for the
2054        // shipped kernel, `GEMV_V2_ROWS` for the v2/v3 twins. v2 takes the R-row reduction
2055        // window as DYNAMIC shared memory (R * blockDim.x floats); v3 takes that plus its
2056        // cp.async stage buffers.
2057        let nb = crate::mmv_block();
2058        let rpb = if arm >= 1 { crate::GEMV_V2_ROWS } else { 4 };
2059        let blocks: usize = dims.iter().map(|d| d.div_ceil(rpb)).sum();
2060        let f = self.func(match arm {
2061            0 => "qmatvec_kda6_bf16f32",
2062            1 => "qmatvec_kda6_bf16f32_v2",
2063            _ => "qmatvec_kda6_bf16f32_v3",
2064        });
2065        let cfg = LaunchConfig {
2066            grid_dim: (blocks as u32, t as u32, 1),
2067            block_dim: (nb, 1, 1),
2068            shared_mem_bytes: match arm {
2069                0 => 0,
2070                1 => (crate::GEMV_V2_ROWS as u32) * nb * 4,
2071                _ => crate::gemv_v3_smem_bytes(nb as usize) as u32,
2072            },
2073        };
2074        let inf = in_f as i32;
2075        let d = dims.map(|v| v as i32);
2076        let mi = t as i32;
2077        let [o0, o1, o2, o3, o4, o5] = outs;
2078        let stream = self.gpu.stream();
2079        let mut b = stream.launch_builder(&f);
2080        b.arg(wq)
2081            .arg(wk)
2082            .arg(wv)
2083            .arg(wfa)
2084            .arg(wga)
2085            .arg(wb)
2086            .arg(x)
2087            .arg(&mut *o0)
2088            .arg(&mut *o1)
2089            .arg(&mut *o2)
2090            .arg(&mut *o3)
2091            .arg(&mut *o4)
2092            .arg(&mut *o5)
2093            .arg(&inf)
2094            .arg(&d[0])
2095            .arg(&d[1])
2096            .arg(&d[2])
2097            .arg(&d[3])
2098            .arg(&d[4])
2099            .arg(&d[5])
2100            .arg(&mi);
2101        unsafe { b.launch(cfg)? };
2102        Ok(())
2103    }
2104}
2105
2106#[cfg(test)]
2107mod kda_conv3_default_tests {
2108    use super::kda_conv3_on_from;
2109
2110    #[test]
2111    fn arch_keyed_default_with_explicit_override() {
2112        assert!(kda_conv3_on_from(None, "100a"));
2113        assert!(!kda_conv3_on_from(None, "120a"));
2114        assert!(kda_conv3_on_from(Some("1"), "120a"));
2115        assert!(!kda_conv3_on_from(Some("0"), "100a"));
2116    }
2117}