Skip to main content

memra_engine/
hybrid_forward.rs

1//! Hybrid forward pass (Stage-1, f32, prefill, single sequence). Per layer dispatches to a
2//! linear-attention (Gated DeltaNet) or full-attention mixer, then SwiGLU FFN. Matches
3//! llama.cpp src/models/qwen35.cpp node-for-node.
4
5use crate::Engine;
6use crate::cache::Cache;
7use cudarc::driver::CudaSlice;
8use memra_gguf::config::ModelConfig;
9
10/// Resident trunk transients for the eager prime (piecewise-graph foundation; see
11/// HybridModel::prime_slabs). Every live buffer prefix is fully overwritten before use per prime;
12/// capacity beyond the current token count must never cross a shape-sensitive boundary.
13pub struct PrimeSlabs {
14    pub t_cap: usize,
15    pub h: CudaSlice<f32>,
16    pub x1: CudaSlice<f32>,
17    pub z: CudaSlice<f32>,
18    pub act: CudaSlice<f32>,
19    pub xa: CudaSlice<f32>,
20    pub xb: CudaSlice<f32>,
21    pub h16: CudaSlice<u8>,
22    pub z16: CudaSlice<u8>,
23    /// piecewise boundary slabs (increment 2): GEMM outputs land here so the
24    /// downstream captured segments see fixed addresses.
25    pub gate: CudaSlice<f32>, // t * n_ff_max
26    pub up: CudaSlice<f32>,      // t * n_ff_max
27    pub ffn_out: CudaSlice<f32>, // t * n_embd
28    /// piecewise increment 3: per-layer S-glue segment graphs (down-add + next
29    /// attn-norm, ALL-slab IO, zero in-graph allocations -> keeperless capture is
30    /// clean). Baked at this t_cap; replay only when t == t_cap. seg_glue[il] fires
31    /// between layer il and il+1 (ping-pong parity is deterministic per il).
32    pub seg_glue: Vec<Option<cudarc::driver::CudaGraph>>,
33    /// increment 5 (core-split edition): the mixer out-GEMM writes _into_ `mixed`
34    /// directly (no staging copy — the increment-4 copy route was refuted), making
35    /// S-mid [add + post-norm] all-slab and capturable.
36    pub mixed: CudaSlice<f32>,
37    pub seg_mid: Vec<Option<cudarc::driver::CudaGraph>>,
38    pub seg_t: usize,
39}
40
41// Split prime ranges cannot enter the full-range segment-graph arm, and every slab access
42// is serialized by its device mutex after binding that device's CUDA context on the thread.
43unsafe impl Send for PrimeSlabs {}
44
45/// Shared-expert gate+up at t==1: NVFP4 fused2 (the ornith15/qwen35moe NVFP4 mints keep
46/// gate/up_shexp uniformly NVFP4, so the Q8-only fused2 never fired there and the pair fell
47/// to two mr2 singles + two re-quantizes of the same z — 2 of the 8 unfused launches/layer
48/// the orndecode B=1 census ranked at 17.1%), else the Q8_0 fused2 (the Q8 35B mint), else
49/// two singles. ONE helper for all three shexp dispatch sites — the MEMRA_GDN_MMA
50/// three-read-sites defect is the precedent for not inlining this thrice. Fusion law
51/// everywhere: per (tensor,row) the fused seg body is verbatim, so fused == singles
52/// bit-identically, and the shared (zq, zd) is the same quantize each single recomputes.
53fn shexp_gate_up_t1(
54    e: &Engine,
55    gate_shexp: &crate::model::GpuTensor,
56    up_shexp: &crate::model::GpuTensor,
57    z: &CudaSlice<f32>,
58    zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
59) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
60    let is_nvfp4 = |w: &crate::model::GpuTensor| matches!(w, crate::model::GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4);
61    if is_nvfp4(gate_shexp) && is_nvfp4(up_shexp) {
62        // Reuse the caller's t==1 z-quantize when one exists (the zq8 seam the dev arm
63        // already consumes) — the helper's own quantize is the identical kernel on the
64        // identical input, so this drops one launch per MoE layer without moving a byte.
65        let pair = match zq8 {
66            Some((zq, zd)) => e.matmul_nvfp4_fused2(gate_shexp, up_shexp, zq, zd, 1)?,
67            None => {
68                let (zq, zd) = e.quantize_q8_1(z, 1, gate_shexp.in_features())?;
69                e.matmul_nvfp4_fused2(gate_shexp, up_shexp, &zq, &zd, 1)?
70            }
71        };
72        if let Some(pair) = pair {
73            return Ok(pair);
74        }
75    }
76    match e.matmul_q8_fused2_x(gate_shexp, up_shexp, z)? {
77        Some(pair) => Ok(pair),
78        None => Ok((e.matmul(gate_shexp, z, 1)?, e.matmul(up_shexp, z, 1)?)),
79    }
80}
81
82fn active_matrix_values(
83    available: usize,
84    rows: usize,
85    columns: usize,
86    label: &str,
87) -> Result<usize, String> {
88    let required = rows
89        .checked_mul(columns)
90        .ok_or_else(|| format!("{label} shape overflows: {rows}x{columns}"))?;
91    if available < required {
92        return Err(format!(
93            "{label} has {available} values, fewer than the active {rows}x{columns} ({required})"
94        ));
95    }
96    Ok(required)
97}
98
99fn step_grouped_decode_shape(prefill: bool, tokens: usize) -> bool {
100    !prefill && tokens == 1
101}
102
103fn parse_step_ep_grouped_prefill(value: Option<&str>) -> Result<bool, String> {
104    match value {
105        None | Some("") | Some("0") => Ok(false),
106        Some("1") => Ok(true),
107        Some(value) => Err(format!(
108            "MEMRA_STEP_EP_GROUPED_PREFILL={value:?} is invalid; expected 0 or 1"
109        )),
110    }
111}
112
113fn step_ep_grouped_prefill_enabled() -> Result<bool, String> {
114    parse_step_ep_grouped_prefill(
115        std::env::var("MEMRA_STEP_EP_GROUPED_PREFILL")
116            .ok()
117            .as_deref(),
118    )
119}
120
121fn step_grouped_prefill_shape(enabled: bool, prefill: bool, tokens: usize) -> bool {
122    enabled && prefill && (PRIME_MIN_T..=crate::cache::PRIME_CHUNK_MAX_TOKENS).contains(&tokens)
123}
124
125fn parse_step_tp_prefill(value: Option<&str>) -> Result<bool, String> {
126    match value {
127        None | Some("") | Some("0") => Ok(false),
128        Some("1") => Ok(true),
129        Some(value) => Err(format!(
130            "MEMRA_STEP_TP_PREFILL={value:?} is invalid; expected 0 or 1"
131        )),
132    }
133}
134
135fn step_tp_prefill_enabled() -> Result<bool, String> {
136    parse_step_tp_prefill(std::env::var("MEMRA_STEP_TP_PREFILL").ok().as_deref())
137}
138
139fn validate_step_prime_batch_modes(tp_prefill: bool, grouped_prefill: bool) -> Result<(), String> {
140    if grouped_prefill && !tp_prefill {
141        return Err("MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into());
142    }
143    if tp_prefill {
144        return Err(
145            "Step TP4 cross-request prime batching did not clear the live-server performance \
146             gate; use per-session grouped prefill"
147                .into(),
148        );
149    }
150    Ok(())
151}
152
153fn step_tp_prefill_shape(
154    enabled: bool,
155    tokens: usize,
156    ranks: usize,
157    native_p2p: bool,
158    has_rank_local_attention: bool,
159    fp8_kv: bool,
160) -> bool {
161    // TP2 admitted 2026-08-25 behind the same off-by-default door. The prefill body is
162    // rank-count-generic (every geometry check divides by `ranks`); only this shape ever
163    // named 4. TP4's high-context NO-GO was a TRANSPORT verdict — token-row column
164    // gathers plus remote O blocks issue ~61,440 peer copies per 4K attention layer on
165    // that placement — which is not evidence about a 2-card native-P2P placement that
166    // reduces O rank-locally. TP2 is UNQUALIFIED until its own prefill argmax + TTFT
167    // receipts land; the door stays off by default.
168    enabled
169        && tokens >= PRIME_MIN_T
170        && matches!(ranks, 2 | 4)
171        && native_p2p
172        && has_rank_local_attention
173        && !fp8_kv
174}
175
176fn empty_cache_layers<T>(n: usize) -> Vec<Option<T>> {
177    std::iter::repeat_with(|| None).take(n).collect()
178}
179
180/// Temporarily move a PP-2 cache's layer state into two independently-owned cache shells.
181/// The stage walkers then receive disjoint `&mut Cache` values and can run on separate host
182/// threads without aliasing. GPU buffers are moved, not copied; Drop restores every layer
183/// and publishes the last position completed by both stages.
184struct PrimeCacheStages<'a> {
185    parent: &'a mut Cache,
186    cut: usize,
187    stage0: Cache,
188    stage1: Cache,
189}
190
191impl<'a> PrimeCacheStages<'a> {
192    fn new(parent: &'a mut Cache, cut: usize) -> Self {
193        let n = parent.kv.len();
194        assert_eq!(parent.recur.len(), n, "cache layer vectors disagree");
195        assert!(cut <= n, "PP-2 cache cut {cut} exceeds {n} layers");
196        let mut kv0 = empty_cache_layers(n);
197        let mut kv1 = empty_cache_layers(n);
198        let mut tp_kv0 = empty_cache_layers(n);
199        let mut tp_kv1 = empty_cache_layers(n);
200        let mut recur0 = empty_cache_layers(n);
201        let mut recur1 = empty_cache_layers(n);
202        for i in 0..cut {
203            kv0[i] = parent.kv[i].take();
204            tp_kv0[i] = parent.tp_kv[i].take();
205            recur0[i] = parent.recur[i].take();
206        }
207        for i in cut..n {
208            kv1[i] = parent.kv[i].take();
209            tp_kv1[i] = parent.tp_kv[i].take();
210            recur1[i] = parent.recur[i].take();
211        }
212        let pos = parent.pos;
213        let max_ctx = parent.max_ctx;
214        Self {
215            parent,
216            cut,
217            stage0: Cache {
218                kv: kv0,
219                tp_kv: tp_kv0,
220                recur: recur0,
221                pos,
222                max_ctx,
223                last_logits_dev: None,
224                dflash_taps: None,
225            },
226            stage1: Cache {
227                kv: kv1,
228                tp_kv: tp_kv1,
229                recur: recur1,
230                pos,
231                max_ctx,
232                last_logits_dev: None,
233                dflash_taps: None,
234            },
235        }
236    }
237
238    fn parts(&mut self) -> (&mut Cache, &mut Cache) {
239        (&mut self.stage0, &mut self.stage1)
240    }
241}
242
243impl Drop for PrimeCacheStages<'_> {
244    fn drop(&mut self) {
245        let n = self.parent.kv.len();
246        for i in 0..n {
247            let source = if i < self.cut {
248                &mut self.stage0
249            } else {
250                &mut self.stage1
251            };
252            debug_assert!(self.parent.kv[i].is_none());
253            debug_assert!(self.parent.tp_kv[i].is_none());
254            debug_assert!(self.parent.recur[i].is_none());
255            self.parent.kv[i] = source.kv[i].take();
256            self.parent.tp_kv[i] = source.tp_kv[i].take();
257            self.parent.recur[i] = source.recur[i].take();
258        }
259        self.parent.pos = self.stage0.pos.min(self.stage1.pos);
260    }
261}
262
263/// task #18 (attn side): one sequence's pre-attention outputs (post-rope q/k, v, out-gate).
264pub(crate) struct AttnPre {
265    pub q: cudarc::driver::CudaSlice<f32>,
266    pub k: cudarc::driver::CudaSlice<f32>,
267    pub v: cudarc::driver::CudaSlice<f32>,
268    pub gate: Option<cudarc::driver::CudaSlice<f32>>,
269}
270
271/// task #18: one sequence's GDN prep outputs (the scan inputs).
272pub(crate) struct GdnPrep {
273    pub hk: usize,
274    pub q_l2: cudarc::driver::CudaSlice<f32>,
275    pub k_l2: cudarc::driver::CudaSlice<f32>,
276    pub v_g: cudarc::driver::CudaSlice<f32>,
277    pub beta: cudarc::driver::CudaSlice<f32>,
278    pub g_log: cudarc::driver::CudaSlice<f32>,
279    pub kb16: Option<cudarc::driver::CudaSlice<u8>>,
280    pub qb16: Option<cudarc::driver::CudaSlice<u8>>,
281}
282
283/// Device scratch for the burst verify stream (see `verify_stream_scratch`).
284pub(crate) struct VerifyStreamScratch {
285    pub pos_d: CudaSlice<i32>,
286    pub row_ctrs: Vec<CudaSlice<i32>>,
287}
288use crate::hybrid::{FullAttnLayer, HybridModel, LinearAttnLayer, Mixer, MoeWeights};
289
290struct MoeInputTraceWriter {
291    dir: std::path::PathBuf,
292    index: std::fs::File,
293    payloads: std::collections::HashMap<u16, (std::fs::File, u64)>,
294}
295
296static MOE_INPUT_TRACE_WRITER: std::sync::OnceLock<std::sync::Mutex<Option<MoeInputTraceWriter>>> =
297    std::sync::OnceLock::new();
298
299/// STAGE-2 GROUPED DECODE gate (MEMRA_MOE_GDEC, default ON; `=0` restores the sequential
300/// per-expert launch chain). See `moe_gdec_token`.
301fn gdec_enabled() -> bool {
302    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
303    *E.get_or_init(|| {
304        std::env::var("MEMRA_MOE_GDEC")
305            .map(|v| v != "0")
306            .unwrap_or(true)
307    })
308}
309
310/// SLAB-LOCAL RESIDENT ARM gate (lane/pp-leverb 2026-08-08, MEMRA_MOE_SLAB, default ON;
311/// `=0` restores the SLRU dispatch even when resident slabs exist). Read PER CALL, never
312/// memoized — probes A/B the two provenances in one process (the MEMRA_PRIME_PP pattern).
313/// See `moe_ffn_sequential_zq8`'s slab_local arm: the sigmoid-router archs (step35/M3/Hy3)
314/// are denied every `dev_exps` consumer (pairs/dev route softmax), so before this arm the
315/// fits-VRAM resident slabs were UPLOADED for them but never READ — the SLRU kept staging
316/// the same bytes beside a dead copy (37 GB H2D per pp4096 prime on the Step SKU, anatomy
317/// receipt). The arm reads the SAME bytes through the SAME kernels; only the pointer
318/// PROVENANCE changes (slab base + ex*stride vs SLRU slot address) — the bit-identity class
319/// `moe_ffn_dev`'s resident arm already documents against its SLRU arm.
320fn moe_slab_enabled() -> bool {
321    std::env::var("MEMRA_MOE_SLAB").as_deref() != Ok("0")
322}
323
324/// Expert-grouped dispatch remains opt-in after the local 5090 transfer gate rejected the
325/// default flip. `=0` selects the established path, while any other explicit value enables the
326/// grouped research arm for the current call.
327fn moe_grouped_enabled(_cfg: &ModelConfig, _prefill: bool) -> bool {
328    std::env::var("MEMRA_MOE_GROUPED")
329        .map(|value| value != "0")
330        .unwrap_or(false)
331}
332
333/// Deterministic in-token expert prefetch. `MEMRA_MOE_PREFETCH=1` overlaps memory-source H2D on the
334/// copy stream; selecting the opt-in worker spill backend enables the same known-next hook for disk.
335fn moe_prefetch_enabled() -> bool {
336    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
337    *E.get_or_init(|| {
338        std::env::var("MEMRA_MOE_PREFETCH").as_deref() == Ok("1")
339            || crate::spill_pread::worker_enabled()
340    })
341}
342
343/// Best-effort OS page-cache prefetch distance for mmap-backed expert ranges. Independent of the
344/// H2D copy-stream experiment so storage->RAM and RAM->HBM overlap can be measured separately.
345/// The opt-in default stays one expert to preserve the original experiment; spill rigs can widen
346/// it with `MEMRA_MOE_PAGE_PREFETCH_WINDOW` to cover NVMe latency.
347fn moe_page_prefetch_window() -> usize {
348    static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
349    *W.get_or_init(|| {
350        page_prefetch_window_from_values(
351            std::env::var("MEMRA_MOE_PAGE_PREFETCH").as_deref() == Ok("1"),
352            std::env::var("MEMRA_MOE_PAGE_PREFETCH_WINDOW")
353                .ok()
354                .as_deref(),
355        )
356    })
357}
358
359fn page_prefetch_window_from_values(enabled: bool, raw_window: Option<&str>) -> usize {
360    if !enabled {
361        return 0;
362    }
363    raw_window.and_then(|value| value.parse().ok()).unwrap_or(1)
364}
365
366/// Return only the newly exposed positions in a rolling lookahead window. Position zero seeds the
367/// full window; each later position adds one expert at the far edge. Thus widening the window does
368/// not repeatedly issue `MADV_WILLNEED` for the same range.
369fn page_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
370    if window == 0 || position >= len {
371        return len..len;
372    }
373    let (start, count) = if position == 0 {
374        (1, window)
375    } else {
376        (position.saturating_add(window), 1)
377    };
378    let start = start.min(len);
379    start..start.saturating_add(count).min(len)
380}
381
382/// Grouped worker-I/O schedule: prime the first active expert before the loop, then queue exactly
383/// one known-next expert at each iteration. Returning positions keeps expert ordering authoritative.
384fn grouped_worker_prefetch_position(order_len: usize, current: Option<usize>) -> Option<usize> {
385    let position = current.map_or(0, |position| position.saturating_add(1));
386    (position < order_len).then_some(position)
387}
388
389/// Fill the worker ring with complete experts, retaining one pinned buffer for an unexpected
390/// demand miss. Each expert has gate/up/down extents, so depth 16 admits a rolling five-expert
391/// window. Position zero primes the current expert too: its three independent reads can run in
392/// parallel instead of demand-serializing gate, up, and down before any useful GPU work exists.
393fn worker_prefetch_window() -> usize {
394    static WINDOW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
395    *WINDOW.get_or_init(|| {
396        let automatic = crate::spill_pread::configured_depth().saturating_sub(1) / 3;
397        std::env::var("MEMRA_SPILL_WORKER_EXPERT_WINDOW")
398            .ok()
399            .and_then(|value| value.parse::<usize>().ok())
400            .unwrap_or(automatic.max(1))
401    })
402}
403
404/// Return only positions newly exposed by a rolling worker-I/O window. Unlike mmap page advice,
405/// this includes the current expert when the window is seeded so all three current projections
406/// enter the CPU pool together.
407fn worker_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
408    if window == 0 || position >= len {
409        return len..len;
410    }
411    let (start, count) = if position == 0 {
412        (0, window)
413    } else {
414        (position.saturating_add(window).saturating_sub(1), 1)
415    };
416    let start = start.min(len);
417    start..start.saturating_add(count).min(len)
418}
419
420/// LAUNCH-STRUCTURE STAGE 3 gate (MEMRA_MOE_DEV, default ON; `=0` restores host routing). The
421/// zero-DtoH device-dispatch path for fully-resident layers: router top-k output stays on device,
422/// expert weight pointers come from the per-layer device table. Requires the fused router (the
423/// dev path consumes the device sel/w directly), so MEMRA_FUSED_ROUTER=0 also disables it.
424fn moe_dev_enabled() -> bool {
425    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
426    *E.get_or_init(|| {
427        std::env::var("MEMRA_MOE_DEV")
428            .map(|v| v != "0")
429            .unwrap_or(true)
430            && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0"))
431    })
432}
433
434/// Device sigmoid top-k is the default for Step-3.7 / M3 / Hy3 / GLM-DSA. `MEMRA_SIG_ROUTER=0` restores
435/// the full-logit DtoH plus `moe_route_sigmoid_host` oracle without changing expert dispatch.
436fn sigmoid_router_enabled() -> bool {
437    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
438    *E.get_or_init(|| {
439        std::env::var("MEMRA_SIG_ROUTER")
440            .map(|v| v != "0")
441            .unwrap_or(true)
442    })
443}
444
445/// MoE EXPERT dp4a gate (MEMRA_MOE_Q8, default ON; `=0` restores the Stage-A f32-dequant expert
446/// kernels). Applies when gate/up/down expert qtypes are all in the dp4a body set (IQ3_S/IQ4_XS).
447/// FP-order differs from Stage-A (int dp4a + warp tree) — argmax/run-gen/stream-identity gates
448/// arbitrate; the sequential and fused q8 paths ship as a matched pair (MEMRA_MOE_GATE contract).
449fn moe_q8_enabled() -> bool {
450    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
451    *E.get_or_init(|| {
452        std::env::var("MEMRA_MOE_Q8")
453            .map(|v| v != "0")
454            .unwrap_or(true)
455    })
456}
457
458/// gemma4 fast-arm gate: qtypes with an `expert_dot_g` dp4a body (superset used by the gelu
459/// dev arm; the qwen q8 arms keep their own battery-gated q8_expert_supported policy).
460fn expert_dp4a_supported(qt: i32) -> bool {
461    qt == crate::QT_Q4_0
462        || qt == crate::QT_IQ3_S
463        || qt == crate::QT_IQ4_XS
464        || qt == crate::QT_Q3_K
465        || qt == crate::QT_Q4_K
466        || qt == crate::QT_Q6_K
467}
468
469fn q8_expert_supported(qt: i32) -> bool {
470    // k-quant arms added 2026-07-06 (Q3_K/Q4_K/Q6_K bodies for the UD tail layers). Briefly
471    // default-excluded the same day when they appeared to break 35B real-prompt spec — the
472    // ACTUAL culprit was the MoE router's cuBLASLt n-dependence (d994271); with the router
473    // decode-exact at verify t, the k-quant arms pass the full spec battery (p1/p2/p3 + raw
474    // K=1..8) and are DEFAULT ON again (+9 tok/s: 148.9 -> 157.9). MEMRA_MOE_Q8_KQ=0 excludes.
475    static KQ: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
476    let kq = *KQ.get_or_init(|| {
477        std::env::var("MEMRA_MOE_Q8_KQ")
478            .map(|v| v != "0")
479            .unwrap_or(true)
480    });
481    // NVFP4 experts: DEFAULT ON (2026-07-17). The M3-era "decode-vs-verify MISMATCH 3.4e1"
482    // that had this excluded was the missing per-expert macro-scale fold, fixed in the
483    // dev-kernel epilogues + moe_w_scale_by_expert; the 35B ct-NVFP4 artifact now runs the
484    // q8 arm at parity with the IQ4_XS daily (174-178 tok/s, spec K=1..8 exact). M3/Hy3
485    // never reach the q8 arms regardless (sigmoid-router cfg gates on pairs/dev/gdec).
486    // MEMRA_MOE_Q8_NVFP4=0 restores the f32 arm.
487    let nvfp4_q8 = std::env::var("MEMRA_MOE_Q8_NVFP4")
488        .map(|v| v != "0")
489        .unwrap_or(true);
490    qt == crate::QT_IQ3_S
491        || qt == crate::QT_IQ4_XS
492        || (nvfp4_q8 && qt == crate::QT_NVFP4)
493        || (kq && (qt == crate::QT_Q3_K || qt == crate::QT_Q4_K || qt == crate::QT_Q6_K))
494}
495
496/// The decode-once (_dec) and IQ-MMA expert kernels dequant via IQ-specific extractors —
497/// k-quant tensors must fall to the _em dot path instead.
498fn q8_expert_dec_supported(qt: i32) -> bool {
499    qt == crate::QT_IQ3_S || qt == crate::QT_IQ4_XS || qt == crate::QT_Q4_0
500}
501
502/// Grouped-f16 door (MEMRA_MOE_F16G) per-projection admission: the qtype has a dequant-to-f16
503/// kernel in cu/moe_f16_grouped.cu AND the projection's k dimension tiles its block size.
504/// Round 49 widened coverage to q35's UD mix (gate/up IQ3_S x39 + Q3_K x1 + IQ4_XS x1; down
505/// IQ4_XS x37 + Q6_K x3 + Q4_K x1) — the round-47 IQ4_XS/Q4_0-only table admitted ~1 of 41
506/// q35 layers, which is why that cell measured FLAT.
507fn f16g_proj_ok(qt: i32, in_f: usize) -> bool {
508    match qt {
509        crate::QT_Q4_0 => in_f % 32 == 0,
510        crate::QT_IQ4_XS | crate::QT_IQ3_S | crate::QT_Q3_K | crate::QT_Q4_K | crate::QT_Q6_K => {
511            in_f % 256 == 0
512        }
513        // NVFP4 (block 64) added lane/moebatch-q35moe 2026-08-21: the ornith15 expert bank is
514        // uniform NVFP4, which passed the pairs q8 gate but missed BOTH batched doors
515        // (use_mma's dec set and this table), so 14.7k-token prefill rode the per-pair _em
516        // fallback — 88.6% of the prime wall (prime-anatomy receipt).
517        crate::QT_NVFP4 => in_f % 64 == 0,
518        _ => false,
519    }
520}
521
522/// STAGE 3 prewarm gate (MEMRA_MOE_PREWARM, default ON; `=0` leaves residency organic). One-shot
523/// per layer: force-admit every block while FREE slots cover the whole layer (never evicts).
524fn moe_prewarm_enabled() -> bool {
525    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
526    *E.get_or_init(|| {
527        std::env::var("MEMRA_MOE_PREWARM")
528            .map(|v| v != "0")
529            .unwrap_or(true)
530    })
531}
532
533/// During a discarded fixed-residency profile, admit CPU-routed misses after their current-token
534/// CPU result is complete. The current result and numeric path are unchanged; later warmup tokens
535/// can then vote for and exercise those experts on GPU before the cache is frozen.
536fn cpu_expert_profile_admit_enabled() -> bool {
537    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
538    *E.get_or_init(|| std::env::var("MEMRA_CPU_EXPERT_FREEZE_PROFILE_ADMIT").as_deref() == Ok("1"))
539}
540
541/// Minimum prompt length for the BATCHED cache prime (`prime_cache`). Below this the tokenwise
542/// decode loop wins anyway (the batched path's GEMM dispatch needs m>=16, and the stateful conv
543/// kernel needs T >= d_conv-1). Callers: generate / generate_spec.
544pub const PRIME_MIN_T: usize = 16;
545
546/// Widest tick the MoE DEV per-token program serves (lane/orndecode-20260822). PRIME_MIN_T
547/// doubled as the dev-arm's upper bound on the assumption that t==16 only ever meant real
548/// prefill; the exact-16 decode tier broke that assumption — at B=16 the MoE stage crossed
549/// onto the t>=MMA_T grouped/kq GEMM program (m_e ~1.6 rows/expert: 52.6% of the tick at
550/// ~104 us/launch) or the `_em` per-pair fallback (67.7 us), both catastrophically slower
551/// than the dev q8 kernels that serve B<=8 (8.8 us gate_up covering a token's whole expert
552/// set). Decode widths 2..=16 now ride dev; the grouped/pairs prefill programs start at 17.
553/// gate2/gate3 byte batteries at B=12/16 are the qualification (bit-checked vs isolated).
554const MOE_DEV_MAX_T: usize = 16;
555const PRIME_PIPE_MICROBATCHES: usize = 8;
556const PRIME_PIPE_MIN_CHUNK: usize = 128;
557const PRIME_PIPE_EDGE_MIN_CHUNK: usize = 64;
558const PRIME_PIPE_LINEAR_WORK: usize = 8;
559
560fn prime_pp2_auto_geometry(n_layers: usize) -> bool {
561    crate::pp::prime_pp_on()
562        && !crate::pp::pp2_streams_off()
563        && crate::pp::pp_cuts(n_layers).is_some_and(|cuts| cuts.len() == 3)
564}
565
566/// Effective internal prime chunk. An explicit MEMRA_PRIME_CHUNK is authoritative.
567/// Naked PP-2 primes use the measured pipeline geometry: up to eight microchunks, never
568/// below 128 tokens, while the legacy 4096-token cap remains the long-context bound.
569pub fn prime_chunk_tokens(t: usize, n_layers: usize) -> usize {
570    if let Ok(value) = std::env::var("MEMRA_PRIME_CHUNK") {
571        let parsed = value
572            .parse::<usize>()
573            .unwrap_or(crate::cache::PRIME_CHUNK_MAX_TOKENS);
574        return if crate::cache::swa_ring_on() {
575            if parsed == 0 {
576                crate::cache::PRIME_CHUNK_MAX_TOKENS
577            } else {
578                parsed.min(crate::cache::PRIME_CHUNK_MAX_TOKENS)
579            }
580        } else {
581            parsed
582        };
583    }
584    let chunk = crate::cache::PRIME_CHUNK_MAX_TOKENS;
585    if prime_pp2_auto_geometry(n_layers) && t >= 2 * PRIME_PIPE_MIN_CHUNK {
586        chunk.min(
587            t.div_ceil(PRIME_PIPE_MICROBATCHES)
588                .max(PRIME_PIPE_MIN_CHUNK),
589        )
590    } else {
591        chunk
592    }
593}
594
595fn fixed_prime_chunk_ranges(t: usize, chunk: usize) -> Vec<(usize, usize)> {
596    fixed_prime_chunk_ranges_for_ring(t, chunk, crate::cache::swa_ring_on())
597}
598
599fn fixed_prime_chunk_ranges_for_ring(t: usize, chunk: usize, ring_on: bool) -> Vec<(usize, usize)> {
600    if chunk == 0 || t <= chunk {
601        return vec![(0, t)];
602    }
603    let mut ranges = Vec::with_capacity(t.div_ceil(chunk));
604    let mut start = 0usize;
605    while start < t {
606        let mut end = (start + chunk).min(t);
607        if t - end > 0 && t - end < PRIME_MIN_T {
608            if ring_on {
609                let shifted = t - PRIME_MIN_T;
610                end = if shifted > start { shifted } else { t };
611            } else {
612                end = t;
613            }
614        }
615        ranges.push((start, end));
616        start = end;
617    }
618    ranges
619}
620
621fn prime_chunk_work(prefix: usize, total: usize) -> u128 {
622    let prefix = prefix as u128;
623    prefix * (prefix + (PRIME_PIPE_LINEAR_WORK as u128) * (total as u128))
624}
625
626fn dynamic_prime_chunk_ranges(
627    t: usize,
628    fixed_chunk: usize,
629    fixed: &[(usize, usize)],
630) -> Vec<(usize, usize)> {
631    let n = fixed.len();
632    if n < 3 {
633        return fixed.to_vec();
634    }
635
636    let max_first = t - (n - 1) * PRIME_MIN_T;
637    let first = fixed_chunk
638        .div_ceil(2)
639        .max(PRIME_PIPE_EDGE_MIN_CHUNK)
640        .min(max_first);
641    let mut ranges = Vec::with_capacity(n);
642    ranges.push((0, first));
643
644    let first_work = prime_chunk_work(first, t);
645    let work_span = prime_chunk_work(t, t) - first_work;
646    let denominator = (n - 1) as u128;
647    let mut previous = first;
648    for boundary in 1..n - 1 {
649        let target = first_work * denominator + work_span * (boundary as u128);
650        let remaining = n - 1 - boundary;
651        let mut low = previous + PRIME_MIN_T;
652        let mut high = t - remaining * PRIME_MIN_T;
653        while low < high {
654            let mid = low + (high - low) / 2;
655            if prime_chunk_work(mid, t) * denominator >= target {
656                high = mid;
657            } else {
658                low = mid + 1;
659            }
660        }
661        ranges.push((previous, low));
662        previous = low;
663    }
664    ranges.push((previous, t));
665    ranges
666}
667
668/// Internal prime ranges. The naked PP-2 pipeline defaults to a short-fill,
669/// equal-modeled-time schedule; MEMRA_PRIME_CHUNK_SCHED=fixed restores the measured
670/// equal-token ranges. An explicit MEMRA_PRIME_CHUNK always retains fixed semantics.
671///
672/// `gdn_grid`: the model runs the chunked GDN WY scan (`HybridModel::gdn_prime_grid_on`) —
673/// AUTO-scheduled internal boundaries are then snapped down to the WY-chunk grid
674/// (`align_prime_ranges_to_gdn`; the spec-longctx grid law, extended from serve splits to
675/// the PP prime microchunks). Explicit MEMRA_PRIME_CHUNK keeps its operator-authoritative
676/// (fixed, unaligned) semantics — the FLAGS caveat documents that identity contract.
677pub fn prime_chunk_ranges(t: usize, n_layers: usize, gdn_grid: bool) -> Vec<(usize, usize)> {
678    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
679    let chunk = prime_chunk_tokens(t, n_layers);
680    let fixed = fixed_prime_chunk_ranges(t, chunk);
681    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
682        Ok(value) => value == "dynamic",
683        Err(_) => true,
684    };
685    if explicit_chunk {
686        return fixed;
687    }
688    let ranges = if !dynamic || !prime_pp2_auto_geometry(n_layers) {
689        fixed
690    } else {
691        dynamic_prime_chunk_ranges(t, chunk, &fixed)
692    };
693    // MEMRA_PRIME_GRID_ALIGN=0 is the shared rollback seam of the grid law (same env the
694    // worker's serve-boundary alignment honors, read per call so gates can flip it
695    // in-process): the legacy off-grid auto schedule — the toothed cell's broken arm.
696    if gdn_grid && std::env::var("MEMRA_PRIME_GRID_ALIGN").as_deref() != Ok("0") {
697        align_prime_ranges_to_gdn(&ranges, t, Engine::gdn_chunk_size())
698    } else {
699        ranges
700    }
701}
702
703/// Snap AUTO prime-range internal boundaries DOWN to the GDN WY-chunk grid (lane/
704/// hermes-perf-fixes, 2026-08-23 — the missing helper the PP-auto-ranges finding names).
705///
706/// THE LAW THIS EXTENDS (measured, research/multiturn-cache-20260821/
707/// LONGCTX-EXACTNESS-20260821.md; the serve-split half already ships as the worker's
708/// `grid_align_boundary`): under the chunked WY scan a prompt primed as two calls split at
709/// L is bit-identical to the monolithic prime iff `L % gdn_chunk_size() == 0` — an off-grid
710/// call start shifts the fold grid and materializes recurrent state at a point the
711/// monolithic program never computes. The prime loop walks these ranges as separate
712/// `prime_layers` calls, so INTERNAL microchunk boundaries are the same seam: the PP-2
713/// auto geometry (`t.div_ceil(8).max(128)` fills, and every dynamic short-fill boundary)
714/// lands off the 32-token grid for most prompt lengths, which is exactly the
715/// chunk-value bit-identity the GDN lane falsified (FLAGS PRIME_CHUNK/SCHED caveat).
716///
717/// Boundaries only move DOWN (earlier is always semantically safe — same argument as the
718/// worker's alignment); a boundary that collapses onto its predecessor is dropped (ranges
719/// merge). The final range always ends at `t`. Aligning down only GROWS the tail
720/// remainder, so the fixed-schedule tail-merge rule is never re-violated. Cost bound: at
721/// most `c-1` tokens shift per boundary.
722pub fn align_prime_ranges_to_gdn(
723    ranges: &[(usize, usize)],
724    t: usize,
725    c: usize,
726) -> Vec<(usize, usize)> {
727    if c == 0 || ranges.len() < 2 {
728        return ranges.to_vec();
729    }
730    let mut out: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
731    let mut start = 0usize;
732    for (i, &(_, end)) in ranges.iter().enumerate() {
733        let e = if i + 1 == ranges.len() {
734            t
735        } else {
736            end / c * c
737        };
738        if e > start {
739            out.push((start, e));
740            start = e;
741        } // else: boundary collapsed onto its predecessor — merge into the next range
742    }
743    debug_assert_eq!(out.last().map(|&(_, e)| e), Some(t));
744    out
745}
746
747struct HeadSplit {
748    pin: u64,
749    w1: CudaSlice<u8>,
750    hn1: CudaSlice<f32>,
751    y1: CudaSlice<f32>,
752    logits_e: CudaSlice<f32>,
753    ev_hn: cudarc::driver::CudaEvent,
754    ev_done: cudarc::driver::CudaEvent,
755    raw_hn1: u64,
756    raw_y1: u64,
757    raw_logits_hi: u64,
758    /// SAMPLED-TAIL scratch (perturbed row + the filter's threshold/z/max slots + the row
759    /// index). Allocating these per token cost more than the split head saved: the first
760    /// sampled-split measurement came in at 78.25 tok/s against 78.96 for the unsplit head,
761    /// which is five allocations per token, not arithmetic.
762    samp: Option<SampScratch>,
763}
764
765struct SampScratch {
766    pb: CudaSlice<f32>,
767    th: CudaSlice<f32>,
768    z: CudaSlice<f32>,
769    mx: CudaSlice<f32>,
770    rows: CudaSlice<i32>,
771}
772/// HEAD-SPLIT workspace (host + device twins share it).
773static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
774
775/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
776/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
777/// input bits — rank1's local selection is bit-equal to the root's.
778#[allow(clippy::type_complexity)]
779static DEV1_ROUTER_REPS: std::sync::Mutex<
780    Option<(
781        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
782        Option<CudaSlice<f32>>,
783    )>,
784> = std::sync::Mutex::new(None);
785
786/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
787/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
788/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
789#[allow(clippy::type_complexity)]
790static SHEXP_D1_REPS: std::sync::Mutex<
791    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
792> = std::sync::Mutex::new(None);
793#[allow(clippy::type_complexity)]
794static SHEXP_D1_WS: std::sync::Mutex<
795    Option<(
796        (usize, usize),
797        CudaSlice<f32>,
798        CudaSlice<f32>,
799        CudaSlice<f32>,
800        cudarc::driver::CudaEvent,
801        cudarc::driver::CudaEvent,
802    )>,
803> = std::sync::Mutex::new(None);
804
805/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
806static SHEXP_OV_WS: std::sync::Mutex<
807    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
808> = std::sync::Mutex::new(None);
809
810impl HybridModel {
811    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
812    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
813    /// an off-grid prime-call boundary shifts the WY fold grid (see
814    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
815    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
816    pub fn gdn_prime_grid_on(&self) -> bool {
817        Engine::gdn_chunked_enabled()
818            && self
819                .layers
820                .iter()
821                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
822    }
823
824    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
825    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
826    /// (the device buffers must be addressable on both sides — the TP registry builds its
827    /// own Engine per rank, so this is a real seam, not a formality).
828    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
829        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
830    }
831
832    fn step35_tp_qkv(
833        &self,
834        e: &Engine,
835        fa: &FullAttnLayer,
836        h: &CudaSlice<f32>,
837        t: usize,
838    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
839        let Some(tp) = fa.step_tp_qkv.as_ref() else {
840            return Ok(None);
841        };
842        let values = active_matrix_values(
843            h.len(),
844            t,
845            self.cfg.n_embd as usize,
846            "Step TP QKV activation",
847        )?;
848        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
849        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
850        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
851        // round-trip on every execute that the peer transport exists to remove. The
852        // device twins are byte-identical by construction (the same bytes travel dtod
853        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
854        // The host arm below remains the transport for !native_p2p (host staging IS that
855        // transport) and for a root context this engine cannot address.
856        if Self::step35_tp_device_resident(e, tp) {
857            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
858            // on theirs (same context, different streams).
859            e.stream().synchronize()?;
860            let q = tp
861                .runtime
862                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
863            let k = tp
864                .runtime
865                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
866            let v = tp
867                .runtime
868                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
869            Self::step35_tp_log_once(tp, "qkv", "device-resident");
870            return Ok(Some(vec![q, k, v]));
871        }
872        let host = e.dtoh_view(&h.slice(0..values))?;
873        let q = if tp.runtime.native_p2p() {
874            tp.runtime
875                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
876        } else {
877            tp.runtime
878                .bf16_column_parallel_resident(&tp.q, &host, t)?
879                .gathered
880        };
881        let k = if tp.runtime.native_p2p() {
882            tp.runtime
883                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
884        } else {
885            tp.runtime
886                .bf16_column_parallel_resident(&tp.k, &host, t)?
887                .gathered
888        };
889        let v = if tp.runtime.native_p2p() {
890            tp.runtime
891                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
892        } else {
893            tp.runtime
894                .bf16_column_parallel_resident(&tp.v, &host, t)?
895                .gathered
896        };
897        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
898        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
899    }
900
901    /// One transport banner per (projection, transport) — the old per-call eprintln fired
902    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
903    /// unbouncing (the sibling grouped-EP path already learned this).
904    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
905        use std::sync::atomic::{AtomicBool, Ordering};
906        static LOGGED: [AtomicBool; 4] = [
907            AtomicBool::new(false),
908            AtomicBool::new(false),
909            AtomicBool::new(false),
910            AtomicBool::new(false),
911        ];
912        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
913        if LOGGED[idx].swap(true, Ordering::Relaxed) {
914            return;
915        }
916        eprintln!(
917            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
918             tensor_parallel=true attention_local=true kv_local=true transport={} \
919             native_p2p={} bulk_p2p={} activation={activation} \
920             output={} performance_claim=false (logged once per transport)",
921            tp.layer,
922            tp.devices,
923            tp.runtime.transport_label(),
924            tp.runtime.native_p2p(),
925            tp.runtime.bulk_p2p(),
926            if activation == "device-resident" {
927                "root-resident"
928            } else {
929                "root-readback"
930            },
931        );
932    }
933
934    fn step35_tp_o(
935        &self,
936        e: &Engine,
937        fa: &FullAttnLayer,
938        activation: &CudaSlice<f32>,
939        tokens: usize,
940    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
941        let Some(tp) = fa.step_tp_qkv.as_ref() else {
942            return Ok(None);
943        };
944        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
945        // of the attention output, no host O staging, root-resident reduction consumed in
946        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
947        if Self::step35_tp_device_resident(e, tp) {
948            e.stream().synchronize()?; // producer fence, as the QKV half
949            let output = tp
950                .runtime
951                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
952            Self::step35_tp_log_once(tp, "o", "device-resident");
953            return Ok(Some(output));
954        }
955        let host = e.dtoh(activation)?;
956        let output = if tp.runtime.native_p2p() {
957            tp.runtime
958                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
959        } else {
960            tp.runtime
961                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
962        };
963        Self::step35_tp_log_once(tp, "o", "host-canonical");
964        Ok(Some(e.htod(&output)?))
965    }
966
967    fn step35_o(
968        &self,
969        e: &Engine,
970        fa: &FullAttnLayer,
971        activation: &CudaSlice<f32>,
972        tokens: usize,
973    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
974        match self.step35_tp_o(e, fa, activation, tokens)? {
975            Some(output) => Ok(output),
976            None => e.matmul(&fa.wo, activation, tokens),
977        }
978    }
979
980    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
981    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
982    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
983    /// (it forces a dtoh + host hash per layer).
984    fn prime_trace_path() -> Option<&'static str> {
985        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
986        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
987            .as_deref()
988    }
989
990    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
991    /// each prime_layers stage and accumulates wall time per stage class, printed after
992    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
993    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
994    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
995    fn prime_anatomy_on() -> bool {
996        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
997        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
998    }
999
1000    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
1001        static S: [std::sync::atomic::AtomicU64; 5] = [
1002            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
1003            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
1004            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
1005            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
1006            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
1007        ];
1008        &S
1009    }
1010
1011    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
1012    pub fn forward(
1013        &self,
1014        e: &Engine,
1015        tokens: &[u32],
1016    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1017        if self.is_gemma4_e4b() {
1018            return self.gemma4_e4b_forward(e, tokens, false);
1019        }
1020        if self.uses_gemma_program() {
1021            return self.gemma4_forward(e, tokens, false);
1022        }
1023        let cfg = &self.cfg;
1024        let n_embd = cfg.n_embd as usize;
1025        let t = tokens.len();
1026        let eps = cfg.rms_eps;
1027        let pos: Vec<i32> = (0..t as i32).collect();
1028        let pos_d = e.htod_i32(&pos)?;
1029
1030        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1031
1032        for (il, layer) in self.layers.iter().enumerate() {
1033            // attn_norm
1034            let mut h = e.uninit(t * n_embd)?;
1035            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1036
1037            let mixed = match &layer.mixer {
1038                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1039                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1040                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1041            };
1042
1043            // residual 1
1044            let mut x1 = e.uninit(t * n_embd)?;
1045            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1046
1047            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1048            let mut z = e.uninit(t * n_embd)?;
1049            e.rms_norm(
1050                &x1,
1051                layer.post_attn_norm.float_data(),
1052                &mut z,
1053                n_embd,
1054                t,
1055                eps,
1056            )?;
1057            let ffn_out = match &layer.ffn {
1058                crate::hybrid::Ffn::Dense {
1059                    ffn_gate,
1060                    ffn_up,
1061                    ffn_down,
1062                } => {
1063                    let n_ff = ffn_gate.out_features();
1064                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1065                    let up = g2.pop().unwrap();
1066                    let gate = g2.pop().unwrap();
1067                    let mut act = e.uninit(t * n_ff)?;
1068                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1069                    // both the dense MLP and the shared expert, and its limit is
1070                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1071                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1072                    Self::ffn_act_lim(
1073                        e,
1074                        &self.cfg,
1075                        &gate,
1076                        &up,
1077                        1.0,
1078                        1.0,
1079                        self.cfg.clamp_shexp_at(il as u32),
1080                        &mut act,
1081                        t * n_ff,
1082                    )?;
1083                    e.matmul(ffn_down, &act, t)?
1084                }
1085                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1086            };
1087            let mut x2 = e.uninit(t * n_embd)?;
1088            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1089            x = x2;
1090        }
1091
1092        let mut hn = e.uninit(t * n_embd)?;
1093        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1094        let logits = e.matmul(&self.output, &hn, t)?;
1095        Ok(e.dtoh(&logits)?)
1096    }
1097
1098    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1099    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1100    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1101    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1102    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1103    pub fn forward_last(
1104        &self,
1105        e: &Engine,
1106        tokens: &[u32],
1107    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1108        if self.uses_gemma_program() {
1109            return self.gemma4_forward(e, tokens, true);
1110        }
1111        let cfg = &self.cfg;
1112        let n_embd = cfg.n_embd as usize;
1113        let t = tokens.len();
1114        let eps = cfg.rms_eps;
1115        let pos: Vec<i32> = (0..t as i32).collect();
1116        let pos_d = e.htod_i32(&pos)?;
1117
1118        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1119        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1120        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1121        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1122        let anat = Self::prime_anatomy_on();
1123        let mut anat_last = if anat {
1124            e.stream().synchronize()?;
1125            Some(std::time::Instant::now())
1126        } else {
1127            None
1128        };
1129        macro_rules! anat_mark {
1130            ($slot:expr) => {
1131                if let Some(ts) = anat_last.as_mut() {
1132                    e.stream().synchronize()?;
1133                    Self::prime_anatomy_slots()[$slot].fetch_add(
1134                        ts.elapsed().as_nanos() as u64,
1135                        std::sync::atomic::Ordering::Relaxed,
1136                    );
1137                    *ts = std::time::Instant::now();
1138                }
1139            };
1140        }
1141        for (il, layer) in self.layers.iter().enumerate() {
1142            let mut h = e.uninit(t * n_embd)?;
1143            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1144            if probe {
1145                e.stream().synchronize()?;
1146                eprintln!("[probe] L{il} norm ok");
1147            }
1148            anat_mark!(4);
1149            let mixed = match &layer.mixer {
1150                Mixer::Full(fa) => {
1151                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1152                    anat_mark!(0);
1153                    y
1154                }
1155                Mixer::Linear(la) => {
1156                    let y = self.linear_attn(e, la, &h, t)?;
1157                    anat_mark!(1);
1158                    y
1159                }
1160                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1161            };
1162            if probe {
1163                e.stream().synchronize()?;
1164                eprintln!("[probe] L{il} mixer ok");
1165            }
1166            let mut x1 = e.uninit(t * n_embd)?;
1167            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1168            let mut z = e.uninit(t * n_embd)?;
1169            e.rms_norm(
1170                &x1,
1171                layer.post_attn_norm.float_data(),
1172                &mut z,
1173                n_embd,
1174                t,
1175                eps,
1176            )?;
1177            anat_mark!(4);
1178            let ffn_out = match &layer.ffn {
1179                crate::hybrid::Ffn::Dense {
1180                    ffn_gate,
1181                    ffn_up,
1182                    ffn_down,
1183                } => {
1184                    let n_ff = ffn_gate.out_features();
1185                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1186                    let up = g2.pop().unwrap();
1187                    let gate = g2.pop().unwrap();
1188                    let mut act = e.uninit(t * n_ff)?;
1189                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1190                    Self::ffn_act_lim(
1191                        e,
1192                        &self.cfg,
1193                        &gate,
1194                        &up,
1195                        1.0,
1196                        1.0,
1197                        self.cfg.clamp_shexp_at(il as u32),
1198                        &mut act,
1199                        t * n_ff,
1200                    )?;
1201                    let y = e.matmul(ffn_down, &act, t)?;
1202                    anat_mark!(3);
1203                    y
1204                }
1205                crate::hybrid::Ffn::Moe(m) => {
1206                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1207                    anat_mark!(2);
1208                    y
1209                }
1210            };
1211            if probe {
1212                e.stream().synchronize()?;
1213                eprintln!("[probe] L{il} ffn ok");
1214            }
1215            let mut x2 = e.uninit(t * n_embd)?;
1216            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1217            x = x2;
1218        }
1219        if anat {
1220            let s = Self::prime_anatomy_slots();
1221            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1222            eprintln!(
1223                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1224                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1225                ms(0),
1226                ms(1),
1227                ms(2),
1228                ms(3),
1229                ms(4)
1230            );
1231        }
1232        // norm over all T, then slice the LAST row and run lm_head on that single row.
1233        let mut hn = e.uninit(t * n_embd)?;
1234        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1235        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1236        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1237        let mut hlast = e.uninit(n_embd)?;
1238        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1239        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1240        Ok(e.dtoh(&logits)?)
1241    }
1242
1243    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1244    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1245    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1246    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1247    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1248    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1249    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1250    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1251    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1252    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1253    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1254    ///   (c) `cache.pos`/KV len/len_d advance by T.
1255    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1256    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1257    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1258    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1259    ///
1260    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1261    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1262    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1263    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1264    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1265    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1266    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1267    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1268    /// differently under load — research/tick-seg-20260807, receipt in
1269    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1270    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1271    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1272    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1273    /// caller that SPLITS one request across calls passes the remainder.
1274    pub fn prime_cache(
1275        &self,
1276        e: &Engine,
1277        tokens: &[u32],
1278        cache: &mut Cache,
1279        queued_after: usize,
1280    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1281        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1282    }
1283
1284    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1285    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1286    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1287    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1288    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1289    pub fn prime_cache_overlaid(
1290        &self,
1291        e: &Engine,
1292        tokens: &[u32],
1293        cache: &mut Cache,
1294        queued_after: usize,
1295        overlay: Option<&crate::vision::EmbedOverlay>,
1296    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1297        let n_embd = self.cfg.n_embd as usize;
1298        let t = tokens.len();
1299        // MEMRA_PRIME_TROWS=1: prefill through the same-session t-row walk (per-row t=1
1300        // program = the tokenwise-prime ORACLE class) — replaces the host-canonical
1301        // per-token step-TP prime. Text-only fresh primes; anything else falls through.
1302        if overlay.is_none() {
1303            if let Some(out) = self.step35_prime_trows(e, tokens, cache)? {
1304                return Ok(out);
1305            }
1306        }
1307        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1308        // session cache — every chunk (including the first) takes the continuation arm
1309        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1310        assert!(
1311            t >= PRIME_MIN_T,
1312            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1313        );
1314        assert!(
1315            cache.pos + t <= cache.max_ctx,
1316            "prime_cache: prompt exceeds cache max_ctx"
1317        );
1318
1319        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1320        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1321        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1322        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1323        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1324        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1325        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1326        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1327        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1328        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1329        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1330        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1331            if self.is_gemma4_e4b() {
1332                if overlay.is_some() {
1333                    return Err(
1334                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1335                    );
1336                }
1337                return self.gemma4_e4b_prime(e, tokens, cache);
1338            }
1339            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1340            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1341            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1342            // spans become bidirectional attention islands (lane/gemma-vision).
1343            return self.gemma4_prime(e, tokens, cache, overlay);
1344        }
1345        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1346        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1347        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1348        // the prefill's ARITHMETIC, so two rigs with different values produced different
1349        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1350        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1351        // (VERDICT.md) — and it is NOT what docs originally said:
1352        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1353        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1354        //     output head), so growing a chunk cannot move an existing row's value.
1355        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1356        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1357        //     not describe our leak.
1358        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1359        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1360        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1361        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1362        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1363        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1364        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1365        // the source — every row is in one numeric class, so the chunk size no longer steers
1366        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1367        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1368        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1369        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1370        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1371        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1372        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1373        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1374        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1375        // across calls, the request still ends at the same absolute position, whatever the tick
1376        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1377        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1378        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1379        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1380        // on in a measured default run.
1381        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1382        let seq_end = if legacy_calllocal {
1383            cache.pos + t
1384        } else {
1385            cache.pos + t + queued_after
1386        };
1387        if ranges.len() == 1 {
1388            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1389        }
1390        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1391        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1392        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1393        // this lane owns the balanced two-stage schedule only.
1394        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1395            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1396                if overlay.is_some() {
1397                    return Err(
1398                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1399                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1400                            .into(),
1401                    );
1402                }
1403                if crate::pp::pp_multi_stream_same_device() {
1404                    return Err(
1405                        "prime chunk pipeline refused with 2 stage streams on one device — \
1406                         that concurrent-stream placement remains quarantined by the deferred \
1407                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1408                         the serial split."
1409                            .into(),
1410                    );
1411                }
1412                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1413            }
1414        }
1415        let mut hiddens = e.uninit(t * n_embd)?;
1416        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1417        for &(start, end) in &ranges {
1418            // chunked prime writes tap rows at the chunk's absolute offset
1419            if let Some(taps) = cache.dflash_taps.as_mut() {
1420                taps.base = start;
1421            }
1422            let (l, hs, x) =
1423                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1424            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1425            last = Some((l, hs));
1426        }
1427        let (logits, h_seed) = last.unwrap();
1428        Ok((logits, h_seed, hiddens))
1429    }
1430
1431    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1432    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1433    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1434    /// norm, lm head, and caller hidden-stack copy as the serial split.
1435    fn prime_cache_pp2_pipelined(
1436        &self,
1437        e: &Engine,
1438        tokens: &[u32],
1439        cache: &mut Cache,
1440        seq_end: usize,
1441        ranges: &[(usize, usize)],
1442        fence: &[usize],
1443    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1444        debug_assert_eq!(fence.len(), 3);
1445        debug_assert!(ranges.len() >= 2);
1446        let rt = crate::pp::PpNRt::get(e)?;
1447        assert_eq!(
1448            rt.n_stages(),
1449            2,
1450            "prime pipeline requires exactly two PP stages"
1451        );
1452        let n_embd = self.cfg.n_embd as usize;
1453        let t = tokens.len();
1454        let initial_base = cache.pos;
1455        let caller_stream = e.stream();
1456
1457        // #87 reverse publication before any new stage allocation, then prewarm both
1458        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1459        // after stage 1(N) is queued would synchronize that stream and erase the first
1460        // overlap on a two-chunk prompt.
1461        rt.fence_stages_behind(&caller_stream)?;
1462        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1463        rt.prepare_overlap_slots(0, max_payload)?;
1464
1465        let mut hiddens = e.uninit(t * n_embd)?;
1466        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1467        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1468        let (cache0, cache1) = stage_caches.parts();
1469        let (first_start, first_end) = ranges[0];
1470        let mut slot = self.prime_pp2_stage0_enqueue(
1471            e,
1472            rt,
1473            &tokens[first_start..first_end],
1474            cache0,
1475            seq_end,
1476            fence,
1477            initial_base + first_start,
1478            true,
1479        )?;
1480        cache0.pos = initial_base + first_end;
1481
1482        for (i, &(start, end)) in ranges.iter().enumerate() {
1483            let base = initial_base + start;
1484            debug_assert_eq!(
1485                cache1.pos, base,
1486                "stage 1 must drain chunks in original position order"
1487            );
1488            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1489                let next_base = initial_base + next_start;
1490                debug_assert_eq!(
1491                    cache0.pos, next_base,
1492                    "stage 0 must issue chunks in original position order"
1493                );
1494                let cache0_stage = &mut *cache0;
1495                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1496                // on one host thread therefore serialize even if the calls are ordered as
1497                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1498                // stage 1 consumes slot N while stage 0 produces slot N+1.
1499                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1500                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1501                        let next = self
1502                            .prime_pp2_stage0_enqueue(
1503                                e,
1504                                rt,
1505                                &tokens[next_start..next_end],
1506                                cache0_stage,
1507                                seq_end,
1508                                fence,
1509                                next_base,
1510                                true,
1511                            )
1512                            .map_err(|err| err.to_string())?;
1513                        cache0_stage.pos = initial_base + next_end;
1514                        Ok(next)
1515                    });
1516                    let x = self.prime_pp2_stage1_enqueue(
1517                        e,
1518                        rt,
1519                        slot,
1520                        end - start,
1521                        cache1,
1522                        seq_end,
1523                        fence,
1524                        base,
1525                        true,
1526                    )?;
1527                    let out = {
1528                        rt.bind_stage(1)?;
1529                        let _st1 = rt.enter(1);
1530                        let e1 = rt.engine(1, e);
1531                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1532                    };
1533                    let next = stage0
1534                        .join()
1535                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1536                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1537                    Ok((out, Some(next)))
1538                })?
1539            } else {
1540                let x = self.prime_pp2_stage1_enqueue(
1541                    e,
1542                    rt,
1543                    slot,
1544                    end - start,
1545                    cache1,
1546                    seq_end,
1547                    fence,
1548                    base,
1549                    true,
1550                )?;
1551                let out = {
1552                    rt.bind_stage(1)?;
1553                    let _st1 = rt.enter(1);
1554                    let e1 = rt.engine(1, e);
1555                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1556                };
1557                (out, None)
1558            };
1559
1560            rt.publish_to(1, &caller_stream)?;
1561            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1562            last = Some((out.0, out.1));
1563            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1564
1565            if let Some(next) = next_slot {
1566                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1567                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1568                // Stage 0(N+1) is already queued before this wait is appended, so its
1569                // overlap with stage 1(N) is preserved.
1570                rt.fence_stages_behind(&caller_stream)?;
1571                slot = next;
1572            }
1573        }
1574
1575        debug_assert_eq!(cache0.pos, initial_base + t);
1576        debug_assert_eq!(cache1.pos, initial_base + t);
1577        let (logits, h_seed) = last.unwrap();
1578        Ok((logits, h_seed, hiddens))
1579    }
1580
1581    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1582    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1583    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1584    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1585    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1586    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1587    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1588        if Engine::gdn_db_on()
1589            && Engine::gdn_chunked_enabled()
1590            && t >= 16
1591            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1592            && num_k * 2 == num_v
1593        {
1594            num_k
1595        } else {
1596            num_v
1597        }
1598    }
1599
1600    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1601    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1602    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1603    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1604    fn f16out_on(e: &Engine, t: usize) -> bool {
1605        crate::f16_ffi::pp_f16_enabled()
1606            && t >= 16
1607            && !e.verify_exact_on()
1608            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1609    }
1610
1611    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1612    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1613    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1614    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1615    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1616    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1617    /// see one entry, byte-identical behavior.
1618    pub fn prime_slabs_get(
1619        &self,
1620        e: &Engine,
1621        t: usize,
1622        n_embd: usize,
1623        n_ff_max: usize,
1624    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1625        let mut slabs = self.prime_slabs.lock().unwrap();
1626        let dev = e.ctx().ordinal();
1627        let need_new = match slabs.get(&dev) {
1628            None => true,
1629            Some(sl) => sl.lock().unwrap().t_cap < t,
1630        };
1631        if need_new {
1632            slabs.insert(
1633                dev,
1634                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1635                    t_cap: t,
1636                    h: e.uninit(t * n_embd)?,
1637                    x1: e.uninit(t * n_embd)?,
1638                    z: e.uninit(t * n_embd)?,
1639                    act: e.uninit(t * n_ff_max)?,
1640                    xa: e.uninit(t * n_embd)?,
1641                    xb: e.uninit(t * n_embd)?,
1642                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1643                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1644                    gate: e.uninit(t * n_ff_max)?,
1645                    up: e.uninit(t * n_ff_max)?,
1646                    ffn_out: e.uninit(t * n_embd)?,
1647                    seg_glue: Vec::new(),
1648                    mixed: e.uninit(t * n_embd)?,
1649                    seg_mid: Vec::new(),
1650                    seg_t: 0,
1651                })),
1652            );
1653        }
1654        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1655    }
1656
1657    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1658    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1659    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1660    fn prime_chunk(
1661        &self,
1662        e: &Engine,
1663        tokens: &[u32],
1664        cache: &mut Cache,
1665        seq_end: usize,
1666        chunk_off: usize,
1667        overlay: Option<&crate::vision::EmbedOverlay>,
1668    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1669        if crate::pp::pp_host_bounce_active()
1670            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1671        {
1672            return Err(
1673                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1674                 has no active prime stage split and would peer-read remote weights; keep \
1675                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1676                    .into(),
1677            );
1678        }
1679        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1680        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1681        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1682        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1683        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1684        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1685        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1686        // loader is off and there is nothing remote to split for.
1687        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1688            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1689                if overlay.is_some() {
1690                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1691                         run single-device or MEMRA_PRIME_PP=0"
1692                        .into());
1693                }
1694                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1695            }
1696        }
1697        if crate::pp::pp_host_bounce_active() {
1698            return Err(
1699                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1700                 refusing an unsplit remote-weight walk"
1701                    .into(),
1702            );
1703        }
1704        let t = tokens.len();
1705        let base = cache.pos;
1706        debug_assert!(
1707            seq_end >= base + t,
1708            "prime_chunk: seq_end must cover this chunk"
1709        );
1710        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1711        let pos_d = e.htod_i32(&pos)?;
1712
1713        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1714        if let Some(ov) = overlay {
1715            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1716            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1717            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1718            let n_embd = self.cfg.n_embd as usize;
1719            for &(pos, row_off, n_rows) in &ov.spans {
1720                let lo = pos.max(chunk_off);
1721                let hi = (pos + n_rows).min(chunk_off + t);
1722                if lo < hi {
1723                    let src_row = row_off + (lo - pos);
1724                    let view = ov
1725                        .rows
1726                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1727                    e.copy_view_into(
1728                        &mut x_embed,
1729                        (lo - chunk_off) * n_embd,
1730                        &view,
1731                        (hi - lo) * n_embd,
1732                    )?;
1733                }
1734            }
1735        }
1736        let x = self.prime_layers(
1737            e,
1738            x_embed,
1739            0,
1740            self.layers.len(),
1741            &pos_d,
1742            t,
1743            base,
1744            cache,
1745            seq_end,
1746        )?;
1747        self.prime_chunk_epilogue(e, x, t, cache)
1748    }
1749
1750    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1751    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1752    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1753    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1754    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1755    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1756    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1757    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1758    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1759    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1760    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1761    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1762    ///     each stage walks through its own resident transients;
1763    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1764    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1765    #[allow(clippy::too_many_arguments)]
1766    fn prime_layers(
1767        &self,
1768        e: &Engine,
1769        x_in: CudaSlice<f32>,
1770        lo: usize,
1771        hi: usize,
1772        pos_d: &CudaSlice<i32>,
1773        t: usize,
1774        base: usize,
1775        cache: &mut Cache,
1776        seq_end: usize,
1777    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1778        let cfg = &self.cfg;
1779        let n_embd = cfg.n_embd as usize;
1780        let eps = cfg.rms_eps;
1781        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1782        // standalone convert launches). Only when the f16 lane serves and T reaches the
1783        // GEMM tier; bit-identical either way.
1784        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1785        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1786        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1787        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1788        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1789        // capacity tail must stay behind checked views. The hidden-stack return clones the
1790        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1791        let n_ff_max = self
1792            .layers
1793            .iter()
1794            .map(|l| match &l.ffn {
1795                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1796                _ => n_embd,
1797            })
1798            .max()
1799            .unwrap_or(n_embd)
1800            .max(n_embd);
1801        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1802        let slab = if use_slabs {
1803            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1804        } else {
1805            None
1806        };
1807        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1808        let mut x_own; // fallback storage when slabs are off
1809        type SlabRefs<'a> = (
1810            &'a mut CudaSlice<f32>,
1811            &'a mut CudaSlice<f32>,
1812            &'a mut CudaSlice<f32>,
1813            &'a mut CudaSlice<f32>,
1814            &'a mut CudaSlice<u8>,
1815            &'a mut CudaSlice<u8>,
1816            &'a mut CudaSlice<f32>,
1817            &'a mut CudaSlice<f32>,
1818            &'a mut CudaSlice<f32>,
1819        );
1820        let (mut x_cur, mut x_nxt, sl): (
1821            &mut CudaSlice<f32>,
1822            &mut CudaSlice<f32>,
1823            Option<SlabRefs>,
1824        );
1825        let mut seg: Option<(
1826            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1827            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1828            &mut CudaSlice<f32>,
1829            &mut usize,
1830        )> = None;
1831        let mut x_own2;
1832        match slab_guard.as_mut() {
1833            Some(g) => {
1834                let slabs = &mut **g;
1835                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1836                let PrimeSlabs {
1837                    xa,
1838                    xb,
1839                    h,
1840                    x1,
1841                    z,
1842                    act,
1843                    h16,
1844                    z16,
1845                    gate,
1846                    up,
1847                    ffn_out,
1848                    seg_glue,
1849                    mixed,
1850                    seg_mid,
1851                    seg_t,
1852                    ..
1853                } = slabs;
1854                x_cur = xa;
1855                x_nxt = xb;
1856                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1857                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1858            }
1859            None => {
1860                x_own = x_in;
1861                x_own2 = e.uninit(t * n_embd)?;
1862                x_cur = &mut x_own;
1863                x_nxt = &mut x_own2;
1864                sl = None;
1865            }
1866        }
1867        let mut alloc_h;
1868        let mut alloc_x1;
1869        let mut alloc_z;
1870        let mut alloc_act;
1871        let mut alloc_h16;
1872        let mut alloc_z16;
1873        let mut alloc_gate;
1874        let mut alloc_up;
1875        let mut alloc_fo;
1876        let (h, x1, z, act): (
1877            &mut CudaSlice<f32>,
1878            &mut CudaSlice<f32>,
1879            &mut CudaSlice<f32>,
1880            &mut CudaSlice<f32>,
1881        );
1882        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
1883        let (sl_gate, sl_up, sl_fo): (
1884            &mut CudaSlice<f32>,
1885            &mut CudaSlice<f32>,
1886            &mut CudaSlice<f32>,
1887        );
1888        match sl {
1889            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
1890                h = a;
1891                x1 = b;
1892                z = c;
1893                act = d;
1894                h16 = e16;
1895                z16 = f16b;
1896                sl_gate = g;
1897                sl_up = u;
1898                sl_fo = fo;
1899            }
1900            None => {
1901                alloc_h = e.uninit(t * n_embd)?;
1902                alloc_x1 = e.uninit(t * n_embd)?;
1903                alloc_z = e.uninit(t * n_embd)?;
1904                alloc_act = e.uninit(t * n_ff_max)?;
1905                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1906                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1907                alloc_gate = e.uninit(t * n_ff_max)?;
1908                alloc_up = e.uninit(t * n_ff_max)?;
1909                alloc_fo = e.uninit(t * n_embd)?;
1910                h = &mut alloc_h;
1911                x1 = &mut alloc_x1;
1912                z = &mut alloc_z;
1913                act = &mut alloc_act;
1914                h16 = &mut alloc_h16;
1915                z16 = &mut alloc_z16;
1916                sl_gate = &mut alloc_gate;
1917                sl_up = &mut alloc_up;
1918                sl_fo = &mut alloc_fo;
1919            }
1920        }
1921        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
1922        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
1923        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
1924        // first prime at this t (capture does not execute -> launch right after).
1925        let n_layers = self.layers.len();
1926        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
1927        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
1928        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
1929        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
1930        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
1931        // machinery stays (byte-identical) as their foundation.
1932        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
1933        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
1934        // step35 rides its own mixer through the normal per-layer arm below.
1935        let use_seg = f16fuse
1936            && seg.is_some()
1937            && !self.uses_sliding_gated_moe_program()
1938            && lo == 0
1939            && hi == n_layers
1940            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
1941        if let Some((sg, sm, _, st)) = seg.as_mut() {
1942            if **st != t {
1943                sg.clear();
1944                sg.extend((0..n_layers).map(|_| None));
1945                sm.clear();
1946                sm.extend((0..n_layers).map(|_| None));
1947                **st = t;
1948            }
1949        }
1950        {
1951            let layer_lo = &self.layers[lo];
1952            if f16fuse {
1953                e.rms_norm_f16out(
1954                    x_cur,
1955                    layer_lo.attn_norm.float_data(),
1956                    h,
1957                    h16,
1958                    n_embd,
1959                    t,
1960                    eps,
1961                )?;
1962            } else {
1963                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
1964            }
1965        }
1966        let anat = Self::prime_anatomy_on();
1967        let mut anat_last = if anat {
1968            e.stream().synchronize()?;
1969            Some(std::time::Instant::now())
1970        } else {
1971            None
1972        };
1973        // Closes the region that just ENDED into `slot`, restarting the clock.
1974        macro_rules! anat_mark {
1975            ($slot:expr) => {
1976                if let Some(ts) = anat_last.as_mut() {
1977                    e.stream().synchronize()?;
1978                    Self::prime_anatomy_slots()[$slot].fetch_add(
1979                        ts.elapsed().as_nanos() as u64,
1980                        std::sync::atomic::Ordering::Relaxed,
1981                    );
1982                    *ts = std::time::Instant::now();
1983                }
1984            };
1985        }
1986        for il in lo..hi {
1987            let layer = &self.layers[il];
1988            let hx16 = if f16fuse { Some(&*h16) } else { None };
1989            if use_seg {
1990                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
1991                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
1992                let (pre, pre16, w_out) = match &layer.mixer {
1993                    Mixer::Full(fa) => {
1994                        let g3 = match hx16 {
1995                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
1996                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
1997                        };
1998                        let (pre, pre16) =
1999                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
2000                        (pre, pre16, &fa.wo)
2001                    }
2002                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2003                    Mixer::Linear(la) => {
2004                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2005                        let g4 = match hx16 {
2006                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
2007                            None => e.matmul_group(&ws, h, t)?,
2008                        };
2009                        let (pre, pre16) =
2010                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
2011                        (pre, pre16, &la.ssm_out)
2012                    }
2013                };
2014                {
2015                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
2016                    let pre_n = pre.len() / t;
2017                    let xh_pre = match pre16 {
2018                        Some(x) => x,
2019                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
2020                    };
2021                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
2022                        let y = e.matmul(w_out, &pre, t)?;
2023                        e.copy_into(mslab, 0, &y, t * n_embd)?;
2024                    }
2025                    if sm[il].is_none() {
2026                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2027                        let w_post = layer.post_attn_norm.float_data();
2028                        e.stream().synchronize()?;
2029                        e.stream()
2030                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2031                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2032                            e.add(x_cur, mslab, x1, t * n_embd)?;
2033                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2034                            Ok(())
2035                        })();
2036                        let g = e.stream().end_capture(
2037                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2038                        r?;
2039                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2040                    }
2041                    sm[il].as_ref().unwrap().launch()?;
2042                }
2043            } else {
2044                let mixed = match &layer.mixer {
2045                    Mixer::Full(fa) => {
2046                        let y =
2047                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2048                        anat_mark!(0);
2049                        y
2050                    }
2051                    Mixer::Linear(la) => {
2052                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2053                        anat_mark!(1);
2054                        y
2055                    }
2056                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2057                };
2058                if f16fuse {
2059                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2060                    // bit-identical) — the standalone add pass disappears.
2061                    e.add_rms_norm_f16out(
2062                        x_cur,
2063                        &mixed,
2064                        layer.post_attn_norm.float_data(),
2065                        x1,
2066                        z,
2067                        z16,
2068                        n_embd,
2069                        t,
2070                        eps,
2071                    )?;
2072                } else {
2073                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2074                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2075                }
2076                anat_mark!(4);
2077            }
2078            let zx16 = if f16fuse { Some(&*z16) } else { None };
2079            match &layer.ffn {
2080                crate::hybrid::Ffn::Dense {
2081                    ffn_gate,
2082                    ffn_up,
2083                    ffn_down,
2084                } => {
2085                    let n_ff = ffn_gate.out_features();
2086                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2087                    // the allocating group + copy when a mirror is missing.
2088                    let mut into_ok = false;
2089                    if let Some(xh) = zx16 {
2090                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2091                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2092                    }
2093                    if !into_ok {
2094                        let mut g2 = match zx16 {
2095                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2096                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2097                        };
2098                        let up_y = g2.pop().unwrap();
2099                        let gate_y = g2.pop().unwrap();
2100                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2101                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2102                    }
2103                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2104                    // operand in-epilogue; non-silu activations keep the standalone convert.
2105                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2106                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2107                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2108                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2109                    {
2110                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2111                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2112                        Some(a16)
2113                    } else {
2114                        Self::ffn_act_lim(
2115                            e,
2116                            &self.cfg,
2117                            sl_gate,
2118                            sl_up,
2119                            1.0,
2120                            1.0,
2121                            d_lim,
2122                            act,
2123                            t * n_ff,
2124                        )?;
2125                        None
2126                    };
2127                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2128                    let xh_act = match act16 {
2129                        Some(x) => x,
2130                        None => e.f16_act(act, t * n_ff, n_ff)?,
2131                    };
2132                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2133                        let y = e.matmul(ffn_down, &*act, t)?;
2134                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2135                    }
2136                }
2137                crate::hybrid::Ffn::Moe(m) => {
2138                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2139                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2140                    anat_mark!(2);
2141                }
2142            }
2143            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2144                anat_mark!(3);
2145            }
2146            if use_seg && il + 1 < hi {
2147                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2148                let w_next = self.layers[il + 1].attn_norm.float_data();
2149                let (sg, _, _, _) = seg.as_mut().unwrap();
2150                if sg[il].is_none() {
2151                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2152                    e.stream().synchronize()?;
2153                    e.stream()
2154                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2155                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2156                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2157                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2158                        Ok(())
2159                    })();
2160                    let g = e.stream().end_capture(
2161                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2162                    );
2163                    r?;
2164                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2165                }
2166                sg[il].as_ref().unwrap().launch()?;
2167            } else {
2168                if il + 1 < hi {
2169                    let w_next = self.layers[il + 1].attn_norm.float_data();
2170                    if f16fuse {
2171                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2172                    } else {
2173                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2174                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2175                    }
2176                } else {
2177                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2178                }
2179            }
2180            anat_mark!(4);
2181            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2182            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2183            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2184            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2185            // unset (the default) costs one OnceLock read per layer.
2186            if let Some(path) = Self::prime_trace_path() {
2187                let row = (base + t - 1) as usize;
2188                let host = e.dtoh(x_nxt)?;
2189                let last = &host[(t - 1) * n_embd..t * n_embd];
2190                use std::io::Write as _;
2191                let mut f = std::fs::OpenOptions::new()
2192                    .create(true)
2193                    .append(true)
2194                    .open(path)?;
2195                let mut h64: u64 = 0xcbf29ce484222325;
2196                for v in last {
2197                    h64 ^= v.to_bits() as u64;
2198                    h64 = h64.wrapping_mul(0x100000001b3);
2199                }
2200                writeln!(
2201                    f,
2202                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2203                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2204                    last[0], last[1], last[2]
2205                )?;
2206            }
2207            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2208            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2209            self.dflash_tap(e, cache, il, x_nxt, t)?;
2210            std::mem::swap(&mut x_cur, &mut x_nxt);
2211        }
2212        if anat {
2213            let s = Self::prime_anatomy_slots();
2214            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2215            eprintln!(
2216                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2217                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2218                ms(0),
2219                ms(1),
2220                ms(2),
2221                ms(3),
2222                ms(4)
2223            );
2224        }
2225        // hidden-stack return: clone the final x out of the slab
2226        let mut x = e.uninit(t * n_embd)?;
2227        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2228        drop(slab_guard);
2229        Ok(x)
2230    }
2231
2232    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2233    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2234    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2235    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2236    fn prime_chunk_epilogue(
2237        &self,
2238        e: &Engine,
2239        x: CudaSlice<f32>,
2240        t: usize,
2241        cache: &mut Cache,
2242    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2243        let n_embd = self.cfg.n_embd as usize;
2244        let eps = self.cfg.rms_eps;
2245        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2246        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2247        // the post-norm copy happens after hn exists).
2248        let mut h_seed = e.uninit(n_embd)?;
2249        if !crate::spec::spec_hpost() {
2250            e.copy_view_into(
2251                &mut h_seed,
2252                0,
2253                &x.slice((t - 1) * n_embd..t * n_embd),
2254                n_embd,
2255            )?;
2256        }
2257        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2258        let mut hn = e.uninit(t * n_embd)?;
2259        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2260        if crate::spec::spec_hpost() {
2261            e.copy_view_into(
2262                &mut h_seed,
2263                0,
2264                &hn.slice((t - 1) * n_embd..t * n_embd),
2265                n_embd,
2266            )?;
2267        }
2268        let last = e.view(&hn, t * n_embd);
2269        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2270        let mut hlast = e.uninit(n_embd)?;
2271        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2272        let logits = e.matmul(&self.output, &hlast, 1)?;
2273        cache.pos += t;
2274        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2275        // post-norm stack hn (MEMRA_SPEC_HPOST).
2276        Ok((
2277            e.dtoh(&logits)?,
2278            h_seed,
2279            if crate::spec::spec_hpost() { hn } else { x },
2280        ))
2281    }
2282
2283    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2284    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2285    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2286    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2287    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2288    /// prefill kernels. Structure mirrors the verify split exactly:
2289    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2290    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2291    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2292    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2293    ///                  there via the sharded loader) → `publish_to`
2294    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2295    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2296    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2297    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2298    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2299    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2300    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2301    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2302    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2303    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2304    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2305    /// and its liveness counter is bumped here — the gate goes green with this function.
2306    fn prime_chunk_ppn(
2307        &self,
2308        e: &Engine,
2309        tokens: &[u32],
2310        cache: &mut Cache,
2311        seq_end: usize,
2312        fence: &[usize],
2313    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2314        let rt = crate::pp::PpNRt::get(e)?;
2315        let n_st = fence.len() - 1;
2316        assert_eq!(
2317            rt.n_stages(),
2318            n_st,
2319            "PpNRt stage count {} != fence stages {n_st}",
2320            rt.n_stages()
2321        );
2322        let n_embd = self.cfg.n_embd as usize;
2323        let t = tokens.len();
2324        let base = cache.pos;
2325        debug_assert!(
2326            seq_end >= base + t,
2327            "prime_chunk_ppn: seq_end must cover this chunk"
2328        );
2329        let payload = t * n_embd;
2330        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2331        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2332        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2333        let caller_stream = e.stream();
2334        rt.fence_stages_behind(&caller_stream)?;
2335
2336        if n_st == 2 {
2337            let slot =
2338                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2339            let x =
2340                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2341            let out = {
2342                rt.bind_stage(1)?;
2343                let _st1 = rt.enter(1);
2344                let e1 = rt.engine(1, e);
2345                self.prime_chunk_epilogue(e1, x, t, cache)?
2346            };
2347            rt.publish_to(1, &caller_stream)?;
2348            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2349            return Ok(out);
2350        }
2351
2352        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2353
2354        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2355        let mut slot = {
2356            let _st0 = rt.enter(0);
2357            let e0 = rt.engine(0, e);
2358            let pos_d = e0.htod_i32(&pos)?;
2359            let x = self.embed(e0, tokens)?;
2360            let x =
2361                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2362            rt.tx(0, &x, payload)?
2363            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2364        };
2365
2366        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2367        for s in 1..n_st - 1 {
2368            let _st = rt.enter(s);
2369            let es = rt.engine(s, e);
2370            let pos_d = es.htod_i32(&pos)?;
2371            let x = rt.rx(s - 1, slot, payload)?;
2372            let x = self.prime_layers(
2373                es,
2374                x,
2375                fence[s],
2376                fence[s + 1],
2377                &pos_d,
2378                t,
2379                base,
2380                cache,
2381                seq_end,
2382            )?;
2383            slot = rt.tx(s, &x, payload)?;
2384        }
2385
2386        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2387        let _stl = rt.enter(n_st - 1);
2388        let el = rt.engine(n_st - 1, e);
2389        let pos_d = el.htod_i32(&pos)?;
2390        let x = rt.rx(n_st - 2, slot, payload)?;
2391        let x = self.prime_layers(
2392            el,
2393            x,
2394            fence[n_st - 1],
2395            fence[n_st],
2396            &pos_d,
2397            t,
2398            base,
2399            cache,
2400            seq_end,
2401        )?;
2402        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2403        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2404        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2405        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2406        // stage stream host-side, but the law is stated in events, not in a dtoh side
2407        // effect a later deferred form would remove.
2408        rt.publish_to(n_st - 1, &caller_stream)?;
2409        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2410        Ok(out)
2411    }
2412
2413    fn prime_pp2_stage0_enqueue(
2414        &self,
2415        e: &Engine,
2416        rt: &crate::pp::PpNRt,
2417        tokens: &[u32],
2418        cache: &mut Cache,
2419        seq_end: usize,
2420        fence: &[usize],
2421        base: usize,
2422        pipelined: bool,
2423    ) -> Result<usize, Box<dyn std::error::Error>> {
2424        let t = tokens.len();
2425        let n_embd = self.cfg.n_embd as usize;
2426        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2427        rt.bind_stage(0)?;
2428        let _st0 = rt.enter(0);
2429        let e0 = rt.engine(0, e);
2430        let pos_d = e0.htod_i32(&pos)?;
2431        let x = self.embed(e0, tokens)?;
2432        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2433        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2434        if pipelined {
2435            rt.tx_pipelined(0, &x, t * n_embd)
2436        } else {
2437            rt.tx(0, &x, t * n_embd)
2438        }
2439    }
2440
2441    fn prime_pp2_stage1_enqueue(
2442        &self,
2443        e: &Engine,
2444        rt: &crate::pp::PpNRt,
2445        slot: usize,
2446        t: usize,
2447        cache: &mut Cache,
2448        seq_end: usize,
2449        fence: &[usize],
2450        base: usize,
2451        pipelined: bool,
2452    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2453        let n_embd = self.cfg.n_embd as usize;
2454        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2455        rt.bind_stage(1)?;
2456        let _st1 = rt.enter(1);
2457        let e1 = rt.engine(1, e);
2458        let pos_d = e1.htod_i32(&pos)?;
2459        let x = rt.rx(0, slot, t * n_embd)?;
2460        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2461        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2462    }
2463
2464    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2465    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2466    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2467    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2468    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2469    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2470    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2471    /// bookkeeping still runs on the host per call — the real replay path moves the write
2472    /// slot to the len_d device counter (increment 3).
2473    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2474    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2475    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2476    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2477    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2478    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2479    pub fn prime_chunk_captured(
2480        &self,
2481        e: &Engine,
2482        x_in: &CudaSlice<f32>,
2483        pos_d: &CudaSlice<i32>,
2484        t: usize,
2485        cache: &mut Cache,
2486        len_d: &CudaSlice<i32>,
2487        logits_out: &mut CudaSlice<f32>,
2488        h_seed_out: &mut CudaSlice<f32>,
2489    ) -> Result<(), Box<dyn std::error::Error>> {
2490        let cfg = &self.cfg;
2491        let n_embd = cfg.n_embd as usize;
2492        let eps = cfg.rms_eps;
2493        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2494        let mut x = e.uninit(t * n_embd)?;
2495        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2496        for (il, layer) in self.layers.iter().enumerate() {
2497            let mut h = e.uninit(t * n_embd)?;
2498            let mut hx16: Option<CudaSlice<u8>> = None;
2499            if f16fuse {
2500                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2501                e.rms_norm_f16out(
2502                    &x,
2503                    layer.attn_norm.float_data(),
2504                    &mut h,
2505                    &mut b16,
2506                    n_embd,
2507                    t,
2508                    eps,
2509                )?;
2510                hx16 = Some(b16);
2511            } else {
2512                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2513            }
2514            let mixed = match &layer.mixer {
2515                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2516                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2517                // come from the caller (see step35_attn_pre_wo's doc note).
2518                Mixer::Full(fa) => {
2519                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2520                }
2521                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2522                Mixer::Linear(la) => {
2523                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2524                    let g4 = match hx16.as_ref() {
2525                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2526                        None => e.matmul_group(&ws, &h, t)?,
2527                    };
2528                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2529                }
2530            };
2531            let mut x1 = e.uninit(t * n_embd)?;
2532            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2533            let mut z = e.uninit(t * n_embd)?;
2534            let mut zx16: Option<CudaSlice<u8>> = None;
2535            if f16fuse {
2536                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2537                e.rms_norm_f16out(
2538                    &x1,
2539                    layer.post_attn_norm.float_data(),
2540                    &mut z,
2541                    &mut b16,
2542                    n_embd,
2543                    t,
2544                    eps,
2545                )?;
2546                zx16 = Some(b16);
2547            } else {
2548                e.rms_norm(
2549                    &x1,
2550                    layer.post_attn_norm.float_data(),
2551                    &mut z,
2552                    n_embd,
2553                    t,
2554                    eps,
2555                )?;
2556            }
2557            let ffn_out = match &layer.ffn {
2558                crate::hybrid::Ffn::Dense {
2559                    ffn_gate,
2560                    ffn_up,
2561                    ffn_down,
2562                } => {
2563                    let n_ff = ffn_gate.out_features();
2564                    let mut g2 = match &zx16 {
2565                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2566                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2567                    };
2568                    let up = g2.pop().unwrap();
2569                    let gate = g2.pop().unwrap();
2570                    let mut act = e.uninit(t * n_ff)?;
2571                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2572                    Self::ffn_act_lim(
2573                        e,
2574                        &self.cfg,
2575                        &gate,
2576                        &up,
2577                        1.0,
2578                        1.0,
2579                        self.cfg.clamp_shexp_at(il as u32),
2580                        &mut act,
2581                        t * n_ff,
2582                    )?;
2583                    e.matmul(ffn_down, &act, t)?
2584                }
2585                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2586            };
2587            let mut x2 = e.uninit(t * n_embd)?;
2588            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2589            x = x2;
2590        }
2591        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2592        if !crate::spec::spec_hpost() {
2593            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2594        }
2595        let mut hn = e.uninit(t * n_embd)?;
2596        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2597        if crate::spec::spec_hpost() {
2598            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2599        }
2600        let mut hlast = e.uninit(n_embd)?;
2601        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2602        let logits = e.matmul(&self.output, &hlast, 1)?;
2603        let nv = logits.len();
2604        e.copy_into(logits_out, 0, &logits, nv)?;
2605        Ok(())
2606    }
2607
2608    fn step35_prime_batch_on() -> bool {
2609        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2610    }
2611
2612    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2613    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2614    #[allow(clippy::too_many_arguments)]
2615    fn step35_prime_batch_layers(
2616        &self,
2617        e: &Engine,
2618        mut x: CudaSlice<f32>,
2619        lo: usize,
2620        hi: usize,
2621        ts: &[usize],
2622        offs: &[usize],
2623        pos_ds: &[CudaSlice<i32>],
2624        caches: &mut [&mut Cache],
2625    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2626        let cfg = &self.cfg;
2627        let n_embd = cfg.n_embd as usize;
2628        let eps = cfg.rms_eps;
2629        let b = ts.len();
2630        let total: usize = ts.iter().sum();
2631        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2632
2633        let split = |e: &Engine,
2634                     y: &CudaSlice<f32>,
2635                     dim: usize|
2636         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2637            let mut out = Vec::with_capacity(b);
2638            for s in 0..b {
2639                let mut ys = e.uninit(ts[s] * dim)?;
2640                e.copy_view_into(
2641                    &mut ys,
2642                    0,
2643                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2644                    ts[s] * dim,
2645                )?;
2646                out.push(ys);
2647            }
2648            Ok(out)
2649        };
2650
2651        for il in lo..hi {
2652            let layer = &self.layers[il];
2653            let Mixer::Full(fa) = &layer.mixer else {
2654                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2655            };
2656
2657            let mut h = e.uninit(total * n_embd)?;
2658            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2659            if f16fuse {
2660                e.rms_norm_f16out(
2661                    &x,
2662                    layer.attn_norm.float_data(),
2663                    &mut h,
2664                    &mut hx16,
2665                    n_embd,
2666                    total,
2667                    eps,
2668                )?;
2669            } else {
2670                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2671            }
2672
2673            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2674            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2675            // application stay verbatim.
2676            let gate_w = fa
2677                .attn_gate
2678                .as_ref()
2679                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2680            let mut g4 = if f16fuse {
2681                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2682            } else {
2683                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2684            };
2685            let gate = g4.pop().unwrap();
2686            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2687                (0..b).map(|_| Vec::with_capacity(3)).collect();
2688            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2689                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2690                    parts[s].push(ys);
2691                }
2692            }
2693            let gates = split(e, &gate, gate_w.out_features())?;
2694            let geometry = self.step35_geom(il);
2695            let hd = geometry.head_dim_k as usize;
2696            let nh = geometry.n_head as usize;
2697            let mut ag_cat = e.uninit(total * nh * hd)?;
2698            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2699                let ag = self.step35_attn_pre_wo(
2700                    e,
2701                    fa,
2702                    g3s,
2703                    None,
2704                    Some(&gate),
2705                    &pos_ds[s],
2706                    ts[s],
2707                    Some(&mut *caches[s]),
2708                    il,
2709                    ts[s],
2710                )?;
2711                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2712            }
2713            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2714
2715            let mut x1 = e.uninit(total * n_embd)?;
2716            let mut z = e.uninit(total * n_embd)?;
2717            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2718            if f16fuse {
2719                e.add_rms_norm_f16out(
2720                    &x,
2721                    &mixed,
2722                    layer.post_attn_norm.float_data(),
2723                    &mut x1,
2724                    &mut z,
2725                    &mut zx16,
2726                    n_embd,
2727                    total,
2728                    eps,
2729                )?;
2730            } else {
2731                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2732                e.rms_norm(
2733                    &x1,
2734                    layer.post_attn_norm.float_data(),
2735                    &mut z,
2736                    n_embd,
2737                    total,
2738                    eps,
2739                )?;
2740            }
2741
2742            let ffn_out = match &layer.ffn {
2743                crate::hybrid::Ffn::Dense {
2744                    ffn_gate,
2745                    ffn_up,
2746                    ffn_down,
2747                } => {
2748                    let n_ff = ffn_gate.out_features();
2749                    let mut g2 = if f16fuse {
2750                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2751                    } else {
2752                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2753                    };
2754                    let up = g2.pop().unwrap();
2755                    let gate = g2.pop().unwrap();
2756                    let mut act = e.uninit(total * n_ff)?;
2757                    let d_lim = cfg.clamp_shexp_at(il as u32);
2758                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2759                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2760                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2761                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2762                            Some(y) => y,
2763                            None => e.matmul(ffn_down, &act, total)?,
2764                        }
2765                    } else {
2766                        Self::ffn_act_lim(
2767                            e,
2768                            cfg,
2769                            &gate,
2770                            &up,
2771                            1.0,
2772                            1.0,
2773                            d_lim,
2774                            &mut act,
2775                            total * n_ff,
2776                        )?;
2777                        e.matmul(ffn_down, &act, total)?
2778                    }
2779                }
2780                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2781            };
2782            let mut x2 = e.uninit(total * n_embd)?;
2783            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2784            x = x2;
2785        }
2786        Ok(x)
2787    }
2788
2789    fn step35_prime_batch_epilogue(
2790        &self,
2791        e: &Engine,
2792        x: CudaSlice<f32>,
2793        ts: &[usize],
2794        offs: &[usize],
2795        caches: &mut [&mut Cache],
2796    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2797        let n_embd = self.cfg.n_embd as usize;
2798        let total: usize = ts.iter().sum();
2799        let mut hn = e.uninit(total * n_embd)?;
2800        e.rms_norm(
2801            &x,
2802            self.output_norm.float_data(),
2803            &mut hn,
2804            n_embd,
2805            total,
2806            self.cfg.rms_eps,
2807        )?;
2808
2809        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2810        let mut out = Vec::with_capacity(ts.len());
2811        for s in 0..ts.len() {
2812            let mut hidden = e.uninit(ts[s] * n_embd)?;
2813            e.copy_view_into(
2814                &mut hidden,
2815                0,
2816                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
2817                ts[s] * n_embd,
2818            )?;
2819            let last0 = (offs[s] + ts[s] - 1) * n_embd;
2820            let mut h_seed = e.uninit(n_embd)?;
2821            e.copy_view_into(
2822                &mut h_seed,
2823                0,
2824                &hidden_src.slice(last0..last0 + n_embd),
2825                n_embd,
2826            )?;
2827            // Exactness-first: the serial reference runs the output head at m=1.
2828            let mut hlast = e.uninit(n_embd)?;
2829            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
2830            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
2831            caches[s].pos += ts[s];
2832            out.push((logits, h_seed, hidden));
2833        }
2834        Ok(out)
2835    }
2836
2837    fn step35_prime_cache_batch(
2838        &self,
2839        e: &Engine,
2840        prompts: &[&[u32]],
2841        caches: &mut [&mut Cache],
2842    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2843        validate_step_prime_batch_modes(
2844            step_tp_prefill_enabled()?,
2845            step_ep_grouped_prefill_enabled()?,
2846        )?;
2847        if crate::pp::pp_host_bounce_active()
2848            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
2849        {
2850            return Err(
2851                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
2852                 stage split; refusing an unsplit remote-weight walk"
2853                    .into(),
2854            );
2855        }
2856        if !Self::step35_prime_batch_on() {
2857            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
2858        }
2859        if caches.iter().any(|c| c.pos != 0) {
2860            return Err(
2861                "step35 batched prime currently supports complete fresh prompts only; \
2862                 continuation/tick chunks require per-request queued_after"
2863                    .into(),
2864            );
2865        }
2866
2867        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2868        for &t in &ts {
2869            assert!(
2870                t >= PRIME_MIN_T,
2871                "step35 batched prime needs T >= {PRIME_MIN_T}"
2872            );
2873        }
2874        for (s, c) in caches.iter().enumerate() {
2875            assert!(
2876                ts[s] <= c.max_ctx,
2877                "step35 batched prime exceeds cache max_ctx"
2878            );
2879        }
2880        let offs: Vec<usize> = ts
2881            .iter()
2882            .scan(0usize, |a, &t| {
2883                let o = *a;
2884                *a += t;
2885                Some(o)
2886            })
2887            .collect();
2888        let total: usize = ts.iter().sum();
2889        let payload = total * self.cfg.n_embd as usize;
2890        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2891        let positions: Vec<Vec<i32>> = ts.iter().map(|&t| (0..t as i32).collect()).collect();
2892        let upload_positions =
2893            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
2894                positions
2895                    .iter()
2896                    .map(|p| e.htod_i32(p))
2897                    .collect::<Result<_, _>>()
2898            };
2899
2900        static ONCE: std::sync::Once = std::sync::Once::new();
2901        ONCE.call_once(|| {
2902            eprintln!(
2903                "[step35-prime-batch] first concat prime: B={} tokens={total}",
2904                prompts.len()
2905            );
2906        });
2907
2908        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
2909            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
2910                let rt = crate::pp::PpNRt::get(e)?;
2911                let n_st = fence.len() - 1;
2912                assert_eq!(
2913                    rt.n_stages(),
2914                    n_st,
2915                    "step35 prime batch stage count mismatch"
2916                );
2917                let caller_stream = e.stream();
2918                rt.fence_stages_behind(&caller_stream)?;
2919
2920                let mut slot = {
2921                    let _st0 = rt.enter(0);
2922                    let e0 = rt.engine(0, e);
2923                    let pos_ds = upload_positions(e0)?;
2924                    let x = self.embed(e0, &cat_tokens)?;
2925                    let x = self.step35_prime_batch_layers(
2926                        e0, x, fence[0], fence[1], &ts, &offs, &pos_ds, caches,
2927                    )?;
2928                    rt.tx(0, &x, payload)?
2929                };
2930                for s in 1..n_st - 1 {
2931                    let _st = rt.enter(s);
2932                    let es = rt.engine(s, e);
2933                    let pos_ds = upload_positions(es)?;
2934                    let x = rt.rx(s - 1, slot, payload)?;
2935                    let x = self.step35_prime_batch_layers(
2936                        es,
2937                        x,
2938                        fence[s],
2939                        fence[s + 1],
2940                        &ts,
2941                        &offs,
2942                        &pos_ds,
2943                        caches,
2944                    )?;
2945                    slot = rt.tx(s, &x, payload)?;
2946                }
2947
2948                let _stl = rt.enter(n_st - 1);
2949                let el = rt.engine(n_st - 1, e);
2950                let pos_ds = upload_positions(el)?;
2951                let x = rt.rx(n_st - 2, slot, payload)?;
2952                let x = self.step35_prime_batch_layers(
2953                    el,
2954                    x,
2955                    fence[n_st - 1],
2956                    fence[n_st],
2957                    &ts,
2958                    &offs,
2959                    &pos_ds,
2960                    caches,
2961                )?;
2962                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
2963                rt.publish_to(n_st - 1, &caller_stream)?;
2964                crate::pp::STEP35_PRIME_BATCH_SPLITS
2965                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2966                out
2967            } else {
2968                let pos_ds = upload_positions(e)?;
2969                let x = self.embed(e, &cat_tokens)?;
2970                let x = self.step35_prime_batch_layers(
2971                    e,
2972                    x,
2973                    0,
2974                    self.layers.len(),
2975                    &ts,
2976                    &offs,
2977                    &pos_ds,
2978                    caches,
2979                )?;
2980                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2981            }
2982        } else {
2983            let pos_ds = upload_positions(e)?;
2984            let x = self.embed(e, &cat_tokens)?;
2985            let x = self.step35_prime_batch_layers(
2986                e,
2987                x,
2988                0,
2989                self.layers.len(),
2990                &ts,
2991                &offs,
2992                &pos_ds,
2993                caches,
2994            )?;
2995            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2996        };
2997        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2998        Ok(out)
2999    }
3000
3001    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
3002    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
3003    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
3004    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
3005    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
3006    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
3007    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
3008    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
3009    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
3010    /// over the quantized past; Linear: the stateful pad_view twin — the same state
3011    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
3012    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
3013    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
3014    /// back to single-chunk serving).
3015    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
3016    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
3017    pub fn prime_cache_batch(
3018        &self,
3019        e: &Engine,
3020        prompts: &[&[u32]],
3021        caches: &mut [&mut Cache],
3022    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3023        if crate::pp::pp_cuts(self.layers.len()).is_some()
3024            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
3025        {
3026            return Err("pipeline rewrite is not qualified for batched prime".into());
3027        }
3028        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3029            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3030                return Err("neither batched-prime nor eager rewrite is qualified".into());
3031            }
3032            if prompts.len() != caches.len() {
3033                return Err("prime fallback prompt/cache shape mismatch".into());
3034            }
3035            static ONCE: std::sync::Once = std::sync::Once::new();
3036            ONCE.call_once(|| {
3037                eprintln!(
3038                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3039                );
3040            });
3041            return prompts
3042                .iter()
3043                .copied()
3044                .zip(caches.iter_mut())
3045                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3046                .collect();
3047        }
3048        let cfg = &self.cfg;
3049        let n_embd = cfg.n_embd as usize;
3050        let eps = cfg.rms_eps;
3051        let b = prompts.len();
3052        assert!(b >= 1 && b == caches.len());
3053        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3054        let carried = pos0s.iter().any(|&p| p > 0);
3055        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3056        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3057        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3058        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3059        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3060        if self.uses_gemma_program() {
3061            return Err(
3062                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3063                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3064                    .into(),
3065            );
3066        }
3067        // Step35 has a dedicated concat walk: the generic core below cannot express its
3068        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3069        if self.uses_sliding_gated_moe_program() {
3070            return self.step35_prime_cache_batch(e, prompts, caches);
3071        }
3072        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3073        for &t in &ts {
3074            assert!(
3075                t >= PRIME_MIN_T,
3076                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3077            );
3078        }
3079        for (s, c) in caches.iter().enumerate() {
3080            assert!(
3081                c.pos + ts[s] <= c.max_ctx,
3082                "prime_cache_batch: prompt exceeds cache max_ctx"
3083            );
3084        }
3085        let total: usize = ts.iter().sum();
3086        let offs: Vec<usize> = ts
3087            .iter()
3088            .scan(0usize, |a, &t| {
3089                let o = *a;
3090                *a += t;
3091                Some(o)
3092            })
3093            .collect();
3094        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3095        let pos_ds: Vec<CudaSlice<i32>> = ts
3096            .iter()
3097            .zip(&pos0s)
3098            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3099            .collect::<Result<_, _>>()?;
3100        // split a concat [total, dim] buffer into per-seq copies
3101        let split = |e: &Engine,
3102                     y: &CudaSlice<f32>,
3103                     dim: usize|
3104         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3105            let mut out = Vec::with_capacity(b);
3106            for s in 0..b {
3107                let mut ys = e.uninit(ts[s] * dim)?;
3108                e.copy_view_into(
3109                    &mut ys,
3110                    0,
3111                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3112                    ts[s] * dim,
3113                )?;
3114                out.push(ys);
3115            }
3116            Ok(out)
3117        };
3118
3119        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3120        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3121        for (il, layer) in self.layers.iter().enumerate() {
3122            let mut h = e.uninit(total * n_embd)?;
3123            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3124            e.rms_norm_f16out(
3125                &x,
3126                layer.attn_norm.float_data(),
3127                &mut h,
3128                &mut hx16,
3129                n_embd,
3130                total,
3131                eps,
3132            )?;
3133            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3134            let mut mixed = e.uninit(total * n_embd)?;
3135            match &layer.mixer {
3136                Mixer::Full(fa) => {
3137                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3138                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3139                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3140                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3141                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3142                    // back to the per-seq dispatch.
3143                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3144                    let (n_head, n_head_kv, head_dim) = (
3145                        geometry.n_head as usize,
3146                        geometry.n_head_kv as usize,
3147                        geometry.head_dim_k as usize,
3148                    );
3149                    let fa_scale = geometry.attention_scale();
3150                    let use_favl = !carried
3151                        && (2..=8).contains(&b)
3152                        && (head_dim == 256 || head_dim == 128)
3153                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3154                        && std::env::var("MEMRA_NOFA").is_err()
3155                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3156                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3157                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3158                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3159                    if use_favl {
3160                        let (qf_w, kf_w, vf_w) = (
3161                            fa.wq.out_features(),
3162                            fa.wk.out_features(),
3163                            fa.wv.out_features(),
3164                        );
3165                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3166                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3167                        // cannot check its own extents; `qf_w` is the wq out-features that set
3168                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3169                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3170                        struct APre {
3171                            q: CudaSlice<f32>,
3172                            gate: Option<CudaSlice<f32>>,
3173                            qn: CudaSlice<f32>,
3174                            kn: CudaSlice<f32>,
3175                        }
3176                        let mut aps = Vec::with_capacity(b);
3177                        for &t in ts.iter().take(b) {
3178                            aps.push(APre {
3179                                q: e.uninit(t * n_head * head_dim)?,
3180                                gate: Some(e.uninit(t * n_head * head_dim)?),
3181                                qn: e.uninit(t * n_head * head_dim)?,
3182                                kn: e.uninit(t * n_head_kv * head_dim)?,
3183                            });
3184                        }
3185                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3186                            let kvl = caches[0].kv[il].as_ref().unwrap();
3187                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3188                        };
3189                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3190                            .map(|s| {
3191                                let (o, t) = (offs[s], ts[s]);
3192                                let kvl = caches[s].kv[il].as_ref().unwrap();
3193                                assert!(
3194                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3195                                    "prime_cache_batch attn vl: fresh + capacity"
3196                                );
3197                                crate::AttnPreVl {
3198                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3199                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3200                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3201                                    q: e.addr_f32(&aps[s].q),
3202                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3203                                    qn: e.addr_f32(&aps[s].qn),
3204                                    kn: e.addr_f32(&aps[s].kn),
3205                                    kc: e.addr_u8(&kvl.k),
3206                                    vc: e.addr_u8(&kvl.v),
3207                                    t: t as i32,
3208                                    pad: 0,
3209                                }
3210                            })
3211                            .collect();
3212                        e.attn_pre_vl8(
3213                            &pargs,
3214                            fa.q_norm.float_data(),
3215                            fa.k_norm.float_data(),
3216                            head_dim,
3217                            geometry.n_rot as usize,
3218                            n_head,
3219                            n_head_kv,
3220                            self.cfg.rms_eps,
3221                            geometry.rope_base,
3222                            1.0,
3223                            kv_dim_k,
3224                            kv_dim_v,
3225                            ktb,
3226                            vtb,
3227                        )?;
3228                        for s in 0..b {
3229                            let kvl = caches[s].kv[il].as_mut().unwrap();
3230                            kvl.len += ts[s];
3231                            let new_len = kvl.len as i32;
3232                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3233                        }
3234                        let mut attns = Vec::with_capacity(b);
3235                        let mut mirrors = Vec::with_capacity(b);
3236                        for &t in ts.iter().take(b) {
3237                            attns.push(e.uninit(t * n_head * head_dim)?);
3238                            let n = t * n_head_kv * head_dim;
3239                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3240                        }
3241                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3242                        // promoted single-seq config is on; else the mma favl.
3243                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3244                            Ok("0") => false,
3245                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3246                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3247                            // portable build.
3248                            Ok("1") => {
3249                                crate::refuse_portable_force(
3250                                    "MEMRA_FA3=1",
3251                                    "the sm_90a fa3/bf16 kernels",
3252                                );
3253                                true
3254                            }
3255                            _ => cfg!(memra_hopper_mma),
3256                        };
3257                        if fa3_on {
3258                            let mut q16s = Vec::with_capacity(b);
3259                            let mut v16s = Vec::with_capacity(b);
3260                            for s in 0..b {
3261                                let t = ts[s];
3262                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3263                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3264                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3265                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3266                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3267                                e.f32_to_bf16_v(
3268                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3269                                    &mut v16,
3270                                    t * n_head_kv * head_dim,
3271                                )?;
3272                                q16s.push(q16);
3273                                v16s.push((k16, v16));
3274                            }
3275                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3276                            let mut kp = qp;
3277                            let mut vp = qp;
3278                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3279                            let mut tsv = [0i32; 8];
3280                            for s in 0..b {
3281                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3282                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3283                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3284                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3285                                tsv[s] = ts[s] as i32;
3286                            }
3287                            let rc = unsafe {
3288                                crate::fa3_vl_raw(
3289                                    qp.as_ptr(),
3290                                    kp.as_ptr(),
3291                                    vp.as_ptr(),
3292                                    op.as_ptr(),
3293                                    tsv.as_ptr(),
3294                                    b as i32,
3295                                    n_head as i32,
3296                                    n_head_kv as i32,
3297                                    head_dim as i32,
3298                                    fa_scale,
3299                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3300                                )
3301                            };
3302                            if rc != 0 {
3303                                return Err(format!("memra_fa3_vl rc={rc}").into());
3304                            }
3305                        } else {
3306                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3307                                .map(|s| crate::FaSeqVl {
3308                                    q: e.addr_f32(&aps[s].qn),
3309                                    k16: e.addr_u8(&mirrors[s].0),
3310                                    v16: e.addr_u8(&mirrors[s].1),
3311                                    o: e.addr_f32(&attns[s]),
3312                                    kf: e.addr_f32(&aps[s].kn),
3313                                    vf: e.addr_f32v(
3314                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3315                                    ),
3316                                    t: ts[s] as i32,
3317                                    pad: 0,
3318                                })
3319                                .collect();
3320                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3321                        }
3322                        for (s, attn) in attns.into_iter().enumerate() {
3323                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3324                                e,
3325                                attn,
3326                                &aps[s].gate,
3327                                ts[s],
3328                                n_head,
3329                                head_dim,
3330                            )?;
3331                            let mut done = false;
3332                            if let Some(xh) = &ag16 {
3333                                done = e.try_f16_gemm_pre_into_off(
3334                                    &fa.wo,
3335                                    xh,
3336                                    ts[s],
3337                                    &mut mixed,
3338                                    offs[s] * n_embd,
3339                                )?;
3340                            }
3341                            if !done {
3342                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3343                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3344                            }
3345                        }
3346                    } else {
3347                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3348                            (0..b).map(|_| Vec::new()).collect();
3349                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3350                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3351                                parts[s].push(ys);
3352                            }
3353                        }
3354                        for (s, g3s) in parts.into_iter().enumerate() {
3355                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3356                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3357                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3358                            )?;
3359                            let mut done = false;
3360                            if let Some(xh) = &ag16 {
3361                                done = e.try_f16_gemm_pre_into_off(
3362                                    &fa.wo,
3363                                    xh,
3364                                    ts[s],
3365                                    &mut mixed,
3366                                    offs[s] * n_embd,
3367                                )?;
3368                            }
3369                            if !done {
3370                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3371                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3372                            }
3373                        }
3374                    }
3375                }
3376                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3377                Mixer::Linear(la) => {
3378                    // task #16: NO split copies (cores read row-offset views of the concat
3379                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3380                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3381                    // varlen K5 launch for all sequences.
3382                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3383                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3384                    let outs =
3385                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3386                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3387                        let (o, t) = (offs[s], ts[s]);
3388                        let mut done = false;
3389                        if let Some(xh) = &gn16 {
3390                            done = e.try_f16_gemm_pre_into_off(
3391                                &la.ssm_out,
3392                                xh,
3393                                t,
3394                                &mut mixed,
3395                                o * n_embd,
3396                            )?;
3397                        }
3398                        if !done {
3399                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3400                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3401                        }
3402                    }
3403                }
3404            }
3405            let mut x1 = e.uninit(total * n_embd)?;
3406            let mut z = e.uninit(total * n_embd)?;
3407            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3408            e.add_rms_norm_f16out(
3409                &x,
3410                &mixed,
3411                layer.post_attn_norm.float_data(),
3412                &mut x1,
3413                &mut z,
3414                &mut zx16,
3415                n_embd,
3416                total,
3417                eps,
3418            )?;
3419            let ffn_out = match &layer.ffn {
3420                crate::hybrid::Ffn::Dense {
3421                    ffn_gate,
3422                    ffn_up,
3423                    ffn_down,
3424                } => {
3425                    let n_ff = ffn_gate.out_features();
3426                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3427                    let up = g2.pop().unwrap();
3428                    let gate = g2.pop().unwrap();
3429                    let mut act = e.uninit(total * n_ff)?;
3430                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3431                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3432                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3433                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3434                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3435                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3436                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3437                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3438                            Some(y) => y,
3439                            None => e.matmul(ffn_down, &act, total)?,
3440                        }
3441                    } else {
3442                        Self::ffn_act_lim(
3443                            e,
3444                            &self.cfg,
3445                            &gate,
3446                            &up,
3447                            1.0,
3448                            1.0,
3449                            d_lim,
3450                            &mut act,
3451                            total * n_ff,
3452                        )?;
3453                        e.matmul(ffn_down, &act, total)?
3454                    }
3455                }
3456                crate::hybrid::Ffn::Moe(m) => {
3457                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3458                }
3459            };
3460            let mut x2 = e.uninit(total * n_embd)?;
3461            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3462            x = x2;
3463        }
3464        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3465        let mut hn = e.uninit(total * n_embd)?;
3466        e.rms_norm(
3467            &x,
3468            self.output_norm.float_data(),
3469            &mut hn,
3470            n_embd,
3471            total,
3472            eps,
3473        )?;
3474        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3475        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3476        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3477        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3478        // argmax battery arbitrates, same as every other prefill GEMM change.
3479        let mut hcat = e.uninit(b * n_embd)?;
3480        for s in 0..b {
3481            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3482            e.copy_view_into(
3483                &mut hcat,
3484                s * n_embd,
3485                &hn.slice(last0..last0 + n_embd),
3486                n_embd,
3487            )?;
3488        }
3489        let logits_cat = if b >= 2 {
3490            e.try_f16_gemm(&self.output, &hcat, b)?
3491        } else {
3492            None
3493        };
3494        let logits_host: Option<Vec<f32>> = match &logits_cat {
3495            Some(lc) => Some(e.dtoh(lc)?),
3496            None => None,
3497        };
3498        let n_vocab = self.output.out_features();
3499        let mut hidden_all = if crate::spec::spec_hpost() {
3500            split(e, &hn, n_embd)?
3501        } else {
3502            split(e, &x, n_embd)?
3503        };
3504        let mut out = Vec::with_capacity(b);
3505        for s in 0..b {
3506            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3507            let mut h_seed = e.uninit(n_embd)?;
3508            if !crate::spec::spec_hpost() {
3509                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3510            } else {
3511                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3512            }
3513            let logits = match &logits_host {
3514                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3515                None => {
3516                    let mut hlast = e.uninit(n_embd)?;
3517                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3518                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3519                }
3520            };
3521            caches[s].pos += ts[s];
3522            out.push((logits, h_seed, hidden_all.remove(0)));
3523        }
3524        Ok(out)
3525    }
3526
3527    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3528    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3529    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3530    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3531    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3532    ///
3533    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3534    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3535    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3536    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3537    #[allow(clippy::too_many_arguments)]
3538    fn full_attn_prime(
3539        &self,
3540        e: &Engine,
3541        fa: &FullAttnLayer,
3542        h: &CudaSlice<f32>,
3543        hx: Option<&CudaSlice<u8>>,
3544        pos_d: &CudaSlice<i32>,
3545        t: usize,
3546        cache: &mut Cache,
3547        il: usize,
3548        seq_end: usize,
3549    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3550        if self.uses_sliding_gated_moe_program() {
3551            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3552        }
3553        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3554        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3555        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3556        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3557        let g3 = match hx {
3558            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3559            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3560        };
3561        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3562    }
3563
3564    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3565    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3566    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3567    fn full_attn_prime_core(
3568        &self,
3569        e: &Engine,
3570        fa: &FullAttnLayer,
3571        g3: Vec<CudaSlice<f32>>,
3572        pos_d: &CudaSlice<i32>,
3573        t: usize,
3574        cache: &mut Cache,
3575        il: usize,
3576    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3577        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3578        if let Some(xh) = &ag16 {
3579            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3580                return Ok(y);
3581            }
3582        }
3583        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3584    }
3585
3586    fn full_attn_prime_core_inner(
3587        &self,
3588        e: &Engine,
3589        fa: &FullAttnLayer,
3590        g3: Vec<CudaSlice<f32>>,
3591        pos_d: &CudaSlice<i32>,
3592        t: usize,
3593        cache: &mut Cache,
3594        il: usize,
3595    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3596        let cfg = &self.cfg;
3597        let geometry = cfg.full_attention_geometry_at(il as u32);
3598        let n_head = geometry.n_head as usize;
3599        let n_head_kv = geometry.n_head_kv as usize;
3600        let head_dim = geometry.head_dim_k as usize;
3601        let scale = geometry.attention_scale();
3602        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3603        let AttnPre { q, k, v, gate } = pre;
3604        let mut attn = e.uninit(t * n_head * head_dim)?;
3605        self.full_attn_prime_fa_dispatch(
3606            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3607        )?;
3608        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3609    }
3610
3611    /// task #18 (attn side): projections tail through KV append — everything before the
3612    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3613    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3614    #[allow(clippy::type_complexity)]
3615    fn full_attn_prime_pre_fa(
3616        &self,
3617        e: &Engine,
3618        fa: &FullAttnLayer,
3619        mut g3: Vec<CudaSlice<f32>>,
3620        pos_d: &CudaSlice<i32>,
3621        t: usize,
3622        cache: &mut Cache,
3623        il: usize,
3624    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3625        let cfg = &self.cfg;
3626        let geometry = cfg.full_attention_geometry_at(il as u32);
3627        let n_head = geometry.n_head as usize;
3628        let n_head_kv = geometry.n_head_kv as usize;
3629        let head_dim = geometry.head_dim_k as usize;
3630        let eps = cfg.rms_eps;
3631
3632        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3633        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3634        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3635        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3636        let v = g3.pop().unwrap();
3637        let mut k = g3.pop().unwrap();
3638        let qf = g3.pop().unwrap();
3639        let (mut q, gate) = if gated {
3640            let mut q = e.uninit(t * n_head * head_dim)?;
3641            let mut gate = e.uninit(t * n_head * head_dim)?;
3642            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3643            (q, Some(gate))
3644        } else {
3645            (qf, None)
3646        };
3647
3648        let mut qn = e.uninit(t * n_head * head_dim)?;
3649        e.rms_norm(
3650            &q,
3651            fa.q_norm.float_data(),
3652            &mut qn,
3653            head_dim,
3654            n_head * t,
3655            eps,
3656        )?;
3657        q = qn;
3658        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3659        e.rms_norm(
3660            &k,
3661            fa.k_norm.float_data(),
3662            &mut kn,
3663            head_dim,
3664            n_head_kv * t,
3665            eps,
3666        )?;
3667        k = kn;
3668        let rope_dims = geometry.n_rot as usize;
3669        e.rope_neox(
3670            &mut q,
3671            pos_d,
3672            head_dim,
3673            rope_dims,
3674            n_head,
3675            t,
3676            geometry.rope_base,
3677            1.0,
3678        )?;
3679        e.rope_neox(
3680            &mut k,
3681            pos_d,
3682            head_dim,
3683            rope_dims,
3684            n_head_kv,
3685            t,
3686            geometry.rope_base,
3687            1.0,
3688        )?;
3689
3690        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3691        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3692        {
3693            let kvl = cache.kv[il].as_mut().unwrap();
3694            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3695            e.append_kv_quantized_rows(
3696                &k,
3697                &v,
3698                &mut kvl.k,
3699                &mut kvl.v,
3700                kvl.len,
3701                t,
3702                kvl.kv_dim_k,
3703                kvl.kv_dim_v,
3704                kvl.k_tok_bytes,
3705                kvl.v_tok_bytes,
3706                crate::Engine::kv_fp8_on(),
3707            )?;
3708            kvl.len += t;
3709            let new_len = kvl.len as i32;
3710            e.set_i32_one(&mut kvl.len_d, new_len)?;
3711        }
3712
3713        let base_len = {
3714            let kvl = cache.kv[il].as_ref().unwrap();
3715            kvl.len - t // KV rows present BEFORE this chunk's append above
3716        };
3717        Ok((AttnPre { q, k, v, gate }, base_len))
3718    }
3719
3720    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3721    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3722    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3723    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3724    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3725    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3726    #[allow(clippy::too_many_arguments)]
3727    fn full_attn_prime_fa_dispatch(
3728        &self,
3729        e: &Engine,
3730        q: &CudaSlice<f32>,
3731        k: &CudaSlice<f32>,
3732        v: &CudaSlice<f32>,
3733        attn: &mut CudaSlice<f32>,
3734        base_len: usize,
3735        t: usize,
3736        cache: &mut Cache,
3737        il: usize,
3738        head_dim: usize,
3739        n_head: usize,
3740        n_head_kv: usize,
3741        scale: f32,
3742    ) -> Result<(), Box<dyn std::error::Error>> {
3743        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3744        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3745        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3746        // attend through the quantized cache exactly like every later chunk (quantize-then-
3747        // attend). One numeric class for every row => the chunk size cannot decide where a
3748        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3749        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
3750        // pin-the-boundary approach).
3751        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
3752        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
3753        // with the fix unconditional, only re-introducing the class edge can prove the gate
3754        // still detects the mechanism. Never on in a measured default run.
3755        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
3756            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3757                e.sdpa_naive(
3758                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3759                )?;
3760            } else {
3761                e.fa_prefill(
3762                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3763                )?;
3764            }
3765            return Ok(());
3766        }
3767        let kvl = cache.kv[il].as_ref().unwrap();
3768        let t_kv = base_len + t;
3769        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
3770        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
3771        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
3772        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
3773        // same numeric class, so the uniform contract holds on the fallback too.
3774        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3775            e.sdpa_naive_quantized_view(
3776                q,
3777                &k_view,
3778                &v_view,
3779                attn,
3780                head_dim,
3781                n_head,
3782                n_head_kv,
3783                t,
3784                t_kv,
3785                scale,
3786                true,
3787                kvl.k_tok_bytes,
3788                kvl.v_tok_bytes,
3789            )?;
3790            return Ok(());
3791        }
3792        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
3793        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
3794        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
3795        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
3796        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
3797        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
3798        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
3799        let deqw = std::env::var("MEMRA_PRIME_DEQW")
3800            .map(|v| v != "0")
3801            .unwrap_or(true);
3802        if deqw {
3803            e.fa_prefill_view_ws(
3804                q,
3805                &k_view,
3806                &v_view,
3807                attn,
3808                head_dim,
3809                n_head,
3810                n_head_kv,
3811                t,
3812                t_kv,
3813                scale,
3814                true,
3815                kvl.k_tok_bytes,
3816                kvl.v_tok_bytes,
3817                crate::Engine::kv_fp8_on(),
3818            )?;
3819        } else {
3820            e.fa_prefill_view(
3821                q,
3822                &k_view,
3823                &v_view,
3824                attn,
3825                head_dim,
3826                n_head,
3827                n_head_kv,
3828                t,
3829                t_kv,
3830                scale,
3831                true,
3832                kvl.k_tok_bytes,
3833                kvl.v_tok_bytes,
3834                crate::Engine::kv_fp8_on(),
3835            )?;
3836        }
3837        Ok(())
3838    }
3839
3840    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
3841    /// (bit-identical composition) and hands wo its fp16 operand directly.
3842    fn full_attn_prime_post_fa(
3843        &self,
3844        e: &Engine,
3845        attn: CudaSlice<f32>,
3846        gate: &Option<CudaSlice<f32>>,
3847        t: usize,
3848        n_head: usize,
3849        head_dim: usize,
3850    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3851        let (attn_g, ag16) = match gate {
3852            Some(gate) => {
3853                let n = t * n_head * head_dim;
3854                let mut ag = e.uninit(n)?;
3855                if Self::f16out_on(e, t) {
3856                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
3857                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
3858                    (ag, Some(a16))
3859                } else {
3860                    let mut gsig = e.uninit(n)?;
3861                    e.sigmoid(gate, &mut gsig, n)?;
3862                    e.mul(&attn, &gsig, &mut ag, n)?;
3863                    (ag, None)
3864                }
3865            }
3866            None => (attn, None),
3867        };
3868        Ok((attn_g, ag16))
3869    }
3870
3871    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
3872    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
3873    /// carried THROUGH the cache like the spec verify does: carried-ring conv
3874    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
3875    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
3876    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
3877    fn linear_attn_prime(
3878        &self,
3879        e: &Engine,
3880        la: &LinearAttnLayer,
3881        h: &CudaSlice<f32>,
3882        hx: Option<&CudaSlice<u8>>,
3883        t: usize,
3884        cache: &mut Cache,
3885        il: usize,
3886    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3887        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
3888        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3889        let g4 = match hx {
3890            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
3891            None => e.matmul_group(&ws, h, t)?,
3892        };
3893        self.linear_attn_prime_core(e, la, g4, t, cache, il)
3894    }
3895
3896    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
3897    fn linear_attn_prime_core(
3898        &self,
3899        e: &Engine,
3900        la: &LinearAttnLayer,
3901        mut g4: Vec<CudaSlice<f32>>,
3902        t: usize,
3903        cache: &mut Cache,
3904        il: usize,
3905    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3906        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
3907    }
3908
3909    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
3910    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
3911    /// conv ring writes back from the true tail. None = classic path, byte-identical.
3912    #[allow(clippy::too_many_arguments)]
3913    fn linear_attn_prime_core_pad_inner(
3914        &self,
3915        e: &Engine,
3916        la: &LinearAttnLayer,
3917        mut g4: Vec<CudaSlice<f32>>,
3918        t: usize,
3919        cache: &mut Cache,
3920        il: usize,
3921        pad_len: Option<&CudaSlice<i32>>,
3922    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3923        // shim over the view twin (task #16): full-range views of the owned buffers.
3924        let geometry = la.geometry;
3925        let d_state = geometry.key_head_dim as usize;
3926        let num_k = geometry.key_heads as usize;
3927        let num_v = geometry.value_heads as usize;
3928        let key_dim = d_state * num_k;
3929        let value_dim = geometry.value_head_dim as usize * num_v;
3930        let conv_dim = key_dim * 2 + value_dim;
3931        let alpha = g4.pop().unwrap(); // [T, num_v]
3932        let beta_raw = g4.pop().unwrap(); // [T, num_v]
3933        let z = g4.pop().unwrap(); // [T, value_dim]
3934        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
3935        self.linear_attn_prime_core_pad_view(
3936            e,
3937            la,
3938            &qkv_mixed.slice(0..t * conv_dim),
3939            &z.slice(0..t * value_dim),
3940            &beta_raw.slice(0..t * num_v),
3941            &alpha.slice(0..t * num_v),
3942            t,
3943            cache,
3944            il,
3945            pad_len,
3946        )
3947    }
3948
3949    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
3950    /// shared verbatim by the per-seq scan path and the varlen batched path.
3951    #[allow(clippy::too_many_arguments)]
3952    fn linear_attn_gdn_prep(
3953        &self,
3954        e: &Engine,
3955        la: &LinearAttnLayer,
3956        qkv_mixed: &cudarc::driver::CudaView<f32>,
3957        beta_raw: &cudarc::driver::CudaView<f32>,
3958        alpha: &cudarc::driver::CudaView<f32>,
3959        t: usize,
3960        cache: &mut Cache,
3961        il: usize,
3962        pad_len: Option<&CudaSlice<i32>>,
3963    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
3964        let cfg = &self.cfg;
3965        let geometry = la.geometry;
3966        let d_state = geometry.key_head_dim as usize;
3967        let num_k = geometry.key_heads as usize;
3968        let num_v = geometry.value_heads as usize;
3969        let d_conv = geometry.conv_kernel as usize;
3970        let key_dim = d_state * num_k; // 2048
3971        let value_dim = geometry.value_head_dim as usize * num_v;
3972        let conv_dim = key_dim * 2 + value_dim; // 8192
3973        let eps = cfg.rms_eps;
3974        debug_assert!(
3975            t >= d_conv - 1,
3976            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
3977        );
3978
3979        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
3980        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
3981        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
3982        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
3983        let rl = cache.recur[il].as_mut().unwrap();
3984        let hk = Self::gdn_hk(e, t, num_v, num_k);
3985        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
3986        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
3987        let mut q_g = e.uninit(d_state * hk * t)?;
3988        let mut k_g = e.uninit(d_state * hk * t)?;
3989        let mut v_g = e.uninit(d_state * num_v * t)?;
3990        if conv_fuse {
3991            e.ssm_conv1d_gdn_state_pad(
3992                qkv_mixed,
3993                &mut rl.conv_state,
3994                la.ssm_conv1d.float_data(),
3995                &mut q_g,
3996                &mut k_g,
3997                &mut v_g,
3998                conv_dim,
3999                t,
4000                d_conv,
4001                d_state,
4002                num_v,
4003                num_k,
4004                key_dim,
4005                hk,
4006                pad_len,
4007            )?;
4008        } else {
4009            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
4010            e.ssm_conv1d_tm_state_pad_v(
4011                qkv_mixed,
4012                &mut rl.conv_state,
4013                la.ssm_conv1d.float_data(),
4014                &mut conv_out,
4015                conv_dim,
4016                t,
4017                d_conv,
4018                pad_len,
4019            )?;
4020            e.qkv_to_gdn_repack(
4021                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
4022            )?;
4023        }
4024        let mut q_l2 = e.uninit(d_state * hk * t)?;
4025        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
4026        // Emitted only where a consumer exists (the wgmma config) — on other arches the
4027        // alloc + epilogue stores would be pure waste.
4028        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4029            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4030            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4031            Some(qb)
4032        } else {
4033            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4034            None
4035        };
4036        let mut k_l2 = e.uninit(d_state * hk * t)?;
4037        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4038        let kb16 = if Engine::l2_v2_on(d_state) {
4039            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4040            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4041            Some(kb)
4042        } else {
4043            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4044            None
4045        };
4046        let mut beta = e.uninit(t * num_v)?;
4047        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4048        let mut g_log = e.uninit(t * num_v)?;
4049        e.gdn_glog_v(
4050            alpha,
4051            la.ssm_dt.float_data(),
4052            la.ssm_a.float_data(),
4053            &mut g_log,
4054            num_v,
4055            t,
4056        )?;
4057        if let Some(len_d) = pad_len {
4058            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4059        }
4060        Ok(GdnPrep {
4061            hk,
4062            q_l2,
4063            k_l2,
4064            v_g,
4065            beta,
4066            g_log,
4067            kb16,
4068            qb16,
4069        })
4070    }
4071
4072    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4073    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4074    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4075    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4076    #[allow(clippy::too_many_arguments)]
4077    fn linear_attn_prime_core_batch(
4078        &self,
4079        e: &Engine,
4080        la: &LinearAttnLayer,
4081        g4: &[CudaSlice<f32>],
4082        offs: &[usize],
4083        ts: &[usize],
4084        caches: &mut [&mut Cache],
4085        il: usize,
4086    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4087        let geometry = la.geometry;
4088        let d_state = geometry.key_head_dim as usize;
4089        let num_k = geometry.key_heads as usize;
4090        let num_v = geometry.value_heads as usize;
4091        let d_conv = geometry.conv_kernel as usize;
4092        let key_dim = d_state * num_k;
4093        let value_dim = geometry.value_head_dim as usize * num_v;
4094        let conv_dim = key_dim * 2 + value_dim;
4095        let eps = self.cfg.rms_eps;
4096        let scale = 1.0 / (d_state as f32).sqrt();
4097        let b = ts.len();
4098        let c = Engine::gdn_chunk_size();
4099        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4100        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4101        let carried = caches.iter().any(|c| c.pos > 0);
4102        let use_vl = !carried
4103            && (2..=8).contains(&b)
4104            && Engine::gdn_chunked_enabled()
4105            && ts.iter().all(|&t| t >= 16)
4106            && e.gdn_mma_enabled(c)
4107            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4108        if !use_vl {
4109            return (0..b)
4110                .map(|s| {
4111                    let (o, t) = (offs[s], ts[s]);
4112                    self.linear_attn_prime_core_pad_view(
4113                        e,
4114                        la,
4115                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4116                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4117                        &g4[2].slice(o * num_v..(o + t) * num_v),
4118                        &g4[3].slice(o * num_v..(o + t) * num_v),
4119                        t,
4120                        caches[s],
4121                        il,
4122                        None,
4123                    )
4124                })
4125                .collect();
4126        }
4127        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4128        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4129        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4130        struct SeqBufs {
4131            conv_out: CudaSlice<f32>,
4132            q_g: CudaSlice<f32>,
4133            k_g: CudaSlice<f32>,
4134            v_g: CudaSlice<f32>,
4135            q_l2: CudaSlice<f32>,
4136            k_l2: CudaSlice<f32>,
4137            beta: CudaSlice<f32>,
4138            g_log: CudaSlice<f32>,
4139            gn: CudaSlice<f32>,
4140            gn16: CudaSlice<u8>,
4141        }
4142        let f16o = Self::f16out_on(e, 16);
4143        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4144        let mut sb = Vec::with_capacity(b);
4145        let mut pres = Vec::with_capacity(b);
4146        for &t in ts.iter().take(b) {
4147            sb.push(SeqBufs {
4148                conv_out: e.uninit(conv_dim * t)?,
4149                q_g: e.uninit(d_state * hk * t)?,
4150                k_g: e.uninit(d_state * hk * t)?,
4151                v_g: e.uninit(d_state * num_v * t)?,
4152                q_l2: e.uninit(d_state * hk * t)?,
4153                k_l2: e.uninit(d_state * hk * t)?,
4154                beta: e.uninit(t * num_v)?,
4155                g_log: e.uninit(t * num_v)?,
4156                gn: e.uninit(d_state * num_v * t)?,
4157                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4158            });
4159            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4160        }
4161        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4162            .map(|s| {
4163                let (o, t) = (offs[s], ts[s]);
4164                let rl = caches[s].recur[il].as_ref().unwrap();
4165                crate::GdnPrepVl {
4166                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4167                    conv_state: e.addr_f32(&rl.conv_state),
4168                    conv_out: e.addr_f32(&sb[s].conv_out),
4169                    q_g: e.addr_f32(&sb[s].q_g),
4170                    k_g: e.addr_f32(&sb[s].k_g),
4171                    v_g: e.addr_f32(&sb[s].v_g),
4172                    q_l2: e.addr_f32(&sb[s].q_l2),
4173                    k_l2: e.addr_f32(&sb[s].k_l2),
4174                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4175                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4176                    beta: e.addr_f32(&sb[s].beta),
4177                    g_log: e.addr_f32(&sb[s].g_log),
4178                    o: e.addr_f32(&pres[s].o),
4179                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4180                    gn: e.addr_f32(&sb[s].gn),
4181                    gn16: e.addr_u8(&sb[s].gn16),
4182                    kb16: if Engine::l2_v2_on(d_state) {
4183                        e.addr_u8(&pres[s].kb16)
4184                    } else {
4185                        0
4186                    },
4187                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4188                        e.addr_u8(&pres[s].qb16)
4189                    } else {
4190                        0
4191                    },
4192                    t: t as i32,
4193                    pad: 0,
4194                }
4195            })
4196            .collect();
4197        let args: Vec<crate::GdnSeqVl> = (0..b)
4198            .map(|s| {
4199                let rl = caches[s].recur[il].as_ref().unwrap();
4200                crate::GdnSeqVl {
4201                    kb16: e.addr_u8(&pres[s].kb16),
4202                    gcum: e.addr_f32(&pres[s].gcum),
4203                    beta: e.addr_f32(&sb[s].beta),
4204                    u: e.addr_f32(&pres[s].u),
4205                    wb16: e.addr_u8(&pres[s].wb16),
4206                    y: e.addr_u8(&pres[s].y16),
4207                    ssnap: e.addr_u8(&pres[s].ssnap16),
4208                    state_in: e.addr_f32(&rl.ssm_state),
4209                    state_out: e.addr_f32(&rl.ssm_state_alt),
4210                    q: e.addr_f32(&sb[s].q_l2),
4211                    p: e.addr_f32(&pres[s].p),
4212                    o: e.addr_f32(&pres[s].o),
4213                    k: e.addr_f32(&sb[s].k_l2),
4214                    v: e.addr_f32(&sb[s].v_g),
4215                    g: e.addr_f32(&sb[s].g_log),
4216                    a: e.addr_f32(&pres[s].a),
4217                    w: e.addr_f32(&pres[s].w),
4218                    t: ts[s] as i32,
4219                    nc: pres[s].nc as i32,
4220                }
4221            })
4222            .collect();
4223        e.gdn_prep_vl8(
4224            &prep_args,
4225            la.ssm_conv1d.float_data(),
4226            la.ssm_dt.float_data(),
4227            la.ssm_a.float_data(),
4228            conv_dim,
4229            d_conv,
4230            d_state,
4231            num_v,
4232            num_k,
4233            key_dim,
4234            hk,
4235            eps,
4236        )?;
4237        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4238        // both standalone mirror launches vanish on the default config.
4239        if !Engine::l2_v2_on(d_state) {
4240            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4241        }
4242        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4243        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4244            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4245            if !Engine::l2_v2_on(d_state) {
4246                for s in 0..b {
4247                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4248                }
4249            }
4250            let mut wa = [crate::GdnWVl::default(); 8];
4251            for s in 0..b {
4252                wa[s] = crate::GdnWVl {
4253                    qb16: e.addr_u8(&pres[s].qb16),
4254                    pb16: e.addr_u8(&pres[s].pb16),
4255                };
4256            }
4257            Some(crate::GdnWVl8(wa))
4258        } else {
4259            None
4260        };
4261        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4262        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4263        if f16o {
4264            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4265        }
4266        // per-seq state swap (+ non-f16out tail fallback)
4267        let mut out = Vec::with_capacity(b);
4268        for (s, bufs) in sb.into_iter().enumerate() {
4269            let rl = caches[s].recur[il].as_mut().unwrap();
4270            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4271            let (o, t) = (offs[s], ts[s]);
4272            let SeqBufs { mut gn, gn16, .. } = bufs;
4273            if f16o {
4274                out.push((gn, Some(gn16)));
4275            } else {
4276                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4277                e.gated_rmsnorm_zv(
4278                    &pres[s].o,
4279                    la.ssm_norm.float_data(),
4280                    &z_v,
4281                    &mut gn,
4282                    d_state,
4283                    num_v * t,
4284                    eps,
4285                )?;
4286                out.push((gn, None));
4287            }
4288        }
4289        Ok(out)
4290    }
4291
4292    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4293    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4294    /// Same kernels, same values, byte-identical to the Vec shim above.
4295    #[allow(clippy::too_many_arguments)]
4296    fn linear_attn_prime_core_pad_view(
4297        &self,
4298        e: &Engine,
4299        la: &LinearAttnLayer,
4300        qkv_mixed: &cudarc::driver::CudaView<f32>,
4301        z: &cudarc::driver::CudaView<f32>,
4302        beta_raw: &cudarc::driver::CudaView<f32>,
4303        alpha: &cudarc::driver::CudaView<f32>,
4304        t: usize,
4305        cache: &mut Cache,
4306        il: usize,
4307        pad_len: Option<&CudaSlice<i32>>,
4308    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4309        let cfg = &self.cfg;
4310        let geometry = la.geometry;
4311        let d_state = geometry.key_head_dim as usize;
4312        let num_v = geometry.value_heads as usize;
4313        let eps = cfg.rms_eps;
4314        let scale = 1.0 / (d_state as f32).sqrt();
4315
4316        let prep =
4317            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4318
4319        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4320        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4321        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4322        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4323        // verify keep the sequential kernel).
4324        let mut o = e.uninit(d_state * num_v * t)?;
4325        let rl = cache.recur[il].as_mut().unwrap();
4326        {
4327            let crate::cache::RecurLayer {
4328                ssm_state,
4329                ssm_state_alt,
4330                ..
4331            } = rl;
4332            e.gdn_scan_prefill(
4333                &prep.q_l2,
4334                &prep.k_l2,
4335                &prep.v_g,
4336                &prep.g_log,
4337                &prep.beta,
4338                prep.kb16.as_ref(),
4339                prep.qb16.as_ref(),
4340                ssm_state,
4341                ssm_state_alt,
4342                &mut o,
4343                num_v,
4344                t,
4345                scale,
4346                prep.hk,
4347            )?;
4348        }
4349        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4350
4351        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4352        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4353        let mut gn = e.uninit(d_state * num_v * t)?;
4354        let gn16 = if Self::f16out_on(e, t) {
4355            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4356            e.gated_rmsnorm_f16out_zv(
4357                &o,
4358                la.ssm_norm.float_data(),
4359                z,
4360                &mut gn,
4361                &mut g16,
4362                d_state,
4363                num_v * t,
4364                eps,
4365            )?;
4366            Some(g16)
4367        } else {
4368            e.gated_rmsnorm_zv(
4369                &o,
4370                la.ssm_norm.float_data(),
4371                z,
4372                &mut gn,
4373                d_state,
4374                num_v * t,
4375                eps,
4376            )?;
4377            None
4378        };
4379        Ok((gn, gn16))
4380    }
4381
4382    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4383    #[allow(clippy::too_many_arguments)]
4384    fn linear_attn_prime_core_pad(
4385        &self,
4386        e: &Engine,
4387        la: &LinearAttnLayer,
4388        g4: Vec<CudaSlice<f32>>,
4389        t: usize,
4390        cache: &mut Cache,
4391        il: usize,
4392        pad_len: Option<&CudaSlice<i32>>,
4393    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4394        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4395        if let Some(xh) = &gn16 {
4396            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4397                return Ok(y);
4398            }
4399        }
4400        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4401    }
4402
4403    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4404    ///
4405    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4406    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4407    pub fn full_attn(
4408        &self,
4409        e: &Engine,
4410        fa: &FullAttnLayer,
4411        h: &CudaSlice<f32>,
4412        pos_d: &CudaSlice<i32>,
4413        t: usize,
4414        il: usize,
4415    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4416        if self.uses_sliding_gated_moe_program() {
4417            return self.step35_attn(e, fa, h, pos_d, t, il);
4418        }
4419        let cfg = &self.cfg;
4420        let _n_embd = cfg.n_embd as usize;
4421        let geometry = cfg.full_attention_geometry_at(il as u32);
4422        let n_head = geometry.n_head as usize;
4423        let n_head_kv = geometry.n_head_kv as usize;
4424        let head_dim = geometry.head_dim_k as usize;
4425        let eps = cfg.rms_eps;
4426        let scale = geometry.attention_scale();
4427
4428        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4429        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4430        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4431        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4432        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4433        let v = g3.pop().unwrap();
4434        let mut k = g3.pop().unwrap();
4435        let qf = g3.pop().unwrap();
4436        let (mut q, gate) = if gated {
4437            let mut q = e.uninit(t * n_head * head_dim)?;
4438            let mut gate = e.uninit(t * n_head * head_dim)?;
4439            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4440            (q, Some(gate))
4441        } else {
4442            (qf, None)
4443        };
4444
4445        // QK-norm (per head_dim row), then partial RoPE.
4446        let mut qn = e.uninit(t * n_head * head_dim)?;
4447        e.rms_norm(
4448            &q,
4449            fa.q_norm.float_data(),
4450            &mut qn,
4451            head_dim,
4452            n_head * t,
4453            eps,
4454        )?;
4455        q = qn;
4456        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4457        e.rms_norm(
4458            &k,
4459            fa.k_norm.float_data(),
4460            &mut kn,
4461            head_dim,
4462            n_head_kv * t,
4463            eps,
4464        )?;
4465        k = kn;
4466        let rope_dims = geometry.n_rot as usize;
4467        e.rope_neox(
4468            &mut q,
4469            pos_d,
4470            head_dim,
4471            rope_dims,
4472            n_head,
4473            t,
4474            geometry.rope_base,
4475            1.0,
4476        )?;
4477        e.rope_neox(
4478            &mut k,
4479            pos_d,
4480            head_dim,
4481            rope_dims,
4482            n_head_kv,
4483            t,
4484            geometry.rope_base,
4485            1.0,
4486        )?;
4487
4488        // SDPA
4489        let mut attn = e.uninit(t * n_head * head_dim)?;
4490        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4491        // falls back to naive sdpa.
4492        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4493            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4494            e.sdpa_naive(
4495                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4496            )?;
4497        } else {
4498            e.fa_prefill(
4499                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4500            )?;
4501        }
4502
4503        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4504        let attn_g = match &gate {
4505            Some(gate) => {
4506                let mut gsig = e.uninit(t * n_head * head_dim)?;
4507                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4508                let mut ag = e.uninit(t * n_head * head_dim)?;
4509                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4510                ag
4511            }
4512            None => attn,
4513        };
4514
4515        // o projection
4516        let o = e.matmul(&fa.wo, &attn_g, t)?;
4517        Ok(o)
4518    }
4519
4520    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4521    pub fn linear_attn(
4522        &self,
4523        e: &Engine,
4524        la: &LinearAttnLayer,
4525        h: &CudaSlice<f32>,
4526        t: usize,
4527    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4528        let cfg = &self.cfg;
4529        let _n_embd = cfg.n_embd as usize;
4530        let geometry = la.geometry;
4531        let d_state = geometry.key_head_dim as usize;
4532        let num_k = geometry.key_heads as usize;
4533        let num_v = geometry.value_heads as usize;
4534        let d_conv = geometry.conv_kernel as usize;
4535        let head_k = d_state;
4536        let head_v = geometry.value_head_dim as usize;
4537        let key_dim = head_k * num_k; // 2048
4538        let value_dim = head_v * num_v; // 4096
4539        let conv_dim = key_dim * 2 + value_dim; // 8192
4540        let eps = cfg.rms_eps;
4541        let scale = 1.0 / (d_state as f32).sqrt();
4542
4543        // projections
4544        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4545        let mut g4 = e.matmul_group(
4546            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4547            h,
4548            t,
4549        )?;
4550        let alpha = g4.pop().unwrap(); // [T, num_v]
4551        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4552        let z = g4.pop().unwrap(); // [T, value_dim]
4553        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4554
4555        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4556        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4557        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4558        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4559        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4560        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4561        let _ = (head_k, head_v);
4562        let mut q_g = e.uninit(d_state * num_v * t)?;
4563        let mut k_g = e.uninit(d_state * num_v * t)?;
4564        let mut v_g = e.uninit(d_state * num_v * t)?;
4565        e.ssm_conv1d_gdn(
4566            &qkv_mixed,
4567            la.ssm_conv1d.float_data(),
4568            &mut q_g,
4569            &mut k_g,
4570            &mut v_g,
4571            conv_dim,
4572            t,
4573            d_conv,
4574            d_state,
4575            num_v,
4576            num_k,
4577            key_dim,
4578        )?;
4579        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4580        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4581        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4582        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4583        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4584        let v_gd = v_g;
4585
4586        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4587        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4588        let mut beta = e.uninit(t * num_v)?;
4589        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4590        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4591        let mut g_log = e.uninit(t * num_v)?;
4592        e.gdn_glog(
4593            &alpha,
4594            la.ssm_dt.float_data(),
4595            la.ssm_a.float_data(),
4596            &mut g_log,
4597            num_v,
4598            t,
4599        )?;
4600
4601        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4602        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4603        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4604        let mut o = e.uninit(d_state * num_v * t)?;
4605        e.gdn_scan_prefill(
4606            &q_l2,
4607            &k_l2,
4608            &v_gd,
4609            &g_log,
4610            &beta,
4611            None,
4612            None,
4613            &state_in,
4614            &mut state_out,
4615            &mut o,
4616            num_v,
4617            t,
4618            scale,
4619            num_v,
4620        )?;
4621
4622        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4623        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4624        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4625        // o rows are (t*num_v+vh) too. Good.
4626        let mut gn = e.uninit(d_state * num_v * t)?;
4627        e.gated_rmsnorm(
4628            &o,
4629            la.ssm_norm.float_data(),
4630            &z,
4631            &mut gn,
4632            d_state,
4633            num_v * t,
4634            eps,
4635        )?;
4636
4637        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4638        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4639        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4640        let out = e.matmul(&la.ssm_out, &gn, t)?;
4641        Ok(out)
4642    }
4643}
4644
4645impl HybridModel {
4646    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4647    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4648    ///
4649    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4650    /// different 860160-byte block than the same expert of layer 7).
4651    ///
4652    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4653    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4654    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4655    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4656    pub fn moe_ffn_il(
4657        &self,
4658        e: &Engine,
4659        m: &MoeWeights,
4660        z: &CudaSlice<f32>,
4661        t: usize,
4662        il: u16,
4663    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4664        Self::moe_ffn_inner(
4665            e,
4666            m,
4667            z,
4668            None,
4669            t,
4670            &self.cfg,
4671            il,
4672            self.max_moe_block(),
4673            false,
4674            None,
4675            self.uses_sliding_gated_moe_program(),
4676        )
4677    }
4678
4679    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4680    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4681    pub fn moe_ffn_il_prefill(
4682        &self,
4683        e: &Engine,
4684        m: &MoeWeights,
4685        z: &CudaSlice<f32>,
4686        t: usize,
4687        il: u16,
4688    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4689        Self::moe_ffn_inner(
4690            e,
4691            m,
4692            z,
4693            None,
4694            t,
4695            &self.cfg,
4696            il,
4697            self.max_moe_block(),
4698            true,
4699            Some(&self.step_grouped_prefill),
4700            self.uses_sliding_gated_moe_program(),
4701        )
4702    }
4703
4704    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4705    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4706    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4707    pub fn moe_ffn_il_zq8(
4708        &self,
4709        e: &Engine,
4710        m: &MoeWeights,
4711        z: &CudaSlice<f32>,
4712        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4713        t: usize,
4714        il: u16,
4715    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4716        Self::moe_ffn_inner(
4717            e,
4718            m,
4719            z,
4720            zq8,
4721            t,
4722            &self.cfg,
4723            il,
4724            self.max_moe_block(),
4725            false,
4726            None,
4727            self.uses_sliding_gated_moe_program(),
4728        )
4729    }
4730
4731    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4732    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4733    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4734    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4735    ///
4736    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4737    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4738    pub(crate) fn moe_ffn(
4739        e: &Engine,
4740        m: &MoeWeights,
4741        z: &CudaSlice<f32>,
4742        t: usize,
4743        cfg: &ModelConfig,
4744        il: u16,
4745        max_block: usize,
4746    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4747        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4748    }
4749
4750    #[allow(clippy::too_many_arguments)]
4751    pub(crate) fn moe_ffn_inner(
4752        e: &Engine,
4753        m: &MoeWeights,
4754        z: &CudaSlice<f32>,
4755        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4756        t: usize,
4757        cfg: &ModelConfig,
4758        il: u16,
4759        max_block: usize,
4760        prefill: bool,
4761        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
4762        sliding_gated_moe: bool,
4763    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4764        let worker_io = crate::spill_pread::worker_enabled();
4765        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
4766        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
4767            e.with_moe_cache(max_block, |cache, _| {
4768                cache.begin_forward_epoch(il, t);
4769                if worker_io {
4770                    cache.begin_worker_scope();
4771                }
4772                Ok(())
4773            })?;
4774        }
4775        if m.step_ep.is_some() || m.step_tp.is_some() {
4776            let moe = cfg
4777                .moe
4778                .as_ref()
4779                .ok_or("Step distributed execution requires MoE model metadata")?;
4780            let n_embd = cfg.n_embd as usize;
4781            let n_expert = moe.expert_count as usize;
4782            let n_used = moe.expert_used_count as usize;
4783            let sigmoid = cfg
4784                .sigmoid_router()
4785                .ok_or("Step distributed execution requires the Step sigmoid router")?;
4786            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
4787            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
4788            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
4789            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
4790                return Err(
4791                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
4792                );
4793            }
4794            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
4795                return Err(format!(
4796                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
4797                    PRIME_MIN_T,
4798                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
4799                )
4800                .into());
4801            }
4802            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
4803            let grouped_prefill_shape =
4804                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
4805            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
4806                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
4807            }) {
4808                let (selected, route_weights) =
4809                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
4810                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
4811                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
4812                Self::trace_moe_input(e, il, t, n_embd, z)?;
4813                let selected = selected
4814                    .iter()
4815                    .map(|&expert| expert as usize)
4816                    .collect::<Vec<_>>();
4817
4818                // The narrow route readback above orders the owning-stage producer. The grouped
4819                // runtime then copies the resident root activation into its persistent rank inputs.
4820                e.stream().synchronize()?;
4821                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
4822                    state.projection.set_activation_limit(ep.activation_limit)?;
4823                    ep.runtime
4824                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
4825                            ep.experts.e4m3()?,
4826                            &mut state.projection,
4827                            z,
4828                            t,
4829                            &selected,
4830                        )?;
4831                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
4832                        &state.projection,
4833                        &mut state.combine,
4834                        &route_weights,
4835                    )?;
4836                    ep.runtime.execute_step_grouped_expert_parallel_gate(
4837                        ep.experts.e4m3()?,
4838                        &mut state.projection,
4839                    )?;
4840                    ep.runtime.execute_step_grouped_expert_parallel_combine(
4841                        &state.projection,
4842                        &mut state.combine,
4843                    )?;
4844                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
4845                        &state.projection,
4846                        &state.combine,
4847                        e,
4848                    )?;
4849                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
4850                    if prefill {
4851                        // A shared plan may be reused by the next layer on a different runtime
4852                        // stream. Complete the owning-stage copy before its source is overwritten.
4853                        e.stream().synchronize()?;
4854                    }
4855                    eprintln!(
4856                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
4857                         attention_layout=tensor-parallel expert_layout=expert-parallel \
4858                         expert_transport={} native_p2p=true route_control=host-narrow \
4859                         input=root-device projection_workspaces=persistent \
4860                         combine=root-device output=owning-stage-device \
4861                         prefill={prefill} batched_decode=false capacity={} \
4862                         performance_claim=false",
4863                        ep.devices,
4864                        ep.runtime.transport_label(),
4865                        state.projection.max_tokens(),
4866                    );
4867                    Ok::<_, Box<dyn std::error::Error>>(output)
4868                };
4869
4870                if grouped_prefill_shape {
4871                    let grouped_prefill = grouped_prefill
4872                        .ok_or("Step grouped prefill has no model-scoped executor")?;
4873                    let mut shared = grouped_prefill
4874                        .lock()
4875                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
4876                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
4877                        state.devices != ep.devices
4878                            || state.grouped.projection.max_tokens() < t
4879                            || state.grouped.projection.input_width() != n_embd
4880                            || state.grouped.projection.expert_width()
4881                                != moe.expert_ff_length as usize
4882                    });
4883                    if needs_prepare {
4884                        let seed_input = vec![0.0f32; n_embd];
4885                        let seed_selected = &selected[..n_used];
4886                        let seed_weights = &route_weights[..n_used];
4887                        let projection = ep
4888                            .runtime
4889                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
4890                                ep.experts.e4m3()?,
4891                                &seed_input,
4892                                1,
4893                                seed_selected,
4894                                ep.activation_limit,
4895                                t,
4896                            )?;
4897                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
4898                            &projection,
4899                            seed_weights,
4900                        )?;
4901                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
4902                            devices: ep.devices.clone(),
4903                            grouped: crate::hybrid::StepEpGroupedDecode {
4904                                projection,
4905                                combine,
4906                            },
4907                        });
4908                        eprintln!(
4909                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
4910                             shared_across_layers=true performance_claim=false",
4911                            ep.devices,
4912                        );
4913                    }
4914                    return execute(
4915                        &mut shared
4916                            .state
4917                            .as_mut()
4918                            .expect("Step grouped prefill state prepared above")
4919                            .grouped,
4920                    );
4921                }
4922
4923                let mut grouped = ep
4924                    .grouped_decode
4925                    .as_ref()
4926                    .expect("grouped decode presence checked above")
4927                    .lock()
4928                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
4929                return execute(&mut grouped);
4930            }
4931            if grouped_prefill_shape {
4932                return Err(
4933                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
4934                        .into(),
4935                );
4936            }
4937            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
4938            // expert program — the per-layer host logits readback (the last per-layer host
4939            // sync) disappears. Selection tie-breaking may differ from the host router:
4940            // numeric-class door, run-gen argmax gate + boot battery.
4941            if t == 1
4942                && crate::tp::step_nvfp4_dev_routes_enabled()?
4943                && crate::tp::step_tp_dev_router_enabled()?
4944            {
4945                if let Some(tp) = &m.step_tp {
4946                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
4947                        let (sf, route_norm) = sigmoid;
4948                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
4949                        // before the router — the rank streams overlap the gemv+topk.
4950                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
4951                        // from its own z copy (replicated deterministic router — identical
4952                        // bits in, identical sel/w out) and starts its sweep without
4953                        // waiting the root's sel broadcast.
4954                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4955                        let d1_router = *D1_ROUTER.get_or_init(|| {
4956                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
4957                        });
4958                        if d1_router {
4959                            let (sf_h, rn_h) = sigmoid;
4960                            let n_ex = m.gate_exps.n_expert;
4961                            let act_ct = m.active_count();
4962                            let _ = tp.runtime.nvfp4_routes_prestage_with(
4963                                bank,
4964                                e,
4965                                z,
4966                                |rank1, in1, sel1, w1| {
4967                                    let mut guard = DEV1_ROUTER_REPS
4968                                        .lock()
4969                                        .map_err(|_| "dev1 router replica lock")?;
4970                                    let (reps, scratch) =
4971                                        guard.get_or_insert_with(|| (Default::default(), None));
4972                                    if !reps.contains_key(&il) {
4973                                        use cudarc::driver::DevicePtr;
4974                                        let (g1, p1, a1) = (
4975                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
4976                                            rank1.htod(&vec![0.0f32; n_ex])?,
4977                                            rank1.alloc_u8_uninit(n_ex)?,
4978                                        );
4979                                        for (src, dst_len, dst) in [
4980                                            (
4981                                                {
4982                                                    let s = e.stream();
4983                                                    let (p, _g) =
4984                                                        m.gate_inp.float_data().device_ptr(&s);
4985                                                    p as u64
4986                                                },
4987                                                n_ex * n_embd * 4,
4988                                                {
4989                                                    let s = rank1.stream();
4990                                                    let (p, _g) = g1.device_ptr(&s);
4991                                                    p as u64
4992                                                },
4993                                            ),
4994                                            (
4995                                                {
4996                                                    let s = e.stream();
4997                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
4998                                                    p as u64
4999                                                },
5000                                                n_ex * 4,
5001                                                {
5002                                                    let s = rank1.stream();
5003                                                    let (p, _g) = p1.device_ptr(&s);
5004                                                    p as u64
5005                                                },
5006                                            ),
5007                                            (
5008                                                {
5009                                                    let s = e.stream();
5010                                                    let (p, _g) =
5011                                                        m.active_experts_dev.device_ptr(&s);
5012                                                    p as u64
5013                                                },
5014                                                n_ex,
5015                                                {
5016                                                    let s = rank1.stream();
5017                                                    let (p, _g) = a1.device_ptr(&s);
5018                                                    p as u64
5019                                                },
5020                                            ),
5021                                        ] {
5022                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
5023                                        }
5024                                        rank1.stream().synchronize()?;
5025                                        reps.insert(il, (g1, p1, a1));
5026                                    }
5027                                    if scratch.is_none() {
5028                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5029                                    }
5030                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5031                                    let logits1 = scratch.as_mut().expect("armed above");
5032                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5033                                    rank1.moe_router_sigmoid_topk_into(
5034                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5035                                        w1,
5036                                    )?;
5037                                    Ok(true)
5038                                },
5039                            )?;
5040                        } else {
5041                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5042                        }
5043                        // Persistent selection buffers: the allocating topk built two fresh
5044                        // slices per layer; sel/w land in process-static rows instead
5045                        // (host-op diet — same kernel, same bytes).
5046                        static SELW: std::sync::Mutex<
5047                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5048                        > = std::sync::Mutex::new(None);
5049                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5050                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5051                            *selw = Some((
5052                                e.ctx().ordinal(),
5053                                e.htod_i32(&vec![0i32; n_used])?,
5054                                e.htod(&vec![0.0f32; n_used])?,
5055                            ));
5056                        }
5057                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5058                        e.moe_router_sigmoid_topk_into(
5059                            &logits,
5060                            t,
5061                            n_expert,
5062                            n_used,
5063                            m.active_count(),
5064                            &m.exp_probs_b_dev,
5065                            &m.active_experts_dev,
5066                            sf,
5067                            route_norm,
5068                            sel_d,
5069                            w_d,
5070                        )?;
5071                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5072                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5073                        // PREJOIN hook so it executes while the peer rank drains its sweep
5074                        // (fills dev0's join wait); apply adds the identical values after.
5075                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5076                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5077                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5078                        });
5079                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5080                        // expert runs on rank1 — the idle device — same kernels, same
5081                        // split program, down row root-resident: bit-identical.
5082                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5083                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5084                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5085                        }) && tp.runtime.rank_engine(1).is_some();
5086                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5087                        // overlap ws + ones row and hand their RAW pointers to the routed
5088                        // run — the join add folds the shexp apply into one launch.
5089                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5090                        let tail3 = *TAIL3
5091                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5092                        let mut ov_issued = false;
5093                        let mut d1_issued = false;
5094                        let mut tail_folded = false;
5095                        let mut output = if shexp_d1 {
5096                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5097                            tp.runtime
5098                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5099                                    bank,
5100                                    e,
5101                                    z,
5102                                    &sel_d,
5103                                    &w_d,
5104                                    n_used,
5105                                    tp.activation_limit,
5106                                    || {
5107                                        d1_issued = Self::shexp_dev1_issue(
5108                                            e, rank1, m, z, cfg, il, n_embd,
5109                                        )?;
5110                                        Ok(())
5111                                    },
5112                                )?
5113                        } else if shexp_ov {
5114                            // Raw sh/ones pointers for the fused tail (persistent statics;
5115                            // pointers stable, no lock held across the routed call). The
5116                            // sh CONTENT is written by the prejoin-issued kernels earlier
5117                            // on e's stream — stream order covers the fused add.
5118                            let post_add = if tail3 {
5119                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5120                            } else {
5121                                None
5122                            };
5123                            let used_post = post_add.is_some();
5124                            let out = tp
5125                                .runtime
5126                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5127                                    bank,
5128                                    e,
5129                                    z,
5130                                    &sel_d,
5131                                    &w_d,
5132                                    n_used,
5133                                    tp.activation_limit,
5134                                    || {
5135                                        ov_issued =
5136                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5137                                        Ok(())
5138                                    },
5139                                    post_add,
5140                                )?;
5141                            // ov_issued false with post_add armed = an early-return arm
5142                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5143                            // fall through to the normal shexp add (battery v22 receipt:
5144                            // the strict error here failed every graph-door boot).
5145                            if used_post && ov_issued {
5146                                tail_folded = true; // apply folded into the join add
5147                            }
5148                            out
5149                        } else {
5150                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5151                                bank,
5152                                e,
5153                                z,
5154                                &sel_d,
5155                                &w_d,
5156                                n_used,
5157                                tp.activation_limit,
5158                            )?
5159                        };
5160                        if output.len() != t * n_embd {
5161                            return Err(format!(
5162                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5163                                output.len()
5164                            )
5165                            .into());
5166                        }
5167                        if tail_folded {
5168                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5169                        } else if d1_issued {
5170                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5171                        } else if ov_issued {
5172                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5173                        } else {
5174                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5175                        }
5176                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5177                            std::sync::atomic::AtomicU64::new(0);
5178                        let layer_bit = 1u64 << (il as u64 % 64);
5179                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5180                            & layer_bit
5181                            == 0
5182                        {
5183                            eprintln!(
5184                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5185                                 expert_transport={} native_p2p={} router=device \
5186                                 activation=host-canonical accumulation=host-canonical \
5187                                 output=e-device io=device performance_claim=false \
5188                                 (logged once per layer)",
5189                                tp.devices,
5190                                tp.runtime.transport_label(),
5191                                tp.runtime.native_p2p(),
5192                            );
5193                        }
5194                        return Ok(output);
5195                    }
5196                }
5197            }
5198            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5199            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5200            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5201            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5202            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5203            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5204            let route_started = route_timing.then(std::time::Instant::now);
5205            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5206                e,
5207                &logits,
5208                z,
5209                t,
5210                n_embd,
5211                n_expert,
5212                n_used,
5213                m.exp_probs_b.as_deref(),
5214                sigmoid,
5215                m.active_experts.as_deref(),
5216            )?;
5217            if let Some(started) = route_started {
5218                use std::sync::atomic::Ordering;
5219                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5220                    + started.elapsed().as_nanos() as u64;
5221                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5222                if calls % 430 == 0 {
5223                    eprintln!(
5224                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5225                        ns as f64 / 1.0e6,
5226                        ns as f64 / calls as f64 / 1.0e3,
5227                    );
5228                }
5229            }
5230            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5231            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5232            Self::trace_moe_input(e, il, t, n_embd, z)?;
5233            let selected = selected
5234                .iter()
5235                .map(|&expert| expert as usize)
5236                .collect::<Vec<_>>();
5237            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5238            // combined output comes back as an e-context row — no host round-trip, no host
5239            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5240            // both preserve f32 bits), gated by greedy token identity.
5241            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5242                if let Some(tp) = &m.step_tp {
5243                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5244                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5245                            bank,
5246                            e,
5247                            z,
5248                            &selected,
5249                            &route_weights,
5250                            n_used,
5251                            tp.activation_limit,
5252                        )?;
5253                        if output.len() != t * n_embd {
5254                            return Err(format!(
5255                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5256                                output.len()
5257                            )
5258                            .into());
5259                        }
5260                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5261                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5262                            std::sync::atomic::AtomicU64::new(0);
5263                        let layer_bit = 1u64 << (il as u64 % 64);
5264                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5265                            & layer_bit
5266                            == 0
5267                        {
5268                            eprintln!(
5269                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5270                                 expert_transport={} native_p2p={} activation=host-canonical \
5271                                 accumulation=host-canonical output=e-device io=device \
5272                                 performance_claim=false (logged once per layer)",
5273                                tp.devices,
5274                                tp.runtime.transport_label(),
5275                                tp.runtime.native_p2p(),
5276                            );
5277                        }
5278                        return Ok(output);
5279                    }
5280                }
5281            }
5282            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5283                (
5284                    match &tp.experts {
5285                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5286                            tp.runtime.run_tensor_parallel_routes(
5287                                bank,
5288                                &input,
5289                                t,
5290                                &selected,
5291                                &route_weights,
5292                                n_used,
5293                            )?
5294                        }
5295                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5296                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5297                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5298                                    bank,
5299                                    &input,
5300                                    &selected,
5301                                    &route_weights,
5302                                    n_used,
5303                                    tp.activation_limit,
5304                                )?
5305                            } else {
5306                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5307                                    bank,
5308                                    &input,
5309                                    t,
5310                                    &selected,
5311                                    &route_weights,
5312                                    n_used,
5313                                    tp.activation_limit,
5314                                )?
5315                            }
5316                        }
5317                    },
5318                    "tp",
5319                    &tp.devices,
5320                    tp.runtime.transport_label(),
5321                    tp.runtime.native_p2p(),
5322                )
5323            } else {
5324                let ep = m
5325                    .step_ep
5326                    .as_ref()
5327                    .ok_or("Step distributed runtime has no EP or TP state")?;
5328                (
5329                    match &ep.experts {
5330                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5331                            ep.runtime.run_routed_experts(
5332                                bank,
5333                                &input,
5334                                t,
5335                                &selected,
5336                                &route_weights,
5337                                n_used,
5338                                ep.activation_limit,
5339                            )?
5340                        }
5341                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5342                            ep.runtime.run_routed_experts_nvfp4(
5343                                bank,
5344                                &input,
5345                                t,
5346                                &selected,
5347                                &route_weights,
5348                                n_used,
5349                                ep.activation_limit,
5350                            )?
5351                        }
5352                    },
5353                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5354                    &ep.devices,
5355                    ep.runtime.transport_label(),
5356                    ep.runtime.native_p2p(),
5357                )
5358            };
5359            if routed.len() != t * n_embd {
5360                return Err(format!(
5361                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5362                    routed.len()
5363                )
5364                .into());
5365            }
5366            let mut output = e.htod(&routed)?;
5367            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5368            // Once per layer per process: the topology contract line is a boot receipt, not a
5369            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5370            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5371            let layer_bit = 1u64 << (il as u64 % 64);
5372            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5373                == 0
5374            {
5375                eprintln!(
5376                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5377                     expert_transport={transport} native_p2p={native_p2p} \
5378                     activation={} accumulation={} output={} \
5379                     performance_claim=false (logged once per layer)",
5380                    if let Some(ep) = &m.step_ep {
5381                        ep.runtime.expert_activation_label()
5382                    } else {
5383                        "host-canonical"
5384                    },
5385                    if let Some(ep) = &m.step_ep {
5386                        ep.runtime.expert_accumulation_label()
5387                    } else {
5388                        "host-canonical"
5389                    },
5390                    if let Some(ep) = &m.step_ep {
5391                        ep.runtime.expert_output_label()
5392                    } else {
5393                        "host-accumulated"
5394                    },
5395                );
5396                if let Some(ep) = &m.step_ep {
5397                    if let Some(limit) = ep.activation_limit {
5398                        eprintln!(
5399                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5400                             formula=min-silu-times-clamped-up performance_claim=false"
5401                        );
5402                    }
5403                }
5404            }
5405            return Ok(output);
5406        }
5407        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5408            let moe = cfg.moe.as_ref().unwrap();
5409            let n_expert = moe.expert_count as usize;
5410            let n_used = moe.expert_used_count as usize;
5411            let sigmoid = cfg.sigmoid_router().unwrap();
5412            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5413            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5414            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5415        }
5416        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5417        // current caller into this research arm; the naked default stays on the established path.
5418        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5419            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5420            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5421            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5422            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5423            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5424            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5425                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5426                let g_host = e.dtoh(&grouped_out)?;
5427                let s_host = e.dtoh(&seq_out)?;
5428                let g_bytes: &[u8] = unsafe {
5429                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5430                };
5431                let s_bytes: &[u8] = unsafe {
5432                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5433                };
5434                if g_bytes == s_bytes {
5435                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5436                } else {
5437                    let diffs = g_host
5438                        .iter()
5439                        .zip(s_host.iter())
5440                        .enumerate()
5441                        .filter(|(_, (a, b))| a != b)
5442                        .count();
5443                    let maxdiff = g_host
5444                        .iter()
5445                        .zip(s_host.iter())
5446                        .map(|(a, b)| (a - b).abs())
5447                        .fold(0.0f32, f32::max);
5448                    panic!(
5449                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5450                        g_host.len()
5451                    );
5452                }
5453            }
5454            return Ok(grouped_out);
5455        }
5456        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5457    }
5458
5459    fn sigmoid_resident_dev_eligible(
5460        e: &Engine,
5461        m: &MoeWeights,
5462        cfg: &ModelConfig,
5463        sliding_gated_moe: bool,
5464    ) -> bool {
5465        let Some(moe) = cfg.moe.as_ref() else {
5466            return false;
5467        };
5468        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5469        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5470        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5471        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5472            std::env::var("MEMRA_MOE_STATS").is_ok()
5473                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5474                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5475                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5476                || std::env::var("MEMRA_MOE_GATE").is_ok()
5477        });
5478        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5479            if dev.dev != e.ctx().ordinal() {
5480                return false;
5481            }
5482            let q8 = moe_q8_enabled()
5483                && q8_expert_supported(m.gate_exps.qtype)
5484                && q8_expert_supported(m.up_exps.qtype)
5485                && q8_expert_supported(m.down_exps.qtype);
5486            let fp8 = dev.fp8_blk.is_some()
5487                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5488                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5489                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5490            q8 || fp8
5491        });
5492        sliding_gated_moe
5493            && sigmoid_router_enabled()
5494            && moe_dev_enabled()
5495            && moe_slab_enabled()
5496            && !observation_mode
5497            && moe.expert_used_count <= 8
5498            && m.has_uniform_expert_layout()
5499            && m.gate_exps.macros.is_none()
5500            && m.up_exps.macros.is_none()
5501            && m.down_exps.macros.is_none()
5502            && !m.has_macros
5503            && resident_layout_supported
5504    }
5505
5506    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5507    pub(crate) fn moe_ffn_sequential(
5508        e: &Engine,
5509        m: &MoeWeights,
5510        z: &CudaSlice<f32>,
5511        t: usize,
5512        cfg: &ModelConfig,
5513        il: u16,
5514        max_block: usize,
5515    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5516        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5517    }
5518
5519    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5520    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5521    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5522    fn moe_router_logits(
5523        e: &Engine,
5524        m: &MoeWeights,
5525        z: &CudaSlice<f32>,
5526        t: usize,
5527        cfg: &ModelConfig,
5528    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5529        if t < PRIME_MIN_T {
5530            // Decode and speculative verify use one fixed per-row reduction program.
5531            if crate::router_kernel_on() {
5532                e.router_gemv(
5533                    m.gate_inp.float_data(),
5534                    z,
5535                    cfg.n_embd as usize,
5536                    m.gate_exps.n_expert,
5537                    t,
5538                )
5539            } else {
5540                e.matmul_decode_exact(&m.gate_inp, z, t)
5541            }
5542        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5543            e.router_gemv(
5544                m.gate_inp.float_data(),
5545                z,
5546                cfg.n_embd as usize,
5547                m.gate_exps.n_expert,
5548                t,
5549            )
5550        } else {
5551            e.matmul(&m.gate_inp, z, t)
5552        }
5553    }
5554
5555    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5556    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5557    /// trace is independent of the dispatch optimization selected for the forward.
5558    fn trace_moe_routes(
5559        il: u16,
5560        t: usize,
5561        sel_all: &[u32],
5562        weights: &[f32],
5563    ) -> Result<(), Box<dyn std::error::Error>> {
5564        use std::io::Write as _;
5565        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5566            let mut f = std::fs::OpenOptions::new()
5567                .create(true)
5568                .append(true)
5569                .open(path)?;
5570            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5571            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5572        }
5573        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5574            let mut f = std::fs::OpenOptions::new()
5575                .create(true)
5576                .append(true)
5577                .open(path)?;
5578            let pairs: Vec<String> = sel_all
5579                .iter()
5580                .zip(weights)
5581                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5582                .collect();
5583            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5584        }
5585        Ok(())
5586    }
5587
5588    #[allow(clippy::too_many_arguments)]
5589    fn trace_sigmoid_router_logits(
5590        e: &Engine,
5591        il: u16,
5592        t: usize,
5593        n_expert: usize,
5594        n_used: usize,
5595        logits: &CudaSlice<f32>,
5596        m: &MoeWeights,
5597        (scaling_factor, route_norm): (f32, bool),
5598    ) -> Result<(), Box<dyn std::error::Error>> {
5599        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5600            return Ok(());
5601        }
5602        let logits = e.dtoh(logits)?;
5603        let active: Vec<u8> = m
5604            .active_experts
5605            .as_ref()
5606            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5607            .unwrap_or_else(|| vec![1; n_expert]);
5608        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5609        crate::sigrouter_contract::capture_served_logits(
5610            il as u32,
5611            t,
5612            n_expert,
5613            n_used,
5614            scaling_factor,
5615            route_norm,
5616            &active,
5617            &bias,
5618            &logits,
5619        )?;
5620        Ok(())
5621    }
5622
5623    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
5624    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
5625    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
5626    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
5627    fn trace_moe_input(
5628        e: &Engine,
5629        il: u16,
5630        t: usize,
5631        n_embd: usize,
5632        z: &CudaSlice<f32>,
5633    ) -> Result<(), Box<dyn std::error::Error>> {
5634        use std::io::Write as _;
5635        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
5636            return Ok(());
5637        };
5638        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
5639        let host = e.dtoh_view(&z.slice(0..values))?;
5640        let bytes = unsafe {
5641            std::slice::from_raw_parts(
5642                host.as_ptr().cast::<u8>(),
5643                host.len() * std::mem::size_of::<f32>(),
5644            )
5645        };
5646        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
5647        let mut state = state
5648            .lock()
5649            .map_err(|_| "MoE input trace writer lock is poisoned")?;
5650        if state.is_none() {
5651            let dir = std::path::PathBuf::from(&dir);
5652            std::fs::create_dir_all(&dir)?;
5653            let index = std::fs::OpenOptions::new()
5654                .create(true)
5655                .append(true)
5656                .open(dir.join("index.jsonl"))?;
5657            *state = Some(MoeInputTraceWriter {
5658                dir,
5659                index,
5660                payloads: std::collections::HashMap::new(),
5661            });
5662        }
5663        let writer = state.as_mut().unwrap();
5664        if writer.dir != std::path::Path::new(&dir) {
5665            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
5666        }
5667        let file_name = format!("layer-{il:03}.f32");
5668        if !writer.payloads.contains_key(&il) {
5669            let payload = std::fs::OpenOptions::new()
5670                .create(true)
5671                .append(true)
5672                .open(writer.dir.join(&file_name))?;
5673            let offset = payload.metadata()?.len();
5674            writer.payloads.insert(il, (payload, offset));
5675        }
5676        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
5677        let row_offset = *offset;
5678        payload.write_all(bytes)?;
5679        *offset += bytes.len() as u64;
5680        writeln!(
5681            writer.index,
5682            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
5683             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
5684             \"payload_bytes\":{}}}",
5685            bytes.len()
5686        )?;
5687        Ok(())
5688    }
5689
5690    #[allow(clippy::too_many_arguments)]
5691    pub(crate) fn moe_ffn_sequential_zq8(
5692        e: &Engine,
5693        m: &MoeWeights,
5694        z: &CudaSlice<f32>,
5695        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5696        t: usize,
5697        cfg: &ModelConfig,
5698        il: u16,
5699        max_block: usize,
5700    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5701        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
5702        let moe = cfg.moe.as_ref().unwrap();
5703        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
5704        let n_expert = moe.expert_count as usize; // 256
5705        let n_used = moe.expert_used_count as usize; // 8
5706        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
5707
5708        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
5709        debug_assert_eq!(m.gate_exps.in_f, n_embd);
5710        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
5711        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
5712        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
5713        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
5714
5715        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
5716        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
5717        let lim_exp = cfg.clamp_exp_at(il as u32);
5718        let lim_shexp = cfg.clamp_shexp_at(il as u32);
5719        let use_cache = Engine::moe_cache_enabled();
5720        let uniform_experts = m.has_uniform_expert_layout();
5721        let moe_q8 = uniform_experts
5722            && moe_q8_enabled()
5723            && q8_expert_supported(m.gate_exps.qtype)
5724            && q8_expert_supported(m.up_exps.qtype)
5725            && q8_expert_supported(m.down_exps.qtype);
5726        // Experimental secondary backend: complete experts already resident in the SLRU stay on
5727        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
5728        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
5729        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
5730        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
5731        // commands and CI have no llama.cpp or OpenMP dependency.
5732        let cpu_expert_requested = crate::cpu_experts::configured();
5733        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
5734            return Err(std::io::Error::other(
5735                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
5736            )
5737            .into());
5738        }
5739        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
5740        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
5741        // Those backends are each deterministic but are different numeric configurations, so a
5742        // later prefill eviction can change greedy output. Freeze after the first real prefill;
5743        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
5744        // staging below and cannot change backend assignment.
5745        let freeze_cpu_residency = cpu_expert_requested
5746            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
5747        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
5748            .ok()
5749            .and_then(|value| value.parse::<usize>().ok())
5750            .is_some_and(|tokens| tokens > 0);
5751        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
5752            e.freeze_moe_cache();
5753        }
5754        let cache_frozen = use_cache && e.moe_cache_frozen();
5755        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
5756
5757        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
5758        // cannot change logits, selected expert ids, or routing weights.
5759        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5760        if let Some(sig) = cfg.sigmoid_router() {
5761            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
5762        }
5763
5764        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
5765        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
5766        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
5767        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
5768        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
5769        // per-token host stall that dominated the 35B decode wall after stages 1+2.
5770        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
5771        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
5772        // only difference is where sel/w/pointers are READ from (device instead of params).
5773        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
5774        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
5775        // Any non-resident layer falls through to host routing + the gdec/sequential path.
5776        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
5777        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
5778        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
5779        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
5780        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
5781        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
5782        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
5783        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
5784        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
5785        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
5786        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
5787        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
5788        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
5789        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
5790        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
5791        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
5792        // now rides the dev loop below (same kernels per token as decode); pairs serves real
5793        // prefill (t >= 16, where spec never verifies).
5794        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
5795        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
5796        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
5797        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
5798        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
5799        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
5800        // ride the macro-aware sequential/staged paths below or every expert output is off by
5801        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
5802        let no_exp_macros = m.gate_exps.macros.is_none()
5803            && m.up_exps.macros.is_none()
5804            && m.down_exps.macros.is_none();
5805        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
5806        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
5807        // so it cannot even see the per-layer limit.
5808        if cfg.sigmoid_router().is_none()
5809            && cfg.m3.is_none()
5810            && cfg.hy3.is_none()
5811            && !cfg.swiglu_clamped_at(il as u32)
5812            && no_exp_macros
5813            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
5814            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
5815            // pairs serves real prefill from 17 up.
5816            && t > MOE_DEV_MAX_T
5817            && m.dev_exps.is_some()
5818            && moe_q8_enabled()
5819            && q8_expert_supported(m.gate_exps.qtype)
5820            && q8_expert_supported(m.up_exps.qtype)
5821            && q8_expert_supported(m.down_exps.qtype)
5822            && std::env::var("MEMRA_MOE_PAIRS")
5823                .map(|v| v != "0")
5824                .unwrap_or(true)
5825            && std::env::var("MEMRA_MOE_STATS").is_err()
5826        {
5827            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
5828        }
5829
5830        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
5831        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
5832        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
5833        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
5834        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
5835        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
5836        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
5837        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
5838        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
5839        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
5840        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
5841        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
5842        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
5843        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
5844        // Keyed off sigmoid_router() so arch #4 is denied by construction.
5845        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
5846        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
5847        let dev_ok = uniform_experts
5848            && cfg.sigmoid_router().is_none()
5849            && cfg.m3.is_none()
5850            && cfg.hy3.is_none()
5851            && !cfg.swiglu_clamped_at(il as u32);
5852        // Observation modes must route through the host-visible selection below. Otherwise a fully
5853        // resident layer returns through device dispatch before its trace/stats row is recorded,
5854        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
5855        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
5856            || std::env::var("MEMRA_MOE_TRACE").is_ok()
5857            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5858            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
5859        if dev_ok
5860            && t <= MOE_DEV_MAX_T
5861            && m.dev_exps.is_some()
5862            && n_used <= 8
5863            && moe_dev_enabled()
5864            && !observe_routes
5865        {
5866            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5867        }
5868        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
5869            let row_ok = e.with_moe_cache(max_block, |c, eng| {
5870                if moe_prewarm_enabled() {
5871                    c.prewarm_layer(il, m, eng)?;
5872                }
5873                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
5874            })?;
5875            if row_ok {
5876                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5877            }
5878        }
5879
5880        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
5881        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
5882            if cpu_hybrid {
5883                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
5884                    e,
5885                    &logits,
5886                    z,
5887                    t,
5888                    n_embd,
5889                    n_expert,
5890                    n_used,
5891                    m.exp_probs_b.as_deref(),
5892                    sig,
5893                    m.active_experts.as_deref(),
5894                )?;
5895                (sel, w, Some(input))
5896            } else {
5897                let (sel, w) =
5898                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
5899                (sel, w, None)
5900            }
5901        } else {
5902            let (sel, w) =
5903                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
5904            (sel, w, None)
5905        };
5906        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
5907
5908        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
5909        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
5910        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
5911        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
5912        Self::trace_moe_input(e, il, t, n_embd, z)?;
5913
5914        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
5915        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
5916        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
5917        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
5918        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
5919        // wait for each pending block, so later copies can overlap the earlier expert kernels while
5920        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
5921        // T=1; batched forwards can have token-local consumers still in flight between selections.
5922        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
5923        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
5924        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
5925        let worker_disk_prefetch =
5926            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
5927        let promote_worker_h2d =
5928            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
5929        if promote_worker_h2d {
5930            let mut selected_blocks = Vec::with_capacity(n_used * 3);
5931            for &ex in sel_all.iter().take(n_used) {
5932                let ex = ex as u16;
5933                selected_blocks.extend([
5934                    BlockId::new(il, PROJ_GATE, ex),
5935                    BlockId::new(il, PROJ_UP, ex),
5936                    BlockId::new(il, PROJ_DOWN, ex),
5937                ]);
5938            }
5939            for &ex in sel_all.iter().take(n_used) {
5940                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
5941            }
5942            e.with_moe_cache(max_block, |cache, eng| {
5943                cache.promote_worker_reads_at_safe_boundary(
5944                    &selected_blocks,
5945                    &selected_blocks,
5946                    eng,
5947                )?;
5948                Ok(())
5949            })?;
5950        }
5951
5952        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
5953        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
5954        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
5955            let mut cnt = vec![0u32; n_expert];
5956            for &s in sel_all.iter() {
5957                cnt[s as usize] += 1;
5958            }
5959            let total = sel_all.len() as f64;
5960            let mut h = 0.0f64;
5961            let mut active = 0usize;
5962            for &c in &cnt {
5963                if c > 0 {
5964                    active += 1;
5965                    let p = c as f64 / total;
5966                    h -= p * p.log2();
5967                }
5968            }
5969            let maxc = cnt.iter().copied().max().unwrap_or(0);
5970            println!(
5971                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
5972                il,
5973                t,
5974                sel_all.len(),
5975                active,
5976                n_expert,
5977                h,
5978                (n_expert as f64).log2(),
5979                total / active.max(1) as f64,
5980                maxc
5981            );
5982        }
5983
5984        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
5985        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
5986        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
5987        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
5988        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
5989        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
5990        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
5991        // zeroed-then-accumulated exactly as before (fallback).
5992        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
5993        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
5994        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
5995        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
5996        let gdec_may_fire = uniform_experts
5997            && use_cache
5998            && n_used <= 8
5999            && gdec_enabled()
6000            && !cfg.swiglu_clamped_at(il as u32);
6001        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
6002        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
6003        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
6004        // archs the slabs were uploaded but never read, and every expert went through the
6005        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
6006        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
6007        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
6008        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
6009        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
6010        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
6011        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
6012        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
6013        // strictly worse than staging); under PP-2 without the prime walker this admits
6014        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
6015        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
6016        let slab_local = m
6017            .dev_exps
6018            .as_ref()
6019            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
6020        let slab_bases = slab_local.map(|d| {
6021            use cudarc::driver::DevicePtr;
6022            let s = e.stream();
6023            let (pg, _g0) = d.gate.device_ptr(&s);
6024            let (pu, _g1) = d.up.device_ptr(&s);
6025            let (pd, _g2) = d.down.device_ptr(&s);
6026            (pg as u64, pu as u64, pd as u64)
6027        });
6028        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6029        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6030        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6031        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6032        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6033        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6034        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6035        // all-resident tokens, staged loop for misses), which is a dispatch-class
6036        // comparison, not a provenance one.
6037        let slab_fused_may_fire = slab_bases.is_some()
6038            && n_used <= 8
6039            && gdec_enabled()
6040            && !cfg.swiglu_clamped_at(il as u32)
6041            && cfg.m3.is_none()
6042            && no_exp_macros
6043            && moe_q8;
6044        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6045        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6046        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6047            e.uninit(t * n_embd)?
6048        } else {
6049            e.zeros(t * n_embd)?
6050        };
6051        // The router readback above already established a host boundary. Copy each small-t hidden
6052        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6053        let cpu_input = if cpu_hybrid {
6054            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6055        } else {
6056            None
6057        };
6058
6059        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6060        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6061        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6062        // measured ~123 memsets/token of the decode wall).
6063        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6064        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6065        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6066        let mut scratch_g: Option<CudaSlice<u8>> = None;
6067        let mut scratch_u: Option<CudaSlice<u8>> = None;
6068        let mut scratch_d: Option<CudaSlice<u8>> = None;
6069        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6070        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6071
6072        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6073        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6074        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6075        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6076        let page_window = moe_page_prefetch_window();
6077
6078        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6079        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6080        for tok in 0..t {
6081            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6082            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6083            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6084            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6085
6086            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6087            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6088            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6089            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6090            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6091            // miss falls through to the sequential loop below, which admits as before. In steady
6092            // state on a fully-resident rig every token-layer takes the grouped path.
6093            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6094            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6095            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6096            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6097            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6098            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6099            let no_macros = m.gate_exps.macros.is_none()
6100                && m.up_exps.macros.is_none()
6101                && m.down_exps.macros.is_none();
6102            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6103            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6104            // with pointers computed from the resident slab base + ex*stride instead of
6105            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6106            // slab holds every expert by construction, so this arm never falls through
6107            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6108            // staging both die). Bit-identity class: pointer provenance only, the same
6109            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6110            // slab exists it is strictly better (no lock, no miss).
6111            if slab_fused_may_fire {
6112                let (pg, pu, pd) = slab_bases.unwrap();
6113                let mut gp = [0u64; 8];
6114                let mut up = [0u64; 8];
6115                let mut dp = [0u64; 8];
6116                for (j, &ex) in sel.iter().enumerate() {
6117                    let ex = ex as usize;
6118                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6119                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6120                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6121                }
6122                let mut wv = [0f32; 8];
6123                wv[..n_used].copy_from_slice(w);
6124                if tok_q8.is_none() {
6125                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6126                }
6127                let (zq, zd) = tok_q8.as_ref().unwrap();
6128                let act = e.moe_gate_up_silu8_q8(
6129                    crate::WPtr8(gp),
6130                    crate::WPtr8(up),
6131                    zq,
6132                    zd,
6133                    n_embd,
6134                    n_ff_exp,
6135                    n_used,
6136                    m.gate_exps.qtype,
6137                    m.up_exps.qtype,
6138                    m.gate_exps.row_bytes,
6139                    m.up_exps.row_bytes,
6140                )?;
6141                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6142                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6143                e.moe_down8_fma_q8(
6144                    crate::WPtr8(dp),
6145                    crate::F32x8(wv),
6146                    &aq2,
6147                    &ad2,
6148                    &mut dst,
6149                    n_ff_exp,
6150                    n_embd,
6151                    n_used,
6152                    m.down_exps.qtype,
6153                    m.down_exps.row_bytes,
6154                )?;
6155                continue;
6156            }
6157            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6158                if tok_q8.is_none() {
6159                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6160                }
6161                let (zq, zd) = tok_q8.as_ref().unwrap();
6162                if Self::moe_gdec_token_q8(
6163                    e,
6164                    m,
6165                    il,
6166                    max_block,
6167                    zq,
6168                    zd,
6169                    sel,
6170                    w,
6171                    &mut moe_out,
6172                    tok,
6173                    n_embd,
6174                    n_ff_exp,
6175                    n_used,
6176                )? {
6177                    continue;
6178                }
6179            } else if gdec_may_fire
6180                && cfg.m3.is_none()
6181                && no_macros
6182                && Self::moe_gdec_token(
6183                    e,
6184                    m,
6185                    il,
6186                    max_block,
6187                    &zt,
6188                    sel,
6189                    w,
6190                    &mut moe_out,
6191                    tok,
6192                    n_embd,
6193                    n_ff_exp,
6194                    n_used,
6195                )?
6196            {
6197                continue;
6198            }
6199
6200            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6201            // slab pair could fire. This token fell through to a sequential axpy loop, which
6202            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6203            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6204            // has no fallible predicate), included for the allocation invariant's symmetry.
6205            if gdec_may_fire || slab_fused_may_fire {
6206                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6207                e.memset_zeros_view(&mut row)?;
6208            }
6209
6210            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6211            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6212            // stall this path exists to remove, while mixing projections would require another
6213            // activation round-trip. Weight addresses remain valid until this worker is joined at
6214            // the bottom of the token scope.
6215            let mut cpu_mask = vec![false; sel.len()];
6216            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6217                let gpu_resident = if use_cache {
6218                    e.with_moe_cache(max_block, |cache, _| {
6219                        Ok(sel
6220                            .iter()
6221                            .map(|&expert| {
6222                                let expert = expert as u16;
6223                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6224                                    .into_iter()
6225                                    .filter(|&projection| {
6226                                        cache
6227                                            .resident(BlockId::new(il, projection, expert))
6228                                            .is_some()
6229                                    })
6230                                    .count()
6231                            })
6232                            .collect::<Vec<_>>())
6233                    })?
6234                } else {
6235                    vec![0; sel.len()]
6236                };
6237                let mut cpu_selected = Vec::new();
6238                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6239                    if gpu_resident[index] != 3 {
6240                        cpu_mask[index] = true;
6241                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6242                        let expert = expert as usize;
6243                        cpu_selected.push((expert, route_weight));
6244                    }
6245                }
6246                if crate::cpu_experts::predictor_enabled() {
6247                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6248                    // from this layer's MoE input and prefetches predicted-and-missing
6249                    // experts into the companion RAM cache. Never blocks this thread.
6250                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6251                    crate::cpu_experts::predictor_submit(il, row);
6252                }
6253                if cpu_selected.is_empty() {
6254                    None
6255                } else {
6256                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6257                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6258                        .map_err(std::io::Error::other)?;
6259                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6260                }
6261            } else {
6262                None
6263            };
6264
6265            let worker_window = worker_disk_prefetch
6266                .then(worker_prefetch_window)
6267                .unwrap_or(0);
6268            for (j, &ex) in sel.iter().enumerate() {
6269                if cpu_mask[j] {
6270                    continue;
6271                }
6272                let ex = ex as usize;
6273                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6274                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6275                // fused form) and macro-carrying artifacts — still have their bytes in the
6276                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6277                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6278                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6279                if let Some(d) = slab_local {
6280                    let gl = m.gate_exps.expert_layout(ex);
6281                    let ul = m.up_exps.expert_layout(ex);
6282                    let dl = m.down_exps.expert_layout(ex);
6283                    let (g0, u0, d0) = (
6284                        ex * m.gate_exps.expert_stride,
6285                        ex * m.up_exps.expert_stride,
6286                        ex * m.down_exps.expert_stride,
6287                    );
6288                    let (gate, up) = if moe_q8 {
6289                        if tok_q8.is_none() {
6290                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6291                        }
6292                        let (zq, zd) = tok_q8.as_ref().unwrap();
6293                        (
6294                            e.qmatvec_expert_q8(
6295                                &d.gate,
6296                                g0..g0 + gl.len,
6297                                zq,
6298                                zd,
6299                                1,
6300                                m.gate_exps.in_f,
6301                                m.gate_exps.out_f,
6302                                gl.qtype,
6303                                gl.row_bytes,
6304                            )?,
6305                            e.qmatvec_expert_q8(
6306                                &d.up,
6307                                u0..u0 + ul.len,
6308                                zq,
6309                                zd,
6310                                1,
6311                                m.up_exps.in_f,
6312                                m.up_exps.out_f,
6313                                ul.qtype,
6314                                ul.row_bytes,
6315                            )?,
6316                        )
6317                    } else {
6318                        (
6319                            e.qmatvec_view(
6320                                &d.gate,
6321                                g0..g0 + gl.len,
6322                                &zt,
6323                                1,
6324                                m.gate_exps.in_f,
6325                                m.gate_exps.out_f,
6326                                gl.qtype,
6327                                gl.row_bytes,
6328                            )?,
6329                            e.qmatvec_view(
6330                                &d.up,
6331                                u0..u0 + ul.len,
6332                                &zt,
6333                                1,
6334                                m.up_exps.in_f,
6335                                m.up_exps.out_f,
6336                                ul.qtype,
6337                                ul.row_bytes,
6338                            )?,
6339                        )
6340                    };
6341                    let mut act = e.uninit(n_ff_exp)?;
6342                    Self::ffn_act_lim(
6343                        e,
6344                        cfg,
6345                        &gate,
6346                        &up,
6347                        m.gate_exps.macro_scale(ex),
6348                        m.up_exps.macro_scale(ex),
6349                        lim_exp,
6350                        &mut act,
6351                        n_ff_exp,
6352                    )?;
6353                    let y = if moe_q8 {
6354                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6355                        e.qmatvec_expert_q8(
6356                            &d.down,
6357                            d0..d0 + dl.len,
6358                            &aq2,
6359                            &ad2,
6360                            1,
6361                            m.down_exps.in_f,
6362                            m.down_exps.out_f,
6363                            dl.qtype,
6364                            dl.row_bytes,
6365                        )?
6366                    } else {
6367                        let actv = act.slice(0..n_ff_exp);
6368                        e.qmatvec_view(
6369                            &d.down,
6370                            d0..d0 + dl.len,
6371                            &actv,
6372                            1,
6373                            m.down_exps.in_f,
6374                            m.down_exps.out_f,
6375                            dl.qtype,
6376                            dl.row_bytes,
6377                        )?
6378                    };
6379                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6380                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6381                    continue;
6382                }
6383                for next in page_prefetch_positions(j, sel.len(), page_window) {
6384                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6385                }
6386                let keep = [
6387                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6388                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6389                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6390                ];
6391                if worker_disk_prefetch && worker_window > 0 {
6392                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6393                        Self::moe_prefetch_disk_expert(
6394                            e,
6395                            il,
6396                            sel[next] as usize,
6397                            m,
6398                            max_block,
6399                            &keep,
6400                        )?;
6401                    }
6402                } else if cache_dispatch
6403                    && !cpu_hybrid
6404                    && moe_prefetch_enabled()
6405                    && j + 1 < sel.len()
6406                {
6407                    let next = sel[j + 1] as usize;
6408                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6409                }
6410                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6411                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6412                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6413                    // layouts stay on the metadata-aware f32 path.
6414                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6415                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6416                    }
6417                    let gate = if gate_q8 {
6418                        let (zq, zd) = tok_q8.as_ref().unwrap();
6419                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6420                    } else {
6421                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6422                    };
6423                    let up = if up_q8 {
6424                        let (zq, zd) = tok_q8.as_ref().unwrap();
6425                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6426                    } else {
6427                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6428                    };
6429                    let mut act = e.uninit(n_ff_exp)?;
6430                    Self::ffn_act_lim(
6431                        e,
6432                        cfg,
6433                        &gate,
6434                        &up,
6435                        m.gate_exps.macro_scale(ex),
6436                        m.up_exps.macro_scale(ex),
6437                        lim_exp,
6438                        &mut act,
6439                        n_ff_exp,
6440                    )?;
6441                    let y = if down_q8 {
6442                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6443                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6444                    } else {
6445                        let actv = act.slice(0..n_ff_exp);
6446                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6447                    };
6448                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6449                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6450                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6451                } else if cache_dispatch {
6452                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6453                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6454                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6455                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6456                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6457                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6458                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6459                    Self::ffn_act_lim(
6460                        e,
6461                        cfg,
6462                        &gate,
6463                        &up,
6464                        m.gate_exps.macro_scale(ex),
6465                        m.up_exps.macro_scale(ex),
6466                        lim_exp,
6467                        &mut act,
6468                        n_ff_exp,
6469                    )?;
6470                    let actv = act.slice(0..n_ff_exp);
6471                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6472                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6473                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6474                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6475                } else if cache_frozen {
6476                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6477                    // first prime. Reuse every fixed resident projection directly and stage only a
6478                    // true miss through the ordinary scratch slot. This preserves the established
6479                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6480                    let gate = Self::moe_frozen_gemm(
6481                        e,
6482                        il,
6483                        PROJ_GATE,
6484                        ex,
6485                        m,
6486                        max_block,
6487                        &zt,
6488                        &mut scratch_g,
6489                        g_len,
6490                    )?;
6491                    let up = Self::moe_frozen_gemm(
6492                        e,
6493                        il,
6494                        PROJ_UP,
6495                        ex,
6496                        m,
6497                        max_block,
6498                        &zt,
6499                        &mut scratch_u,
6500                        u_len,
6501                    )?;
6502                    let mut act = e.uninit(n_ff_exp)?;
6503                    Self::ffn_act_lim(
6504                        e,
6505                        cfg,
6506                        &gate,
6507                        &up,
6508                        m.gate_exps.macro_scale(ex),
6509                        m.up_exps.macro_scale(ex),
6510                        lim_exp,
6511                        &mut act,
6512                        n_ff_exp,
6513                    )?;
6514                    let actv = act.slice(0..n_ff_exp);
6515                    let y = Self::moe_frozen_gemm(
6516                        e,
6517                        il,
6518                        PROJ_DOWN,
6519                        ex,
6520                        m,
6521                        max_block,
6522                        &actv,
6523                        &mut scratch_d,
6524                        d_len,
6525                    )?;
6526                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6527                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6528                } else {
6529                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6530                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6531                    // fully overwrites the byte range the GEMM reads).
6532                    if scratch_g.is_none() {
6533                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6534                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6535                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6536                    }
6537                    let (sg, su, sd) = (
6538                        scratch_g.as_mut().unwrap(),
6539                        scratch_u.as_mut().unwrap(),
6540                        scratch_d.as_mut().unwrap(),
6541                    );
6542                    let gl = m.gate_exps.expert_layout(ex);
6543                    let ul = m.up_exps.expert_layout(ex);
6544                    let dl = m.down_exps.expert_layout(ex);
6545                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6546                    let gate = e.qmatvec_view(
6547                        sg,
6548                        0..gl.len,
6549                        &zt,
6550                        1,
6551                        m.gate_exps.in_f,
6552                        m.gate_exps.out_f,
6553                        gl.qtype,
6554                        gl.row_bytes,
6555                    )?;
6556
6557                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6558                    let up = e.qmatvec_view(
6559                        su,
6560                        0..ul.len,
6561                        &zt,
6562                        1,
6563                        m.up_exps.in_f,
6564                        m.up_exps.out_f,
6565                        ul.qtype,
6566                        ul.row_bytes,
6567                    )?;
6568
6569                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6570                    Self::ffn_act_lim(
6571                        e,
6572                        cfg,
6573                        &gate,
6574                        &up,
6575                        m.gate_exps.macro_scale(ex),
6576                        m.up_exps.macro_scale(ex),
6577                        lim_exp,
6578                        &mut act,
6579                        n_ff_exp,
6580                    )?;
6581
6582                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6583                    let actv = act.slice(0..n_ff_exp);
6584                    let y = e.qmatvec_view(
6585                        sd,
6586                        0..dl.len,
6587                        &actv,
6588                        1,
6589                        m.down_exps.in_f,
6590                        m.down_exps.out_f,
6591                        dl.qtype,
6592                        dl.row_bytes,
6593                    )?;
6594
6595                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6596                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6597                }
6598            }
6599            if let Some(worker) = cpu_worker {
6600                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6601                let cpu_output = e.htod(&cpu_output)?;
6602                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6603                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6604            }
6605            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6606                for (j, &ex) in sel.iter().enumerate() {
6607                    if cpu_mask[j] {
6608                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6609                    }
6610                }
6611            }
6612        }
6613
6614        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6615        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6616        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6617        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6618        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6619            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
6620        {
6621            let n_ff_sh = gate_shexp.out_features(); // 512
6622            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
6623            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
6624            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
6625            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
6626            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
6627            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
6628            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
6629            let verify_t = t > 1 && t < PRIME_MIN_T;
6630            let (sg_gate, sg_up) = if t == 1 {
6631                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
6632            } else if verify_t {
6633                (
6634                    e.matmul_decode_exact(gate_shexp, z, t)?,
6635                    e.matmul_decode_exact(up_shexp, z, t)?,
6636                )
6637            } else {
6638                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
6639            };
6640            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
6641            Self::ffn_act_lim(
6642                e,
6643                cfg,
6644                &sg_gate,
6645                &sg_up,
6646                1.0,
6647                1.0,
6648                lim_shexp,
6649                &mut sa,
6650                t * n_ff_sh,
6651            )?;
6652            let sh = if verify_t {
6653                e.matmul_decode_exact(down_shexp, &sa, t)?
6654            } else {
6655                e.matmul(down_shexp, &sa, t)?
6656            }; // [T, n_embd]
6657
6658            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
6659            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
6660            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
6661            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
6662            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
6663            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
6664            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
6665            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
6666            // expert's contribution into every token's residual, so under cross-request
6667            // concat prefill a session's hidden state depended on its co-arrivals' token
6668            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
6669            let g = match &m.gate_inp_shexp {
6670                Some(gate_inp_shexp) => {
6671                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
6672                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
6673                    } else {
6674                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
6675                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
6676                        e.sigmoid(&gs, &mut g, t)?;
6677                        g
6678                    }
6679                }
6680                None => e.htod(&vec![1.0f32; t])?,
6681            };
6682            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
6683            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
6684        }
6685
6686        Ok(moe_out)
6687    }
6688
6689    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
6690    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
6691    pub fn stage1_h2d_per_token(&self) -> u64 {
6692        use crate::hybrid::Ffn;
6693        let n_used = self
6694            .cfg
6695            .moe
6696            .as_ref()
6697            .map(|m| m.expert_used_count as u64)
6698            .unwrap_or(0);
6699        let mut bytes = 0u64;
6700        for l in self.layers.iter() {
6701            if let Ffn::Moe(m) = &l.ffn {
6702                bytes += n_used
6703                    * (m.gate_exps.max_expert_bytes()
6704                        + m.up_exps.max_expert_bytes()
6705                        + m.down_exps.max_expert_bytes()) as u64;
6706            }
6707        }
6708        bytes
6709    }
6710
6711    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
6712    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
6713    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
6714    pub(crate) fn max_moe_block(&self) -> usize {
6715        use crate::hybrid::Ffn;
6716        let mut mx = 0usize;
6717        let mut scan = |ffn: &Ffn| {
6718            if let Ffn::Moe(m) = ffn {
6719                mx = mx
6720                    .max(m.gate_exps.max_expert_bytes())
6721                    .max(m.up_exps.max_expert_bytes())
6722                    .max(m.down_exps.max_expert_bytes());
6723            }
6724        };
6725        for l in self.layers.iter() {
6726            scan(&l.ffn);
6727        }
6728        if let Some(mtp) = self.mtp.as_ref() {
6729            scan(&mtp.ffn);
6730        }
6731        mx
6732    }
6733
6734    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
6735    /// but have no bytes and therefore consume no residency slot.
6736    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
6737        use crate::hybrid::Ffn;
6738        let mut sizes = Vec::new();
6739        let mut scan = |ffn: &Ffn| {
6740            let Ffn::Moe(m) = ffn else { return };
6741            for ex in 0..m.gate_exps.n_expert {
6742                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
6743                    continue;
6744                }
6745                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
6746                    let len = exps.expert_layout(ex).len;
6747                    if len > 0 {
6748                        sizes.push(len);
6749                    }
6750                }
6751            }
6752        };
6753        for layer in &self.layers {
6754            scan(&layer.ffn);
6755        }
6756        if let Some(mtp) = &self.mtp {
6757            scan(&mtp.ffn);
6758        }
6759        sizes
6760    }
6761
6762    /// Persist the frozen residency set so a later process can restage it directly and skip
6763    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
6764    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
6765    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
6766    /// post-freeze argmax gate still validates the serving assignment.
6767    pub fn save_cpu_expert_residency_profile(
6768        &self,
6769        e: &Engine,
6770        path: &std::path::Path,
6771    ) -> Result<(), Box<dyn std::error::Error>> {
6772        let Some(ids) = e.export_moe_residency() else {
6773            return Err("no MoE residency cache to persist".into());
6774        };
6775        let mut body = format!(
6776            "memra-freeze-profile v1 max_block={} blocks={}\n",
6777            self.max_moe_block(),
6778            ids.len()
6779        );
6780        for (layer, proj, ex) in &ids {
6781            body.push_str(&format!("{layer} {proj} {ex}\n"));
6782        }
6783        let tmp = path.with_extension("tmp");
6784        std::fs::write(&tmp, body)?;
6785        std::fs::rename(&tmp, path)?;
6786        println!(
6787            "[moe-cache] freeze profile saved: {} blocks -> {}",
6788            ids.len(),
6789            path.display()
6790        );
6791        Ok(())
6792    }
6793
6794    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
6795    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
6796    /// missing or its header does not match this model's slot geometry.
6797    pub fn restore_cpu_expert_residency_profile(
6798        &self,
6799        e: &Engine,
6800        path: &std::path::Path,
6801    ) -> Result<bool, Box<dyn std::error::Error>> {
6802        use crate::hybrid::Ffn;
6803        use crate::moe_cache::BlockId;
6804        let Ok(content) = std::fs::read_to_string(path) else {
6805            return Ok(false);
6806        };
6807        let mut lines = content.lines();
6808        let Some(header) = lines.next() else {
6809            return Ok(false);
6810        };
6811        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
6812        if !header.starts_with(&expected) {
6813            println!(
6814                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
6815                path.display()
6816            );
6817            return Ok(false);
6818        }
6819        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
6820            std::collections::HashMap::new();
6821        for line in lines {
6822            let mut fields = line.split_whitespace();
6823            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
6824            else {
6825                continue;
6826            };
6827            let (Ok(layer), Ok(proj), Ok(ex)) =
6828                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
6829            else {
6830                continue;
6831            };
6832            by_layer
6833                .entry(layer)
6834                .or_default()
6835                .push(BlockId::new(layer, proj, ex));
6836        }
6837        let requested: usize = by_layer.values().map(Vec::len).sum();
6838        if requested == 0 {
6839            return Ok(false);
6840        }
6841        let max_block = self.max_moe_block();
6842        let mut restaged = 0usize;
6843        let mut stage_layer =
6844            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
6845                let Ffn::Moe(m) = ffn else { return Ok(()) };
6846                let Some(ids) = by_layer.get(&layer_index) else {
6847                    return Ok(());
6848                };
6849                e.with_moe_cache(max_block, |cache, eng| {
6850                    for id in ids {
6851                        if cache.restage_block(*id, m, eng)? {
6852                            restaged += 1;
6853                        }
6854                    }
6855                    Ok(())
6856                })
6857            };
6858        for (index, layer) in self.layers.iter().enumerate() {
6859            stage_layer(index as u16, &layer.ffn)?;
6860        }
6861        if let Some(mtp) = self.mtp.as_ref() {
6862            stage_layer(u16::MAX, &mtp.ffn)?;
6863        }
6864        e.freeze_moe_cache();
6865        println!(
6866            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
6867            path.display()
6868        );
6869        Ok(true)
6870    }
6871
6872    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
6873    pub fn freeze_cpu_expert_residency(
6874        &self,
6875        e: &Engine,
6876    ) -> Result<(), Box<dyn std::error::Error>> {
6877        e.freeze_moe_cache();
6878        Ok(())
6879    }
6880
6881    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
6882    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
6883    /// the model's activation exactly.
6884    ///
6885    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
6886    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
6887    /// form for anything that can land on a clamped layer.
6888    pub fn ffn_act(
6889        e: &Engine,
6890        cfg: &ModelConfig,
6891        gate: &CudaSlice<f32>,
6892        up: &CudaSlice<f32>,
6893        act: &mut CudaSlice<f32>,
6894        n: usize,
6895    ) -> Result<(), Box<dyn std::error::Error>> {
6896        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
6897    }
6898
6899    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
6900    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
6901    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
6902    #[allow(clippy::too_many_arguments)]
6903    pub(crate) fn ffn_act_scaled(
6904        e: &Engine,
6905        cfg: &ModelConfig,
6906        gate: &CudaSlice<f32>,
6907        up: &CudaSlice<f32>,
6908        gs: f32,
6909        us: f32,
6910        act: &mut CudaSlice<f32>,
6911        n: usize,
6912    ) -> Result<(), Box<dyn std::error::Error>> {
6913        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
6914    }
6915
6916    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
6917    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
6918    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
6919    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
6920    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
6921    ///                 arrays are SEPARATE and a layer can have one without the other.
6922    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
6923    /// already known live.
6924    #[allow(clippy::too_many_arguments)]
6925    pub(crate) fn ffn_act_lim(
6926        e: &Engine,
6927        cfg: &ModelConfig,
6928        gate: &CudaSlice<f32>,
6929        up: &CudaSlice<f32>,
6930        gs: f32,
6931        us: f32,
6932        limit: Option<f32>,
6933        act: &mut CudaSlice<f32>,
6934        n: usize,
6935    ) -> Result<(), Box<dyn std::error::Error>> {
6936        if let Some(m3) = cfg.m3.as_ref() {
6937            debug_assert!(
6938                limit.is_none(),
6939                "m3 swigluoai and step35 clamp are different archs"
6940            );
6941            return e.swigluoai_mul_scaled(
6942                gate,
6943                up,
6944                gs,
6945                us,
6946                m3.swiglu_alpha,
6947                m3.swiglu_limit,
6948                act,
6949                n,
6950            );
6951        }
6952        if let Some(l) = limit {
6953            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
6954        }
6955        if gs == 1.0 && us == 1.0 {
6956            return e.silu_mul(gate, up, act, n);
6957        }
6958        e.silu_mul_scaled(gate, up, gs, us, act, n)
6959    }
6960
6961    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
6962    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
6963    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
6964    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
6965    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
6966    fn moe_route(
6967        e: &Engine,
6968        logits: &CudaSlice<f32>,
6969        t: usize,
6970        n_expert: usize,
6971        n_used: usize,
6972    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6973        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
6974    }
6975
6976    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
6977    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
6978    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
6979    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
6980    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
6981    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
6982    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
6983    #[allow(clippy::too_many_arguments)]
6984    fn moe_route_sigmoid_cfg(
6985        e: &Engine,
6986        logits: &CudaSlice<f32>,
6987        t: usize,
6988        n_expert: usize,
6989        n_used: usize,
6990        m: &MoeWeights,
6991        (sf, route_norm): (f32, bool),
6992    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6993        if sigmoid_router_enabled() {
6994            return e.moe_router_sigmoid_topk_host(
6995                logits,
6996                t,
6997                n_expert,
6998                n_used,
6999                m.active_count(),
7000                &m.exp_probs_b_dev,
7001                &m.active_experts_dev,
7002                sf,
7003                route_norm,
7004            );
7005        }
7006        let lg = e.dtoh(logits)?;
7007        Self::moe_route_sigmoid_host(
7008            &lg,
7009            t,
7010            n_expert,
7011            n_used,
7012            m.exp_probs_b.as_deref(),
7013            sf,
7014            route_norm,
7015            m.active_experts.as_deref(),
7016        )
7017    }
7018
7019    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
7020    /// the existing softmax device kernel has no mask input.
7021    fn moe_route_cfg(
7022        e: &Engine,
7023        logits: &CudaSlice<f32>,
7024        t: usize,
7025        n_expert: usize,
7026        n_used: usize,
7027        active: Option<&[bool]>,
7028    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7029        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7030        // rollback) via the single-sync pinned readback — softmax arch only.
7031        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7032            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7033        }
7034        // Host oracle (the §D bit-identity reference).
7035        let lg = e.dtoh(logits)?; // [T*n_expert] host
7036        let mut sel = vec![0u32; t * n_used];
7037        let mut w_out = vec![0f32; t * n_used];
7038        for tok in 0..t {
7039            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7040            // softmax over ALL n_expert (stable: subtract max)
7041            let maxl = row
7042                .iter()
7043                .enumerate()
7044                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7045                .map(|(_, &x)| x)
7046                .fold(f32::NEG_INFINITY, f32::max);
7047            let mut probs = vec![0f32; n_expert];
7048            let mut den = 0f32;
7049            for i in 0..n_expert {
7050                if active.is_some_and(|mask| !mask[i]) {
7051                    continue;
7052                }
7053                let x = (row[i] - maxl).exp();
7054                probs[i] = x;
7055                den += x;
7056            }
7057            for p in probs.iter_mut() {
7058                *p /= den;
7059            }
7060            // stable DESC sort: prob DESC, ascending-index tiebreak.
7061            let mut idx: Vec<usize> = (0..n_expert)
7062                .filter(|&i| active.is_none_or(|mask| mask[i]))
7063                .collect();
7064            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7065            let sl = &idx[..n_used];
7066            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7067            let mut ws: f32 = wv.iter().sum();
7068            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7069            for x in wv.iter_mut() {
7070                *x /= ws;
7071            }
7072            for j in 0..n_used {
7073                sel[tok * n_used + j] = sl[j] as u32;
7074                w_out[tok * n_used + j] = wv[j];
7075            }
7076        }
7077        Ok((sel, w_out))
7078    }
7079
7080    #[allow(clippy::too_many_arguments)]
7081    fn moe_route_sigmoid_with_input(
7082        e: &Engine,
7083        logits: &CudaSlice<f32>,
7084        input: &CudaSlice<f32>,
7085        t: usize,
7086        in_features: usize,
7087        n_expert: usize,
7088        n_used: usize,
7089        bias: Option<&[f32]>,
7090        (sf, route_norm): (f32, bool),
7091        active: Option<&[bool]>,
7092    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7093        let logit_values =
7094            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7095        let input_values =
7096            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7097        let (lg, input) = e.dtoh_pair_views(
7098            &logits.slice(0..logit_values),
7099            &input.slice(0..input_values),
7100        )?;
7101        let (sel, w) =
7102            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7103        Ok((sel, w, input))
7104    }
7105
7106    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7107    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7108    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7109    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7110    pub fn start_moe_prefetch_predictor(
7111        &self,
7112        e: &Engine,
7113        cfg: &ModelConfig,
7114    ) -> Result<(), Box<dyn std::error::Error>> {
7115        use crate::hybrid::Ffn;
7116        let Some(sig) = cfg.sigmoid_router() else {
7117            return Err("prefetch predictor requires a sigmoid-router arch".into());
7118        };
7119        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7120            .export_moe_residency()
7121            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7122            .into_iter()
7123            .collect();
7124        let mut layers = Vec::new();
7125        for (index, layer) in self.layers.iter().enumerate() {
7126            let Ffn::Moe(m) = &layer.ffn else { continue };
7127            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7128                continue;
7129            };
7130            let router = e.dtoh(data)?;
7131            let n_expert = m.gate_exps.n_expert;
7132            let n_embd = m.gate_exps.in_f;
7133            if router.len() != n_embd * n_expert {
7134                continue;
7135            }
7136            let build = |exps: &crate::model::HostExps| {
7137                (0..n_expert)
7138                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7139                    .collect::<Vec<_>>()
7140            };
7141            layers.push((
7142                index as u16,
7143                crate::cpu_experts::PredictLayerInit {
7144                    router,
7145                    bias: m.exp_probs_b.clone(),
7146                    active: m.active_experts.clone(),
7147                    n_embd,
7148                    n_used: cfg
7149                        .moe
7150                        .as_ref()
7151                        .map(|moe| moe.expert_used_count as usize)
7152                        .ok_or("prefetch predictor requires MoE config")?,
7153                    sig,
7154                    weights_n_expert: n_expert,
7155                    gate: build(&m.gate_exps),
7156                    up: build(&m.up_exps),
7157                    down: build(&m.down_exps),
7158                },
7159            ));
7160        }
7161        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7162    }
7163
7164    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7165    /// selection math to the rollback runtime, applied to host-computed logits.
7166    #[allow(clippy::too_many_arguments)]
7167    pub fn moe_route_sigmoid_host_public(
7168        logits: &[f32],
7169        t: usize,
7170        n_expert: usize,
7171        n_used: usize,
7172        bias: Option<&[f32]>,
7173        sf: f32,
7174        route_norm: bool,
7175        active: Option<&[bool]>,
7176    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7177        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7178    }
7179
7180    #[allow(clippy::too_many_arguments)]
7181    fn moe_route_sigmoid_host(
7182        lg: &[f32],
7183        t: usize,
7184        n_expert: usize,
7185        n_used: usize,
7186        bias: Option<&[f32]>,
7187        sf: f32,
7188        route_norm: bool,
7189        active: Option<&[bool]>,
7190    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7191        let active_count = active
7192            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7193            .unwrap_or(n_expert);
7194        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7195        if lg.len() != t * n_expert {
7196            return Err(format!(
7197                "sigmoid router logits length mismatch: got {}, expected {}",
7198                lg.len(),
7199                t * n_expert,
7200            )
7201            .into());
7202        }
7203        let mut sel = vec![0u32; t * n_used];
7204        let mut w_out = vec![0f32; t * n_used];
7205        for tok in 0..t {
7206            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7207            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7208            // selection score = sigmoid + bias; weight = plain sigmoid.
7209            let selsc: Vec<f32> = match bias {
7210                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7211                None => scores.clone(),
7212            };
7213            let mut idx: Vec<usize> = (0..n_expert)
7214                .filter(|&i| active.is_none_or(|mask| mask[i]))
7215                .collect();
7216            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7217            let sl = &idx[..n_used];
7218            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7219            if route_norm {
7220                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7221                for x in wv.iter_mut() {
7222                    *x = *x / ws * sf;
7223                }
7224            } else {
7225                for x in wv.iter_mut() {
7226                    *x *= sf;
7227                }
7228            }
7229            for j in 0..n_used {
7230                sel[tok * n_used + j] = sl[j] as u32;
7231                w_out[tok * n_used + j] = wv[j];
7232            }
7233        }
7234        Ok((sel, w_out))
7235    }
7236
7237    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7238    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7239    /// macro-scaled experts, and observation modes are denied by the caller.
7240    #[allow(clippy::too_many_arguments)]
7241    fn moe_ffn_sigmoid_dev(
7242        e: &Engine,
7243        m: &MoeWeights,
7244        z: &CudaSlice<f32>,
7245        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7246        logits: &CudaSlice<f32>,
7247        t: usize,
7248        cfg: &ModelConfig,
7249        il: u16,
7250        (scaling_factor, route_norm): (f32, bool),
7251    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7252        let moe = cfg.moe.as_ref().unwrap();
7253        let n_embd = cfg.n_embd as usize;
7254        let n_expert = moe.expert_count as usize;
7255        let n_used = moe.expert_used_count as usize;
7256        let n_ff_exp = moe.expert_ff_length as usize;
7257        let dev = m.dev_exps.as_ref().unwrap();
7258        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7259        debug_assert!(m.has_uniform_expert_layout());
7260        debug_assert!(!m.has_macros);
7261
7262        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7263            logits,
7264            t,
7265            n_expert,
7266            n_used,
7267            m.active_count(),
7268            &m.exp_probs_b_dev,
7269            &m.active_experts_dev,
7270            scaling_factor,
7271            route_norm,
7272        )?;
7273        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7274        if let Some(fp8) = dev.fp8_blk.as_ref() {
7275            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7276            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7277            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7278            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7279            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7280            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7281
7282            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7283            // activations with block-128 E4M3 weights. This deliberately
7284            // simple resident reference is the correctness oracle for later
7285            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7286            // load-time Q8 diagnostic representation, so one process never
7287            // crosses between numerical programs.
7288            let selected = e.dtoh_i32(&sel_d)?;
7289            let route_weights = e.dtoh(&w_d)?;
7290            let mut moe_out = e.zeros(t * n_embd)?;
7291            for tok in 0..t {
7292                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7293                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7294                for j in 0..n_used {
7295                    let pair = tok * n_used + j;
7296                    let expert = selected[pair] as usize;
7297                    let gate = Self::moe_resident_fp8_e4m3(
7298                        e,
7299                        &m.gate_exps,
7300                        &dev.gate,
7301                        &fp8.gate,
7302                        expert,
7303                        &zt,
7304                        1,
7305                    )?;
7306                    let up = Self::moe_resident_fp8_e4m3(
7307                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7308                    )?;
7309                    let mut act = e.uninit(n_ff_exp)?;
7310                    Self::ffn_act_lim(
7311                        e,
7312                        cfg,
7313                        &gate,
7314                        &up,
7315                        1.0,
7316                        1.0,
7317                        cfg.clamp_exp_at(il as u32),
7318                        &mut act,
7319                        n_ff_exp,
7320                    )?;
7321                    let act = act.slice(0..n_ff_exp);
7322                    let down = Self::moe_resident_fp8_e4m3(
7323                        e,
7324                        &m.down_exps,
7325                        &dev.down,
7326                        &fp8.down,
7327                        expert,
7328                        &act,
7329                        1,
7330                    )?;
7331                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7332                }
7333            }
7334            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7335                eprintln!(
7336                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7337                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7338                    cfg.clamp_exp_at(il as u32).is_some(),
7339                );
7340            }
7341            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7342            return Ok(moe_out);
7343        }
7344        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7345            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7346            (combined, combined)
7347        } else {
7348            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7349        };
7350        let (zq, zd) = match (t, zq8) {
7351            (1, Some((q, d))) => (q.clone(), d.clone()),
7352            _ => e.quantize_q8_1(z, t, n_embd)?,
7353        };
7354        let n_pairs = t * n_used;
7355        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7356            // The final Step layers retain the established separate gate/up -> clamp -> down
7357            // arithmetic. Pair rows are derived from token position; selected expert ids and
7358            // routing weights remain the device router's buffers throughout.
7359            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7360            let pair_tok_d = e.htod_i32(&pair_tok)?;
7361            let gate = e.moe_pairs_matvec_q8(
7362                &dev.ptr_row,
7363                0,
7364                &pair_tok_d,
7365                &sel_d,
7366                &zq,
7367                &zd,
7368                n_embd,
7369                n_ff_exp,
7370                n_expert,
7371                n_pairs,
7372                m.gate_exps.qtype,
7373                gate_row_bytes,
7374            )?;
7375            let up = e.moe_pairs_matvec_q8(
7376                &dev.ptr_row,
7377                1,
7378                &pair_tok_d,
7379                &sel_d,
7380                &zq,
7381                &zd,
7382                n_embd,
7383                n_ff_exp,
7384                n_expert,
7385                n_pairs,
7386                m.up_exps.qtype,
7387                up_row_bytes,
7388            )?;
7389            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7390            Self::ffn_act_lim(
7391                e,
7392                cfg,
7393                &gate,
7394                &up,
7395                1.0,
7396                1.0,
7397                cfg.clamp_exp_at(il as u32),
7398                &mut act,
7399                n_pairs * n_ff_exp,
7400            )?;
7401            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7402            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7403            let pair_self_d = e.htod_i32(&pair_self)?;
7404            let down = e.moe_pairs_matvec_q8(
7405                &dev.ptr_row,
7406                2,
7407                &pair_self_d,
7408                &sel_d,
7409                &aq2,
7410                &ad2,
7411                n_ff_exp,
7412                n_embd,
7413                n_expert,
7414                n_pairs,
7415                m.down_exps.qtype,
7416                m.down_exps.row_bytes,
7417            )?;
7418            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7419            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7420            let tok_off_d = e.htod_i32(&tok_off)?;
7421            let tok_ids_d = e.htod_i32(&tok_ids)?;
7422            let mut output = e.uninit(t * n_embd)?;
7423            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7424            output
7425        } else {
7426            let act = e.moe_gate_up_silu8_dev_q8_rows(
7427                &dev.ptr_row,
7428                &sel_d,
7429                &zq,
7430                &zd,
7431                t,
7432                n_embd,
7433                n_ff_exp,
7434                n_used,
7435                n_expert,
7436                m.gate_exps.qtype,
7437                m.up_exps.qtype,
7438                gate_row_bytes,
7439                up_row_bytes,
7440                &m.dev_macros,
7441            )?;
7442            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7443            let mut output = e.uninit(t * n_embd)?;
7444            e.moe_down8_fma_dev_q8_rows_g(
7445                &dev.ptr_row,
7446                &sel_d,
7447                &w_d,
7448                &aq2,
7449                &ad2,
7450                &mut output,
7451                t,
7452                n_ff_exp,
7453                n_embd,
7454                n_used,
7455                n_expert,
7456                m.down_exps.qtype,
7457                m.down_exps.row_bytes,
7458            )?;
7459            output
7460        };
7461
7462        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7463            eprintln!(
7464                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7465                cfg.clamp_exp_at(il as u32).is_some(),
7466                dev.gu_il,
7467            );
7468        }
7469        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7470        Ok(moe_out)
7471    }
7472
7473    #[allow(clippy::too_many_arguments)]
7474    fn moe_resident_fp8_e4m3(
7475        e: &Engine,
7476        exps: &crate::model::HostExps,
7477        bytes: &CudaSlice<u8>,
7478        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7479        expert: usize,
7480        x: &cudarc::driver::CudaView<f32>,
7481        m: usize,
7482    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7483        let layout = exps.expert_layout(expert);
7484        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7485        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7486        let byte_start = expert * exps.expert_stride;
7487        let scale_start = expert * scales.expert_stride;
7488        let weight = bytes.slice(byte_start..byte_start + layout.len);
7489        let scale = scales
7490            .scales
7491            .slice(scale_start..scale_start + scales.expert_stride);
7492        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7493    }
7494
7495    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7496    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7497    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7498    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7499    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7500    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7501    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7502    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7503    fn moe_ffn_pairs(
7504        e: &Engine,
7505        m: &MoeWeights,
7506        z: &CudaSlice<f32>,
7507        logits: &CudaSlice<f32>,
7508        t: usize,
7509        cfg: &ModelConfig,
7510    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7511        let moe = cfg.moe.as_ref().unwrap();
7512        let n_embd = cfg.n_embd as usize;
7513        let n_expert = moe.expert_count as usize;
7514        let n_used = moe.expert_used_count as usize;
7515        let n_ff_exp = moe.expert_ff_length as usize;
7516        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7517        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7518        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7519        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7520        debug_assert!(
7521            !cfg.swiglu_clamped_anywhere(),
7522            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7523        );
7524        let dev = m.dev_exps.as_ref().unwrap();
7525        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7526        let (rbg_d, rbu_d) = if dev.gu_il {
7527            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7528            (sxx, sxx)
7529        } else {
7530            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7531        };
7532
7533        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7534        let n_pairs = t * n_used;
7535        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7536        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7537        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7538        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7539        let pair_w: Vec<f32> = w_all.clone();
7540        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7541        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7542        let pt = e.htod_i32(&pair_tok)?;
7543        let px = e.htod_i32(&pair_ex)?;
7544        let pw = e.htod(&pair_w)?;
7545        let toff = e.htod_i32(&tok_off)?;
7546        let tids = e.htod_i32(&tok_ids)?;
7547
7548        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7549        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7550        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7551        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7552        for p in 0..n_pairs {
7553            by_ex[pair_ex[p] as usize].push(p as i32);
7554        }
7555        let mut ex_ids: Vec<i32> = Vec::new();
7556        let mut ex_off: Vec<i32> = vec![0];
7557        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7558        for (ex, list) in by_ex.iter().enumerate() {
7559            if list.is_empty() {
7560                continue;
7561            }
7562            ex_ids.push(ex as i32);
7563            ex_pairs.extend_from_slice(list);
7564            ex_off.push(ex_pairs.len() as i32);
7565        }
7566        let n_active = ex_ids.len();
7567        let exi = e.htod_i32(&ex_ids)?;
7568        let exo = e.htod_i32(&ex_off)?;
7569        let exp_d = e.htod_i32(&ex_pairs)?;
7570        let _ = &px; // pair-major twin keeps it; em path uses CSR
7571
7572        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7573        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7574        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7575        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7576        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7577        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7578        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7579        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7580        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7581        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7582        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7583        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7584        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7585        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7586        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7587        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7588        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7589        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7590        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7591        let mma_t = *MMA_T.get_or_init(|| {
7592            std::env::var("MEMRA_MOE_MMA_T")
7593                .ok()
7594                .and_then(|v| v.parse().ok())
7595                .unwrap_or(16)
7596        });
7597        let use_mma = std::env::var("MEMRA_MOE_MMA")
7598            .map(|v| v != "0")
7599            .unwrap_or(true)
7600            && t >= mma_t
7601            && q8_expert_dec_supported(m.gate_exps.qtype)
7602            && q8_expert_dec_supported(m.up_exps.qtype)
7603            && q8_expert_dec_supported(m.down_exps.qtype)
7604            && n_embd % 256 == 0
7605            && n_ff_exp % 256 == 0;
7606        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7607        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7608        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7609        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7610        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7611        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7612        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7613        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7614        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7615        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7616        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7617        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7618        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7619        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
7620        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
7621        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
7622            && q8_expert_dec_supported(m.up_exps.qtype)
7623            && q8_expert_dec_supported(m.down_exps.qtype)
7624            && n_embd % 256 == 0
7625            && n_ff_exp % 256 == 0;
7626        let f16g_mode = crate::moe_f16g_mode();
7627        let f16g = f16g_mode != 0
7628            && t >= mma_t
7629            && (f16g_mode != 3 || !mma_capable)
7630            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
7631            && f16g_proj_ok(m.up_exps.qtype, n_embd)
7632            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
7633        if use_mma || f16g {
7634            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
7635            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
7636            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
7637            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
7638            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
7639            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
7640            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
7641            let y_down = if f16g {
7642                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
7643                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
7644                // permute at the very end back to pair-id order for the scatter.
7645                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
7646                let csr_tok_d = e.htod_i32(&csr_tok)?;
7647                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
7648                let g_csr = e.moe_f16_grouped(
7649                    &dev.ptr_row,
7650                    0,
7651                    n_expert,
7652                    &exi,
7653                    &ex_off,
7654                    &exo,
7655                    &z_f16,
7656                    &z_s,
7657                    n_embd,
7658                    n_ff_exp,
7659                    n_active,
7660                    n_pairs,
7661                    m.gate_exps.qtype,
7662                    rbg_d,
7663                )?;
7664                let u_csr = e.moe_f16_grouped(
7665                    &dev.ptr_row,
7666                    1,
7667                    n_expert,
7668                    &exi,
7669                    &ex_off,
7670                    &exo,
7671                    &z_f16,
7672                    &z_s,
7673                    n_embd,
7674                    n_ff_exp,
7675                    n_active,
7676                    n_pairs,
7677                    m.up_exps.qtype,
7678                    rbu_d,
7679                )?;
7680                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
7681                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
7682                let d_csr = e.moe_f16_grouped(
7683                    &dev.ptr_row,
7684                    2,
7685                    n_expert,
7686                    &exi,
7687                    &ex_off,
7688                    &exo,
7689                    &a_f16,
7690                    &a_s,
7691                    n_ff_exp,
7692                    n_embd,
7693                    n_active,
7694                    n_pairs,
7695                    m.down_exps.qtype,
7696                    m.down_exps.row_bytes,
7697                )?;
7698                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
7699            } else {
7700                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
7701                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
7702                let gate = e.mmq_iq_experts(
7703                    &dev.ptr_row,
7704                    0,
7705                    n_expert,
7706                    &exi,
7707                    &exo,
7708                    &exp_d,
7709                    &pt,
7710                    &z_scr,
7711                    n_embd,
7712                    n_ff_exp,
7713                    n_active,
7714                    n_pairs,
7715                    t,
7716                    m.gate_exps.qtype,
7717                    rbg_d,
7718                )?;
7719                let up = e.mmq_iq_experts(
7720                    &dev.ptr_row,
7721                    1,
7722                    n_expert,
7723                    &exi,
7724                    &exo,
7725                    &exp_d,
7726                    &pt,
7727                    &z_scr,
7728                    n_embd,
7729                    n_ff_exp,
7730                    n_active,
7731                    n_pairs,
7732                    t,
7733                    m.up_exps.qtype,
7734                    rbu_d,
7735                )?;
7736                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
7737                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
7738                // registers and writes ONLY the quantized scratch — the two-pass chain
7739                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
7740                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
7741                let a_scr = if crate::moe_fuse_actq_on() {
7742                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
7743                } else {
7744                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7745                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
7746                };
7747                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7748                let pself = e.htod_i32(&pair_self)?;
7749                e.mmq_iq_experts(
7750                    &dev.ptr_row,
7751                    2,
7752                    n_expert,
7753                    &exi,
7754                    &exo,
7755                    &exp_d,
7756                    &pself,
7757                    &a_scr,
7758                    n_ff_exp,
7759                    n_embd,
7760                    n_active,
7761                    n_pairs,
7762                    n_pairs,
7763                    m.down_exps.qtype,
7764                    m.down_exps.row_bytes,
7765                )?
7766            };
7767            let mut moe_out = e.uninit(t * n_embd)?;
7768            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7769            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7770                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7771            {
7772                let n_ff_sh = gate_shexp.out_features();
7773                let sg_gate = e.matmul(gate_shexp, z, t)?;
7774                let sg_up = e.matmul(up_shexp, z, t)?;
7775                let mut sa = e.uninit(t * n_ff_sh)?;
7776                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7777                let sh = e.matmul(down_shexp, &sa, t)?;
7778                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7779                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
7780                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
7781                // i.e. the one real prefill actually takes on a resident-expert MoE model,
7782                // so the concat-prime isolation fix has to land here as well.
7783                let g = match &m.gate_inp_shexp {
7784                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7785                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7786                    }
7787                    Some(gate_inp_shexp) => {
7788                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7789                        let mut g = e.uninit(t)?;
7790                        e.sigmoid(&gs, &mut g, t)?;
7791                        g
7792                    }
7793                    None => e.htod(&vec![1.0f32; t])?,
7794                };
7795                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7796            }
7797            return Ok(moe_out);
7798        }
7799
7800        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
7801        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
7802        let dec = std::env::var("MEMRA_MOE_DEC")
7803            .map(|v| v != "0")
7804            .unwrap_or(true);
7805        let matvec = |proj,
7806                      exi: &_,
7807                      exo: &_,
7808                      exp_d: &_,
7809                      pt: &_,
7810                      aq: &_,
7811                      ad: &_,
7812                      inf,
7813                      outf,
7814                      qtype,
7815                      rb|
7816         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7817            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
7818            let dec = dec && q8_expert_dec_supported(qtype);
7819            if dec {
7820                e.moe_pairs_matvec_q8_dec(
7821                    &dev.ptr_row,
7822                    proj,
7823                    exi,
7824                    exo,
7825                    exp_d,
7826                    pt,
7827                    aq,
7828                    ad,
7829                    inf,
7830                    outf,
7831                    n_expert,
7832                    n_active,
7833                    n_pairs,
7834                    qtype,
7835                    rb,
7836                )
7837            } else {
7838                e.moe_pairs_matvec_q8_em(
7839                    &dev.ptr_row,
7840                    proj,
7841                    exi,
7842                    exo,
7843                    exp_d,
7844                    pt,
7845                    aq,
7846                    ad,
7847                    inf,
7848                    outf,
7849                    n_expert,
7850                    n_active,
7851                    n_pairs,
7852                    qtype,
7853                    rb,
7854                )
7855            }
7856        };
7857        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7858        let gate = matvec(
7859            0,
7860            &exi,
7861            &exo,
7862            &exp_d,
7863            &pt,
7864            &zq,
7865            &zd,
7866            n_embd,
7867            n_ff_exp,
7868            m.gate_exps.qtype,
7869            rbg_d,
7870        )?;
7871        let up = matvec(
7872            1,
7873            &exi,
7874            &exo,
7875            &exp_d,
7876            &pt,
7877            &zq,
7878            &zd,
7879            n_embd,
7880            n_ff_exp,
7881            m.up_exps.qtype,
7882            rbu_d,
7883        )?;
7884        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7885        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7886        // down consumes PAIR-major activation rows: pair_tok = identity.
7887        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7888        let pself = e.htod_i32(&pair_self)?;
7889        let y_down = matvec(
7890            2,
7891            &exi,
7892            &exo,
7893            &exp_d,
7894            &pself,
7895            &aq2,
7896            &ad2,
7897            n_ff_exp,
7898            n_embd,
7899            m.down_exps.qtype,
7900            m.down_exps.row_bytes,
7901        )?;
7902        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
7903        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7904
7905        // SHARED EXPERT epilogue — same as the other paths.
7906        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7907        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7908        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7909            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7910        {
7911            let n_ff_sh = gate_shexp.out_features();
7912            // These decode-exact forms are required by the new Step resident arm. Keep the
7913            // established grouped shared-expert program for every other architecture: widening
7914            // this to Gemma changed its speculative acceptance despite green argmax gates.
7915            let step_exact = true;
7916            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
7917            let (sg_gate, sg_up) = if step_exact && t == 1 {
7918                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
7919            } else if verify_t {
7920                let mut fused = None;
7921                if crate::spec::spec_fused_t()
7922                    && (2..=4).contains(&t)
7923                    && e.uses_q8_1_fast(gate_shexp)
7924                    && e.uses_q8_1_fast(up_shexp)
7925                {
7926                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7927                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
7928                }
7929                match fused {
7930                    Some(pair) => pair,
7931                    None => (
7932                        e.matmul_decode_exact(gate_shexp, z, t)?,
7933                        e.matmul_decode_exact(up_shexp, z, t)?,
7934                    ),
7935                }
7936            } else {
7937                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
7938            };
7939            let mut sa = e.uninit(t * n_ff_sh)?;
7940            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7941            let sh = if verify_t {
7942                e.matmul_decode_exact(down_shexp, &sa, t)?
7943            } else {
7944                e.matmul(down_shexp, &sa, t)?
7945            };
7946            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7947            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
7948            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
7949            // dispatch choice cannot change bits.
7950            let g = match &m.gate_inp_shexp {
7951                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7952                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7953                }
7954                Some(gate_inp_shexp) => {
7955                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7956                    let mut g = e.uninit(t)?;
7957                    e.sigmoid(&gs, &mut g, t)?;
7958                    g
7959                }
7960                None => e.htod(&vec![1.0f32; t])?,
7961            };
7962            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7963        }
7964        Ok(moe_out)
7965    }
7966
7967    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
7968    #[allow(clippy::too_many_arguments)]
7969    #[allow(clippy::too_many_arguments)]
7970    fn moe_ffn_dev(
7971        e: &Engine,
7972        m: &MoeWeights,
7973        z: &CudaSlice<f32>,
7974        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7975        logits: &CudaSlice<f32>,
7976        t: usize,
7977        cfg: &ModelConfig,
7978        il: u16,
7979        max_block: usize,
7980    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7981        let moe = cfg.moe.as_ref().unwrap();
7982        let n_embd = cfg.n_embd as usize;
7983        let n_expert = moe.expert_count as usize;
7984        let n_used = moe.expert_used_count as usize;
7985        let n_ff_exp = moe.expert_ff_length as usize;
7986        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
7987        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
7988        // clamped layers; assert both so a future caller that skips the gate fails loudly.
7989        debug_assert!(
7990            cfg.sigmoid_router().is_none(),
7991            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
7992        );
7993        debug_assert!(
7994            !cfg.swiglu_clamped_at(il as u32),
7995            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
7996        );
7997
7998        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
7999        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
8000        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
8001        // skipped entirely for macro-free experts (every k-quant GGUF).
8002        if m.has_macros {
8003            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
8004        }
8005
8006        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
8007        let mut moe_out = e.uninit(t * n_embd)?;
8008
8009        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
8010        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
8011        if let Some(dev) = m.dev_exps.as_ref() {
8012            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
8013            // the combined stride; up's base is offset in the ptr table. Down unchanged.
8014            let (rbg_d, rbu_d) = if dev.gu_il {
8015                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8016                (sxx, sxx)
8017            } else {
8018                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8019            };
8020            let q8 = moe_q8_enabled()
8021                && q8_expert_supported(m.gate_exps.qtype)
8022                && q8_expert_supported(m.up_exps.qtype)
8023                && q8_expert_supported(m.down_exps.qtype);
8024            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
8025            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
8026            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
8027            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8028            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8029            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8030            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8031            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8032            let rows_arm = q8
8033                && t > 1
8034                && crate::spec::spec_m2()
8035                && n_ff_exp == 512
8036                && n_used <= 8
8037                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8038                    .map(|v| v.is_empty() || v == "v")
8039                    .unwrap_or(true)
8040                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8041                    .map(|v| v.is_empty() || v == "w8h2v")
8042                    .unwrap_or(true);
8043            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8044            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8045            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8046            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8047            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8048            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8049            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8050            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8051            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8052                .ok()
8053                .and_then(|v| v.parse::<i32>().ok())
8054                .unwrap_or(1);
8055            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8056            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8057            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8058            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8059            // axis. Three chain-pinning attempts did not close it (receipts,
8060            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8061            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8062            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8063            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8064            // never decode-batch-gate at B=8 on the MoE model itself.
8065            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8066            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8067            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8068            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8069            // de-admission verdict above stands until those gates are GREEN on the MoE
8070            // artifact; this door must never default on.
8071            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8072            let csr_qt = |qt: i32| {
8073                qt == crate::QT_IQ4_XS
8074                    || qt == crate::QT_IQ3_S
8075                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8076            };
8077            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8078            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8079            let csr_arm = rows_arm
8080                && csr_mode > 0
8081                && t <= csr_t_max
8082                && csr_uniform
8083                && csr_qt(m.gate_exps.qtype)
8084                && csr_qt(m.up_exps.qtype)
8085                && csr_qt(m.down_exps.qtype);
8086            if csr_arm {
8087                if csr_mode == 2 {
8088                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8089                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8090                }
8091                let n_pairs = t * n_used;
8092                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8093                let act = e.moe_gate_up_silu8_dev_q8_csr(
8094                    &dev.ptr_row,
8095                    &sel_d,
8096                    &zq,
8097                    &zd,
8098                    n_pairs,
8099                    n_embd,
8100                    n_ff_exp,
8101                    n_used,
8102                    n_expert,
8103                    m.gate_exps.qtype,
8104                    m.up_exps.qtype,
8105                    rbg_d,
8106                    rbu_d,
8107                )?;
8108                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8109                // down stays on the _rows twin — BOTH CSR down variants measured negative
8110                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8111                // 16-group rows have too little decode to amortize any dedup structure.
8112                e.moe_down8_fma_dev_q8_rows(
8113                    &dev.ptr_row,
8114                    &sel_d,
8115                    &w_d,
8116                    &aq2,
8117                    &ad2,
8118                    &mut moe_out,
8119                    t,
8120                    n_ff_exp,
8121                    n_embd,
8122                    n_used,
8123                    n_expert,
8124                    m.down_exps.qtype,
8125                    m.down_exps.row_bytes,
8126                )?;
8127                if csr_mode == 2 {
8128                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8129                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8130                        &dev.ptr_row,
8131                        &sel_d,
8132                        &zq,
8133                        &zd,
8134                        t,
8135                        n_embd,
8136                        n_ff_exp,
8137                        n_used,
8138                        n_expert,
8139                        m.gate_exps.qtype,
8140                        m.up_exps.qtype,
8141                        rbg_d,
8142                        rbu_d,
8143                        &m.dev_macros,
8144                    )?;
8145                    let mut out_r = e.uninit(t * n_embd)?;
8146                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8147                    e.moe_down8_fma_dev_q8_rows(
8148                        &dev.ptr_row,
8149                        &sel_d,
8150                        &w_d,
8151                        &aq2r,
8152                        &ad2r,
8153                        &mut out_r,
8154                        t,
8155                        n_ff_exp,
8156                        n_embd,
8157                        n_used,
8158                        n_expert,
8159                        m.down_exps.qtype,
8160                        m.down_exps.row_bytes,
8161                    )?;
8162                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8163                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8164                    let ba = a1
8165                        .iter()
8166                        .zip(&a2)
8167                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8168                        .count();
8169                    let bo = o1
8170                        .iter()
8171                        .zip(&o2)
8172                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8173                        .count();
8174                    if ba + bo > 0 {
8175                        eprintln!(
8176                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8177                            a1.len(),
8178                            o1.len()
8179                        );
8180                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8181                        let sel_h = e.dtoh_i32(&sel_d)?;
8182                        let mut shown = 0;
8183                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8184                            if x.to_bits() != y.to_bits() && shown < 4 {
8185                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8186                                let ex = sel_h[p];
8187                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8188                                eprintln!(
8189                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8190                                );
8191                                shown += 1;
8192                            }
8193                        }
8194                        std::process::exit(3);
8195                    }
8196                }
8197            } else if rows_arm {
8198                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8199                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8200                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8201                    use std::sync::atomic::{AtomicU64, Ordering};
8202                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8203                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8204                    static CALLS: AtomicU64 = AtomicU64::new(0);
8205                    let sel_h = e.dtoh_i32(&sel_d)?;
8206                    let mut u: Vec<i32> = sel_h.clone();
8207                    u.sort_unstable();
8208                    u.dedup();
8209                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8210                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8211                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8212                    if c % 480 == 0 {
8213                        let p = PAIRS.load(Ordering::Relaxed);
8214                        let q = UNIQ.load(Ordering::Relaxed);
8215                        eprintln!(
8216                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8217                            q as f64 / p as f64
8218                        );
8219                    }
8220                }
8221                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8222                let act = e.moe_gate_up_silu8_dev_q8_rows(
8223                    &dev.ptr_row,
8224                    &sel_d,
8225                    &zq,
8226                    &zd,
8227                    t,
8228                    n_embd,
8229                    n_ff_exp,
8230                    n_used,
8231                    n_expert,
8232                    m.gate_exps.qtype,
8233                    m.up_exps.qtype,
8234                    rbg_d,
8235                    rbu_d,
8236                    &m.dev_macros,
8237                )?;
8238                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8239                e.moe_down8_fma_dev_q8_rows(
8240                    &dev.ptr_row,
8241                    &sel_d,
8242                    &w_d,
8243                    &aq2,
8244                    &ad2,
8245                    &mut moe_out,
8246                    t,
8247                    n_ff_exp,
8248                    n_embd,
8249                    n_used,
8250                    n_expert,
8251                    m.down_exps.qtype,
8252                    m.down_exps.row_bytes,
8253                )?;
8254            } else {
8255                for tok in 0..t {
8256                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8257                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8258                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8259                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8260                    if q8 {
8261                        let (zq, zd) = match (t, zq8) {
8262                            (1, Some((q, d))) => (q.clone(), d.clone()),
8263                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8264                        };
8265                        let act = e.moe_gate_up_silu8_dev_q8(
8266                            &dev.ptr_row,
8267                            &selt,
8268                            &zq,
8269                            &zd,
8270                            n_embd,
8271                            n_ff_exp,
8272                            n_used,
8273                            n_expert,
8274                            m.gate_exps.qtype,
8275                            m.up_exps.qtype,
8276                            rbg_d,
8277                            rbu_d,
8278                            &m.dev_macros,
8279                        )?;
8280                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8281                        e.moe_down8_fma_dev_q8(
8282                            &dev.ptr_row,
8283                            &selt,
8284                            &wt,
8285                            &aq2,
8286                            &ad2,
8287                            &mut dst,
8288                            n_ff_exp,
8289                            n_embd,
8290                            n_used,
8291                            n_expert,
8292                            m.down_exps.qtype,
8293                            m.down_exps.row_bytes,
8294                        )?;
8295                    } else {
8296                        let act = e.moe_gate_up_silu8_dev(
8297                            &dev.ptr_row,
8298                            &selt,
8299                            &zt,
8300                            n_embd,
8301                            n_ff_exp,
8302                            n_used,
8303                            n_expert,
8304                            m.gate_exps.qtype,
8305                            m.up_exps.qtype,
8306                            rbg_d,
8307                            rbu_d,
8308                            &m.dev_macros,
8309                        )?;
8310                        e.moe_down8_fma_dev(
8311                            &dev.ptr_row,
8312                            &selt,
8313                            &wt,
8314                            &act,
8315                            &mut dst,
8316                            n_ff_exp,
8317                            n_embd,
8318                            n_used,
8319                            n_expert,
8320                            m.down_exps.qtype,
8321                            m.down_exps.row_bytes,
8322                        )?;
8323                    }
8324                }
8325            }
8326        } else {
8327            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8328            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8329            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8330            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8331            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8332            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8333            let q8 = moe_q8_enabled()
8334                && q8_expert_supported(m.gate_exps.qtype)
8335                && q8_expert_supported(m.up_exps.qtype)
8336                && q8_expert_supported(m.down_exps.qtype);
8337            e.with_moe_cache(max_block, |c, eng| {
8338                let row = c
8339                    .layer_dev_row(il, n_expert, eng)?
8340                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8341                for tok in 0..t {
8342                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8343                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8344                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8345                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8346                    if q8 {
8347                        let (zq, zd) = match (t, zq8) {
8348                            (1, Some((q, d))) => (q.clone(), d.clone()),
8349                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8350                        };
8351                        let act = eng.moe_gate_up_silu8_dev_q8(
8352                            row,
8353                            &selt,
8354                            &zq,
8355                            &zd,
8356                            n_embd,
8357                            n_ff_exp,
8358                            n_used,
8359                            n_expert,
8360                            m.gate_exps.qtype,
8361                            m.up_exps.qtype,
8362                            m.gate_exps.row_bytes,
8363                            m.up_exps.row_bytes,
8364                            &m.dev_macros,
8365                        )?;
8366                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8367                        eng.moe_down8_fma_dev_q8(
8368                            row,
8369                            &selt,
8370                            &wt,
8371                            &aq2,
8372                            &ad2,
8373                            &mut dst,
8374                            n_ff_exp,
8375                            n_embd,
8376                            n_used,
8377                            n_expert,
8378                            m.down_exps.qtype,
8379                            m.down_exps.row_bytes,
8380                        )?;
8381                    } else {
8382                        let act = eng.moe_gate_up_silu8_dev(
8383                            row,
8384                            &selt,
8385                            &zt,
8386                            n_embd,
8387                            n_ff_exp,
8388                            n_used,
8389                            n_expert,
8390                            m.gate_exps.qtype,
8391                            m.up_exps.qtype,
8392                            m.gate_exps.row_bytes,
8393                            m.up_exps.row_bytes,
8394                            &m.dev_macros,
8395                        )?;
8396                        eng.moe_down8_fma_dev(
8397                            row,
8398                            &selt,
8399                            &wt,
8400                            &act,
8401                            &mut dst,
8402                            n_ff_exp,
8403                            n_embd,
8404                            n_used,
8405                            n_expert,
8406                            m.down_exps.qtype,
8407                            m.down_exps.row_bytes,
8408                        )?;
8409                    }
8410                }
8411                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8412                c.hits += (t * 3 * n_used) as u64;
8413                Ok(())
8414            })?;
8415        }
8416
8417        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8418        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8419        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8420        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8421        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8422            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8423        {
8424            let n_ff_sh = gate_shexp.out_features();
8425            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8426            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8427            let verify_t = t > 1 && t < PRIME_MIN_T;
8428            let (sg_gate, sg_up) = if t == 1 {
8429                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8430            } else if verify_t {
8431                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8432                // rides one shared quantize + one fused2 batched launch instead of two
8433                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8434                let mut fused = None;
8435                if crate::spec::spec_fused_t()
8436                    && (2..=4).contains(&t)
8437                    && e.uses_q8_1_fast(gate_shexp)
8438                    && e.uses_q8_1_fast(up_shexp)
8439                {
8440                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8441                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8442                }
8443                match fused {
8444                    Some(pair) => pair,
8445                    None => (
8446                        e.matmul_decode_exact(gate_shexp, z, t)?,
8447                        e.matmul_decode_exact(up_shexp, z, t)?,
8448                    ),
8449                }
8450            } else {
8451                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8452            };
8453            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8454            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8455            let sh = if verify_t {
8456                e.matmul_decode_exact(down_shexp, &sa, t)?
8457            } else {
8458                e.matmul(down_shexp, &sa, t)?
8459            };
8460            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8461            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8462            // between the two arms; prefill keeps the batched cuBLASLt linear).
8463            let g = match &m.gate_inp_shexp {
8464                Some(gate_inp_shexp) => {
8465                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8466                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8467                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8468                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8469                    } else {
8470                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8471                        let mut g = e.uninit(t)?;
8472                        e.sigmoid(&gs, &mut g, t)?;
8473                        g
8474                    }
8475                }
8476                None => e.htod(&vec![1.0f32; t])?,
8477            };
8478            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8479        }
8480
8481        Ok(moe_out)
8482    }
8483
8484    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8485    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8486    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8487    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8488    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8489    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8490    /// the collected raw pointers cannot move between collection and launch (single-threaded
8491    /// decode; the lock is held only for collection, launches are stream-ordered after any
8492    /// prior same-stream staging writes).
8493    #[allow(clippy::too_many_arguments)]
8494    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8495    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8496    #[allow(clippy::too_many_arguments)]
8497    fn moe_gdec_token_q8(
8498        e: &Engine,
8499        m: &MoeWeights,
8500        il: u16,
8501        max_block: usize,
8502        zq: &CudaSlice<i8>,
8503        zd: &CudaSlice<f32>,
8504        sel: &[u32],
8505        w: &[f32],
8506        moe_out: &mut CudaSlice<f32>,
8507        tok: usize,
8508        n_embd: usize,
8509        n_ff_exp: usize,
8510        n_used: usize,
8511    ) -> Result<bool, Box<dyn std::error::Error>> {
8512        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8513        use cudarc::driver::DevicePtr;
8514        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8515            let mut g = [0u64; 8];
8516            let mut u = [0u64; 8];
8517            let mut d = [0u64; 8];
8518            for (j, &ex) in sel.iter().enumerate() {
8519                let ex = ex as u16;
8520                let (Some(sg), Some(su), Some(sd)) = (
8521                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8522                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8523                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8524                ) else {
8525                    return Ok(None);
8526                };
8527                let __s = eng.stream();
8528                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8529                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8530                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8531                g[j] = pg as u64;
8532                u[j] = pu as u64;
8533                d[j] = pd as u64;
8534            }
8535            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8536                for &ex in sel {
8537                    let ex = ex as u16;
8538                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8539                        c.note_profile_hit(BlockId::new(il, proj, ex));
8540                    }
8541                }
8542            }
8543            c.hits += (3 * n_used) as u64;
8544            Ok(Some((g, u, d)))
8545        })?;
8546        let Some((g, u, d)) = ptrs else {
8547            return Ok(false);
8548        };
8549        let mut wv = [0f32; 8];
8550        wv[..n_used].copy_from_slice(w);
8551        let act = e.moe_gate_up_silu8_q8(
8552            crate::WPtr8(g),
8553            crate::WPtr8(u),
8554            zq,
8555            zd,
8556            n_embd,
8557            n_ff_exp,
8558            n_used,
8559            m.gate_exps.qtype,
8560            m.up_exps.qtype,
8561            m.gate_exps.row_bytes,
8562            m.up_exps.row_bytes,
8563        )?;
8564        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8565        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8566        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8567        e.moe_down8_fma_q8(
8568            crate::WPtr8(d),
8569            crate::F32x8(wv),
8570            &aq2,
8571            &ad2,
8572            &mut dst,
8573            n_ff_exp,
8574            n_embd,
8575            n_used,
8576            m.down_exps.qtype,
8577            m.down_exps.row_bytes,
8578        )?;
8579        Ok(true)
8580    }
8581
8582    fn moe_gdec_token(
8583        e: &Engine,
8584        m: &MoeWeights,
8585        il: u16,
8586        max_block: usize,
8587        zt: &cudarc::driver::CudaView<f32>,
8588        sel: &[u32],
8589        w: &[f32],
8590        moe_out: &mut CudaSlice<f32>,
8591        tok: usize,
8592        n_embd: usize,
8593        n_ff_exp: usize,
8594        n_used: usize,
8595    ) -> Result<bool, Box<dyn std::error::Error>> {
8596        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8597        use cudarc::driver::DevicePtr;
8598        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8599        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8600            let mut g = [0u64; 8];
8601            let mut u = [0u64; 8];
8602            let mut d = [0u64; 8];
8603            for (j, &ex) in sel.iter().enumerate() {
8604                let ex = ex as u16;
8605                let (Some(sg), Some(su), Some(sd)) = (
8606                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8607                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8608                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8609                ) else {
8610                    return Ok(None);
8611                };
8612                let __s = eng.stream();
8613                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8614                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8615                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8616                g[j] = pg as u64;
8617                u[j] = pu as u64;
8618                d[j] = pd as u64;
8619            }
8620            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8621                for &ex in sel {
8622                    let ex = ex as u16;
8623                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8624                        c.note_profile_hit(BlockId::new(il, proj, ex));
8625                    }
8626                }
8627            }
8628            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
8629            Ok(Some((g, u, d)))
8630        })?;
8631        let Some((g, u, d)) = ptrs else {
8632            return Ok(false);
8633        };
8634        let mut wv = [0f32; 8];
8635        wv[..n_used].copy_from_slice(w);
8636        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
8637        let act = e.moe_gate_up_silu8(
8638            crate::WPtr8(g),
8639            crate::WPtr8(u),
8640            zt,
8641            n_embd,
8642            n_ff_exp,
8643            n_used,
8644            m.gate_exps.qtype,
8645            m.up_exps.qtype,
8646            m.gate_exps.row_bytes,
8647            m.up_exps.row_bytes,
8648        )?;
8649        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8650        e.moe_down8_fma_into(
8651            crate::WPtr8(d),
8652            crate::F32x8(wv),
8653            &act,
8654            &mut dst,
8655            n_ff_exp,
8656            n_embd,
8657            n_used,
8658            m.down_exps.qtype,
8659            m.down_exps.row_bytes,
8660        )?;
8661        Ok(true)
8662    }
8663
8664    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
8665    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
8666    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
8667    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
8668    fn moe_cached_gemm_q8(
8669        e: &Engine,
8670        il: u16,
8671        proj: u8,
8672        ex: usize,
8673        m: &MoeWeights,
8674        max_block: usize,
8675        aq: &CudaSlice<i8>,
8676        ad: &CudaSlice<f32>,
8677    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8678        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8679        let exps = match proj {
8680            PROJ_GATE => &m.gate_exps,
8681            PROJ_UP => &m.up_exps,
8682            _ => &m.down_exps,
8683        };
8684        let layout = exps.expert_layout(ex);
8685        let id = BlockId::new(il, proj, ex as u16);
8686        let source = exps.expert_source(ex);
8687        e.with_moe_cache(max_block, |c, eng| {
8688            let slot = c.dispatch_source(id, source, eng)?;
8689            let DispatchSlot::Resident(sl) = slot;
8690            let buf = c.slot(sl);
8691            eng.qmatvec_expert_q8(
8692                buf,
8693                0..layout.len,
8694                aq,
8695                ad,
8696                1,
8697                exps.in_f,
8698                exps.out_f,
8699                layout.qtype,
8700                layout.row_bytes,
8701            )
8702        })
8703    }
8704
8705    fn moe_cached_gemm(
8706        e: &Engine,
8707        il: u16,
8708        proj: u8,
8709        ex: usize,
8710        m: &MoeWeights,
8711        max_block: usize,
8712        x: &cudarc::driver::CudaView<f32>,
8713    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8714        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8715        let exps = match proj {
8716            PROJ_GATE => &m.gate_exps,
8717            PROJ_UP => &m.up_exps,
8718            _ => &m.down_exps,
8719        };
8720        let layout = exps.expert_layout(ex);
8721        let id = BlockId::new(il, proj, ex as u16);
8722        let source = exps.expert_source(ex);
8723        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
8724        e.with_moe_cache(max_block, |c, eng| {
8725            let slot = c.dispatch_source(id, source, eng)?;
8726            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
8727            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
8728            let DispatchSlot::Resident(sl) = slot;
8729            let buf = c.slot(sl);
8730            eng.qmatvec_view(
8731                buf,
8732                0..layout.len,
8733                x,
8734                1,
8735                exps.in_f,
8736                exps.out_f,
8737                layout.qtype,
8738                layout.row_bytes,
8739            )
8740        })
8741    }
8742
8743    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
8744    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
8745    /// so the current forward's backend assignment and output remain unchanged.
8746    fn moe_profile_admit_expert(
8747        e: &Engine,
8748        il: u16,
8749        ex: usize,
8750        m: &MoeWeights,
8751        max_block: usize,
8752    ) -> Result<(), Box<dyn std::error::Error>> {
8753        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8754        e.with_moe_cache(max_block, |cache, eng| {
8755            for (proj, exps) in [
8756                (PROJ_GATE, &m.gate_exps),
8757                (PROJ_UP, &m.up_exps),
8758                (PROJ_DOWN, &m.down_exps),
8759            ] {
8760                let id = BlockId::new(il, proj, ex as u16);
8761                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
8762            }
8763            Ok(())
8764        })
8765    }
8766
8767    /// Read a projection from the immutable residency set when present; otherwise use one
8768    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
8769    #[allow(clippy::too_many_arguments)]
8770    fn moe_frozen_gemm(
8771        e: &Engine,
8772        il: u16,
8773        proj: u8,
8774        ex: usize,
8775        m: &MoeWeights,
8776        max_block: usize,
8777        x: &cudarc::driver::CudaView<f32>,
8778        scratch: &mut Option<CudaSlice<u8>>,
8779        scratch_len: usize,
8780    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8781        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
8782        let exps = match proj {
8783            PROJ_GATE => &m.gate_exps,
8784            PROJ_UP => &m.up_exps,
8785            _ => &m.down_exps,
8786        };
8787        let layout = exps.expert_layout(ex);
8788        let id = BlockId::new(il, proj, ex as u16);
8789        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
8790            let Some(slot) = cache.resident(id) else {
8791                return Ok(None);
8792            };
8793            let buf = cache.slot(slot);
8794            Ok(Some(eng.qmatvec_view(
8795                buf,
8796                0..layout.len,
8797                x,
8798                1,
8799                exps.in_f,
8800                exps.out_f,
8801                layout.qtype,
8802                layout.row_bytes,
8803            )?))
8804        })? {
8805            return Ok(output);
8806        }
8807        if scratch.is_none() {
8808            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
8809        }
8810        let scratch = scratch.as_mut().unwrap();
8811        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
8812        e.qmatvec_view(
8813            scratch,
8814            0..layout.len,
8815            x,
8816            1,
8817            exps.in_f,
8818            exps.out_f,
8819            layout.qtype,
8820            layout.row_bytes,
8821        )
8822    }
8823
8824    fn moe_prefetch_expert(
8825        e: &Engine,
8826        il: u16,
8827        ex: usize,
8828        m: &MoeWeights,
8829        max_block: usize,
8830        keep: &[crate::moe_cache::BlockId],
8831    ) -> Result<(), Box<dyn std::error::Error>> {
8832        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8833        e.with_moe_cache(max_block, |c, eng| {
8834            for (proj, exps) in [
8835                (PROJ_GATE, &m.gate_exps),
8836                (PROJ_UP, &m.up_exps),
8837                (PROJ_DOWN, &m.down_exps),
8838            ] {
8839                let id = BlockId::new(il, proj, ex as u16);
8840                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
8841            }
8842            Ok(())
8843        })
8844    }
8845
8846    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
8847    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
8848    fn moe_prefetch_disk_expert(
8849        e: &Engine,
8850        il: u16,
8851        ex: usize,
8852        m: &MoeWeights,
8853        max_block: usize,
8854        keep: &[crate::moe_cache::BlockId],
8855    ) -> Result<(), Box<dyn std::error::Error>> {
8856        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8857        e.with_moe_cache(max_block, |c, eng| {
8858            for (proj, exps) in [
8859                (PROJ_GATE, &m.gate_exps),
8860                (PROJ_UP, &m.up_exps),
8861                (PROJ_DOWN, &m.down_exps),
8862            ] {
8863                let source = exps.expert_source(ex);
8864                if let crate::model::ExpertSource::Disk { .. } = &source {
8865                    let id = BlockId::new(il, proj, ex as u16);
8866                    let _ = c.prefetch_source(id, source, keep, eng)?;
8867                }
8868            }
8869            Ok(())
8870        })
8871    }
8872
8873    #[inline]
8874    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
8875        let _ = m.gate_exps.prefetch_expert_pages(ex);
8876        let _ = m.up_exps.prefetch_expert_pages(ex);
8877        let _ = m.down_exps.prefetch_expert_pages(ex);
8878    }
8879}
8880
8881// ================================================================================================
8882// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
8883//
8884// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
8885// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
8886// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
8887//
8888// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
8889// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
8890// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
8891// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
8892// identical to the per-token loop regardless of expert processing order.
8893//
8894// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
8895// ================================================================================================
8896
8897impl HybridModel {
8898    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
8899    /// sequential fused q8 program over the token axis; clamped layers use the separate
8900    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
8901    #[allow(clippy::too_many_arguments)]
8902    fn moe_ffn_grouped_resident_q8(
8903        e: &Engine,
8904        m: &MoeWeights,
8905        z: &CudaSlice<f32>,
8906        t: usize,
8907        cfg: &ModelConfig,
8908        il: u16,
8909        sel_all: &[u32],
8910        w_all: &[f32],
8911        table: &CudaSlice<u64>,
8912        gu_il: bool,
8913    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8914        let moe = cfg.moe.as_ref().unwrap();
8915        let n_embd = cfg.n_embd as usize;
8916        let n_expert = moe.expert_count as usize;
8917        let n_used = moe.expert_used_count as usize;
8918        let n_ff_exp = moe.expert_ff_length as usize;
8919        let n_pairs = t * n_used;
8920        debug_assert_eq!(sel_all.len(), n_pairs);
8921        debug_assert_eq!(w_all.len(), n_pairs);
8922        debug_assert!(
8923            m.gate_exps.macros.is_none()
8924                && m.up_exps.macros.is_none()
8925                && m.down_exps.macros.is_none(),
8926            "resident grouped q8 does not fold per-expert macro scales",
8927        );
8928
8929        // The rows twins run the resident sequential program verbatim on grid.z = token:
8930        // fused gate/up/SiLU per slot, batched activation quantization, then the original
8931        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
8932        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
8933        // never enter the softmax router.
8934        if !cfg.swiglu_clamped_at(il as u32) {
8935            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8936            let sel_d = e.htod_i32(&sel)?;
8937            let w_d = e.htod(w_all)?;
8938            let (gate_row_bytes, up_row_bytes) = if gu_il {
8939                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8940                (combined, combined)
8941            } else {
8942                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8943            };
8944            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8945            let act = e.moe_gate_up_silu8_dev_q8_rows(
8946                table,
8947                &sel_d,
8948                &zq,
8949                &zd,
8950                t,
8951                n_embd,
8952                n_ff_exp,
8953                n_used,
8954                n_expert,
8955                m.gate_exps.qtype,
8956                m.up_exps.qtype,
8957                gate_row_bytes,
8958                up_row_bytes,
8959                &m.dev_macros,
8960            )?;
8961            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8962            let mut moe_out = e.uninit(t * n_embd)?;
8963            e.moe_down8_fma_dev_q8_rows_g(
8964                table,
8965                &sel_d,
8966                &w_d,
8967                &aq2,
8968                &ad2,
8969                &mut moe_out,
8970                t,
8971                n_ff_exp,
8972                n_embd,
8973                n_used,
8974                n_expert,
8975                m.down_exps.qtype,
8976                m.down_exps.row_bytes,
8977            )?;
8978
8979            if std::env::var("MEMRA_MOE_STATS").is_ok() {
8980                let mut counts = vec![0usize; n_expert];
8981                for &expert in sel_all {
8982                    counts[expert as usize] += 1;
8983                }
8984                let mut sizes: Vec<usize> =
8985                    counts.into_iter().filter(|&count| count != 0).collect();
8986                sizes.sort_unstable();
8987                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
8988                println!(
8989                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
8990                     m_e: min={} median={} mean={mean:.1} max={}",
8991                    sizes.len(),
8992                    n_expert,
8993                    sizes.first().copied().unwrap_or(0),
8994                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
8995                    sizes.last().copied().unwrap_or(0),
8996                );
8997            }
8998            return Ok(moe_out);
8999        }
9000
9001        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
9002        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
9003        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
9004        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
9005        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9006        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
9007        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
9008
9009        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
9010        for (pair, &expert) in pair_ex.iter().enumerate() {
9011            by_expert[expert as usize].push(pair as i32);
9012        }
9013
9014        let pair_tok_d = e.htod_i32(&pair_tok)?;
9015        let pair_ex_d = e.htod_i32(&pair_ex)?;
9016        let pair_w_d = e.htod(w_all)?;
9017        let tok_off_d = e.htod_i32(&tok_off)?;
9018        let tok_ids_d = e.htod_i32(&tok_ids)?;
9019
9020        let matvec = |proj: i32,
9021                      pair_rows: &CudaSlice<i32>,
9022                      aq: &CudaSlice<i8>,
9023                      ad: &CudaSlice<f32>,
9024                      in_f: usize,
9025                      out_f: usize,
9026                      qtype: i32,
9027                      row_bytes: usize|
9028         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9029            e.moe_pairs_matvec_q8(
9030                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9031                row_bytes,
9032            )
9033        };
9034
9035        let (gate_row_bytes, up_row_bytes) = if gu_il {
9036            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9037            (combined, combined)
9038        } else {
9039            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9040        };
9041        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9042        let gate = matvec(
9043            0,
9044            &pair_tok_d,
9045            &zq,
9046            &zd,
9047            n_embd,
9048            n_ff_exp,
9049            m.gate_exps.qtype,
9050            gate_row_bytes,
9051        )?;
9052        let up = matvec(
9053            1,
9054            &pair_tok_d,
9055            &zq,
9056            &zd,
9057            n_embd,
9058            n_ff_exp,
9059            m.up_exps.qtype,
9060            up_row_bytes,
9061        )?;
9062        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9063        Self::ffn_act_lim(
9064            e,
9065            cfg,
9066            &gate,
9067            &up,
9068            1.0,
9069            1.0,
9070            cfg.clamp_exp_at(il as u32),
9071            &mut act,
9072            n_pairs * n_ff_exp,
9073        )?;
9074        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9075        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9076        let pair_self_d = e.htod_i32(&pair_self)?;
9077        let down = matvec(
9078            2,
9079            &pair_self_d,
9080            &aq2,
9081            &ad2,
9082            n_ff_exp,
9083            n_embd,
9084            m.down_exps.qtype,
9085            m.down_exps.row_bytes,
9086        )?;
9087        let mut moe_out = e.uninit(t * n_embd)?;
9088        e.moe_pairs_scatter(
9089            &down,
9090            &pair_w_d,
9091            &tok_off_d,
9092            &tok_ids_d,
9093            &mut moe_out,
9094            t,
9095            n_embd,
9096        )?;
9097
9098        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9099            let mut sizes: Vec<usize> = by_expert
9100                .iter()
9101                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9102                .collect();
9103            sizes.sort_unstable();
9104            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9105            println!(
9106                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9107                 m_e: min={} median={} mean={mean:.1} max={}",
9108                sizes.len(),
9109                n_expert,
9110                sizes.first().copied().unwrap_or(0),
9111                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9112                sizes.last().copied().unwrap_or(0),
9113            );
9114        }
9115        Ok(moe_out)
9116    }
9117
9118    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9119    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9120    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9121    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9122    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9123    #[allow(clippy::too_many_arguments)]
9124    fn shexp_split_matvec(
9125        e: &Engine,
9126        rank1: &Engine,
9127        wg: &CudaSlice<u8>,
9128        wu: &CudaSlice<u8>,
9129        wd: &CudaSlice<u8>,
9130        z: &CudaSlice<f32>,
9131        lim: Option<f32>,
9132        cfg: &ModelConfig,
9133        il: u16,
9134        n_embd: usize,
9135        n_ff_sh: usize,
9136    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9137        use cudarc::driver::DevicePtr;
9138        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9139            return Ok(None);
9140        }
9141        let hf = n_ff_sh / 2;
9142        let nd = n_embd / 2;
9143        struct Rep {
9144            wg1: CudaSlice<u8>,
9145            wu1: CudaSlice<u8>,
9146            wd1: CudaSlice<u8>,
9147        }
9148        struct SplitWs {
9149            pin_dev: usize,
9150            // e side
9151            gate0: CudaSlice<f32>,
9152            up0: CudaSlice<f32>,
9153            act: CudaSlice<f32>,
9154            sh_buf: CudaSlice<f32>,
9155            ev_z: cudarc::driver::CudaEvent,
9156            ev_act0: cudarc::driver::CudaEvent,
9157            // rank1 side
9158            z1: CudaSlice<f32>,
9159            g1: CudaSlice<f32>,
9160            u1: CudaSlice<f32>,
9161            a1h: CudaSlice<f32>,
9162            act1: CudaSlice<f32>,
9163            y1: CudaSlice<f32>,
9164            ev_act1: cudarc::driver::CudaEvent,
9165            ev_y1: cudarc::driver::CudaEvent,
9166            raw_act_e: u64,
9167            raw_sh_e: u64,
9168            raw_z1: u64,
9169            raw_a1h: u64,
9170            raw_act1: u64,
9171            raw_y1: u64,
9172        }
9173        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9174        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9175            std::sync::Mutex::new(None);
9176        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9177        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9178        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9179        let pins = e.ctx().ordinal();
9180        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9181            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9182                let _m = e.gpu.enter_main()?;
9183                (
9184                    e.htod(&vec![0.0f32; hf])?,
9185                    e.htod(&vec![0.0f32; hf])?,
9186                    e.htod(&vec![0.0f32; n_ff_sh])?,
9187                    e.htod(&vec![0.0f32; n_embd])?,
9188                    e.ctx().new_event(None)?,
9189                    e.ctx().new_event(None)?,
9190                )
9191            };
9192            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9193                let _r = rank1.gpu.enter_main()?;
9194                (
9195                    rank1.htod(&vec![0.0f32; n_embd])?,
9196                    rank1.htod(&vec![0.0f32; hf])?,
9197                    rank1.htod(&vec![0.0f32; hf])?,
9198                    rank1.htod(&vec![0.0f32; hf])?,
9199                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9200                    rank1.htod(&vec![0.0f32; nd])?,
9201                    rank1.ctx().new_event(None)?,
9202                    rank1.ctx().new_event(None)?,
9203                )
9204            };
9205            let (raw_act_e, raw_sh_e) = {
9206                let _m = e.gpu.enter_main()?;
9207                let stream = e.stream();
9208                let (a, _g0) = act.device_ptr(&stream);
9209                let (b, _g1) = sh_buf.device_ptr(&stream);
9210                (a as u64, b as u64)
9211            };
9212            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9213                let _r = rank1.gpu.enter_main()?;
9214                let rs = rank1.stream();
9215                let (a, _g0) = z1.device_ptr(&rs);
9216                let (b, _g1) = a1h.device_ptr(&rs);
9217                let (c, _g2) = act1.device_ptr(&rs);
9218                let (d, _g3) = y1.device_ptr(&rs);
9219                (a as u64, b as u64, c as u64, d as u64)
9220            };
9221            *guard = Some(SplitWs {
9222                pin_dev: pins,
9223                gate0,
9224                up0,
9225                act,
9226                sh_buf,
9227                ev_z,
9228                ev_act0,
9229                z1,
9230                g1,
9231                u1,
9232                a1h,
9233                act1,
9234                y1,
9235                ev_act1,
9236                ev_y1,
9237                raw_act_e,
9238                raw_sh_e,
9239                raw_z1,
9240                raw_a1h,
9241                raw_act1,
9242                raw_y1,
9243            });
9244        }
9245        let ws = guard.as_mut().expect("armed above");
9246        let wg_pin = {
9247            let _m = e.gpu.enter_main()?;
9248            let stream = e.stream();
9249            let (p, _g) = wg.device_ptr(&stream);
9250            p as u64
9251        };
9252        if !reps.contains_key(&wg_pin) {
9253            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9254            let mut up = |src: &CudaSlice<u8>,
9255                          off_bytes: usize,
9256                          len: usize|
9257             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9258                use cudarc::driver::sys;
9259                let sptr = {
9260                    let _m = e.gpu.enter_main()?;
9261                    let stream = e.stream();
9262                    let (p, _g) = src.device_ptr(&stream);
9263                    p as u64 + off_bytes as u64
9264                };
9265                let dst = {
9266                    let _r = rank1.gpu.enter_main()?;
9267                    rank1.alloc_u8_uninit(len)?
9268                };
9269                let dptr = {
9270                    let _r = rank1.gpu.enter_main()?;
9271                    let rs = rank1.stream();
9272                    let (p, _g) = dst.device_ptr(&rs);
9273                    p as u64
9274                };
9275                let _r = rank1.gpu.enter_main()?;
9276                let r = unsafe {
9277                    sys::cuMemcpyAsync(
9278                        dptr as sys::CUdeviceptr,
9279                        sptr as sys::CUdeviceptr,
9280                        len,
9281                        rank1.stream().cu_stream() as sys::CUstream,
9282                    )
9283                };
9284                if r != sys::CUresult::CUDA_SUCCESS {
9285                    return Err(format!("shexp split replica upload: {r:?}").into());
9286                }
9287                rank1.stream().synchronize()?;
9288                Ok(dst)
9289            };
9290            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9291            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9292            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9293            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9294        }
9295        let _ = il;
9296        // Per token, evented split flow.
9297        let raw_z = {
9298            let _m = e.gpu.enter_main()?;
9299            let stream = e.stream();
9300            let (p, _g) = z.device_ptr(&stream);
9301            ws.ev_z.record(&stream)?;
9302            p as u64
9303        };
9304        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9305        {
9306            let rep = reps.get(&wg_pin).expect("uploaded above");
9307            let _r = rank1.gpu.enter_main()?;
9308            rank1.stream().wait(&ws.ev_z)?;
9309            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9310            let SplitWs {
9311                z1, g1, u1, a1h, ..
9312            } = &mut *ws;
9313            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9314            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9315            // local place into act1[hf..] + P2P push into e's act[hf..]
9316            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9317            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9318            ws.ev_act1.record(&rank1.stream())?;
9319        }
9320        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9321        {
9322            let _m = e.gpu.enter_main()?;
9323            let SplitWs {
9324                gate0, up0, act, ..
9325            } = &mut *ws;
9326            let wg_lo = wg.slice(0..hf * n_embd * 2);
9327            let wu_lo = wu.slice(0..hf * n_embd * 2);
9328            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9329            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9330            ws.ev_act0.record(&e.stream())?;
9331        }
9332        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9333        {
9334            let rep = reps.get(&wg_pin).expect("uploaded above");
9335            let _r = rank1.gpu.enter_main()?;
9336            rank1.stream().wait(&ws.ev_act0)?;
9337            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9338            let SplitWs { act1, y1, .. } = &mut *ws;
9339            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9340            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9341            ws.ev_y1.record(&rank1.stream())?;
9342        }
9343        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9344        {
9345            let _m = e.gpu.enter_main()?;
9346            e.stream().wait(&ws.ev_act1)?;
9347            let SplitWs { act, sh_buf, .. } = &mut *ws;
9348            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9349            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9350            e.stream().wait(&ws.ev_y1)?;
9351            let mut sh = e.uninit(n_embd)?;
9352            {
9353                let mut dst = sh.slice_mut(0..n_embd);
9354                e.stream()
9355                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9356            }
9357            Ok(Some(sh))
9358        }
9359    }
9360
9361    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9362    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9363    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9364    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9365    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9366    /// the join with the exact add_scaled_rows expression: values unchanged.
9367    fn shexp_overlap_issue(
9368        e: &Engine,
9369        m: &MoeWeights,
9370        z: &CudaSlice<f32>,
9371        cfg: &ModelConfig,
9372        il: u16,
9373        n_embd: usize,
9374    ) -> Result<bool, Box<dyn std::error::Error>> {
9375        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9376            return Ok(false);
9377        }
9378        let (
9379            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9380            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9381            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9382        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9383        else {
9384            return Ok(false);
9385        };
9386        let n_ff_sh = m
9387            .gate_shexp
9388            .as_ref()
9389            .expect("matched Some above")
9390            .out_features();
9391        let lim = cfg.clamp_shexp_at(il as u32);
9392        let mut guard = SHEXP_OV_WS
9393            .lock()
9394            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9395        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9396        if guard
9397            .as_ref()
9398            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9399        {
9400            *guard = Some((
9401                pins.0,
9402                pins.1,
9403                pins.2,
9404                e.uninit(n_ff_sh)?,
9405                e.uninit(n_embd)?,
9406            ));
9407        }
9408        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9409        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9410        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9411        drop(guard);
9412        Ok(true)
9413    }
9414
9415    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9416    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9417    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9418    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9419    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9420    #[allow(clippy::too_many_arguments)]
9421    fn shexp_dev1_issue(
9422        e: &Engine,
9423        rank1: &Engine,
9424        m: &MoeWeights,
9425        z: &CudaSlice<f32>,
9426        cfg: &ModelConfig,
9427        il: u16,
9428        n_embd: usize,
9429    ) -> Result<bool, Box<dyn std::error::Error>> {
9430        use cudarc::driver::DevicePtr;
9431        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9432            return Ok(false);
9433        }
9434        let (
9435            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9436            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9437            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9438        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9439        else {
9440            return Ok(false);
9441        };
9442        let n_ff_sh = m
9443            .gate_shexp
9444            .as_ref()
9445            .expect("matched Some above")
9446            .out_features();
9447        let lim = cfg.clamp_shexp_at(il as u32);
9448        // Shared scratch, geometry-keyed.
9449        let mut ws_guard = SHEXP_D1_WS
9450            .lock()
9451            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9452        if ws_guard
9453            .as_ref()
9454            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9455        {
9456            let (act1, z1, ev_done) = {
9457                let _r1 = rank1.gpu.enter_main()?;
9458                (
9459                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9460                    rank1.htod(&vec![0.0f32; n_embd])?,
9461                    rank1.ctx().new_event(None)?,
9462                )
9463            };
9464            let (sh_root, ev_z) = {
9465                let _main = e.gpu.enter_main()?;
9466                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9467            };
9468            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9469        }
9470        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9471        let mut reps_guard = SHEXP_D1_REPS
9472            .lock()
9473            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9474        let reps = reps_guard.get_or_insert_with(Default::default);
9475        if !reps.contains_key(&il) {
9476            let (wg1, wu1, wd1) = {
9477                let _r1 = rank1.gpu.enter_main()?;
9478                (
9479                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9480                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9481                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9482                )
9483            };
9484            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9485                let s_ptr = {
9486                    let _main = e.gpu.enter_main()?;
9487                    let stream = e.stream();
9488                    let (p, _g) = src.device_ptr(&stream);
9489                    p as u64
9490                };
9491                let d_ptr = {
9492                    let _r1 = rank1.gpu.enter_main()?;
9493                    let stream = rank1.stream();
9494                    let (p, _g) = dst.device_ptr(&stream);
9495                    p as u64
9496                };
9497                let _r1 = rank1.gpu.enter_main()?;
9498                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9499            }
9500            {
9501                let _r1 = rank1.gpu.enter_main()?;
9502                rank1.stream().synchronize()?;
9503            }
9504            reps.insert(il, (wg1, wu1, wd1));
9505        }
9506        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9507        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9508        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9509        // row root-side (single store pass), rings ev_done.
9510        let (raw_z, raw_sh) = {
9511            let _main = e.gpu.enter_main()?;
9512            let stream = e.stream();
9513            let (a, _g0) = z.device_ptr(&stream);
9514            let (b, _g1) = sh_root.device_ptr(&stream);
9515            ev_z.record(&stream)?;
9516            (a as u64, b as u64)
9517        };
9518        {
9519            let _r1 = rank1.gpu.enter_main()?;
9520            rank1.stream().wait(ev_z)?;
9521            let raw_z1 = {
9522                let stream = rank1.stream();
9523                let (p, _g) = z1.device_ptr(&stream);
9524                p as u64
9525            };
9526            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9527            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9528            // down writes the ROOT-resident row over P2P via the raw-output twin of
9529            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9530            // cross-device, so launch on the raw pointer.
9531            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9532            ev_done.record(&rank1.stream())?;
9533        }
9534        Ok(true)
9535    }
9536
9537    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9538    fn shexp_dev1_apply(
9539        e: &Engine,
9540        output: &mut CudaSlice<f32>,
9541        n_embd: usize,
9542    ) -> Result<(), Box<dyn std::error::Error>> {
9543        let guard = SHEXP_D1_WS
9544            .lock()
9545            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9546        let (pin, _, _, sh_root, _, ev_done) =
9547            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9548        if pin.0 != n_embd {
9549            return Err("shexp dev1 width drifted".into());
9550        }
9551        let _main = e.gpu.enter_main()?;
9552        e.stream().wait(ev_done)?;
9553        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9554            std::sync::Mutex::new(None);
9555        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9556        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9557            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9558        }
9559        let ones = &og.as_ref().expect("armed above").1;
9560        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9561        Ok(())
9562    }
9563
9564    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9565    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9566    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9567    fn shexp_overlap_tail_ptrs(
9568        e: &Engine,
9569        m: &MoeWeights,
9570        cfg: &ModelConfig,
9571        n_embd: usize,
9572    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9573        use cudarc::driver::DevicePtr;
9574        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9575            return Ok(None);
9576        }
9577        let (
9578            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9579            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9580            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9581        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9582        else {
9583            return Ok(None);
9584        };
9585        let n_ff_sh = m
9586            .gate_shexp
9587            .as_ref()
9588            .expect("matched Some above")
9589            .out_features();
9590        let mut guard = SHEXP_OV_WS
9591            .lock()
9592            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9593        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9594        if guard
9595            .as_ref()
9596            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9597        {
9598            *guard = Some((
9599                pins.0,
9600                pins.1,
9601                pins.2,
9602                e.uninit(n_ff_sh)?,
9603                e.uninit(n_embd)?,
9604            ));
9605        }
9606        let sh_raw = {
9607            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9608            let stream = e.stream();
9609            let (p, _g) = sh.device_ptr(&stream);
9610            p as u64
9611        };
9612        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9613            std::sync::Mutex::new(None);
9614        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9615        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9616            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9617        }
9618        let ones_raw = {
9619            let stream = e.stream();
9620            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
9621            p as u64
9622        };
9623        Ok(Some((sh_raw, ones_raw)))
9624    }
9625
9626    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
9627    /// add_scaled_rows program the split path used (persistent ones row, no htod).
9628    fn shexp_overlap_apply(
9629        e: &Engine,
9630        output: &mut CudaSlice<f32>,
9631        n_embd: usize,
9632    ) -> Result<(), Box<dyn std::error::Error>> {
9633        let guard = SHEXP_OV_WS
9634            .lock()
9635            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9636        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
9637        if *ne != n_embd {
9638            return Err("shexp overlap width drifted".into());
9639        }
9640        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9641            std::sync::Mutex::new(None);
9642        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
9643        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9644            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9645        }
9646        let ones = &og.as_ref().expect("armed above").1;
9647        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
9648        Ok(())
9649    }
9650
9651    fn moe_ffn_grouped_add_shared(
9652        e: &Engine,
9653        m: &MoeWeights,
9654        z: &CudaSlice<f32>,
9655        t: usize,
9656        cfg: &ModelConfig,
9657        il: u16,
9658        moe_out: &mut CudaSlice<f32>,
9659    ) -> Result<(), Box<dyn std::error::Error>> {
9660        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
9661        // queued matmuls here rather than at the next host readback).
9662        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9663        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9664        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9665        let shexp_started = shexp_timing.then(std::time::Instant::now);
9666        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
9667        if let Some(started) = shexp_started {
9668            use std::sync::atomic::Ordering;
9669            e.stream().synchronize()?;
9670            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9671                + started.elapsed().as_nanos() as u64;
9672            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9673            if calls % 430 == 0 {
9674                eprintln!(
9675                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9676                    ns as f64 / 1.0e6,
9677                    ns as f64 / calls as f64 / 1.0e3,
9678                );
9679            }
9680        }
9681        result
9682    }
9683
9684    #[allow(clippy::too_many_arguments)]
9685    fn moe_ffn_grouped_add_shared_inner(
9686        e: &Engine,
9687        m: &MoeWeights,
9688        z: &CudaSlice<f32>,
9689        t: usize,
9690        cfg: &ModelConfig,
9691        il: u16,
9692        moe_out: &mut CudaSlice<f32>,
9693    ) -> Result<(), Box<dyn std::error::Error>> {
9694        let n_embd = cfg.n_embd as usize;
9695        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
9696            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9697        {
9698            let n_ff_sh = gate_shexp.out_features();
9699            let lim = cfg.clamp_shexp_at(il as u32);
9700            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
9701            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
9702            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
9703            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
9704            // operand pre-quantized (kernel_check-proven identities). This path measured
9705            // 167us/layer as separate matmuls + 5 allocs at decode.
9706            let fused = t == 1
9707                && lim.is_none()
9708                && cfg.m3.is_none()
9709                && e.uses_q8_1_fast(gate_shexp)
9710                && e.uses_q8_1_fast(up_shexp);
9711            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
9712            // the two matvec_bf16 launches matmul would issue).
9713            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
9714                match (gate_shexp, up_shexp) {
9715                    (
9716                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
9717                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
9718                    ) => Some((wg, wu)),
9719                    _ => None,
9720                }
9721            } else {
9722                None
9723            };
9724            let sh = if let Some((wg, wu)) = bf16_dual {
9725                // Persistent shared-expert workspace: sizes are constant across every MoE
9726                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
9727                // the four per-layer allocations. Buffers are fully overwritten each call.
9728                static SHEXP_WS: std::sync::Mutex<
9729                    Option<(
9730                        usize,
9731                        usize,
9732                        usize,
9733                        CudaSlice<f32>,
9734                        CudaSlice<f32>,
9735                        CudaSlice<f32>,
9736                        CudaSlice<f32>,
9737                    )>,
9738                > = std::sync::Mutex::new(None);
9739                let down_bf16 = match down_shexp {
9740                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9741                    _ => None,
9742                };
9743                let mut guard = SHEXP_WS
9744                    .lock()
9745                    .map_err(|_| "shexp workspace lock is poisoned")?;
9746                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9747                if guard
9748                    .as_ref()
9749                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9750                {
9751                    *guard = Some((
9752                        pins.0,
9753                        pins.1,
9754                        pins.2,
9755                        e.uninit(n_ff_sh)?,
9756                        e.uninit(n_ff_sh)?,
9757                        e.uninit(n_ff_sh)?,
9758                        e.uninit(n_embd)?,
9759                    ));
9760                }
9761                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
9762                // through to the single-device arm when ineligible.
9763                {
9764                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9765                    let split_on = *ON
9766                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
9767                    if split_on {
9768                        if let (Some(wd), Some(rank1)) = (
9769                            match down_shexp {
9770                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9771                                _ => None,
9772                            },
9773                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
9774                        ) {
9775                            if let Some(sh) = Self::shexp_split_matvec(
9776                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
9777                            )? {
9778                                drop(guard);
9779                                let gate = match &m.gate_inp_shexp {
9780                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
9781                                        z,
9782                                        gate_inp_shexp.float_data(),
9783                                        n_embd,
9784                                        t,
9785                                    )?,
9786                                    None => e.htod(&vec![1.0f32; t])?,
9787                                };
9788                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9789                                return Ok(());
9790                            }
9791                        }
9792                    }
9793                }
9794                let (_, _, _, gate, up, act, sh_buf) =
9795                    guard.as_mut().expect("shexp workspace initialized above");
9796                if cfg.m3.is_none() {
9797                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
9798                    // per-row program + exact silu/clamped expression, bit-identical.
9799                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9800                    let _ = (&gate, &up);
9801                } else {
9802                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
9803                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
9804                }
9805                if let Some(down) = down_bf16 {
9806                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
9807                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
9808                    // exact f32acc per-row program + the exact add_scaled_rows expression
9809                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
9810                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
9811                    // accumulate consumes the same f32 the split path stored and reloaded.
9812                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9813                    let fuse_da = *FUSE_DA.get_or_init(|| {
9814                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
9815                    });
9816                    if fuse_da && m.gate_inp_shexp.is_none() {
9817                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9818                            std::sync::Mutex::new(None);
9819                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
9820                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9821                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9822                        }
9823                        let ones = &og.as_ref().expect("armed above").1;
9824                        e.matvec_bf16_down_addscale_into(
9825                            down, act, ones, moe_out, n_ff_sh, n_embd,
9826                        )?;
9827                        return Ok(());
9828                    }
9829                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
9830                    let sh = e.uninit(n_embd)?;
9831                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
9832                    let mut sh = sh;
9833                    {
9834                        let mut dst = sh.slice_mut(0..n_embd);
9835                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
9836                    }
9837                    sh
9838                } else {
9839                    e.matmul(down_shexp, act, 1)?
9840                }
9841            } else if fused {
9842                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
9843                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
9844                    Some((gate, up)) => Some((gate, up)),
9845                    None => {
9846                        match (
9847                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
9848                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
9849                        ) {
9850                            (Some(gate), Some(up)) => Some((gate, up)),
9851                            _ => None,
9852                        }
9853                    }
9854                };
9855                match pair {
9856                    Some(((gate, gs), (up, us))) => {
9857                        if e.uses_q8_1_fast(down_shexp) {
9858                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
9859                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
9860                        } else {
9861                            let mut act = e.uninit(n_ff_sh)?;
9862                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
9863                            e.matmul(down_shexp, &act, 1)?
9864                        }
9865                    }
9866                    None => {
9867                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
9868                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
9869                        let mut act = e.uninit(n_ff_sh)?;
9870                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
9871                        e.matmul(down_shexp, &act, 1)?
9872                    }
9873                }
9874            } else {
9875                let sg_gate = e.matmul(gate_shexp, z, t)?;
9876                let sg_up = e.matmul(up_shexp, z, t)?;
9877                let mut sa = e.uninit(t * n_ff_sh)?;
9878                Self::ffn_act_lim(
9879                    e,
9880                    cfg,
9881                    &sg_gate,
9882                    &sg_up,
9883                    1.0,
9884                    1.0,
9885                    lim,
9886                    &mut sa,
9887                    t * n_ff_sh,
9888                )?;
9889                e.matmul(down_shexp, &sa, t)?
9890            };
9891            let gate = match &m.gate_inp_shexp {
9892                Some(gate_inp_shexp) => {
9893                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
9894                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
9895                    } else {
9896                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
9897                        let mut gate = e.uninit(t)?;
9898                        e.sigmoid(&raw, &mut gate, t)?;
9899                        gate
9900                    }
9901                }
9902                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
9903                // synchronizes the stream — measured as the biggest per-layer host gap
9904                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
9905                // device serves every layer; larger t (prefill) keeps the plain htod.
9906                None if t == 1 => {
9907                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9908                        std::sync::Mutex::new(None);
9909                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
9910                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9911                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9912                    }
9913                    let ones = &guard.as_ref().expect("armed above").1;
9914                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
9915                    return Ok(());
9916                }
9917                None => e.htod(&vec![1.0f32; t])?,
9918            };
9919            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9920        }
9921        Ok(())
9922    }
9923
9924    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
9925    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
9926    pub(crate) fn moe_ffn_grouped(
9927        e: &Engine,
9928        m: &MoeWeights,
9929        z: &CudaSlice<f32>,
9930        t: usize,
9931        cfg: &ModelConfig,
9932        il: u16,
9933        max_block: usize,
9934    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9935        let moe = cfg.moe.as_ref().unwrap();
9936        let n_embd = cfg.n_embd as usize;
9937        let n_expert = moe.expert_count as usize;
9938        let n_used = moe.expert_used_count as usize;
9939        let n_ff_exp = moe.expert_ff_length as usize;
9940        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
9941        let lim_exp = cfg.clamp_exp_at(il as u32);
9942
9943        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
9944        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
9945        // enters the softmax-only pairs/dev router.
9946        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
9947        if let Some(sig) = cfg.sigmoid_router() {
9948            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
9949        }
9950        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
9951            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
9952        } else {
9953            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
9954        };
9955        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
9956        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
9957        Self::trace_moe_input(e, il, t, n_embd, z)?;
9958
9959        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
9960        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
9961        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
9962        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
9963        let no_exp_macros = m.gate_exps.macros.is_none()
9964            && m.up_exps.macros.is_none()
9965            && m.down_exps.macros.is_none();
9966        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
9967            m.has_uniform_expert_layout()
9968                && no_exp_macros
9969                && moe_q8_enabled()
9970                && q8_expert_supported(m.gate_exps.qtype)
9971                && q8_expert_supported(m.up_exps.qtype)
9972                && q8_expert_supported(m.down_exps.qtype)
9973                && moe_slab_enabled()
9974                && dev.dev == e.ctx().ordinal()
9975        });
9976        if let Some(dev) = resident_q8 {
9977            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
9978                e,
9979                m,
9980                z,
9981                t,
9982                cfg,
9983                il,
9984                &sel_all,
9985                &w_all,
9986                &dev.ptr_row,
9987                dev.gu_il,
9988            )?;
9989            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
9990            return Ok(moe_out);
9991        }
9992
9993        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
9994        // For each expert e, we need: which tokens use it, their positions in z, their top-k
9995        // slot index (for bit-identical accumulation), and their weights.
9996        struct ExpertGroup {
9997            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
9998            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
9999            weights: Vec<f32>,      // renormalized weight for that token-expert pair
10000        }
10001        let mut groups: Vec<ExpertGroup> = (0..n_expert)
10002            .map(|_| ExpertGroup {
10003                tok_indices: Vec::new(),
10004                slot_indices: Vec::new(),
10005                weights: Vec::new(),
10006            })
10007            .collect();
10008
10009        for tok in 0..t {
10010            for j in 0..n_used {
10011                let ex = sel_all[tok * n_used + j] as usize;
10012                let w = w_all[tok * n_used + j];
10013                groups[ex].tok_indices.push(tok as i32);
10014                groups[ex].slot_indices.push(j as i32);
10015                groups[ex].weights.push(w);
10016            }
10017        }
10018
10019        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
10020        // Each token's 8 expert contributions land in their respective slots.
10021        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
10022        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
10023
10024        // Expert weight dimensions (used in both cache and staging paths).
10025        let g_len = m.gate_exps.max_expert_bytes();
10026        let u_len = m.up_exps.max_expert_bytes();
10027        let d_len = m.down_exps.max_expert_bytes();
10028        let moe_q8 = m.has_uniform_expert_layout()
10029            && moe_q8_enabled()
10030            && q8_expert_supported(m.gate_exps.qtype)
10031            && q8_expert_supported(m.up_exps.qtype)
10032            && q8_expert_supported(m.down_exps.qtype);
10033        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10034        // Interleaved GU slabs require the pointer-table fast path above.
10035        let slab_local = m
10036            .dev_exps
10037            .as_ref()
10038            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10039        let use_cache =
10040            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10041        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10042        // also does: a local resident slab or a live SLRU dispatch.
10043        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10044
10045        // GPU scratch for staging (only allocated without a local slab or cache).
10046        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10047            (
10048                Some(e.alloc_u8(g_len)?),
10049                Some(e.alloc_u8(u_len)?),
10050                Some(e.alloc_u8(d_len)?),
10051            )
10052        } else {
10053            (None, None, None)
10054        };
10055
10056        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10057        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10058        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10059        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10060        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10061        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10062        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10063        // at long prompts where every expert stages regardless. Order is FREE to change without
10064        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10065        // regardless of expert processing order (the whole point of the slots).
10066        let mut order: Vec<usize> = (0..n_expert)
10067            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10068            .collect();
10069        order.sort_by(|&a, &b| {
10070            groups[b]
10071                .tok_indices
10072                .len()
10073                .cmp(&groups[a].tok_indices.len())
10074                .then(a.cmp(&b))
10075        });
10076        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10077        let page_window = moe_page_prefetch_window();
10078        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10079        if worker_disk_prefetch {
10080            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10081                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10082            }
10083        }
10084        for (order_pos, &ex) in order.iter().enumerate() {
10085            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10086                Self::moe_prefetch_host_expert(order[next], m);
10087            }
10088            if worker_disk_prefetch {
10089                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10090                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10091                    let keep = [
10092                        BlockId::new(il, PROJ_GATE, ex as u16),
10093                        BlockId::new(il, PROJ_UP, ex as u16),
10094                        BlockId::new(il, PROJ_DOWN, ex as u16),
10095                    ];
10096                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10097                }
10098            }
10099            let grp = &groups[ex];
10100            let m_e = grp.tok_indices.len();
10101            m_dist.push(m_e);
10102            let gl = m.gate_exps.expert_layout(ex);
10103            let ul = m.up_exps.expert_layout(ex);
10104            let dl = m.down_exps.expert_layout(ex);
10105
10106            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10107            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10108            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10109            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10110            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10111            let dmac = m.down_exps.macro_scale(ex);
10112            let weight_d = if dmac == 1.0 {
10113                e.htod(&grp.weights)?
10114            } else {
10115                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10116                e.htod(&scaled)?
10117            };
10118
10119            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10120            let mut gathered = e.zeros(m_e * n_embd)?;
10121            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10122            let gv = gathered.slice(0..m_e * n_embd);
10123
10124            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10125            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10126            let y = if let Some(dev) = slab_local {
10127                let gate_start = ex * m.gate_exps.expert_stride;
10128                let up_start = ex * m.up_exps.expert_stride;
10129                let down_start = ex * m.down_exps.expert_stride;
10130                if grouped_q8 {
10131                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10132                    let gate = e.qmatvec_expert_q8(
10133                        &dev.gate,
10134                        gate_start..gate_start + gl.len,
10135                        &zq,
10136                        &zd,
10137                        m_e,
10138                        m.gate_exps.in_f,
10139                        m.gate_exps.out_f,
10140                        gl.qtype,
10141                        gl.row_bytes,
10142                    )?;
10143                    let up = e.qmatvec_expert_q8(
10144                        &dev.up,
10145                        up_start..up_start + ul.len,
10146                        &zq,
10147                        &zd,
10148                        m_e,
10149                        m.up_exps.in_f,
10150                        m.up_exps.out_f,
10151                        ul.qtype,
10152                        ul.row_bytes,
10153                    )?;
10154                    let mut act = e.uninit(m_e * n_ff_exp)?;
10155                    Self::ffn_act_lim(
10156                        e,
10157                        cfg,
10158                        &gate,
10159                        &up,
10160                        m.gate_exps.macro_scale(ex),
10161                        m.up_exps.macro_scale(ex),
10162                        lim_exp,
10163                        &mut act,
10164                        m_e * n_ff_exp,
10165                    )?;
10166                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10167                    e.qmatvec_expert_q8(
10168                        &dev.down,
10169                        down_start..down_start + dl.len,
10170                        &aq2,
10171                        &ad2,
10172                        m_e,
10173                        m.down_exps.in_f,
10174                        m.down_exps.out_f,
10175                        dl.qtype,
10176                        dl.row_bytes,
10177                    )?
10178                } else {
10179                    let gate = e.qmatvec_view(
10180                        &dev.gate,
10181                        gate_start..gate_start + gl.len,
10182                        &gv,
10183                        m_e,
10184                        m.gate_exps.in_f,
10185                        m.gate_exps.out_f,
10186                        gl.qtype,
10187                        gl.row_bytes,
10188                    )?;
10189                    let up = e.qmatvec_view(
10190                        &dev.up,
10191                        up_start..up_start + ul.len,
10192                        &gv,
10193                        m_e,
10194                        m.up_exps.in_f,
10195                        m.up_exps.out_f,
10196                        ul.qtype,
10197                        ul.row_bytes,
10198                    )?;
10199                    let mut act = e.uninit(m_e * n_ff_exp)?;
10200                    Self::ffn_act_lim(
10201                        e,
10202                        cfg,
10203                        &gate,
10204                        &up,
10205                        m.gate_exps.macro_scale(ex),
10206                        m.up_exps.macro_scale(ex),
10207                        lim_exp,
10208                        &mut act,
10209                        m_e * n_ff_exp,
10210                    )?;
10211                    let actv = act.slice(0..m_e * n_ff_exp);
10212                    e.qmatvec_view(
10213                        &dev.down,
10214                        down_start..down_start + dl.len,
10215                        &actv,
10216                        m_e,
10217                        m.down_exps.in_f,
10218                        m.down_exps.out_f,
10219                        dl.qtype,
10220                        dl.row_bytes,
10221                    )?
10222                }
10223            } else if use_cache {
10224                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10225                if grouped_q8 {
10226                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10227                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10228                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10229                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10230                        eng.qmatvec_expert_q8(
10231                            cache.buf(slot),
10232                            0..gl.len,
10233                            &zq,
10234                            &zd,
10235                            m_e,
10236                            m.gate_exps.in_f,
10237                            m.gate_exps.out_f,
10238                            gl.qtype,
10239                            gl.row_bytes,
10240                        )
10241                    })?;
10242                    let up = e.with_moe_cache(max_block, |cache, eng| {
10243                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10244                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10245                        eng.qmatvec_expert_q8(
10246                            cache.buf(slot),
10247                            0..ul.len,
10248                            &zq,
10249                            &zd,
10250                            m_e,
10251                            m.up_exps.in_f,
10252                            m.up_exps.out_f,
10253                            ul.qtype,
10254                            ul.row_bytes,
10255                        )
10256                    })?;
10257                    let mut act = e.uninit(m_e * n_ff_exp)?;
10258                    Self::ffn_act_lim(
10259                        e,
10260                        cfg,
10261                        &gate,
10262                        &up,
10263                        m.gate_exps.macro_scale(ex),
10264                        m.up_exps.macro_scale(ex),
10265                        lim_exp,
10266                        &mut act,
10267                        m_e * n_ff_exp,
10268                    )?;
10269                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10270                    e.with_moe_cache(max_block, |cache, eng| {
10271                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10272                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10273                        eng.qmatvec_expert_q8(
10274                            cache.buf(slot),
10275                            0..dl.len,
10276                            &aq2,
10277                            &ad2,
10278                            m_e,
10279                            m.down_exps.in_f,
10280                            m.down_exps.out_f,
10281                            dl.qtype,
10282                            dl.row_bytes,
10283                        )
10284                    })?
10285                } else {
10286                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10287                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10288                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10289                        eng.qmatvec_view(
10290                            cache.buf(slot),
10291                            0..gl.len,
10292                            &gv,
10293                            m_e,
10294                            m.gate_exps.in_f,
10295                            m.gate_exps.out_f,
10296                            gl.qtype,
10297                            gl.row_bytes,
10298                        )
10299                    })?;
10300                    let up = e.with_moe_cache(max_block, |cache, eng| {
10301                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10302                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10303                        eng.qmatvec_view(
10304                            cache.buf(slot),
10305                            0..ul.len,
10306                            &gv,
10307                            m_e,
10308                            m.up_exps.in_f,
10309                            m.up_exps.out_f,
10310                            ul.qtype,
10311                            ul.row_bytes,
10312                        )
10313                    })?;
10314                    let mut act = e.uninit(m_e * n_ff_exp)?;
10315                    Self::ffn_act_lim(
10316                        e,
10317                        cfg,
10318                        &gate,
10319                        &up,
10320                        m.gate_exps.macro_scale(ex),
10321                        m.up_exps.macro_scale(ex),
10322                        lim_exp,
10323                        &mut act,
10324                        m_e * n_ff_exp,
10325                    )?;
10326                    let actv = act.slice(0..m_e * n_ff_exp);
10327                    e.with_moe_cache(max_block, |cache, eng| {
10328                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10329                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10330                        eng.qmatvec_view(
10331                            cache.buf(slot),
10332                            0..dl.len,
10333                            &actv,
10334                            m_e,
10335                            m.down_exps.in_f,
10336                            m.down_exps.out_f,
10337                            dl.qtype,
10338                            dl.row_bytes,
10339                        )
10340                    })?
10341                }
10342            } else {
10343                let sg = scratch_g.as_mut().unwrap();
10344                let su = scratch_u.as_mut().unwrap();
10345                let sd = scratch_d.as_mut().unwrap();
10346                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10347                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10348                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10349                if grouped_q8 {
10350                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10351                    let gate = e.qmatvec_expert_q8(
10352                        sg,
10353                        0..gl.len,
10354                        &zq,
10355                        &zd,
10356                        m_e,
10357                        m.gate_exps.in_f,
10358                        m.gate_exps.out_f,
10359                        gl.qtype,
10360                        gl.row_bytes,
10361                    )?;
10362                    let up = e.qmatvec_expert_q8(
10363                        su,
10364                        0..ul.len,
10365                        &zq,
10366                        &zd,
10367                        m_e,
10368                        m.up_exps.in_f,
10369                        m.up_exps.out_f,
10370                        ul.qtype,
10371                        ul.row_bytes,
10372                    )?;
10373                    let mut act = e.uninit(m_e * n_ff_exp)?;
10374                    Self::ffn_act_lim(
10375                        e,
10376                        cfg,
10377                        &gate,
10378                        &up,
10379                        m.gate_exps.macro_scale(ex),
10380                        m.up_exps.macro_scale(ex),
10381                        lim_exp,
10382                        &mut act,
10383                        m_e * n_ff_exp,
10384                    )?;
10385                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10386                    e.qmatvec_expert_q8(
10387                        sd,
10388                        0..dl.len,
10389                        &aq2,
10390                        &ad2,
10391                        m_e,
10392                        m.down_exps.in_f,
10393                        m.down_exps.out_f,
10394                        dl.qtype,
10395                        dl.row_bytes,
10396                    )?
10397                } else {
10398                    let gate = e.qmatvec_view(
10399                        sg,
10400                        0..gl.len,
10401                        &gv,
10402                        m_e,
10403                        m.gate_exps.in_f,
10404                        m.gate_exps.out_f,
10405                        gl.qtype,
10406                        gl.row_bytes,
10407                    )?;
10408                    let up = e.qmatvec_view(
10409                        su,
10410                        0..ul.len,
10411                        &gv,
10412                        m_e,
10413                        m.up_exps.in_f,
10414                        m.up_exps.out_f,
10415                        ul.qtype,
10416                        ul.row_bytes,
10417                    )?;
10418                    let mut act = e.uninit(m_e * n_ff_exp)?;
10419                    Self::ffn_act_lim(
10420                        e,
10421                        cfg,
10422                        &gate,
10423                        &up,
10424                        m.gate_exps.macro_scale(ex),
10425                        m.up_exps.macro_scale(ex),
10426                        lim_exp,
10427                        &mut act,
10428                        m_e * n_ff_exp,
10429                    )?;
10430                    let actv = act.slice(0..m_e * n_ff_exp);
10431                    e.qmatvec_view(
10432                        sd,
10433                        0..dl.len,
10434                        &actv,
10435                        m_e,
10436                        m.down_exps.in_f,
10437                        m.down_exps.out_f,
10438                        dl.qtype,
10439                        dl.row_bytes,
10440                    )?
10441                }
10442            };
10443
10444            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10445            e.scatter_slot(
10446                &y,
10447                &tok_idx_d,
10448                &slot_idx_d,
10449                &weight_d,
10450                &mut slot_buf,
10451                &mut wbuf,
10452                n_embd,
10453                n_used,
10454                m_e,
10455            )?;
10456        }
10457
10458        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10459        let mut moe_out = e.zeros(t * n_embd)?;
10460        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10461
10462        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10463        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10464            m_dist.sort_unstable();
10465            let active = m_dist.len();
10466            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10467            let median = m_dist[active / 2];
10468            let max_m = *m_dist.last().unwrap();
10469            let min_m = m_dist[0];
10470            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10471            println!(
10472                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10473                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10474                      above_gemm_threshold(>=16)={above16}/{active}"
10475            );
10476        }
10477
10478        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10479        Ok(moe_out)
10480    }
10481
10482    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10483    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10484    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10485    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10486    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10487    /// expert-sum order identical to the sequential path.
10488    pub(crate) fn moe_ffn_lockstep(
10489        &self,
10490        e: &Engine,
10491        m: &MoeWeights,
10492        zbatch: &CudaSlice<f32>,
10493        mrows: usize,
10494        il: u16,
10495        max_block: usize,
10496    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10497        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10498        let cfg = &self.cfg;
10499        let moe = cfg.moe.as_ref().unwrap();
10500        let n_embd = cfg.n_embd as usize;
10501        let n_expert = moe.expert_count as usize;
10502        let n_used = moe.expert_used_count as usize;
10503        let n_ff_exp = moe.expert_ff_length as usize;
10504        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10505        let lim_exp = cfg.clamp_exp_at(il as u32);
10506        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10507
10508        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10509        if let Some(sig) = cfg.sigmoid_router() {
10510            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10511        }
10512        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10513            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10514        } else {
10515            Self::moe_route_cfg(
10516                e,
10517                &logits,
10518                mrows,
10519                n_expert,
10520                n_used,
10521                m.active_experts.as_deref(),
10522            )?
10523        };
10524        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10525
10526        // Residency split at whole-expert granularity against the (frozen) cache.
10527        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10528            Ok((0..n_expert)
10529                .map(|ex| {
10530                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10531                        .into_iter()
10532                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10533                })
10534                .collect())
10535        })?;
10536
10537        struct Group {
10538            rows: Vec<i32>,
10539            slots: Vec<i32>,
10540            weights: Vec<f32>,
10541        }
10542        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10543        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10544        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10545            Default::default();
10546        for row in 0..mrows {
10547            for j in 0..n_used {
10548                let ex = sel_all[row * n_used + j] as usize;
10549                let w = w_all[row * n_used + j];
10550                if resident_expert[ex] {
10551                    let group = groups.entry(ex).or_insert_with(|| Group {
10552                        rows: Vec::new(),
10553                        slots: Vec::new(),
10554                        weights: Vec::new(),
10555                    });
10556                    group.rows.push(row as i32);
10557                    group.slots.push(j as i32);
10558                    group.weights.push(w);
10559                } else {
10560                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10561                    cpu_rows[row].push((ex, w));
10562                    cpu_by_expert.entry(ex).or_default().push((row, w));
10563                }
10564            }
10565        }
10566
10567        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10568        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10569        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10570        // order per row differs from the sequential single-call chunk — part of the
10571        // documented lockstep numeric class.
10572        let host_rows = e.dtoh(zbatch)?;
10573        let rows_ok = crate::cpu_experts::rows_supported();
10574        enum CpuPart {
10575            Single { row: usize },
10576            Rows { rows: Vec<usize> },
10577        }
10578        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10579        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10580        if rows_ok {
10581            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10582                .into_iter()
10583                .filter(|(_, rows)| rows.len() >= 2)
10584                .collect();
10585            shared.sort_by_key(|(ex, _)| *ex);
10586            for (ex, mut row_weights) in shared {
10587                row_weights.sort_by_key(|(row, _)| *row);
10588                let inputs: Vec<(&[f32], f32)> = row_weights
10589                    .iter()
10590                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10591                    .collect();
10592                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10593                    .map_err(std::io::Error::other)?;
10594                for &(row, _) in &row_weights {
10595                    rows_served.insert((row, ex));
10596                }
10597                tickets.push((
10598                    CpuPart::Rows {
10599                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10600                    },
10601                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10602                ));
10603            }
10604        }
10605        for (row, selected) in cpu_rows.iter().enumerate() {
10606            let leftover: Vec<(usize, f32)> = selected
10607                .iter()
10608                .copied()
10609                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10610                .collect();
10611            if leftover.is_empty() {
10612                continue;
10613            }
10614            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10615            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10616                .map_err(std::io::Error::other)?;
10617            tickets.push((
10618                CpuPart::Single { row },
10619                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
10620            ));
10621        }
10622
10623        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
10624        let mut wbuf = e.zeros(mrows * n_used)?;
10625        let mut order: Vec<usize> = groups.keys().copied().collect();
10626        order.sort_by(|&a, &b| {
10627            groups[&b]
10628                .rows
10629                .len()
10630                .cmp(&groups[&a].rows.len())
10631                .then(a.cmp(&b))
10632        });
10633        for &ex in &order {
10634            let group = &groups[&ex];
10635            let m_e = group.rows.len();
10636            let gl = m.gate_exps.expert_layout(ex);
10637            let ul = m.up_exps.expert_layout(ex);
10638            let dl = m.down_exps.expert_layout(ex);
10639            let row_idx_d = e.htod_i32(&group.rows)?;
10640            let slot_idx_d = e.htod_i32(&group.slots)?;
10641            let dmac = m.down_exps.macro_scale(ex);
10642            let weight_d = if dmac == 1.0 {
10643                e.htod(&group.weights)?
10644            } else {
10645                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
10646                e.htod(&scaled)?
10647            };
10648            let mut gathered = e.zeros(m_e * n_embd)?;
10649            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
10650            let gv = gathered.slice(0..m_e * n_embd);
10651            let gate = e.with_moe_cache(max_block, |c, eng| {
10652                let slot = c
10653                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
10654                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10655                eng.qmatvec_view(
10656                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10657                    0..gl.len,
10658                    &gv,
10659                    m_e,
10660                    m.gate_exps.in_f,
10661                    m.gate_exps.out_f,
10662                    gl.qtype,
10663                    gl.row_bytes,
10664                )
10665            })?;
10666            let up = e.with_moe_cache(max_block, |c, eng| {
10667                let slot = c
10668                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
10669                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10670                eng.qmatvec_view(
10671                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10672                    0..ul.len,
10673                    &gv,
10674                    m_e,
10675                    m.up_exps.in_f,
10676                    m.up_exps.out_f,
10677                    ul.qtype,
10678                    ul.row_bytes,
10679                )
10680            })?;
10681            let mut act = e.zeros(m_e * n_ff_exp)?;
10682            Self::ffn_act_lim(
10683                e,
10684                cfg,
10685                &gate,
10686                &up,
10687                m.gate_exps.macro_scale(ex),
10688                m.up_exps.macro_scale(ex),
10689                lim_exp,
10690                &mut act,
10691                m_e * n_ff_exp,
10692            )?;
10693            let actv = act.slice(0..m_e * n_ff_exp);
10694            let y = e.with_moe_cache(max_block, |c, eng| {
10695                let slot = c
10696                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
10697                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10698                eng.qmatvec_view(
10699                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10700                    0..dl.len,
10701                    &actv,
10702                    m_e,
10703                    m.down_exps.in_f,
10704                    m.down_exps.out_f,
10705                    dl.qtype,
10706                    dl.row_bytes,
10707                )
10708            })?;
10709            e.scatter_slot(
10710                &y,
10711                &row_idx_d,
10712                &slot_idx_d,
10713                &weight_d,
10714                &mut slot_buf,
10715                &mut wbuf,
10716                n_embd,
10717                n_used,
10718                m_e,
10719            )?;
10720        }
10721        let mut moe_out = e.zeros(mrows * n_embd)?;
10722        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
10723
10724        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
10725        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
10726        for (part, ticket) in tickets {
10727            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
10728            let mut add_row = |row: usize, chunk: &[f32]| {
10729                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
10730                for (accumulator, value) in sum.iter_mut().zip(chunk) {
10731                    *accumulator += value;
10732                }
10733            };
10734            match part {
10735                CpuPart::Single { row } => add_row(row, &cpu_output),
10736                CpuPart::Rows { rows } => {
10737                    for (slot, row) in rows.into_iter().enumerate() {
10738                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
10739                    }
10740                }
10741            }
10742        }
10743        for (row, sum) in row_sums.into_iter().enumerate() {
10744            let Some(sum) = sum else { continue };
10745            let cpu_output = e.htod(&sum)?;
10746            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
10747            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
10748        }
10749
10750        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10751            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10752        {
10753            let n_ff_sh = gate_shexp.out_features();
10754            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
10755            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
10756            let mut sa = e.zeros(mrows * n_ff_sh)?;
10757            Self::ffn_act_lim(
10758                e,
10759                cfg,
10760                &sg_gate,
10761                &sg_up,
10762                1.0,
10763                1.0,
10764                lim_shexp,
10765                &mut sa,
10766                mrows * n_ff_sh,
10767            )?;
10768            let sh = e.matmul(down_shexp, &sa, mrows)?;
10769            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
10770            // decode matches the single-sequence decode chain bit-for-bit.
10771            let g = match &m.gate_inp_shexp {
10772                Some(gate_inp_shexp) => {
10773                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
10774                }
10775                None => e.htod(&vec![1.0f32; mrows])?,
10776            };
10777            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
10778        }
10779
10780        Ok(moe_out)
10781    }
10782}
10783
10784// ============================ gemma4 (R8 verified wiring) ==================================
10785// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
10786// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
10787// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
10788// gemma variants after the correctness gate).
10789impl HybridModel {
10790    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
10791    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
10792    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
10793    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
10794    ///
10795    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
10796    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
10797    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
10798    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
10799    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
10800    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
10801    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
10802    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
10803    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
10804        let g = self
10805            .cfg
10806            .gemma4
10807            .as_ref()
10808            .expect("gemma4_rope_dims on a non-gemma4 config");
10809        if g.swa_pattern[il] {
10810            g.rope_dims_swa as usize
10811        } else {
10812            g.rope_dims_global as usize
10813        }
10814    }
10815
10816    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
10817        let g = self.cfg.gemma4.as_ref().unwrap();
10818        let swa = g.swa_pattern[il];
10819        let hd = if swa {
10820            g.key_length_swa
10821        } else {
10822            g.key_length_global
10823        } as usize;
10824        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
10825        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
10826        // rows exact (softmax over one element) while every later position drifted).
10827        (
10828            hd,
10829            g.head_count_kv[il] as usize,
10830            self.cfg.n_head as usize,
10831            if swa {
10832                g.rope_base_swa
10833            } else {
10834                g.rope_base_global
10835            },
10836            1.0,
10837            swa,
10838        )
10839    }
10840
10841    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
10842    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
10843    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
10844    pub(crate) fn gemma4_suppress(
10845        &self,
10846        e: &Engine,
10847        ld: &mut CudaSlice<f32>,
10848        t: usize,
10849    ) -> Result<(), Box<dyn std::error::Error>> {
10850        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
10851            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
10852            // stage as primary, and this tail runs only after the last stage). The assert turns
10853            // that argued invariant into a checked one: any topology violating primary==head
10854            // trips here in debug instead of silently peer-reading a device-0 buffer.
10855            #[cfg(debug_assertions)]
10856            crate::debug_assert_tensor_stream_device(
10857                ids,
10858                &e.stream(),
10859                "gemma4_suppress.suppress_d",
10860            );
10861            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
10862        }
10863        Ok(())
10864    }
10865
10866    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
10867    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
10868    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
10869    /// only (v0): attends within `tokens` via the f32 sdpa.
10870    #[allow(clippy::too_many_arguments)]
10871    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
10872    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
10873    /// switching program at `t > sliding_window`. The door is the measured cause of the
10874    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
10875    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
10876    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
10877    /// published prefix KV stops depending on the total prompt length. Off by default because
10878    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
10879    fn gemma_fa_one_program() -> bool {
10880        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10881        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
10882    }
10883
10884    fn gemma4_attn_prime(
10885        &self,
10886        e: &Engine,
10887        fa: &crate::hybrid::FullAttnLayer,
10888        il: usize,
10889        h: &CudaSlice<f32>,
10890        pos_d: &CudaSlice<i32>,
10891        t: usize,
10892        cache: Option<&mut Cache>,
10893        island: Option<&CudaSlice<i32>>,
10894    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10895        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
10896        let eps = self.cfg.rms_eps;
10897        let aux = self.gemma4_aux.as_ref().unwrap();
10898        let ones = aux.ones(e);
10899        #[cfg(debug_assertions)]
10900        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
10901
10902        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
10903        // (h stays borrowed across the triple, so the cache key can't go stale).
10904        e.mmq_act_begin();
10905        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
10906        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10907            let v = e.dtoh(&q0)?;
10908            let nan = v.iter().filter(|x| x.is_nan()).count();
10909            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10910            eprintln!(
10911                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
10912                v.len()
10913            );
10914        }
10915        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
10916        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
10917        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
10918        let v0 = if swa {
10919            e.matmul(&fa.wv, h, t)?
10920        } else {
10921            e.clone_dtod(&k0)?
10922        };
10923        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10924            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
10925                let v = e.dtoh(buf)?;
10926                let nan = v.iter().filter(|x| x.is_nan()).count();
10927                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10928                eprintln!(
10929                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
10930                    v.len()
10931                );
10932            }
10933        }
10934
10935        let mut q = e.uninit(t * nh * hd)?;
10936        let mut k = e.uninit(t * nkv * hd)?;
10937        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
10938        let mut v = e.uninit(t * nkv * hd)?;
10939        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
10940        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
10941        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
10942        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10943        // Island primes take the mask-capable naive kernel below; keep the operands f32
10944        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
10945        let emit = island.is_none()
10946            && t >= 16
10947            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
10948            && *EMIT.get_or_init(|| {
10949                std::env::var("MEMRA_FA_EMIT")
10950                    .map(|s| s != "0")
10951                    .unwrap_or(true)
10952            });
10953        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
10954        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10955        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10956        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
10957        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
10958        let v_f16 = emit
10959            && crate::fa_f16pv_on()
10960            && match hd {
10961                512 => true,
10962                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
10963                _ => false,
10964            };
10965        if emit {
10966            e.rms_norm_qkv_w4b(
10967                &q0,
10968                &k0,
10969                &v0,
10970                fa.q_norm.float_data(),
10971                fa.k_norm.float_data(),
10972                ones,
10973                &mut q,
10974                &mut k,
10975                &mut v,
10976                &mut vb,
10977                hd,
10978                nh * t,
10979                nkv * t,
10980                eps,
10981                v_f16,
10982            )?;
10983        } else {
10984            e.rms_norm_qkv(
10985                &q0,
10986                &k0,
10987                &v0,
10988                fa.q_norm.float_data(),
10989                fa.k_norm.float_data(),
10990                ones,
10991                &mut q,
10992                &mut k,
10993                &mut v,
10994                hd,
10995                nh * t,
10996                nkv * t,
10997                eps,
10998            )?;
10999        }
11000
11001        let ff = if swa {
11002            None
11003        } else {
11004            Some(
11005                aux.rope_freqs(e)
11006                    .expect("gemma4 global rope needs rope_freqs.weight"),
11007            )
11008        };
11009        #[cfg(debug_assertions)]
11010        if let Some(ff) = ff {
11011            crate::debug_assert_tensor_stream_device(
11012                ff,
11013                &e.stream(),
11014                "gemma4_attn_prime.rope_freqs",
11015            );
11016        }
11017        if emit {
11018            e.rope_neox2_bf16e(
11019                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
11020            )?;
11021        } else {
11022            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
11023        }
11024
11025        if let Some(cache) = cache {
11026            let kvl = cache.kv[il].as_mut().unwrap();
11027            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11028            e.append_kv_quantized_rows(
11029                &k,
11030                &v,
11031                &mut kvl.k,
11032                &mut kvl.v,
11033                kvl.len,
11034                t,
11035                kvl.kv_dim_k,
11036                kvl.kv_dim_v,
11037                kvl.k_tok_bytes,
11038                kvl.v_tok_bytes,
11039                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11040            )?;
11041            kvl.len += t;
11042        }
11043        let mut attn = e.zeros(t * nh * hd)?;
11044        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11045        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11046        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11047        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11048        if let Some(span) = island {
11049            // Masked-prefill arm: every layer routes through the island-aware naive
11050            // kernel (correctness-first, same posture as the vision tower v1). The
11051            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11052            // window, the real window beyond it.
11053            let w = if swa && t > win { win } else { 0 };
11054            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11055        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11056            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11057                if emit {
11058                    e.fa_prefill_w_pre(
11059                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11060                    )?;
11061                } else {
11062                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11063                }
11064            } else {
11065                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11066            }
11067        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11068            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11069        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11070            if emit {
11071                e.fa_prefill_hd512_pre(
11072                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11073                )?;
11074            } else {
11075                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11076            }
11077        } else {
11078            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11079        }
11080        Ok(e.matmul(&fa.wo, &attn, t)?)
11081    }
11082
11083    /// Back-compat wrapper (pure prefill, no cache).
11084    fn gemma4_attn(
11085        &self,
11086        e: &Engine,
11087        fa: &crate::hybrid::FullAttnLayer,
11088        il: usize,
11089        h: &CudaSlice<f32>,
11090        pos_d: &CudaSlice<i32>,
11091        t: usize,
11092    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11093        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11094    }
11095
11096    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11097    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11098    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11099    /// the q8z epilogue is quantize_q8_1 verbatim).
11100    fn gemma4_moe_q8(
11101        &self,
11102        e: &Engine,
11103        m: &crate::hybrid::MoeWeights,
11104        bits: &crate::hybrid::Gemma4MoeBits,
11105        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11106        router_in: &CudaSlice<f32>,
11107        t: usize,
11108    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11109        let cfg = &self.cfg;
11110        let moe = cfg.moe.as_ref().unwrap();
11111        let n_embd = cfg.n_embd as usize;
11112        let n_expert = moe.expert_count as usize;
11113        let n_used = moe.expert_used_count as usize;
11114        let n_ff_exp = moe.expert_ff_length as usize;
11115        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11116        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11117        // the pair's 12us is kernel time, not launch gaps.
11118        let logits = if crate::router_kernel_on() {
11119            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11120        } else {
11121            e.matmul(&m.gate_inp, router_in, t)?
11122        };
11123        let dev = m.dev_exps.as_ref().unwrap();
11124        let (sel_d, w_d) =
11125            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11126        let (zq, zd) = mq;
11127        if t == 1 {
11128            let selv = sel_d.slice(0..n_used);
11129            let wv = w_d.slice(0..n_used);
11130            let act = e.moe_gate_up_gelu8_dev_q8(
11131                &dev.ptr_row,
11132                &selv,
11133                zq,
11134                zd,
11135                n_embd,
11136                n_ff_exp,
11137                n_used,
11138                n_expert,
11139                m.gate_exps.qtype,
11140                m.up_exps.qtype,
11141                m.gate_exps.row_bytes,
11142                m.up_exps.row_bytes,
11143            )?;
11144            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11145            let mut moe_out = e.uninit(n_embd)?;
11146            e.moe_down8_fma_dev_q8(
11147                &dev.ptr_row,
11148                &selv,
11149                &wv,
11150                &aq2,
11151                &ad2,
11152                &mut moe_out.slice_mut(0..n_embd),
11153                n_ff_exp,
11154                n_embd,
11155                n_used,
11156                n_expert,
11157                m.down_exps.qtype,
11158                m.down_exps.row_bytes,
11159            )?;
11160            return Ok(moe_out);
11161        }
11162        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11163        let act = if csr {
11164            e.moe_gate_up_gelu8_dev_q8_csr(
11165                &dev.ptr_row,
11166                &sel_d,
11167                zq,
11168                zd,
11169                t * n_used,
11170                n_embd,
11171                n_ff_exp,
11172                n_used,
11173                n_expert,
11174                m.gate_exps.qtype,
11175                m.up_exps.qtype,
11176                m.gate_exps.row_bytes,
11177                m.up_exps.row_bytes,
11178            )?
11179        } else {
11180            e.moe_gate_up_gelu8_dev_q8_rows(
11181                &dev.ptr_row,
11182                &sel_d,
11183                zq,
11184                zd,
11185                t,
11186                n_embd,
11187                n_ff_exp,
11188                n_used,
11189                n_expert,
11190                m.gate_exps.qtype,
11191                m.up_exps.qtype,
11192                m.gate_exps.row_bytes,
11193                m.up_exps.row_bytes,
11194            )?
11195        };
11196        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11197        let mut moe_out = e.uninit(t * n_embd)?;
11198        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11199        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11200        e.moe_down8_fma_dev_q8_rows_g(
11201            &dev.ptr_row,
11202            &sel_d,
11203            &w_d,
11204            &aq2,
11205            &ad2,
11206            &mut moe_out,
11207            t,
11208            n_ff_exp,
11209            n_embd,
11210            n_used,
11211            n_expert,
11212            m.down_exps.qtype,
11213            m.down_exps.row_bytes,
11214        )?;
11215        Ok(moe_out)
11216    }
11217
11218    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11219    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11220    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11221    fn gemma4_moe(
11222        &self,
11223        e: &Engine,
11224        m: &crate::hybrid::MoeWeights,
11225        bits: &crate::hybrid::Gemma4MoeBits,
11226        moe_in: &CudaSlice<f32>,
11227        router_in: &CudaSlice<f32>,
11228        t: usize,
11229    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11230        let cfg = &self.cfg;
11231        let moe = cfg.moe.as_ref().unwrap();
11232        let n_embd = cfg.n_embd as usize;
11233        let n_expert = moe.expert_count as usize;
11234        let n_used = moe.expert_used_count as usize;
11235        let n_ff_exp = moe.expert_ff_length as usize;
11236
11237        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11238        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11239        // batched matmul only at real prefill.
11240        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11241            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11242        } else {
11243            e.matmul(&m.gate_inp, router_in, t)?
11244        };
11245
11246        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11247        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11248        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11249        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11250        if t < PRIME_MIN_T
11251            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11252            && expert_dp4a_supported(m.gate_exps.qtype)
11253            && expert_dp4a_supported(m.up_exps.qtype)
11254            && expert_dp4a_supported(m.down_exps.qtype)
11255            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11256        {
11257            let dev = m.dev_exps.as_ref().unwrap();
11258            let (sel_d, w_d) =
11259                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11260            if t == 1 {
11261                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11262                let selv = sel_d.slice(0..n_used);
11263                let wv = w_d.slice(0..n_used);
11264                let act = e.moe_gate_up_gelu8_dev_q8(
11265                    &dev.ptr_row,
11266                    &selv,
11267                    &zq,
11268                    &zd,
11269                    n_embd,
11270                    n_ff_exp,
11271                    n_used,
11272                    n_expert,
11273                    m.gate_exps.qtype,
11274                    m.up_exps.qtype,
11275                    m.gate_exps.row_bytes,
11276                    m.up_exps.row_bytes,
11277                )?;
11278                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11279                let mut moe_out = e.uninit(n_embd)?;
11280                e.moe_down8_fma_dev_q8(
11281                    &dev.ptr_row,
11282                    &selv,
11283                    &wv,
11284                    &aq2,
11285                    &ad2,
11286                    &mut moe_out.slice_mut(0..n_embd),
11287                    n_ff_exp,
11288                    n_embd,
11289                    n_used,
11290                    n_expert,
11291                    m.down_exps.qtype,
11292                    m.down_exps.row_bytes,
11293                )?;
11294                return Ok(moe_out);
11295            }
11296            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11297            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11298            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11299            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11300            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11301            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11302            let act = if csr {
11303                e.moe_gate_up_gelu8_dev_q8_csr(
11304                    &dev.ptr_row,
11305                    &sel_d,
11306                    &zq,
11307                    &zd,
11308                    t * n_used,
11309                    n_embd,
11310                    n_ff_exp,
11311                    n_used,
11312                    n_expert,
11313                    m.gate_exps.qtype,
11314                    m.up_exps.qtype,
11315                    m.gate_exps.row_bytes,
11316                    m.up_exps.row_bytes,
11317                )?
11318            } else {
11319                e.moe_gate_up_gelu8_dev_q8_rows(
11320                    &dev.ptr_row,
11321                    &sel_d,
11322                    &zq,
11323                    &zd,
11324                    t,
11325                    n_embd,
11326                    n_ff_exp,
11327                    n_used,
11328                    n_expert,
11329                    m.gate_exps.qtype,
11330                    m.up_exps.qtype,
11331                    m.gate_exps.row_bytes,
11332                    m.up_exps.row_bytes,
11333                )?
11334            };
11335            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11336            let mut moe_out = e.uninit(t * n_embd)?;
11337            e.moe_down8_fma_dev_q8_rows_g(
11338                &dev.ptr_row,
11339                &sel_d,
11340                &w_d,
11341                &aq2,
11342                &ad2,
11343                &mut moe_out,
11344                t,
11345                n_ff_exp,
11346                n_embd,
11347                n_used,
11348                n_expert,
11349                m.down_exps.qtype,
11350                m.down_exps.row_bytes,
11351            )?;
11352            return Ok(moe_out);
11353        }
11354
11355        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11356        for (i, &sx) in sel_all.iter().enumerate() {
11357            w_all[i] *= bits.per_expert_scale[sx as usize];
11358        }
11359
11360        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11361        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11362        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11363        if t >= PRIME_MIN_T
11364            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11365            && expert_dp4a_supported(m.gate_exps.qtype)
11366            && expert_dp4a_supported(m.up_exps.qtype)
11367            && expert_dp4a_supported(m.down_exps.qtype)
11368            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11369        {
11370            let dev = m.dev_exps.as_ref().unwrap();
11371            let n_pairs = t * n_used;
11372            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11373            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11374            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11375            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11376            let pt = e.htod_i32(&pair_tok)?;
11377            let pw = e.htod(&w_all)?;
11378            let toff = e.htod_i32(&tok_off)?;
11379            let tids = e.htod_i32(&tok_ids)?;
11380            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11381            for p in 0..n_pairs {
11382                by_ex[pair_ex[p] as usize].push(p as i32);
11383            }
11384            let mut ex_ids: Vec<i32> = Vec::new();
11385            let mut ex_off: Vec<i32> = vec![0];
11386            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11387            for (ex, list) in by_ex.iter().enumerate() {
11388                if list.is_empty() {
11389                    continue;
11390                }
11391                ex_ids.push(ex as i32);
11392                ex_pairs.extend_from_slice(list);
11393                ex_off.push(ex_pairs.len() as i32);
11394            }
11395            let n_active = ex_ids.len();
11396            let exi = e.htod_i32(&ex_ids)?;
11397            let exo = e.htod_i32(&ex_off)?;
11398            let exp_d = e.htod_i32(&ex_pairs)?;
11399            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11400            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11401            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11402            // ragged down k (704) needs no padding here — cublas takes any k.
11403            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11404            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11405            // Hopper default — see moe_f16g_gemma_on.
11406            if crate::moe_f16g_gemma_on()
11407                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11408                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11409                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11410            {
11411                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11412                let csr_tok_d = e.htod_i32(&csr_tok)?;
11413                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11414                let g_csr = e.moe_f16_grouped(
11415                    &dev.ptr_row,
11416                    0,
11417                    n_expert,
11418                    &exi,
11419                    &ex_off,
11420                    &exo,
11421                    &z_f16,
11422                    &z_s,
11423                    n_embd,
11424                    n_ff_exp,
11425                    n_active,
11426                    n_pairs,
11427                    m.gate_exps.qtype,
11428                    m.gate_exps.row_bytes,
11429                )?;
11430                let u_csr = e.moe_f16_grouped(
11431                    &dev.ptr_row,
11432                    1,
11433                    n_expert,
11434                    &exi,
11435                    &ex_off,
11436                    &exo,
11437                    &z_f16,
11438                    &z_s,
11439                    n_embd,
11440                    n_ff_exp,
11441                    n_active,
11442                    n_pairs,
11443                    m.up_exps.qtype,
11444                    m.up_exps.row_bytes,
11445                )?;
11446                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11447                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11448                let d_csr = e.moe_f16_grouped(
11449                    &dev.ptr_row,
11450                    2,
11451                    n_expert,
11452                    &exi,
11453                    &ex_off,
11454                    &exo,
11455                    &a_f16,
11456                    &a_s,
11457                    n_ff_exp,
11458                    n_embd,
11459                    n_active,
11460                    n_pairs,
11461                    m.down_exps.qtype,
11462                    m.down_exps.row_bytes,
11463                )?;
11464                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11465                let mut moe_out = e.uninit(t * n_embd)?;
11466                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11467                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11468                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11469                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11470                    eprintln!(
11471                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11472                        scan(&yd),
11473                        scan(&mo)
11474                    );
11475                }
11476                return Ok(moe_out);
11477            }
11478            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11479            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11480            let mma =
11481                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11482            let (gate, up) = if mma {
11483                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11484                (
11485                    e.mmq_iq_experts(
11486                        &dev.ptr_row,
11487                        0,
11488                        n_expert,
11489                        &exi,
11490                        &exo,
11491                        &exp_d,
11492                        &pt,
11493                        &z_scr,
11494                        n_embd,
11495                        n_ff_exp,
11496                        n_active,
11497                        n_pairs,
11498                        t,
11499                        m.gate_exps.qtype,
11500                        m.gate_exps.row_bytes,
11501                    )?,
11502                    e.mmq_iq_experts(
11503                        &dev.ptr_row,
11504                        1,
11505                        n_expert,
11506                        &exi,
11507                        &exo,
11508                        &exp_d,
11509                        &pt,
11510                        &z_scr,
11511                        n_embd,
11512                        n_ff_exp,
11513                        n_active,
11514                        n_pairs,
11515                        t,
11516                        m.up_exps.qtype,
11517                        m.up_exps.row_bytes,
11518                    )?,
11519                )
11520            } else {
11521                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11522                (
11523                    e.moe_pairs_matvec_q8_dec(
11524                        &dev.ptr_row,
11525                        0,
11526                        &exi,
11527                        &exo,
11528                        &exp_d,
11529                        &pt,
11530                        &zq,
11531                        &zd,
11532                        n_embd,
11533                        n_ff_exp,
11534                        n_expert,
11535                        n_active,
11536                        n_pairs,
11537                        m.gate_exps.qtype,
11538                        m.gate_exps.row_bytes,
11539                    )?,
11540                    e.moe_pairs_matvec_q8_dec(
11541                        &dev.ptr_row,
11542                        1,
11543                        &exi,
11544                        &exo,
11545                        &exp_d,
11546                        &pt,
11547                        &zq,
11548                        &zd,
11549                        n_embd,
11550                        n_ff_exp,
11551                        n_expert,
11552                        n_active,
11553                        n_pairs,
11554                        m.up_exps.qtype,
11555                        m.up_exps.row_bytes,
11556                    )?,
11557                )
11558            };
11559            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11560            let pself = e.htod_i32(&pair_self)?;
11561            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11562            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11563            // to the 256-val superblock (768) while the act quantizer's zero padding
11564            // makes every padded-k product exactly zero (weight overread bytes multiply
11565            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11566            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11567            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11568            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11569            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11570            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11571            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11572            let y_down = if mma {
11573                let in_pad = n_ff_exp.div_ceil(256) * 256;
11574                let a_scr = if crate::moe_fuse_actq_on() {
11575                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11576                } else {
11577                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11578                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11579                };
11580                e.mmq_iq_experts(
11581                    &dev.ptr_row,
11582                    2,
11583                    n_expert,
11584                    &exi,
11585                    &exo,
11586                    &exp_d,
11587                    &pself,
11588                    &a_scr,
11589                    in_pad,
11590                    n_embd,
11591                    n_active,
11592                    n_pairs,
11593                    n_pairs,
11594                    m.down_exps.qtype,
11595                    m.down_exps.row_bytes,
11596                )?
11597            } else {
11598                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11599                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11600                e.moe_pairs_matvec_q8_dec(
11601                    &dev.ptr_row,
11602                    2,
11603                    &exi,
11604                    &exo,
11605                    &exp_d,
11606                    &pself,
11607                    &aq2,
11608                    &ad2,
11609                    n_ff_exp,
11610                    n_embd,
11611                    n_expert,
11612                    n_active,
11613                    n_pairs,
11614                    m.down_exps.qtype,
11615                    m.down_exps.row_bytes,
11616                )?
11617            };
11618            let mut moe_out = e.uninit(t * n_embd)?;
11619            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11620            return Ok(moe_out);
11621        }
11622
11623        let g_len = m.gate_exps.expert_stride;
11624        let u_len = m.up_exps.expert_stride;
11625        let d_len = m.down_exps.expert_stride;
11626        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
11627        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
11628        // the spill fallback.
11629        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
11630        let (mut sg, mut su, mut sd) = if dev.is_some() {
11631            (None, None, None)
11632        } else {
11633            (
11634                Some(e.alloc_u8_uninit(g_len)?),
11635                Some(e.alloc_u8_uninit(u_len)?),
11636                Some(e.alloc_u8_uninit(d_len)?),
11637            )
11638        };
11639        let mut moe_out = e.zeros(t * n_embd)?;
11640        for tok in 0..t {
11641            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
11642            let w = &w_all[tok * n_used..(tok + 1) * n_used];
11643            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
11644            for (j, &ex) in sel.iter().enumerate() {
11645                let ex = ex as usize;
11646                let gate = match dev {
11647                    Some(d) => e.qmatvec_view(
11648                        &d.gate,
11649                        ex * g_len..(ex + 1) * g_len,
11650                        &zt,
11651                        1,
11652                        m.gate_exps.in_f,
11653                        m.gate_exps.out_f,
11654                        m.gate_exps.qtype,
11655                        m.gate_exps.row_bytes,
11656                    )?,
11657                    None => {
11658                        let sg = sg.as_mut().unwrap();
11659                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
11660                        e.qmatvec_view(
11661                            sg,
11662                            0..g_len,
11663                            &zt,
11664                            1,
11665                            m.gate_exps.in_f,
11666                            m.gate_exps.out_f,
11667                            m.gate_exps.qtype,
11668                            m.gate_exps.row_bytes,
11669                        )?
11670                    }
11671                };
11672                let up = match dev {
11673                    Some(d) => e.qmatvec_view(
11674                        &d.up,
11675                        ex * u_len..(ex + 1) * u_len,
11676                        &zt,
11677                        1,
11678                        m.up_exps.in_f,
11679                        m.up_exps.out_f,
11680                        m.up_exps.qtype,
11681                        m.up_exps.row_bytes,
11682                    )?,
11683                    None => {
11684                        let su = su.as_mut().unwrap();
11685                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
11686                        e.qmatvec_view(
11687                            su,
11688                            0..u_len,
11689                            &zt,
11690                            1,
11691                            m.up_exps.in_f,
11692                            m.up_exps.out_f,
11693                            m.up_exps.qtype,
11694                            m.up_exps.row_bytes,
11695                        )?
11696                    }
11697                };
11698                let mut act = e.uninit(n_ff_exp)?;
11699                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
11700                let actv = act.slice(0..n_ff_exp);
11701                let y = match dev {
11702                    Some(d) => e.qmatvec_view(
11703                        &d.down,
11704                        ex * d_len..(ex + 1) * d_len,
11705                        &actv,
11706                        1,
11707                        m.down_exps.in_f,
11708                        m.down_exps.out_f,
11709                        m.down_exps.qtype,
11710                        m.down_exps.row_bytes,
11711                    )?,
11712                    None => {
11713                        let sd = sd.as_mut().unwrap();
11714                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
11715                        e.qmatvec_view(
11716                            sd,
11717                            0..d_len,
11718                            &actv,
11719                            1,
11720                            m.down_exps.in_f,
11721                            m.down_exps.out_f,
11722                            m.down_exps.qtype,
11723                            m.down_exps.row_bytes,
11724                        )?
11725                    }
11726                };
11727                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
11728                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
11729            }
11730        }
11731        Ok(moe_out)
11732    }
11733
11734    /// One gemma4 trunk layer (R8): x -> x_next.
11735    fn gemma4_layer(
11736        &self,
11737        e: &Engine,
11738        il: usize,
11739        layer: &crate::hybrid::HybridLayer,
11740        x: &CudaSlice<f32>,
11741        pos_d: &CudaSlice<i32>,
11742        t: usize,
11743    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11744        let n_embd = self.cfg.n_embd as usize;
11745        let eps = self.cfg.rms_eps;
11746
11747        let mut h = e.zeros(t * n_embd)?;
11748        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
11749        let Mixer::Full(fa) = &layer.mixer else {
11750            panic!("gemma4 layer {il} not full-attn")
11751        };
11752        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
11753        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
11754        let mut cur = e.zeros(t * n_embd)?;
11755        e.rms_norm(
11756            &o,
11757            layer.post_attn_norm.float_data(),
11758            &mut cur,
11759            n_embd,
11760            t,
11761            eps,
11762        )?;
11763        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
11764    }
11765
11766    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
11767    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
11768    /// layer scale — shared verbatim by the prefill, decode and verify paths.
11769    fn gemma4_layer_tail_add(
11770        &self,
11771        e: &Engine,
11772        layer: &crate::hybrid::HybridLayer,
11773        cur: &CudaSlice<f32>,
11774        x: &CudaSlice<f32>,
11775        t: usize,
11776    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11777        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
11778    }
11779
11780    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
11781    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
11782    fn gemma4_layer_tail_add_n(
11783        &self,
11784        e: &Engine,
11785        layer: &crate::hybrid::HybridLayer,
11786        cur: &CudaSlice<f32>,
11787        x: &CudaSlice<f32>,
11788        t: usize,
11789        next_norm: Option<&CudaSlice<f32>>,
11790    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
11791        let n_embd = self.cfg.n_embd as usize;
11792        let bits = layer.gemma4.as_ref().unwrap();
11793        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
11794        let mut xn = e.uninit(t * n_embd)?;
11795        match next_norm {
11796            Some(w) => {
11797                let mut hn = e.uninit(t * n_embd)?;
11798                e.add_scale_rms_norm(
11799                    &sn,
11800                    &attn_out,
11801                    bits.layer_scale,
11802                    w,
11803                    &mut xn,
11804                    &mut hn,
11805                    n_embd,
11806                    t,
11807                    self.cfg.rms_eps,
11808                )?;
11809                Ok((xn, Some(hn)))
11810            }
11811            None => {
11812                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
11813                Ok((xn, None))
11814            }
11815        }
11816    }
11817
11818    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
11819    /// norm — returns (sn, attn_out) for the closing add+scale variants.
11820    fn gemma4_layer_tail_core(
11821        &self,
11822        e: &Engine,
11823        layer: &crate::hybrid::HybridLayer,
11824        cur: &CudaSlice<f32>,
11825        x: &CudaSlice<f32>,
11826        t: usize,
11827    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11828        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
11829    }
11830
11831    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
11832    /// means `cur` is the RAW attention output and the dense entry runs
11833    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
11834    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
11835    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
11836    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
11837    fn gemma4_layer_tail_core_pn(
11838        &self,
11839        e: &Engine,
11840        layer: &crate::hybrid::HybridLayer,
11841        cur: &CudaSlice<f32>,
11842        x: &CudaSlice<f32>,
11843        t: usize,
11844        pre_norm: Option<&CudaSlice<f32>>,
11845        defer_post_norm: bool,
11846    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11847        let n_embd = self.cfg.n_embd as usize;
11848        let eps = self.cfg.rms_eps;
11849        let bits = layer.gemma4.as_ref().unwrap();
11850
11851        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
11852        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
11853        let Some(mbits) = bits.moe_bits.as_ref() else {
11854            let crate::hybrid::Ffn::Dense {
11855                ffn_gate,
11856                ffn_up,
11857                ffn_down,
11858            } = &layer.ffn
11859            else {
11860                panic!("gemma4 dense layer without Dense ffn")
11861            };
11862            let mut attn_out = e.uninit(t * n_embd)?;
11863            let mut zsh = e.uninit(t * n_embd)?;
11864            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
11865            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
11866            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
11867            match pre_norm {
11868                Some(wa) if t == 1 => {
11869                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
11870                        cur,
11871                        wa,
11872                        x,
11873                        bits.ffn_norm.float_data(),
11874                        &mut attn_out,
11875                        &mut zsh,
11876                        n_embd,
11877                        t,
11878                        eps,
11879                    )?);
11880                }
11881                Some(wa) => e.rms_pre_add_rms_norm(
11882                    cur,
11883                    wa,
11884                    x,
11885                    bits.ffn_norm.float_data(),
11886                    &mut attn_out,
11887                    &mut zsh,
11888                    n_embd,
11889                    t,
11890                    eps,
11891                )?,
11892                None => e.add_rms_norm(
11893                    cur,
11894                    x,
11895                    bits.ffn_norm.float_data(),
11896                    &mut attn_out,
11897                    &mut zsh,
11898                    n_embd,
11899                    t,
11900                    eps,
11901                )?,
11902            }
11903            let n_ff = ffn_gate.out_features();
11904            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
11905            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
11906            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
11907            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
11908            // rescue segment C — the megakernel front is closed for the dense tail.
11909            let (gate, up) = if t == 1 {
11910                let (zq, zd) = match zpair {
11911                    Some(p) => p,
11912                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
11913                };
11914                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
11915                    Some(p) => p,
11916                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
11917                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
11918                        Some(p) => p,
11919                        None => (
11920                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
11921                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
11922                        ),
11923                    },
11924                }
11925            } else {
11926                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
11927                // launch for the verify's gate+up — the up segment's blocks fill SMs as
11928                // the gate segment drains (the launch-tail mechanism behind the b-tier
11929                // plateau; first positive after six falsified in-kernel variants).
11930                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11931                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
11932                let fused = if f2b {
11933                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
11934                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
11935                } else {
11936                    None
11937                };
11938                match fused {
11939                    Some(p) => p,
11940                    None => {
11941                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
11942                        e.mmq_act_begin();
11943                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
11944                    }
11945                }
11946            };
11947            let mut act = e.uninit(t * n_ff)?;
11948            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
11949            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
11950            let f0 = if e.uses_q8_1_fast(ffn_down) {
11951                let upv = e.view(&up, t * n_ff);
11952                let up_all = upv.slice(0..t * n_ff);
11953                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
11954                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
11955            } else {
11956                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11957                e.matmul(ffn_down, &act, t)?
11958            };
11959            if defer_post_norm {
11960                return Ok((f0, attn_out));
11961            }
11962            let mut sn = e.uninit(t * n_embd)?;
11963            e.rms_norm(
11964                &f0,
11965                bits.post_ffw_norm.float_data(),
11966                &mut sn,
11967                n_embd,
11968                t,
11969                eps,
11970            )?;
11971            return Ok((sn, attn_out));
11972        };
11973
11974        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
11975        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
11976        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
11977        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
11978        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
11979        let mut attn_out = e.uninit(t * n_embd)?;
11980        let mut router_in = e.uninit(t * n_embd)?;
11981        let fast_moe = match &layer.ffn {
11982            crate::hybrid::Ffn::Moe(m) => {
11983                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11984                    && expert_dp4a_supported(m.gate_exps.qtype)
11985                    && expert_dp4a_supported(m.up_exps.qtype)
11986                    && expert_dp4a_supported(m.down_exps.qtype)
11987                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11988            }
11989            _ => false,
11990        };
11991        let q8z = t < PRIME_MIN_T && fast_moe;
11992        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
11993            let (z0, m2) = e.add_rms_norm3_q8z(
11994                cur,
11995                x,
11996                bits.ffn_norm.float_data(),
11997                &mbits.router_scale_pre,
11998                mbits.pre_ffw_norm_2.float_data(),
11999                &mut attn_out,
12000                &mut router_in,
12001                n_embd,
12002                t,
12003                eps,
12004            )?;
12005            (None, Some(z0), Some(m2))
12006        } else {
12007            let mut zsh = e.uninit(t * n_embd)?;
12008            let mut moe_in = e.uninit(t * n_embd)?;
12009            e.add_rms_norm3(
12010                cur,
12011                x,
12012                bits.ffn_norm.float_data(),
12013                &mbits.router_scale_pre,
12014                mbits.pre_ffw_norm_2.float_data(),
12015                &mut attn_out,
12016                &mut zsh,
12017                &mut router_in,
12018                &mut moe_in,
12019                n_embd,
12020                t,
12021                eps,
12022            )?;
12023            (Some((zsh, moe_in)), None, None)
12024        };
12025        let attn_out2 = attn_out;
12026        #[allow(unused_variables)]
12027        let attn_out = &attn_out2;
12028        let n_ff = mbits.shared_gate.out_features();
12029        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12030            if t == 1 {
12031                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12032                    Some(p) => p,
12033                    None => match e.matmul_nvfp4_fused2(
12034                        &mbits.shared_gate,
12035                        &mbits.shared_up,
12036                        zq,
12037                        zd,
12038                        1,
12039                    )? {
12040                        Some(p) => p,
12041                        None => {
12042                            let h0 = e.zeros(0)?;
12043                            (
12044                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12045                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12046                            )
12047                        }
12048                    },
12049                }
12050            } else {
12051                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12052                let h0 = e.zeros(0)?;
12053                (
12054                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12055                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12056                )
12057            }
12058        } else {
12059            let (zsh, _) = zsh_f32.as_ref().unwrap();
12060            (
12061                e.matmul(&mbits.shared_gate, zsh, t)?,
12062                e.matmul(&mbits.shared_up, zsh, t)?,
12063            )
12064        };
12065        let mut act = e.uninit(t * n_ff)?;
12066        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12067        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12068        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12069            panic!("gemma4 layer not MoE")
12070        };
12071        let moe0 = match (&moe_q8, &zsh_f32) {
12072            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12073            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12074            _ => unreachable!(),
12075        };
12076        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12077        let mut mlp = e.uninit(t * n_embd)?;
12078        let mut moe = e.uninit(t * n_embd)?;
12079        e.rms_norm2x(
12080            &mlp0,
12081            &moe0,
12082            mbits.post_ffw_norm_1.float_data(),
12083            mbits.post_ffw_norm_2.float_data(),
12084            &mut mlp,
12085            &mut moe,
12086            n_embd,
12087            t,
12088            eps,
12089        )?;
12090
12091        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12092        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12093        let mut sum = e.uninit(t * n_embd)?;
12094        let mut sn = e.uninit(t * n_embd)?;
12095        e.add_rms_norm(
12096            &mlp,
12097            &moe,
12098            bits.post_ffw_norm.float_data(),
12099            &mut sum,
12100            &mut sn,
12101            n_embd,
12102            t,
12103            eps,
12104        )?;
12105        Ok((sn, attn_out2))
12106    }
12107
12108    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12109    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12110    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12111    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12112    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12113    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12114    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12115    /// decode == verify == graph parity holds by construction at either seam value.
12116    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12117    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12118        &self,
12119        e: &Engine,
12120        layer: &crate::hybrid::HybridLayer,
12121        o: &CudaSlice<f32>,
12122        x: &CudaSlice<f32>,
12123        t: usize,
12124        next_norm: Option<&CudaSlice<f32>>,
12125    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12126    {
12127        let n_embd = self.cfg.n_embd as usize;
12128        let eps = self.cfg.rms_eps;
12129        let bits = layer.gemma4.as_ref().unwrap();
12130        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12131            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12132                e,
12133                layer,
12134                o,
12135                x,
12136                t,
12137                Some(layer.post_attn_norm.float_data()),
12138                true,
12139            )?;
12140            let mut xn = e.uninit(t * n_embd)?;
12141            return match next_norm {
12142                Some(w) => {
12143                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12144                        &f0,
12145                        bits.post_ffw_norm.float_data(),
12146                        &attn_out,
12147                        bits.layer_scale,
12148                        w,
12149                        &mut xn,
12150                        n_embd,
12151                        t,
12152                        eps,
12153                    )?;
12154                    Ok((xn, Some(pair)))
12155                }
12156                None => {
12157                    let mut sn = e.uninit(t * n_embd)?;
12158                    e.rms_norm(
12159                        &f0,
12160                        bits.post_ffw_norm.float_data(),
12161                        &mut sn,
12162                        n_embd,
12163                        t,
12164                        eps,
12165                    )?;
12166                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12167                    Ok((xn, None))
12168                }
12169            };
12170        }
12171        let mut cur = e.uninit(t * n_embd)?;
12172        e.rms_norm(
12173            o,
12174            layer.post_attn_norm.float_data(),
12175            &mut cur,
12176            n_embd,
12177            t,
12178            eps,
12179        )?;
12180        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12181    }
12182
12183    pub(crate) fn gemma4_layer_tail_add_nq(
12184        &self,
12185        e: &Engine,
12186        layer: &crate::hybrid::HybridLayer,
12187        cur: &CudaSlice<f32>,
12188        x: &CudaSlice<f32>,
12189        t: usize,
12190        next_norm: Option<&CudaSlice<f32>>,
12191    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12192    {
12193        let n_embd = self.cfg.n_embd as usize;
12194        let bits = layer.gemma4.as_ref().unwrap();
12195        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12196        let mut xn = e.uninit(t * n_embd)?;
12197        match next_norm {
12198            Some(w) => {
12199                let pair = e.add_scale_rms_norm_q8_1(
12200                    &sn,
12201                    &attn_out,
12202                    bits.layer_scale,
12203                    w,
12204                    &mut xn,
12205                    n_embd,
12206                    t,
12207                    self.cfg.rms_eps,
12208                )?;
12209                Ok((xn, Some(pair)))
12210            }
12211            None => {
12212                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12213                Ok((xn, None))
12214            }
12215        }
12216    }
12217
12218    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12219    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12220    fn gemma4_forward(
12221        &self,
12222        e: &Engine,
12223        tokens: &[u32],
12224        last_only: bool,
12225    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12226        // E4B routes to its own forward regardless of the caller's entry point (forward /
12227        // forward_last / prime paths all funnel here for gemma4).
12228        if self.is_gemma4_e4b() {
12229            return self.gemma4_e4b_forward(e, tokens, last_only);
12230        }
12231        let n_embd = self.cfg.n_embd as usize;
12232        let t = tokens.len();
12233        let pos: Vec<i32> = (0..t as i32).collect();
12234        let pos_d = e.htod_i32(&pos)?;
12235
12236        let mut x = self.embed(e, tokens)?;
12237        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12238        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12239        // the bring-up bisect vs llama-eval-callback node stats.
12240        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12241        let stat =
12242            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12243                let h = e.dtoh(x)?;
12244                let bad = h.iter().filter(|v| !v.is_finite()).count();
12245                let mx = h
12246                    .iter()
12247                    .filter(|v| v.is_finite())
12248                    .fold(0.0f32, |m, v| m.max(v.abs()));
12249                eprintln!(
12250                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12251                    &h[..3]
12252                );
12253                Ok(())
12254            };
12255        if probe {
12256            stat(e, &x, "embed")?;
12257        }
12258        for (il, layer) in self.layers.iter().enumerate() {
12259            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12260            if probe {
12261                stat(e, &x, &format!("L{il}"))?;
12262            }
12263        }
12264        let mut hn = e.zeros(t * n_embd)?;
12265        e.rms_norm(
12266            &x,
12267            self.output_norm.float_data(),
12268            &mut hn,
12269            n_embd,
12270            t,
12271            self.cfg.rms_eps,
12272        )?;
12273        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12274        let n_vocab = self.output.out_features();
12275        let logits = if last_only {
12276            let hv = e.view(&hn, t * n_embd);
12277            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12278            let mut hlast = e.zeros(n_embd)?;
12279            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12280            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12281            e.softcap(&mut ld, cap, n_vocab)?;
12282            self.gemma4_suppress(e, &mut ld, 1)?;
12283            e.dtoh(&ld)?
12284        } else {
12285            let mut ld = e.matmul(&self.output, &hn, t)?;
12286            e.softcap(&mut ld, cap, t * n_vocab)?;
12287            self.gemma4_suppress(e, &mut ld, t)?;
12288            e.dtoh(&ld)?
12289        };
12290        Ok(logits)
12291    }
12292
12293    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12294    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12295    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12296    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12297    pub(crate) fn gemma4_prime(
12298        &self,
12299        e: &Engine,
12300        tokens: &[u32],
12301        cache: &mut Cache,
12302        overlay: Option<&crate::vision::EmbedOverlay>,
12303    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12304        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12305        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12306        // whole worker process on this line. The worker now primes gemma4 monolithically and
12307        // routes continuation suffixes tokenwise; this is the per-request backstop.
12308        if cache.pos != 0 {
12309            return Err(
12310                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12311                        — prime the full prompt in one call or decode tokenwise"
12312                    .into(),
12313            );
12314        }
12315        let n_embd = self.cfg.n_embd as usize;
12316        let eps = self.cfg.rms_eps;
12317        let t = tokens.len();
12318        let pos: Vec<i32> = (0..t as i32).collect();
12319        let pos_d = e.htod_i32(&pos)?;
12320        let mut x = self.embed(e, tokens)?;
12321        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12322        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12323        // sqrt(n_embd) text scale — the reference scales token batches only
12324        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12325        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12326        // bidirectional within itself, causal+SWA everywhere else, matching the
12327        // reference's llama_set_causal_attn(false) image batch exactly.
12328        let island: Option<CudaSlice<i32>> = match overlay {
12329            Some(ov) => {
12330                let mut span_id = vec![-1i32; t];
12331                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12332                    if pos + n_rows > t {
12333                        return Err(format!(
12334                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12335                            pos + n_rows
12336                        )
12337                        .into());
12338                    }
12339                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12340                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12341                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12342                        *s = i as i32;
12343                    }
12344                }
12345                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12346                // keep the plain causal mask. Exists only so the decisive probe can show
12347                // the island mask itself changes the answer; never on in serving.
12348                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12349                    None
12350                } else {
12351                    Some(e.htod_i32(&span_id)?)
12352                }
12353            }
12354            None => None,
12355        };
12356        for (il, layer) in self.layers.iter().enumerate() {
12357            let mut h = e.zeros(t * n_embd)?;
12358            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12359            let Mixer::Full(fa) = &layer.mixer else {
12360                panic!("gemma4 layer not full-attn")
12361            };
12362            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12363            if trace {
12364                let v = e.dtoh(&h)?;
12365                let nan = v.iter().filter(|x| x.is_nan()).count();
12366                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12367            }
12368            let o =
12369                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12370            if trace {
12371                let v = e.dtoh(&o)?;
12372                let nan = v.iter().filter(|x| x.is_nan()).count();
12373                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12374            }
12375            let mut cur = e.zeros(t * n_embd)?;
12376            e.rms_norm(
12377                &o,
12378                layer.post_attn_norm.float_data(),
12379                &mut cur,
12380                n_embd,
12381                t,
12382                eps,
12383            )?;
12384            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12385            self.dflash_tap(e, cache, il, &x, t)?;
12386            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12387            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12388                let h = e.dtoh(&x)?;
12389                let nan = h.iter().filter(|v| v.is_nan()).count();
12390                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12391                eprintln!(
12392                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12393                    h.len()
12394                );
12395                if nan > 0 {
12396                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12397                }
12398            }
12399        }
12400        cache.pos += t;
12401        let hiddens = e.clone_dtod(&x)?;
12402        let xv = e.view(&x, t * n_embd);
12403        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12404        let mut h_seed = e.zeros(n_embd)?;
12405        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12406        let mut hn = e.uninit(n_embd)?;
12407        e.rms_norm(
12408            &h_seed,
12409            self.output_norm.float_data(),
12410            &mut hn,
12411            n_embd,
12412            1,
12413            eps,
12414        )?;
12415        let mut ld = e.matmul(&self.output, &hn, 1)?;
12416        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12417        e.softcap(&mut ld, cap, self.output.out_features())?;
12418        self.gemma4_suppress(e, &mut ld, 1)?;
12419        let logits = e.dtoh(&ld)?;
12420        Ok((logits, h_seed, hiddens))
12421    }
12422
12423    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12424    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12425    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12426    /// fused norm emits q8 directly — the f32 h never materializes).
12427    fn gemma4_decode_attn(
12428        &self,
12429        e: &Engine,
12430        fa: &crate::hybrid::FullAttnLayer,
12431        il: usize,
12432        hq: &CudaSlice<i8>,
12433        hdq: &CudaSlice<f32>,
12434        pos_d: &CudaSlice<i32>,
12435        cache: &mut Cache,
12436    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12437        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12438        let eps = self.cfg.rms_eps;
12439        let aux = self.gemma4_aux.as_ref().unwrap();
12440        let ones = aux.ones(e);
12441        #[cfg(debug_assertions)]
12442        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12443        let (hq, hdq) = (hq, hdq);
12444        let h0 = e.zeros(0)?;
12445        let h = &h0;
12446        let (q0, k0, v0) = if swa {
12447            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12448                Some(t3) => t3,
12449                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12450                // match — fuse the uniform (q,k) pair and take v as its own single.
12451                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12452                    Some((q0, k0)) => {
12453                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12454                        (q0, k0, v0)
12455                    }
12456                    None => (
12457                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12458                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12459                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12460                    ),
12461                },
12462            }
12463        } else {
12464            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12465                Some(p) => p,
12466                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12467                    Some(p) => p,
12468                    None => (
12469                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12470                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12471                    ),
12472                },
12473            };
12474            let v0 = e.clone_dtod(&k0)?;
12475            (q0, k0, v0)
12476        };
12477        let mut q = e.uninit(nh * hd)?;
12478        let mut k = e.uninit(nkv * hd)?;
12479        let mut v = e.uninit(nkv * hd)?;
12480        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12481        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12482        let ff = if swa {
12483            None
12484        } else {
12485            Some(
12486                aux.rope_freqs(e)
12487                    .expect("gemma4 global rope needs rope_freqs.weight"),
12488            )
12489        };
12490        #[cfg(debug_assertions)]
12491        if let Some(ff) = ff {
12492            crate::debug_assert_tensor_stream_device(
12493                ff,
12494                &e.stream(),
12495                "gemma4_decode_attn.rope_freqs",
12496            );
12497        }
12498        let kvl = cache.kv[il].as_mut().unwrap();
12499        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12500        if crate::Engine::qkv_append_on() {
12501            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12502            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12503            // twin of the dc fold — bit-identical bodies, one launch per layer.
12504            e.rms_norm_qkv_rope_append(
12505                &q0,
12506                &k0,
12507                &v0,
12508                fa.q_norm.float_data(),
12509                fa.k_norm.float_data(),
12510                ones,
12511                &mut q,
12512                &mut k,
12513                &mut v,
12514                hd,
12515                self.gemma4_rope_dims(il),
12516                nh,
12517                nkv,
12518                pos_d,
12519                nh,
12520                nkv,
12521                base,
12522                1.0,
12523                ff,
12524                eps,
12525                &mut kvl.k,
12526                &mut kvl.v,
12527                kvl.len,
12528                kvl.k_tok_bytes,
12529                kvl.v_tok_bytes,
12530                kv_fp8,
12531            )?;
12532        } else {
12533            e.rms_norm_qkv_rope(
12534                &q0,
12535                &k0,
12536                &v0,
12537                fa.q_norm.float_data(),
12538                fa.k_norm.float_data(),
12539                ones,
12540                &mut q,
12541                &mut k,
12542                &mut v,
12543                hd,
12544                self.gemma4_rope_dims(il),
12545                nh,
12546                nkv,
12547                pos_d,
12548                nh,
12549                nkv,
12550                base,
12551                1.0,
12552                ff,
12553                eps,
12554            )?;
12555            e.append_kv_quantized(
12556                &k,
12557                &v,
12558                &mut kvl.k,
12559                &mut kvl.v,
12560                kvl.len,
12561                kvl.kv_dim_k,
12562                kvl.kv_dim_v,
12563                kvl.k_tok_bytes,
12564                kvl.v_tok_bytes,
12565                kv_fp8,
12566            )?;
12567        }
12568        kvl.len += 1;
12569        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12570        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12571        // positional). Globals attend the full history.
12572        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12573        let mut attn = e.uninit(nh * hd)?;
12574        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12575        if !swa
12576            && hd == 512
12577            && kvl.len >= crate::fa512_min_tkv()
12578            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12579        {
12580            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12581            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12582            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12583            let base = kvl.len as i32;
12584            e.i32_set_k(&mut kvl.len_d, base)?;
12585            e.fa_decode_rows(
12586                &q,
12587                &kp,
12588                &vp,
12589                &mut attn,
12590                hd,
12591                nh,
12592                nkv,
12593                kvl.len - 1,
12594                1,
12595                scale,
12596                kvl.k_tok_bytes,
12597                kvl.v_tok_bytes,
12598                Some((&kvl.len_d, -1)),
12599                false,
12600                false,
12601                None,
12602            )?;
12603            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12604        }
12605        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12606        if swa
12607            && kvl.len > win
12608            && hd == 256
12609            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12610        {
12611            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12612            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12613            let base = kvl.len as i32;
12614            e.i32_set_k(&mut kvl.len_d, base)?;
12615            e.fa_decode_rows_w(
12616                &q,
12617                &kp,
12618                &vp,
12619                &mut attn,
12620                hd,
12621                nh,
12622                nkv,
12623                &kvl.len_d,
12624                -1,
12625                1,
12626                scale,
12627                win,
12628                kvl.k_tok_bytes,
12629                kvl.v_tok_bytes,
12630                None,
12631            )?;
12632            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12633        }
12634        let (off_tok, t_kv) = if swa && kvl.len > win {
12635            (kvl.len - win, win)
12636        } else {
12637            (0, kvl.len)
12638        };
12639        let k_view = e.view_u8_range(
12640            &kvl.k,
12641            off_tok * kvl.k_tok_bytes,
12642            (off_tok + t_kv) * kvl.k_tok_bytes,
12643        );
12644        let v_view = e.view_u8_range(
12645            &kvl.v,
12646            off_tok * kvl.v_tok_bytes,
12647            (off_tok + t_kv) * kvl.v_tok_bytes,
12648        );
12649        e.fa_decode_kvmod(
12650            &q,
12651            &k_view,
12652            &v_view,
12653            &mut attn,
12654            hd,
12655            nh,
12656            nkv,
12657            t_kv,
12658            scale,
12659            kvl.k_tok_bytes,
12660            kvl.v_tok_bytes,
12661            swa && crate::Engine::wkv_on(),
12662        )?;
12663        Ok(e.matmul(&fa.wo, &attn, 1)?)
12664    }
12665
12666    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
12667    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
12668    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
12669    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
12670    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
12671    /// in-graph; the driver gates).
12672    #[allow(clippy::too_many_arguments)]
12673    pub fn gemma4_decode_step_dc(
12674        &self,
12675        e: &Engine,
12676        token_d: &CudaSlice<u32>,
12677        pos_d: &mut CudaSlice<i32>,
12678        embd_gpu: &CudaSlice<u8>,
12679        embd_qt: i32,
12680        embd_rb: usize,
12681        cache: &mut Cache,
12682        n_vocab: usize,
12683        cap_bucket_max: Option<(usize, usize)>,
12684    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
12685        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
12686        self.gemma4_decode_step_dc_into(
12687            e,
12688            token_d,
12689            pos_d,
12690            embd_gpu,
12691            embd_qt,
12692            embd_rb,
12693            cache,
12694            n_vocab,
12695            cap_bucket_max,
12696            &mut tok_out,
12697        )?;
12698        Ok(tok_out)
12699    }
12700
12701    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
12702    /// every replay; pass `token_d` itself for the self-feeding graph loop).
12703    #[allow(clippy::too_many_arguments)]
12704    pub fn gemma4_decode_step_dc_into(
12705        &self,
12706        e: &Engine,
12707        token_d: &CudaSlice<u32>,
12708        pos_d: &mut CudaSlice<i32>,
12709        embd_gpu: &CudaSlice<u8>,
12710        embd_qt: i32,
12711        embd_rb: usize,
12712        cache: &mut Cache,
12713        n_vocab: usize,
12714        cap_bucket_max: Option<(usize, usize)>,
12715        tok_out: &mut CudaSlice<u32>,
12716    ) -> Result<(), Box<dyn std::error::Error>> {
12717        let n_embd = self.cfg.n_embd as usize;
12718        let eps = self.cfg.rms_eps;
12719        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
12720        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
12721        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12722        let n_layers = self.layers.len();
12723        for (il, layer) in self.layers.iter().enumerate() {
12724            let (hq, hdq) = match h_carry.take() {
12725                Some(p) => p,
12726                None => {
12727                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
12728                }
12729            };
12730            let Mixer::Full(fa) = &layer.mixer else {
12731                panic!("gemma4 layer {il} not full-attn")
12732            };
12733            let o =
12734                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
12735            let next_norm = if il + 1 < n_layers {
12736                Some(self.layers[il + 1].attn_norm.float_data())
12737            } else {
12738                None
12739            };
12740            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
12741            x = xn;
12742            h_carry = hn;
12743        }
12744        let mut hn = e.uninit(n_embd)?;
12745        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
12746        let mut logits = e.matmul(&self.output, &hn, 1)?;
12747        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
12748        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
12749        e.inc_seqlen(pos_d)?;
12750        if cap_bucket_max.is_none() {
12751            cache.pos += 1;
12752        }
12753        Ok(())
12754    }
12755
12756    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
12757    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
12758    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
12759    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
12760
12761    /// Build the slot set (call OUTSIDE any capture).
12762    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
12763        let n_embd = self.cfg.n_embd as usize;
12764        let n_vocab = self.output.out_features();
12765        let n_layers = self.layers.len();
12766        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
12767        for il in 0..n_layers {
12768            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
12769            qmax = qmax.max(nh * hd);
12770            kvmax = kvmax.max(nkv * hd);
12771            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
12772                ffmax = ffmax.max(ffn_gate.out_features());
12773            }
12774        }
12775        Ok(G4DcSlots {
12776            x: e.uninit(n_embd)?,
12777            xn: e.uninit(n_embd)?,
12778            cur: e.uninit(n_embd)?,
12779            hq: e.alloc_i8_uninit(n_embd)?,
12780            hd_: e.uninit(n_embd / 32)?,
12781            q0: e.uninit(qmax)?,
12782            k0: e.uninit(kvmax)?,
12783            v0: e.uninit(kvmax)?,
12784            q: e.uninit(qmax)?,
12785            k: e.uninit(kvmax)?,
12786            v: e.uninit(kvmax)?,
12787            attn: e.uninit(qmax)?,
12788            o: e.uninit(n_embd)?,
12789            attn_out: e.uninit(n_embd)?,
12790            zsh: e.uninit(n_embd)?,
12791            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
12792            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
12793            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
12794            zd: e.uninit(n_embd.max(qmax) / 32)?,
12795            gate: e.uninit(ffmax)?,
12796            up: e.uninit(ffmax)?,
12797            act: e.uninit(ffmax)?,
12798            actq: e.alloc_i8_uninit(ffmax)?,
12799            actd: e.uninit(ffmax / 32)?,
12800            f0: e.uninit(n_embd)?,
12801            sn: e.uninit(n_embd)?,
12802            hn: e.uninit(n_embd)?,
12803            logits: e.uninit(n_vocab)?,
12804        })
12805    }
12806
12807    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
12808    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
12809    fn g4_matvec_m1_into(
12810        &self,
12811        e: &Engine,
12812        w: &crate::model::GpuTensor,
12813        aq: &CudaSlice<i8>,
12814        ad: &CudaSlice<f32>,
12815        y: &mut CudaSlice<f32>,
12816    ) -> Result<(), Box<dyn std::error::Error>> {
12817        use crate::model::GpuTensor;
12818        let (bytes, qtype, row_bytes, scale, rp) = match w {
12819            GpuTensor::Quant {
12820                bytes,
12821                qtype,
12822                row_bytes,
12823                scale,
12824                rp,
12825                ..
12826            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12827            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
12828        };
12829        let (mbytes, mrp) = match w {
12830            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12831            _ => (bytes, rp),
12832        };
12833        e.qmatvec_mmvq_into(
12834            mbytes,
12835            aq,
12836            ad,
12837            1,
12838            w.in_features(),
12839            w.out_features(),
12840            qtype,
12841            row_bytes,
12842            scale,
12843            mrp,
12844            y,
12845        )
12846    }
12847
12848    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
12849    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
12850    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
12851    #[allow(clippy::too_many_arguments)]
12852    pub fn gemma4_decode_step_dc_slotted(
12853        &self,
12854        e: &Engine,
12855        token_d: &CudaSlice<u32>,
12856        pos_d: &mut CudaSlice<i32>,
12857        embd_gpu: &CudaSlice<u8>,
12858        embd_qt: i32,
12859        embd_rb: usize,
12860        cache: &mut Cache,
12861        n_vocab: usize,
12862        cap_bucket_max: Option<(usize, usize)>,
12863        sl: &mut G4DcSlots,
12864        tok_out: &mut CudaSlice<u32>,
12865        ring: Option<(&mut CudaSlice<u32>, usize)>,
12866    ) -> Result<(), Box<dyn std::error::Error>> {
12867        let n_embd = self.cfg.n_embd as usize;
12868        let eps = self.cfg.rms_eps;
12869        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
12870        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
12871        let n_layers = self.layers.len();
12872        let mut has_carry = false;
12873        for il in 0..n_layers {
12874            if !has_carry {
12875                e.rms_norm_q8_1_into(
12876                    &sl.x,
12877                    self.layers[il].attn_norm.float_data(),
12878                    n_embd,
12879                    1,
12880                    eps,
12881                    &mut sl.hq,
12882                    &mut sl.hd_,
12883                )?;
12884            }
12885            has_carry = true;
12886            let layer = &self.layers[il];
12887            let Mixer::Full(fa) = &layer.mixer else {
12888                panic!("gemma4 layer {il} not full-attn")
12889            };
12890            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
12891            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
12892            // the standalone norm only survives on the unfused seam arm.
12893            if !Engine::g4_pnfold_on() {
12894                e.rms_norm(
12895                    &sl.o,
12896                    layer.post_attn_norm.float_data(),
12897                    &mut sl.cur,
12898                    n_embd,
12899                    1,
12900                    eps,
12901                )?;
12902            }
12903            let next_norm = if il + 1 < n_layers {
12904                Some(self.layers[il + 1].attn_norm.float_data())
12905            } else {
12906                None
12907            };
12908            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
12909            std::mem::swap(&mut sl.x, &mut sl.xn);
12910        }
12911        e.rms_norm(
12912            &sl.x,
12913            self.output_norm.float_data(),
12914            &mut sl.hn,
12915            n_embd,
12916            1,
12917            eps,
12918        )?;
12919        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
12920        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
12921        {
12922            let (zq, zd) = (&sl.zq, &sl.zd);
12923            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
12924            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
12925            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
12926        }
12927        self.gemma4_suppress(e, &mut sl.logits, 1)?;
12928        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
12929        if let Some((ring, base)) = ring {
12930            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
12931            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
12932            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
12933            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
12934        }
12935        e.inc_seqlen(pos_d)?;
12936        if cap_bucket_max.is_none() {
12937            cache.pos += 1;
12938        }
12939        Ok(())
12940    }
12941
12942    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
12943    #[allow(clippy::too_many_arguments)]
12944    fn gemma4_decode_attn_dc_slotted(
12945        &self,
12946        e: &Engine,
12947        fa: &crate::hybrid::FullAttnLayer,
12948        il: usize,
12949        pos_d: &CudaSlice<i32>,
12950        cache: &mut Cache,
12951        cap_bucket_max: Option<(usize, usize)>,
12952        sl: &mut G4DcSlots,
12953    ) -> Result<(), Box<dyn std::error::Error>> {
12954        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12955        let eps = self.cfg.rms_eps;
12956        let aux = self.gemma4_aux.as_ref().unwrap();
12957        let ones = aux.ones(e);
12958        #[cfg(debug_assertions)]
12959        crate::debug_assert_tensor_stream_device(
12960            ones,
12961            &e.stream(),
12962            "gemma4_decode_attn_dc_slotted.ones",
12963        );
12964        {
12965            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
12966            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
12967            if swa {
12968                if !e.matmul_q4_fused3_into(
12969                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
12970                )? {
12971                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
12972                    // (q,k) pair, v through the generic m1 slot matvec — the same two
12973                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
12974                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12975                    {
12976                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
12977                    } else {
12978                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
12979                    }
12980                }
12981            } else {
12982                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12983                    && !e
12984                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12985                {
12986                    return Err("slotted step: fused2 unavailable".into());
12987                }
12988                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
12989                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
12990            }
12991        }
12992        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
12993        // kernel-for-kernel (graph stream-identity gate).
12994        let ff = if swa {
12995            None
12996        } else {
12997            Some(
12998                aux.rope_freqs(e)
12999                    .expect("gemma4 global rope needs rope_freqs.weight"),
13000            )
13001        };
13002        #[cfg(debug_assertions)]
13003        if let Some(ff) = ff {
13004            crate::debug_assert_tensor_stream_device(
13005                ff,
13006                &e.stream(),
13007                "gemma4_decode_attn_dc_slotted.rope_freqs",
13008            );
13009        }
13010        let kvl = cache.kv[il].as_mut().unwrap();
13011        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13012        if crate::Engine::qkv_append_on() {
13013            // append fold (2026-07-23): mirrors dc_into.
13014            e.rms_norm_qkv_rope_append_dc(
13015                &sl.q0,
13016                &sl.k0,
13017                &sl.v0,
13018                fa.q_norm.float_data(),
13019                fa.k_norm.float_data(),
13020                ones,
13021                &mut sl.q,
13022                &mut sl.k,
13023                &mut sl.v,
13024                hd,
13025                self.gemma4_rope_dims(il),
13026                nh,
13027                nkv,
13028                pos_d,
13029                nh,
13030                nkv,
13031                base,
13032                1.0,
13033                ff,
13034                eps,
13035                &mut kvl.k,
13036                &mut kvl.v,
13037                &kvl.len_d,
13038                kvl.k_tok_bytes,
13039                kvl.v_tok_bytes,
13040                kv_fp8,
13041            )?;
13042        } else {
13043            e.rms_norm_qkv_rope(
13044                &sl.q0,
13045                &sl.k0,
13046                &sl.v0,
13047                fa.q_norm.float_data(),
13048                fa.k_norm.float_data(),
13049                ones,
13050                &mut sl.q,
13051                &mut sl.k,
13052                &mut sl.v,
13053                hd,
13054                self.gemma4_rope_dims(il),
13055                nh,
13056                nkv,
13057                pos_d,
13058                nh,
13059                nkv,
13060                base,
13061                1.0,
13062                ff,
13063                eps,
13064            )?;
13065            e.append_kv_quantized_dc(
13066                &sl.k,
13067                &sl.v,
13068                &mut kvl.k,
13069                &mut kvl.v,
13070                &kvl.len_d,
13071                kvl.kv_dim_k,
13072                kvl.kv_dim_v,
13073                kvl.k_tok_bytes,
13074                kvl.v_tok_bytes,
13075                kv_fp8,
13076            )?;
13077        }
13078        e.inc_seqlen(&mut kvl.len_d)?;
13079        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13080        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13081        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13082        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13083        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13084        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13085        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13086        // the dc_into arm branch-for-branch (stream gate).
13087        let mut fa_q8 = false;
13088        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13089            e.fa_decode_rows(
13090                &sl.q,
13091                &k_view,
13092                &v_view,
13093                &mut sl.attn,
13094                hd,
13095                nh,
13096                nkv,
13097                b_glob - 1,
13098                1,
13099                scale,
13100                kvl.k_tok_bytes,
13101                kvl.v_tok_bytes,
13102                Some((&kvl.len_d, -1)),
13103                false,
13104                false,
13105                Some((&mut sl.zq, &mut sl.zd)),
13106            )?;
13107            fa_q8 = true;
13108        } else if swa && b_swa > win && hd == 256 && rows_on {
13109            e.fa_decode_rows_w(
13110                &sl.q,
13111                &k_view,
13112                &v_view,
13113                &mut sl.attn,
13114                hd,
13115                nh,
13116                nkv,
13117                &kvl.len_d,
13118                -1,
13119                1,
13120                scale,
13121                win,
13122                kvl.k_tok_bytes,
13123                kvl.v_tok_bytes,
13124                Some((&mut sl.zq, &mut sl.zd)),
13125            )?;
13126            fa_q8 = true;
13127        } else {
13128            let b = if swa { b_swa } else { b_glob };
13129            e.fa_decode_dc(
13130                &sl.q,
13131                &k_view,
13132                &v_view,
13133                &mut sl.attn,
13134                hd,
13135                nh,
13136                nkv,
13137                &kvl.len_d,
13138                b,
13139                scale,
13140                kvl.k_tok_bytes,
13141                kvl.v_tok_bytes,
13142                swa && crate::Engine::wkv_on(),
13143            )?;
13144        }
13145        if !fa_q8 {
13146            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13147            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13148        }
13149        {
13150            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13151            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13152            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13153        }
13154        Ok(())
13155    }
13156
13157    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13158    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13159    fn gemma4_layer_tail_slotted(
13160        &self,
13161        e: &Engine,
13162        layer: &crate::hybrid::HybridLayer,
13163        next_norm: Option<&CudaSlice<f32>>,
13164        sl: &mut G4DcSlots,
13165    ) -> Result<(), Box<dyn std::error::Error>> {
13166        let n_embd = self.cfg.n_embd as usize;
13167        let eps = self.cfg.rms_eps;
13168        let bits = layer.gemma4.as_ref().unwrap();
13169        let crate::hybrid::Ffn::Dense {
13170            ffn_gate,
13171            ffn_up,
13172            ffn_down,
13173        } = &layer.ffn
13174        else {
13175            return Err("slotted tail: dense ffn only".into());
13176        };
13177        let pnfold = Engine::g4_pnfold_on();
13178        if pnfold {
13179            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13180            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13181            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13182            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13183            e.rms_pre_add_rms_norm_q8z_into(
13184                or,
13185                layer.post_attn_norm.float_data(),
13186                xr,
13187                bits.ffn_norm.float_data(),
13188                &mut sl.attn_out,
13189                &mut sl.zsh,
13190                n_embd,
13191                1,
13192                eps,
13193                &mut sl.zq,
13194                &mut sl.zd,
13195            )?;
13196        } else {
13197            e.add_rms_norm(
13198                &sl.cur,
13199                &sl.x,
13200                bits.ffn_norm.float_data(),
13201                &mut sl.attn_out,
13202                &mut sl.zsh,
13203                n_embd,
13204                1,
13205                eps,
13206            )?;
13207        }
13208        let n_ff = ffn_gate.out_features();
13209        if !pnfold {
13210            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13211            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13212        }
13213        {
13214            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13215            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13216            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13217                && !e.matmul_nvfp4_fused2_into(
13218                    ffn_gate,
13219                    ffn_up,
13220                    zq,
13221                    zd,
13222                    &mut sl.gate,
13223                    &mut sl.up,
13224                )?
13225            {
13226                return Err("slotted tail: ffn fused2 unavailable".into());
13227            }
13228        }
13229        debug_assert!(e.uses_q8_1_fast(ffn_down));
13230        {
13231            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13232            let upv = e.view(upr, n_ff);
13233            let up_all = upv.slice(0..n_ff);
13234            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13235            e.gelu_tanh_mul_q8_1_into(
13236                gr,
13237                &up_all,
13238                &mut sl.act,
13239                n_ff,
13240                1,
13241                &mut sl.actq,
13242                &mut sl.actd,
13243            )?;
13244        }
13245        {
13246            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13247            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13248            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13249        }
13250        if pnfold {
13251            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13252            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13253            if let Some(w) = next_norm {
13254                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13255                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13256                e.rms_pre_add_scale_rms_norm_q8_1_into(
13257                    f0r,
13258                    bits.post_ffw_norm.float_data(),
13259                    aor,
13260                    bits.layer_scale,
13261                    w,
13262                    &mut sl.xn,
13263                    n_embd,
13264                    1,
13265                    eps,
13266                    &mut sl.hq,
13267                    &mut sl.hd_,
13268                )?;
13269                return Ok(());
13270            }
13271        }
13272        e.rms_norm(
13273            &sl.f0,
13274            bits.post_ffw_norm.float_data(),
13275            &mut sl.sn,
13276            n_embd,
13277            1,
13278            eps,
13279        )?;
13280        match next_norm {
13281            Some(w) => {
13282                e.add_scale_rms_norm_q8_1_into(
13283                    &sl.sn,
13284                    &sl.attn_out,
13285                    bits.layer_scale,
13286                    w,
13287                    &mut sl.xn,
13288                    n_embd,
13289                    1,
13290                    eps,
13291                    &mut sl.hq,
13292                    &mut sl.hd_,
13293                )?;
13294            }
13295            None => {
13296                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13297            }
13298        }
13299        Ok(())
13300    }
13301
13302    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13303    #[allow(clippy::too_many_arguments)]
13304    fn gemma4_decode_attn_dc(
13305        &self,
13306        e: &Engine,
13307        fa: &crate::hybrid::FullAttnLayer,
13308        il: usize,
13309        hq: &CudaSlice<i8>,
13310        hdq: &CudaSlice<f32>,
13311        pos_d: &CudaSlice<i32>,
13312        cache: &mut Cache,
13313        cap_bucket_max: Option<(usize, usize)>,
13314    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13315        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13316        let eps = self.cfg.rms_eps;
13317        let aux = self.gemma4_aux.as_ref().unwrap();
13318        let ones = aux.ones(e);
13319        #[cfg(debug_assertions)]
13320        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13321        let (q0, k0, v0) = if swa {
13322            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13323                Some(t3) => t3,
13324                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13325                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13326                    Some((q0, k0)) => {
13327                        let h0 = e.zeros(0)?;
13328                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13329                        (q0, k0, v0)
13330                    }
13331                    None => {
13332                        let h0 = e.zeros(0)?;
13333                        (
13334                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13335                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13336                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13337                        )
13338                    }
13339                },
13340            }
13341        } else {
13342            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13343                Some(p) => p,
13344                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13345                    Some(p) => p,
13346                    None => {
13347                        let h0 = e.zeros(0)?;
13348                        (
13349                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13350                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13351                        )
13352                    }
13353                },
13354            };
13355            let v0 = e.clone_dtod(&k0)?;
13356            (q0, k0, v0)
13357        };
13358        let mut q = e.uninit(nh * hd)?;
13359        let mut k = e.uninit(nkv * hd)?;
13360        let mut v = e.uninit(nkv * hd)?;
13361        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13362        let ff = if swa {
13363            None
13364        } else {
13365            Some(
13366                aux.rope_freqs(e)
13367                    .expect("gemma4 global rope needs rope_freqs.weight"),
13368            )
13369        };
13370        #[cfg(debug_assertions)]
13371        if let Some(ff) = ff {
13372            crate::debug_assert_tensor_stream_device(
13373                ff,
13374                &e.stream(),
13375                "gemma4_decode_attn_dc.rope_freqs",
13376            );
13377        }
13378        let kvl = cache.kv[il].as_mut().unwrap();
13379        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13380        if crate::Engine::qkv_append_on() {
13381            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13382            e.rms_norm_qkv_rope_append_dc(
13383                &q0,
13384                &k0,
13385                &v0,
13386                fa.q_norm.float_data(),
13387                fa.k_norm.float_data(),
13388                ones,
13389                &mut q,
13390                &mut k,
13391                &mut v,
13392                hd,
13393                self.gemma4_rope_dims(il),
13394                nh,
13395                nkv,
13396                pos_d,
13397                nh,
13398                nkv,
13399                base,
13400                1.0,
13401                ff,
13402                eps,
13403                &mut kvl.k,
13404                &mut kvl.v,
13405                &kvl.len_d,
13406                kvl.k_tok_bytes,
13407                kvl.v_tok_bytes,
13408                kv_fp8,
13409            )?;
13410        } else {
13411            e.rms_norm_qkv_rope(
13412                &q0,
13413                &k0,
13414                &v0,
13415                fa.q_norm.float_data(),
13416                fa.k_norm.float_data(),
13417                ones,
13418                &mut q,
13419                &mut k,
13420                &mut v,
13421                hd,
13422                self.gemma4_rope_dims(il),
13423                nh,
13424                nkv,
13425                pos_d,
13426                nh,
13427                nkv,
13428                base,
13429                1.0,
13430                ff,
13431                eps,
13432            )?;
13433            e.append_kv_quantized_dc(
13434                &k,
13435                &v,
13436                &mut kvl.k,
13437                &mut kvl.v,
13438                &kvl.len_d,
13439                kvl.kv_dim_k,
13440                kvl.kv_dim_v,
13441                kvl.k_tok_bytes,
13442                kvl.v_tok_bytes,
13443                kv_fp8,
13444            )?;
13445        }
13446        e.inc_seqlen(&mut kvl.len_d)?;
13447        let mut attn = e.uninit(nh * hd)?;
13448        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13449        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13450        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13451        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13452        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13453        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13454        // (gemma4_e4b_attn, +0.65% valid window).
13455        match cap_bucket_max {
13456            None => {
13457                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13458                // decode (SWA layers attend the last `sliding_window` keys); the device
13459                // counters carry only the append slot + the graph seam.
13460                kvl.len += 1;
13461                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13462                if !swa
13463                    && hd == 512
13464                    && kvl.len >= crate::fa512_min_tkv()
13465                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13466                {
13467                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13468                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13469                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13470                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13471                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13472                    e.fa_decode_rows(
13473                        &q,
13474                        &kp,
13475                        &vp,
13476                        &mut attn,
13477                        hd,
13478                        nh,
13479                        nkv,
13480                        kvl.len - 1,
13481                        1,
13482                        scale,
13483                        kvl.k_tok_bytes,
13484                        kvl.v_tok_bytes,
13485                        Some((&kvl.len_d, -1)),
13486                        false,
13487                        false,
13488                        Some((&mut aq8, &mut ad8)),
13489                    )?;
13490                    fa_q8 = Some((aq8, ad8));
13491                } else if swa
13492                    && kvl.len > win
13493                    && hd == 256
13494                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13495                {
13496                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13497                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13498                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13499                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13500                    e.fa_decode_rows_w(
13501                        &q,
13502                        &kp,
13503                        &vp,
13504                        &mut attn,
13505                        hd,
13506                        nh,
13507                        nkv,
13508                        &kvl.len_d,
13509                        -1,
13510                        1,
13511                        scale,
13512                        win,
13513                        kvl.k_tok_bytes,
13514                        kvl.v_tok_bytes,
13515                        Some((&mut aq8, &mut ad8)),
13516                    )?;
13517                    fa_q8 = Some((aq8, ad8));
13518                } else {
13519                    let (off_tok, t_kv) = if swa && kvl.len > win {
13520                        (kvl.len - win, win)
13521                    } else {
13522                        (0, kvl.len)
13523                    };
13524                    let k_view = e.view_u8_range(
13525                        &kvl.k,
13526                        off_tok * kvl.k_tok_bytes,
13527                        (off_tok + t_kv) * kvl.k_tok_bytes,
13528                    );
13529                    let v_view = e.view_u8_range(
13530                        &kvl.v,
13531                        off_tok * kvl.v_tok_bytes,
13532                        (off_tok + t_kv) * kvl.v_tok_bytes,
13533                    );
13534                    e.fa_decode_kvmod(
13535                        &q,
13536                        &k_view,
13537                        &v_view,
13538                        &mut attn,
13539                        hd,
13540                        nh,
13541                        nkv,
13542                        t_kv,
13543                        scale,
13544                        kvl.k_tok_bytes,
13545                        kvl.v_tok_bytes,
13546                        swa && crate::Engine::wkv_on(),
13547                    )?;
13548                }
13549            }
13550            Some((b_swa, b_glob)) => {
13551                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13552                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13553                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13554                // the RUNG max for the rows family (kernels derive per-replay splits from
13555                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13556                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13557                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13558                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13559                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13560                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13561                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13562                    e.fa_decode_rows(
13563                        &q,
13564                        &k_view,
13565                        &v_view,
13566                        &mut attn,
13567                        hd,
13568                        nh,
13569                        nkv,
13570                        b_glob - 1,
13571                        1,
13572                        scale,
13573                        kvl.k_tok_bytes,
13574                        kvl.v_tok_bytes,
13575                        Some((&kvl.len_d, -1)),
13576                        false,
13577                        false,
13578                        Some((&mut aq8, &mut ad8)),
13579                    )?;
13580                    fa_q8 = Some((aq8, ad8));
13581                } else if swa && b_swa > win && hd == 256 && rows_on {
13582                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13583                    e.fa_decode_rows_w(
13584                        &q,
13585                        &k_view,
13586                        &v_view,
13587                        &mut attn,
13588                        hd,
13589                        nh,
13590                        nkv,
13591                        &kvl.len_d,
13592                        -1,
13593                        1,
13594                        scale,
13595                        win,
13596                        kvl.k_tok_bytes,
13597                        kvl.v_tok_bytes,
13598                        Some((&mut aq8, &mut ad8)),
13599                    )?;
13600                    fa_q8 = Some((aq8, ad8));
13601                } else {
13602                    let b = if swa { b_swa } else { b_glob };
13603                    e.fa_decode_dc(
13604                        &q,
13605                        &k_view,
13606                        &v_view,
13607                        &mut attn,
13608                        hd,
13609                        nh,
13610                        nkv,
13611                        &kvl.len_d,
13612                        b,
13613                        scale,
13614                        kvl.k_tok_bytes,
13615                        kvl.v_tok_bytes,
13616                        swa && crate::Engine::wkv_on(),
13617                    )?;
13618                }
13619            }
13620        }
13621        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
13622        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
13623        if let Some((aq8, ad8)) = fa_q8 {
13624            let mut y = e.uninit(fa.wo.out_features())?;
13625            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
13626            return Ok(y);
13627        }
13628        Ok(e.matmul(&fa.wo, &attn, 1)?)
13629    }
13630
13631    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
13632    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
13633    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
13634    /// views in-graph); caller gates and falls back to the dc-eager loop.
13635    pub fn gemma4_generate_graph(
13636        &self,
13637        e: &Engine,
13638        prompt_pos: usize,
13639        first_token: u32,
13640        cache: &mut Cache,
13641        max_new: usize,
13642        eos: &[u32],
13643        mut on_token: impl FnMut(u32) -> bool,
13644    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
13645        if self.is_gemma4_e4b() {
13646            return Err(
13647                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
13648                    .into(),
13649            );
13650        }
13651        use crate::decode::StopReason;
13652        let n_vocab = self.output.out_features();
13653        let n_embd = self.cfg.n_embd as usize;
13654        let embd_gpu = self
13655            .embd_gpu
13656            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13657        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13658        for kvl in cache.kv.iter_mut().flatten() {
13659            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
13660        }
13661        let mut token_d = e.stream().clone_htod(&[first_token])?;
13662        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
13663        let g4 = self.cfg.gemma4.as_ref().unwrap();
13664        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
13665        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
13666        let nkv_s = g4
13667            .head_count_kv
13668            .iter()
13669            .zip(g4.swa_pattern.iter())
13670            .find(|p| *p.1)
13671            .map(|p| *p.0 as usize)
13672            .unwrap_or(8);
13673        let nkv_g = g4
13674            .head_count_kv
13675            .iter()
13676            .zip(g4.swa_pattern.iter())
13677            .find(|p| !*p.1)
13678            .map(|p| *p.0 as usize)
13679            .unwrap_or(2);
13680        let mut graphs: std::collections::HashMap<
13681            ((bool, usize), (bool, usize), bool, bool),
13682            (
13683                cudarc::driver::CudaGraph,
13684                Vec<Box<dyn std::any::Any + Send>>,
13685            ),
13686        > = Default::default();
13687        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
13688        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
13689        let mut slots = self.g4_dc_slots(e)?;
13690        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
13691        // baked at the door entry (the modulo keeps every capture valid indefinitely).
13692        const RING: usize = 64;
13693        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
13694        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
13695        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
13696        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
13697        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
13698        const DRAIN: usize = 1;
13699        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
13700        let ring_base = prompt_pos;
13701        let mut out = Vec::with_capacity(max_new);
13702        let mut reason = StopReason::MaxNew;
13703        let mut next = first_token;
13704        let mut captures = 0usize;
13705        for _ in 0..max_new {
13706            out.push(next);
13707            if eos.contains(&next) {
13708                reason = StopReason::Eos;
13709                break;
13710            }
13711            if !on_token(next) {
13712                reason = StopReason::Callback;
13713                break;
13714            }
13715            let t_kv = cache.pos + 1;
13716            // Bucket key per ARM (graph arc step 3):
13717            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
13718            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
13719            //    the component collapses to a single marker).
13720            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
13721            //    at/above it — the kernel derives splits from len_d per replay, so buckets
13722            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
13723            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13724            let f512 = crate::fa512_min_tkv();
13725            let key_s = if t_kv > win {
13726                (true, usize::MAX)
13727            } else {
13728                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
13729            };
13730            let (key_g, rung_end) = if t_kv >= f512 {
13731                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
13732                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
13733                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
13734                ((true, end), end)
13735            } else {
13736                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
13737            };
13738            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
13739            if !graphs.contains_key(&key) {
13740                let bucket_max = (t_kv, rung_end);
13741                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
13742                let snap = cache.snapshot(e)?;
13743                let pos_save = e.dtoh_i32_one(&pos_d)?;
13744                let len_save: Vec<Option<i32>> = cache
13745                    .kv
13746                    .iter()
13747                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
13748                    .collect();
13749                let tok_save = e.dtoh_u32_one(&token_d)?;
13750                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
13751                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
13752                // regression class, and this door's measured -8.8%. The keeper pins warmup
13753                // transients so the captured graph holds kernel nodes only.
13754                let graph = {
13755                    let tok_ref = &mut token_d;
13756                    let pos_ref = &mut pos_d;
13757                    let cache_ref = &mut *cache;
13758                    let slots_ref = &mut slots;
13759                    let ring_ref = &mut ring;
13760                    e.capture_graph_retained_flags(
13761                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
13762                        |e| {
13763                        // self-feeding: the argmax writes token_d itself.
13764                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
13765                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
13766                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
13767                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
13768                                                           cache_ref, n_vocab, Some(bucket_max),
13769                                                           sl, tok_ref, Some((rg, ring_base)))
13770                    })?
13771                };
13772                cache.rollback(e, &snap, 0)?;
13773                e.set_i32_one(&mut pos_d, pos_save)?;
13774                for (il, ls) in len_save.iter().enumerate() {
13775                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
13776                        e.set_i32_one(&mut kvl.len_d, *v)?;
13777                    }
13778                }
13779                e.set_u32_one(&mut token_d, tok_save)?;
13780                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
13781                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
13782                        eprintln!("[graph-census] {c:?}");
13783                    }
13784                }
13785                graphs.insert(key, graph);
13786                captures += 1;
13787            }
13788            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
13789            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
13790            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
13791            // the budget; capture warmups already emitted their tokens through the ring.
13792            let mut chunk = 1usize;
13793            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
13794                .ok()
13795                .and_then(|v| v.parse().ok())
13796                .unwrap_or(DRAIN);
13797            while chunk < drain_cap && out.len() + chunk < max_new {
13798                let t_next = cache.pos + 1 + chunk;
13799                let key_s2 = if t_next > win {
13800                    (true, usize::MAX)
13801                } else {
13802                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
13803                };
13804                let key_g2 = if t_next >= f512 {
13805                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
13806                } else {
13807                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
13808                };
13809                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
13810                    break;
13811                }
13812                chunk += 1;
13813            }
13814            let g = &graphs.get(&key).unwrap().0;
13815            for _ in 0..chunk {
13816                g.launch()?;
13817            }
13818            e.stream().synchronize()?;
13819            let ringh = e.dtoh_u32(&ring)?;
13820            for j in 0..chunk {
13821                let pos_j = cache.pos + j;
13822                let tok_j = ringh[(pos_j - ring_base) % RING];
13823                cache.pos += 0; // advanced below in one shot
13824                if j + 1 == chunk {
13825                    next = tok_j;
13826                } else {
13827                    out.push(tok_j);
13828                    if eos.contains(&tok_j) || !on_token(tok_j) {
13829                        reason = if eos.contains(&tok_j) {
13830                            StopReason::Eos
13831                        } else {
13832                            StopReason::Callback
13833                        };
13834                        // roll device/host state back to the stop point.
13835                        let keep = cache.pos + j + 1;
13836                        e.set_i32_one(&mut pos_d, keep as i32)?;
13837                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13838                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
13839                            kvl.len = keep;
13840                        }
13841                        cache.pos = keep;
13842                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13843                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13844                        }
13845                        return Ok((out, reason));
13846                    }
13847                }
13848            }
13849            cache.pos += chunk;
13850            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13851                kvl.len += chunk;
13852            }
13853        }
13854        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13855            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13856        }
13857        Ok((out, reason))
13858    }
13859
13860    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
13861    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
13862    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
13863    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
13864    /// logits (host) + advances cache.pos by t.
13865    pub(crate) fn gemma4_decode_step_t(
13866        &self,
13867        e: &Engine,
13868        tokens: &[u32],
13869        pos0: usize,
13870        cache: &mut Cache,
13871    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13872        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
13873    }
13874
13875    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
13876    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
13877    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
13878    pub(crate) fn gemma4_decode_step_t_am(
13879        &self,
13880        e: &Engine,
13881        tokens: &[u32],
13882        pos0: usize,
13883        cache: &mut Cache,
13884    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13885        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13886        let t = tokens.len();
13887        let n_vocab = self.output.out_features();
13888        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
13889        for i in 0..t {
13890            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
13891        }
13892        Ok((e.dtoh_u32(&toks)?, hn))
13893    }
13894
13895    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
13896    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
13897    pub(crate) fn gemma4_decode_step_t_am_dev(
13898        &self,
13899        e: &Engine,
13900        tok_d: &CudaSlice<u32>,
13901        t: usize,
13902        pos0: usize,
13903        cache: &mut Cache,
13904    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13905        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
13906        let n_vocab = self.output.out_features();
13907        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13908        for i in 0..t {
13909            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13910        }
13911        Ok((vam, hn))
13912    }
13913
13914    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
13915    /// llama's h_nextn convention).
13916    pub(crate) fn gemma4_decode_step_t_h(
13917        &self,
13918        e: &Engine,
13919        tokens: &[u32],
13920        pos0: usize,
13921        cache: &mut Cache,
13922    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13923        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13924        let t = tokens.len();
13925        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
13926        e.softcap(&mut ld, cap, t * self.output.out_features())?;
13927        Ok((e.dtoh(&ld)?, hn))
13928    }
13929
13930    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
13931    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
13932    pub(crate) fn verify_stream_scratch(
13933        &self,
13934        e: &Engine,
13935        cap: usize,
13936    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
13937        Ok(VerifyStreamScratch {
13938            pos_d: e.htod_i32(&vec![0i32; cap])?,
13939            row_ctrs: (0..cap)
13940                .map(|_| e.htod_i32(&[0]))
13941                .collect::<Result<_, _>>()?,
13942        })
13943    }
13944
13945    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
13946    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
13947    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
13948    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
13949    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
13950    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
13951    /// sync, exactly the turnaround the burst exists to remove.
13952    pub(crate) fn gemma4_verify_t_am_stream(
13953        &self,
13954        e: &Engine,
13955        tok_d: &CudaSlice<u32>,
13956        t: usize,
13957        ctr: &CudaSlice<i32>,
13958        hint: usize,
13959        cache: &mut Cache,
13960        scr: &mut VerifyStreamScratch,
13961    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13962        let n_embd = self.cfg.n_embd as usize;
13963        let eps = self.cfg.rms_eps;
13964        assert!(t <= scr.row_ctrs.len() && t <= 64);
13965        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
13966        for i in 0..t {
13967            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
13968        }
13969        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
13970        let embd_gpu = self
13971            .embd_gpu
13972            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13973        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13974        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
13975        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
13976        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13977        let n_layers = self.layers.len();
13978        for (il, layer) in self.layers.iter().enumerate() {
13979            let (hq, hdq) = match h_carry.take() {
13980                Some(p) => p,
13981                None => {
13982                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
13983                }
13984            };
13985            let Mixer::Full(fa) = &layer.mixer else {
13986                panic!("gemma4 layer {il} not full-attn")
13987            };
13988            let o = self
13989                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
13990            let next_norm = if il + 1 < n_layers {
13991                Some(self.layers[il + 1].attn_norm.float_data())
13992            } else {
13993                None
13994            };
13995            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
13996            x = xn;
13997            h_carry = hn;
13998            self.dflash_tap(e, cache, il, &x, t)?;
13999        }
14000        let mut hn = e.uninit(t * n_embd)?;
14001        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14002        let ld = e.matmul(&self.output, &hn, t)?;
14003        let n_vocab = self.output.out_features();
14004        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14005        for i in 0..t {
14006            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14007        }
14008        Ok((vam, hn))
14009    }
14010
14011    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
14012    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
14013    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
14014    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
14015    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
14016    /// kernel later if it shows in the profile).
14017    pub(crate) fn dflash_tap(
14018        &self,
14019        e: &Engine,
14020        cache: &mut Cache,
14021        il: usize,
14022        x: &CudaSlice<f32>,
14023        t: usize,
14024    ) -> Result<(), Box<dyn std::error::Error>> {
14025        let Some(taps) = cache.dflash_taps.as_mut() else {
14026            return Ok(());
14027        };
14028        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14029            return Ok(());
14030        };
14031        let h = taps.hidden;
14032        let n_taps = taps.layer_ids.len();
14033        let base = taps.base;
14034        debug_assert!(
14035            base + t <= taps.t,
14036            "tap window {base}+{t} exceeds sink {}",
14037            taps.t
14038        );
14039        let xv = e.view(x, t * h);
14040        for r in 0..t {
14041            let row = xv.slice(r * h..(r + 1) * h);
14042            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14043        }
14044        Ok(())
14045    }
14046
14047    fn gemma4_verify_trunk(
14048        &self,
14049        e: &Engine,
14050        tokens: &[u32],
14051        pos0: usize,
14052        cache: &mut Cache,
14053        tok_dev: Option<&CudaSlice<u32>>,
14054    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14055        let n_embd = self.cfg.n_embd as usize;
14056        let eps = self.cfg.rms_eps;
14057        let t = tokens.len();
14058        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14059        let pos_d = e.htod_i32(&pos)?;
14060        let mut x = match tok_dev {
14061            Some(td) => {
14062                let embd_gpu = self
14063                    .embd_gpu
14064                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14065                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14066                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14067            }
14068            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14069        };
14070        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14071        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14072        let n_layers = self.layers.len();
14073        for (il, layer) in self.layers.iter().enumerate() {
14074            let (hq, hdq) = match h_carry.take() {
14075                Some(p) => p,
14076                None => {
14077                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14078                }
14079            };
14080            let Mixer::Full(fa) = &layer.mixer else {
14081                panic!("gemma4 layer {il} not full-attn")
14082            };
14083            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14084            let next_norm = if il + 1 < n_layers {
14085                Some(self.layers[il + 1].attn_norm.float_data())
14086            } else {
14087                None
14088            };
14089            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14090            x = xn;
14091            h_carry = hn;
14092            self.dflash_tap(e, cache, il, &x, t)?;
14093        }
14094        let mut hn = e.uninit(t * n_embd)?;
14095        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14096        let mut ld = e.matmul(&self.output, &hn, t)?;
14097        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14098        cache.pos += t;
14099        Ok((ld, hn))
14100    }
14101
14102    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14103    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14104    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14105    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14106    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14107    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14108    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14109    #[allow(clippy::too_many_arguments)]
14110    fn gemma4_verify_attn_stream(
14111        &self,
14112        e: &Engine,
14113        fa: &crate::hybrid::FullAttnLayer,
14114        il: usize,
14115        hq: &CudaSlice<i8>,
14116        hdq: &CudaSlice<f32>,
14117        pos_d: &CudaSlice<i32>,
14118        t: usize,
14119        cache: &mut Cache,
14120        hint: usize,
14121        row_ctrs: &[CudaSlice<i32>],
14122    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14123        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14124        let eps = self.cfg.rms_eps;
14125        let aux = self.gemma4_aux.as_ref().unwrap();
14126        let ones = aux.ones(e);
14127        #[cfg(debug_assertions)]
14128        crate::debug_assert_tensor_stream_device(
14129            ones,
14130            &e.stream(),
14131            "gemma4_verify_attn_stream.ones",
14132        );
14133        let h0 = e.zeros(0)?;
14134        let h = &h0;
14135        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14136        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14137        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14138        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14139        let fused_qkv = if f2b {
14140            if swa {
14141                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14142                    .map(|(a, b, c)| (a, b, Some(c)))
14143            } else {
14144                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14145                    .map(|(a, b)| (a, b, None))
14146            }
14147        } else {
14148            None
14149        };
14150        let (q0, k0, v0) = match fused_qkv {
14151            Some((a, b, cv)) => {
14152                let v = match cv {
14153                    Some(c) => c,
14154                    None => e.clone_dtod(&b)?,
14155                };
14156                (a, b, v)
14157            }
14158            None => {
14159                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14160                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14161                let v0 = if swa {
14162                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14163                } else {
14164                    e.clone_dtod(&k0)?
14165                };
14166                (q0, k0, v0)
14167            }
14168        };
14169        let mut q = e.uninit(t * nh * hd)?;
14170        let mut k = e.uninit(t * nkv * hd)?;
14171        let mut v = e.uninit(t * nkv * hd)?;
14172        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14173        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14174        let ff = if swa {
14175            None
14176        } else {
14177            Some(
14178                aux.rope_freqs(e)
14179                    .expect("gemma4 global rope needs rope_freqs.weight"),
14180            )
14181        };
14182        #[cfg(debug_assertions)]
14183        if let Some(ff) = ff {
14184            crate::debug_assert_tensor_stream_device(
14185                ff,
14186                &e.stream(),
14187                "gemma4_verify_attn_stream.rope_freqs",
14188            );
14189        }
14190        e.rms_norm_qkv_rope(
14191            &q0,
14192            &k0,
14193            &v0,
14194            fa.q_norm.float_data(),
14195            fa.k_norm.float_data(),
14196            ones,
14197            &mut q,
14198            &mut k,
14199            &mut v,
14200            hd,
14201            self.gemma4_rope_dims(il),
14202            nh * t,
14203            nkv * t,
14204            pos_d,
14205            nh,
14206            nkv,
14207            base,
14208            1.0,
14209            ff,
14210            eps,
14211        )?;
14212        let kvl = cache.kv[il].as_mut().unwrap();
14213        // append at the DEVICE slot; the counter advances by t on-device.
14214        e.append_kv_quantized_rows_dc(
14215            &k,
14216            &v,
14217            &mut kvl.k,
14218            &mut kvl.v,
14219            &kvl.len_d,
14220            t,
14221            kvl.kv_dim_k,
14222            kvl.kv_dim_v,
14223            kvl.k_tok_bytes,
14224            kvl.v_tok_bytes,
14225            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14226        )?;
14227        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14228        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14229        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14230        let mut attn = e.uninit(t * nh * hd)?;
14231        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14232        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14233        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14234        // and a stable window regime — the same rung/regime keys as the draft graph).
14235        if swa && hint + 1 >= win {
14236            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14237            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14238            e.fa_decode_rows_w(
14239                &q,
14240                &k_view,
14241                &v_view,
14242                &mut attn,
14243                hd,
14244                nh,
14245                nkv,
14246                &kvl.len_d,
14247                0,
14248                t,
14249                scale,
14250                win,
14251                kvl.k_tok_bytes,
14252                kvl.v_tok_bytes,
14253                None,
14254            )?;
14255        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14256            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14257            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14258            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14259            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14260            // for every row.
14261            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14262            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14263            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14264            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14265            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14266            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14267            // any bucket >= the live length is exact.
14268            let bucket = (hint + t + 2)
14269                .next_power_of_two()
14270                .min(crate::fa512_min_tkv().saturating_sub(1));
14271            let qv = e.view(&q, t * nh * hd);
14272            for i in 0..t {
14273                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14274                let mut q_one = e.uninit(nh * hd)?;
14275                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14276                let mut a_one = e.uninit(nh * hd)?;
14277                e.fa_decode_dc(
14278                    &q_one,
14279                    &k_view,
14280                    &v_view,
14281                    &mut a_one,
14282                    hd,
14283                    nh,
14284                    nkv,
14285                    &row_ctrs[i],
14286                    bucket,
14287                    scale,
14288                    kvl.k_tok_bytes,
14289                    kvl.v_tok_bytes,
14290                    false,
14291                )?;
14292                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14293            }
14294        } else if hd == 512 {
14295            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14296            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14297            e.fa_decode_rows(
14298                &q,
14299                &k_view,
14300                &v_view,
14301                &mut attn,
14302                hd,
14303                nh,
14304                nkv,
14305                hint,
14306                t,
14307                scale,
14308                kvl.k_tok_bytes,
14309                kvl.v_tok_bytes,
14310                Some((&kvl.len_d, 0)),
14311                false,
14312                false,
14313                None,
14314            )?;
14315        } else {
14316            // hd256 under-window: v4 device-len rows twin.
14317            e.fa_decode_rows_dc(
14318                &q,
14319                &k_view,
14320                &v_view,
14321                &mut attn,
14322                hd,
14323                nh,
14324                nkv,
14325                &kvl.len_d,
14326                hint + t,
14327                t,
14328                scale,
14329                kvl.k_tok_bytes,
14330                kvl.v_tok_bytes,
14331                0,
14332                swa && crate::Engine::wkv_on(),
14333            )?;
14334        }
14335        Ok(e.matmul(&fa.wo, &attn, t)?)
14336    }
14337
14338    fn gemma4_verify_attn(
14339        &self,
14340        e: &Engine,
14341        fa: &crate::hybrid::FullAttnLayer,
14342        il: usize,
14343        hq: &CudaSlice<i8>,
14344        hdq: &CudaSlice<f32>,
14345        pos_d: &CudaSlice<i32>,
14346        t: usize,
14347        cache: &mut Cache,
14348    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14349        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14350        let eps = self.cfg.rms_eps;
14351        let aux = self.gemma4_aux.as_ref().unwrap();
14352        let ones = aux.ones(e);
14353        #[cfg(debug_assertions)]
14354        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14355        let n_embd = self.cfg.n_embd as usize;
14356        let _ = n_embd;
14357
14358        let h0 = e.zeros(0)?;
14359        let h = &h0;
14360        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14361        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14362        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14363        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14364        let fused_qkv = if f2b {
14365            if swa {
14366                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14367                    .map(|(a, b, c)| (a, b, Some(c)))
14368            } else {
14369                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14370                    .map(|(a, b)| (a, b, None))
14371            }
14372        } else {
14373            None
14374        };
14375        let (q0, k0, v0) = match fused_qkv {
14376            Some((a, b, cv)) => {
14377                let v = match cv {
14378                    Some(c) => c,
14379                    None => e.clone_dtod(&b)?,
14380                };
14381                (a, b, v)
14382            }
14383            None => {
14384                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14385                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14386                let v0 = if swa {
14387                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14388                } else {
14389                    e.clone_dtod(&k0)?
14390                };
14391                (q0, k0, v0)
14392            }
14393        };
14394        let mut q = e.uninit(t * nh * hd)?;
14395        let mut k = e.uninit(t * nkv * hd)?;
14396        let mut v = e.uninit(t * nkv * hd)?;
14397        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14398        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14399        let ff = if swa {
14400            None
14401        } else {
14402            Some(
14403                aux.rope_freqs(e)
14404                    .expect("gemma4 global rope needs rope_freqs.weight"),
14405            )
14406        };
14407        #[cfg(debug_assertions)]
14408        if let Some(ff) = ff {
14409            crate::debug_assert_tensor_stream_device(
14410                ff,
14411                &e.stream(),
14412                "gemma4_verify_attn.rope_freqs",
14413            );
14414        }
14415        e.rms_norm_qkv_rope(
14416            &q0,
14417            &k0,
14418            &v0,
14419            fa.q_norm.float_data(),
14420            fa.k_norm.float_data(),
14421            ones,
14422            &mut q,
14423            &mut k,
14424            &mut v,
14425            hd,
14426            self.gemma4_rope_dims(il),
14427            nh * t,
14428            nkv * t,
14429            pos_d,
14430            nh,
14431            nkv,
14432            base,
14433            1.0,
14434            ff,
14435            eps,
14436        )?;
14437        let kvl = cache.kv[il].as_mut().unwrap();
14438        let base_len = kvl.len;
14439        e.append_kv_quantized_rows(
14440            &k,
14441            &v,
14442            &mut kvl.k,
14443            &mut kvl.v,
14444            base_len,
14445            t,
14446            kvl.kv_dim_k,
14447            kvl.kv_dim_v,
14448            kvl.k_tok_bytes,
14449            kvl.v_tok_bytes,
14450            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14451        )?;
14452        kvl.len += t;
14453        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14454        let mut attn = e.uninit(t * nh * hd)?;
14455        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14456        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14457        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14458            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14459            // decode rides the SAME symbol at t=1 (parity law).
14460            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14461        if rows_ok && (!swa || base_len + t <= win) {
14462            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14463            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14464            if hd == 512 {
14465                // device-len twin: sync the counter to the verify base (async arg-store).
14466                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14467                e.fa_decode_rows(
14468                    &q,
14469                    &k_view,
14470                    &v_view,
14471                    &mut attn,
14472                    hd,
14473                    nh,
14474                    nkv,
14475                    base_len,
14476                    t,
14477                    scale,
14478                    kvl.k_tok_bytes,
14479                    kvl.v_tok_bytes,
14480                    Some((&kvl.len_d, 0)),
14481                    false,
14482                    swa && crate::Engine::wkv_on(),
14483                    None,
14484                )?;
14485            } else {
14486                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14487                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14488                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14489                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14490                e.fa_decode_rows_dc(
14491                    &q,
14492                    &k_view,
14493                    &v_view,
14494                    &mut attn,
14495                    hd,
14496                    nh,
14497                    nkv,
14498                    &kvl.len_d,
14499                    base_len + t,
14500                    t,
14501                    scale,
14502                    kvl.k_tok_bytes,
14503                    kvl.v_tok_bytes,
14504                    0,
14505                    swa && crate::Engine::wkv_on(),
14506                )?;
14507            }
14508            return Ok(e.matmul(&fa.wo, &attn, t)?);
14509        }
14510        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14511        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14512        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14513        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14514        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14515        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14516        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14517        if hd == 256
14518            && swa
14519            && base_len + 1 >= win
14520            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14521        {
14522            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14523            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14524            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14525            e.fa_decode_rows_w(
14526                &q,
14527                &k_view,
14528                &v_view,
14529                &mut attn,
14530                hd,
14531                nh,
14532                nkv,
14533                &kvl.len_d,
14534                0,
14535                t,
14536                scale,
14537                win,
14538                kvl.k_tok_bytes,
14539                kvl.v_tok_bytes,
14540                None,
14541            )?;
14542            return Ok(e.matmul(&fa.wo, &attn, t)?);
14543        }
14544        for i in 0..t {
14545            let avail = base_len + i + 1;
14546            let (off_tok, t_kv) = if swa && avail > win {
14547                (avail - win, win)
14548            } else {
14549                (0, avail)
14550            };
14551            let k_view = e.view_u8_range(
14552                &kvl.k,
14553                off_tok * kvl.k_tok_bytes,
14554                (off_tok + t_kv) * kvl.k_tok_bytes,
14555            );
14556            let v_view = e.view_u8_range(
14557                &kvl.v,
14558                off_tok * kvl.v_tok_bytes,
14559                (off_tok + t_kv) * kvl.v_tok_bytes,
14560            );
14561            let qi = e.view(&q, t * nh * hd);
14562            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14563            let mut q_one = e.uninit(nh * hd)?;
14564            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14565            let mut a_one = e.uninit(nh * hd)?;
14566            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14567            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14568            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14569            if swa
14570                && avail > win
14571                && hd == 256
14572                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14573            {
14574                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14575                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14576                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14577                e.fa_decode_rows_w(
14578                    &q_one,
14579                    &kp,
14580                    &vp,
14581                    &mut a_one,
14582                    hd,
14583                    nh,
14584                    nkv,
14585                    &kvl.len_d,
14586                    0,
14587                    1,
14588                    scale,
14589                    win,
14590                    kvl.k_tok_bytes,
14591                    kvl.v_tok_bytes,
14592                    None,
14593                )?;
14594            } else if !swa
14595                && hd == 512
14596                && avail >= crate::fa512_min_tkv()
14597                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14598            {
14599                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14600                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14601                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14602                e.fa_decode_rows(
14603                    &q_one,
14604                    &kp,
14605                    &vp,
14606                    &mut a_one,
14607                    hd,
14608                    nh,
14609                    nkv,
14610                    avail - 1,
14611                    1,
14612                    scale,
14613                    kvl.k_tok_bytes,
14614                    kvl.v_tok_bytes,
14615                    Some((&kvl.len_d, 0)),
14616                    false,
14617                    false,
14618                    None,
14619                )?;
14620            } else {
14621                e.fa_decode_kvmod(
14622                    &q_one,
14623                    &k_view,
14624                    &v_view,
14625                    &mut a_one,
14626                    hd,
14627                    nh,
14628                    nkv,
14629                    t_kv,
14630                    scale,
14631                    kvl.k_tok_bytes,
14632                    kvl.v_tok_bytes,
14633                    swa && crate::Engine::wkv_on(),
14634                )?;
14635            }
14636            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14637        }
14638        Ok(e.matmul(&fa.wo, &attn, t)?)
14639    }
14640
14641    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
14642    /// h_seed = pre-output_norm hidden). Advances cache.pos.
14643    pub(crate) fn gemma4_decode_step_h(
14644        &self,
14645        e: &Engine,
14646        token: u32,
14647        cache: &mut Cache,
14648    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14649        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
14650        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
14651        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
14652        // unsplit rather than guessing a fence.
14653        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
14654            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
14655        }
14656        if crate::pp::pp_cuts(self.layers.len()).is_some() {
14657            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
14658        }
14659        let n_embd = self.cfg.n_embd as usize;
14660        let eps = self.cfg.rms_eps;
14661        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14662        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14663        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14664        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
14665        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
14666        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14667        let n_layers = self.layers.len();
14668        for (il, layer) in self.layers.iter().enumerate() {
14669            let (hq, hdq) = match h_carry.take() {
14670                Some(p) => p,
14671                None => {
14672                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
14673                }
14674            };
14675            let Mixer::Full(fa) = &layer.mixer else {
14676                panic!("gemma4 layer {il} not full-attn")
14677            };
14678            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
14679            let next_norm = if il + 1 < n_layers {
14680                Some(self.layers[il + 1].attn_norm.float_data())
14681            } else {
14682                None
14683            };
14684            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14685            x = xn;
14686            h_carry = hn;
14687        }
14688        let mut hn = e.uninit(n_embd)?;
14689        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14690        let h_seed = e.clone_dtod(&x)?;
14691        let mut ld = e.matmul(&self.output, &hn, 1)?;
14692        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14693        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
14694        self.gemma4_suppress(e, &mut ld, 1)?;
14695        let logits = e.dtoh(&ld)?;
14696        cache.pos += 1;
14697        Ok((logits, h_seed))
14698    }
14699
14700    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
14701    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
14702    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
14703    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
14704    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
14705    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
14706    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
14707    fn gemma4_decode_layers(
14708        &self,
14709        e: &Engine,
14710        mut x: CudaSlice<f32>,
14711        lo: usize,
14712        hi: usize,
14713        pos_d: &CudaSlice<i32>,
14714        cache: &mut Cache,
14715    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14716        let n_embd = self.cfg.n_embd as usize;
14717        let eps = self.cfg.rms_eps;
14718        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14719        for il in lo..hi {
14720            let layer = &self.layers[il];
14721            let (hq, hdq) = match h_carry.take() {
14722                Some(p) => p,
14723                // range head: il == lo — norm against THIS layer's attn_norm.
14724                None => {
14725                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
14726                }
14727            };
14728            let Mixer::Full(fa) = &layer.mixer else {
14729                panic!("gemma4 layer {il} not full-attn")
14730            };
14731            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
14732            let next_norm = if il + 1 < hi {
14733                Some(self.layers[il + 1].attn_norm.float_data())
14734            } else {
14735                None
14736            };
14737            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14738            x = xn;
14739            h_carry = hn;
14740        }
14741        Ok(x)
14742    }
14743
14744    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
14745    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
14746    /// boundary handoff — same choreography as the generic arm (decode.rs), same
14747    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
14748    /// stage 1 = layers [split, n) + output_norm + softcapped head.
14749    /// Each stage uploads its own copy of the step's position scalar on its own stream.
14750    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
14751    fn gemma4_decode_step_h_pp2(
14752        &self,
14753        e: &Engine,
14754        token: u32,
14755        cache: &mut Cache,
14756        split: usize,
14757    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14758        if crate::pp::pp2_streams_off() {
14759            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
14760        }
14761        let rt = crate::pp::Pp2Rt::get(e)?;
14762        let e0 = rt.engine(0, e);
14763        let e1 = rt.engine(1, e);
14764        let n_embd = self.cfg.n_embd as usize;
14765        let eps = self.cfg.rms_eps;
14766        let pos = cache.pos as i32;
14767
14768        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
14769        let slot = {
14770            let _st0 = rt.enter(0);
14771            let pos_d = e0.htod_i32(&[pos])?;
14772            #[cfg(debug_assertions)]
14773            crate::debug_assert_tensor_stream_device(
14774                &pos_d,
14775                &e0.stream(),
14776                "gemma4_decode_step_h_pp2.stage0.pos_d",
14777            );
14778            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
14779            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14780            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
14781            rt.tx(0, &x, n_embd)?
14782        };
14783
14784        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
14785        let _st1 = rt.enter(1);
14786        let pos_d = e1.htod_i32(&[pos])?;
14787        #[cfg(debug_assertions)]
14788        crate::debug_assert_tensor_stream_device(
14789            &pos_d,
14790            &e1.stream(),
14791            "gemma4_decode_step_h_pp2.stage1.pos_d",
14792        );
14793        let x = rt.rx(0, slot, n_embd)?;
14794        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
14795
14796        let mut hn = e1.uninit(n_embd)?;
14797        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14798        let h_seed = e1.clone_dtod(&x)?;
14799        let mut ld = e1.matmul(&self.output, &hn, 1)?;
14800        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14801        e1.softcap(&mut ld, cap, self.output.out_features())?;
14802        self.gemma4_suppress(e1, &mut ld, 1)?;
14803        let logits = e1.dtoh(&ld)?;
14804        cache.pos += 1;
14805        Ok((logits, h_seed))
14806    }
14807
14808    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
14809    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
14810    fn gemma4_decode_step_h_pp2_samestream(
14811        &self,
14812        e: &Engine,
14813        token: u32,
14814        cache: &mut Cache,
14815        split: usize,
14816    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14817        let n_embd = self.cfg.n_embd as usize;
14818        let eps = self.cfg.rms_eps;
14819        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14820
14821        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
14822        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14823        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14824        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
14825
14826        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
14827        let boundary_tx = e.clone_dtod(&x)?;
14828        let boundary_rx = e.clone_dtod(&boundary_tx)?;
14829
14830        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
14831        let x =
14832            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
14833
14834        let mut hn = e.uninit(n_embd)?;
14835        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14836        let h_seed = e.clone_dtod(&x)?;
14837        let mut ld = e.matmul(&self.output, &hn, 1)?;
14838        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14839        e.softcap(&mut ld, cap, self.output.out_features())?;
14840        self.gemma4_suppress(e, &mut ld, 1)?;
14841        let logits = e.dtoh(&ld)?;
14842        cache.pos += 1;
14843        Ok((logits, h_seed))
14844    }
14845}
14846
14847// ============================ step35 (Step-3.7-Flash) ==================================
14848// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
14849// FAMILY and not a few branches inside the generic `full_attn*` chain:
14850//
14851//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
14852//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
14853//      shapes and the FA head counts would be wrong on 33 of 45 layers.
14854//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
14855//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
14856//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
14857//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
14858//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
14859//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
14860//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
14861//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
14862//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
14863//
14864// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
14865impl HybridModel {
14866    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
14867    /// synthesize a drafter or trunk layer from a neighboring class.
14868    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
14869        let geometry = self
14870            .cfg
14871            .layer_geometry(il as u32)
14872            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
14873        debug_assert_eq!(
14874            geometry.attention_gate,
14875            memra_gguf::config::AttentionGateKind::SeparateHead
14876        );
14877        geometry
14878    }
14879
14880    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
14881    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
14882    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
14883    ///
14884    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
14885    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
14886    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
14887    /// `cache`:
14888    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
14889    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
14890    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
14891    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
14892    ///     contract, lane/chunkinv-flip).
14893    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
14894    ///     q/k/v, no cache side effect.
14895    ///
14896    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
14897    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
14898    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
14899    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
14900    /// still contains must be masked per query. memra's window convention
14901    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
14902    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
14903    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
14904    ///
14905    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
14906    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
14907    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
14908    ///
14909    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
14910    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
14911    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
14912    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
14913    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
14914    /// hidden rows, and the generated text — a function of the chunk size:
14915    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
14916    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
14917    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
14918    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
14919    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
14920    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
14921    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
14922    ///   one-token change in a documented machine-config knob changed the answer.
14923    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
14924    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
14925    /// the same rows moves the logits by ~1.8.
14926    ///
14927    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
14928    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
14929    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
14930    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
14931    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
14932    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
14933    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
14934    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
14935    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
14936    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
14937    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
14938    /// those with t_kv <= win = 512.
14939    #[allow(clippy::too_many_arguments)]
14940    fn step35_attn_pre_wo(
14941        &self,
14942        e: &Engine,
14943        fa: &FullAttnLayer,
14944        mut g3: Vec<CudaSlice<f32>>,
14945        hg: Option<&CudaSlice<f32>>,
14946        gt_pre: Option<&CudaSlice<f32>>,
14947        pos_d: &CudaSlice<i32>,
14948        t: usize,
14949        cache: Option<&mut Cache>,
14950        il: usize,
14951        seq_end: usize,
14952    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14953        let geometry = self.step35_geom(il);
14954        let hd = geometry.head_dim_k as usize;
14955        let nkv = geometry.n_head_kv as usize;
14956        let nh = geometry.n_head as usize;
14957        let rbase = geometry.rope_base;
14958        let scale = geometry.attention_scale();
14959        let swa = geometry.window.is_some();
14960        let eps = self.cfg.rms_eps;
14961        let win = geometry.window.unwrap_or(0) as usize;
14962        let n_rot = geometry.n_rot as usize;
14963
14964        let v = g3.pop().unwrap();
14965        let k0 = g3.pop().unwrap();
14966        let q0 = g3.pop().unwrap();
14967
14968        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
14969        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
14970        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
14971        let mut q = e.uninit(t * nh * hd)?;
14972        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
14973        let mut k = e.uninit(t * nkv * hd)?;
14974        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
14975        let ff = if geometry.rope_factors {
14976            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
14977        } else {
14978            None
14979        };
14980        #[cfg(debug_assertions)]
14981        if let Some(ff) = ff {
14982            crate::debug_assert_tensor_stream_device(
14983                ff,
14984                &e.stream(),
14985                "step35_attn_pre_wo.rope_freqs",
14986            );
14987        }
14988        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
14989
14990        let mut attn = e.uninit(t * nh * hd)?;
14991        match cache {
14992            Some(cache) => {
14993                let base_len = cache.kv[il].as_ref().unwrap().len;
14994                // Read per layer call, never in a measured default.
14995                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
14996                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
14997                let off = if swa {
14998                    let raw = base_len.saturating_sub(win - 1);
14999                    if legacy_tkv || legacy_calllocal {
15000                        raw
15001                    } else {
15002                        raw & !31usize
15003                    }
15004                } else {
15005                    0
15006                };
15007                {
15008                    let kvl = cache.kv[il].as_mut().unwrap();
15009                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
15010                    let write_row = e.prepare_kv_append(kvl, off, t)?;
15011                    e.append_kv_quantized_rows(
15012                        &k,
15013                        &v,
15014                        &mut kvl.k,
15015                        &mut kvl.v,
15016                        write_row,
15017                        t,
15018                        kvl.kv_dim_k,
15019                        kvl.kv_dim_v,
15020                        kvl.k_tok_bytes,
15021                        kvl.v_tok_bytes,
15022                        crate::Engine::kv_fp8_on(),
15023                    )?;
15024                    kvl.len += t;
15025                    let new_len = kvl.len as i32;
15026                    e.set_i32_one(&mut kvl.len_d, new_len)?;
15027                }
15028                let kvl = cache.kv[il].as_ref().unwrap();
15029                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15030                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15031                // unaligned view offset here. Both halves are load-bearing for the canaries:
15032                // on the FA default the predicate arms agree bitwise wherever they can differ
15033                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15034                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15035                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15036                // on the current FA path: its tile grid starts at the chunk/call boundary.
15037                // SWA: trim the view to the oldest key any query in this chunk can reach —
15038                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15039                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15040                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15041                // the VIEW START — so an unaligned off regroups the same absolute keys into
15042                // different tiles at different chunk sizes = different (m,l) rounding =
15043                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15044                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15045                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15046                // size; the <=31 extra leading keys are older than EVERY query's window
15047                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15048                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15049                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15050                // the floor arm's bits do not move either (gated: G2f, battery 2).
15051                let t_kv = base_len + t - off;
15052                let physical = kvl.physical_rows(off, off + t_kv)?;
15053                let k_view = e.view_u8_range(
15054                    &kvl.k,
15055                    physical.start * kvl.k_tok_bytes,
15056                    physical.end * kvl.k_tok_bytes,
15057                );
15058                let v_view = e.view_u8_range(
15059                    &kvl.v,
15060                    physical.start * kvl.v_tok_bytes,
15061                    physical.end * kvl.v_tok_bytes,
15062                );
15063                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15064                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15065                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15066                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15067                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15068                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15069                // construction, so the invariance assertion MUST break under it (the seam whose
15070                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15071                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15072                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15073                // cached (probes flip it in-process). Never on in a measured default run.
15074                let swa_naive = if legacy_tkv {
15075                    t_kv > win
15076                } else {
15077                    seq_end > win
15078                };
15079                if swa && swa_naive {
15080                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15081                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15082                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15083                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15084                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15085                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15086                    // identically to the unwindowed one modulo the mask, which is the point.
15087                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15088                    // selected on `seq_end` like every arm here, so the class is uniform for
15089                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15090                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15091                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15092                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15093                        e.sdpa_naive_w_quantized_view(
15094                            &q,
15095                            &k_view,
15096                            &v_view,
15097                            &mut attn,
15098                            hd,
15099                            nh,
15100                            nkv,
15101                            t,
15102                            t_kv,
15103                            scale,
15104                            true,
15105                            win,
15106                            kvl.k_tok_bytes,
15107                            kvl.v_tok_bytes,
15108                        )?;
15109                    } else {
15110                        e.fa_prefill_view_ws_w_hd128(
15111                            &q,
15112                            &k_view,
15113                            &v_view,
15114                            &mut attn,
15115                            hd,
15116                            nh,
15117                            nkv,
15118                            t,
15119                            t_kv,
15120                            scale,
15121                            true,
15122                            win,
15123                            kvl.k_tok_bytes,
15124                            kvl.v_tok_bytes,
15125                        )?;
15126                    }
15127                } else if std::env::var("MEMRA_NOFA").is_ok() {
15128                    e.sdpa_naive_quantized_view(
15129                        &q,
15130                        &k_view,
15131                        &v_view,
15132                        &mut attn,
15133                        hd,
15134                        nh,
15135                        nkv,
15136                        t,
15137                        t_kv,
15138                        scale,
15139                        true,
15140                        kvl.k_tok_bytes,
15141                        kvl.v_tok_bytes,
15142                    )?;
15143                } else {
15144                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15145                    // reach past the window, so the window mask is a no-op under causal and every
15146                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15147                    // request either way, which is what makes the chunk size arithmetic-free.
15148                    e.fa_prefill_view_ws(
15149                        &q,
15150                        &k_view,
15151                        &v_view,
15152                        &mut attn,
15153                        hd,
15154                        nh,
15155                        nkv,
15156                        t,
15157                        t_kv,
15158                        scale,
15159                        true,
15160                        kvl.k_tok_bytes,
15161                        kvl.v_tok_bytes,
15162                        crate::Engine::kv_fp8_on(),
15163                    )?;
15164                }
15165            }
15166            None => {
15167                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15168                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15169                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15170                // seq_end here too or it re-opens the same door.
15171                debug_assert_eq!(
15172                    seq_end, t,
15173                    "step35 cacheless prefill is monolithic (seq_end == t)"
15174                );
15175                if swa && seq_end > win {
15176                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15177                } else if std::env::var("MEMRA_NOFA").is_ok() {
15178                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15179                } else {
15180                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15181                }
15182            }
15183        }
15184
15185        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15186        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15187        let gw = fa
15188            .attn_gate
15189            .as_ref()
15190            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15191        let gt_owned = if gt_pre.is_none() {
15192            Some(e.matmul(
15193                gw,
15194                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15195                t,
15196            )?)
15197        } else {
15198            None
15199        };
15200        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15201        let mut ag = e.uninit(t * nh * hd)?;
15202        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15203        Ok(ag)
15204    }
15205
15206    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15207    /// `forward_last`, t2probe). Post-`wo`.
15208    pub(crate) fn step35_attn(
15209        &self,
15210        e: &Engine,
15211        fa: &FullAttnLayer,
15212        h: &CudaSlice<f32>,
15213        pos_d: &CudaSlice<i32>,
15214        t: usize,
15215        il: usize,
15216    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15217        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15218            Some(g3) => g3,
15219            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15220        };
15221        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15222        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15223        self.step35_o(e, fa, &ag, t)
15224    }
15225
15226    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15227    /// resident quantized cache, attend through the cache view). Post-`wo`.
15228    ///
15229    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15230    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15231    /// own extent.
15232    #[allow(clippy::too_many_arguments)]
15233    pub(crate) fn step35_attn_prime(
15234        &self,
15235        e: &Engine,
15236        fa: &FullAttnLayer,
15237        h: &CudaSlice<f32>,
15238        hx: Option<&CudaSlice<u8>>,
15239        pos_d: &CudaSlice<i32>,
15240        t: usize,
15241        cache: &mut Cache,
15242        il: usize,
15243        seq_end: usize,
15244    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15245        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15246            if hx.is_some() {
15247                return Err(
15248                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15249                     pre-quantized prime path"
15250                        .into(),
15251                );
15252            }
15253            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15254        }
15255        let g3 = if fa.step_tp_qkv.is_some() {
15256            if hx.is_some() {
15257                return Err(
15258                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15259                     pre-quantized prime path"
15260                        .into(),
15261                );
15262            }
15263            self.step35_tp_qkv(e, fa, h, t)?
15264                .expect("Step Q/K/V TP disappeared after the presence check")
15265        } else {
15266            match hx {
15267                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15268                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15269            }
15270        };
15271        let ag =
15272            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15273        self.step35_o(e, fa, &ag, t)
15274    }
15275
15276    fn ensure_step_tp_kv_cache(
15277        &self,
15278        e: &Engine,
15279        fa: &FullAttnLayer,
15280        il: usize,
15281        cache: &mut Cache,
15282    ) -> Result<bool, Box<dyn std::error::Error>> {
15283        let tp = fa
15284            .step_tp_qkv
15285            .as_ref()
15286            .ok_or("Step TP cache hydration lost its resident projections")?;
15287        let geometry = self.step35_geom(il);
15288        let window = geometry.window.map(|window| window as usize);
15289        let ranks = tp.runtime.devices().len();
15290        let head_dim = geometry.head_dim_k as usize;
15291        let kv_heads = geometry.n_head_kv as usize;
15292        let max_ctx = cache.max_ctx;
15293
15294        if cache.tp_kv[il].is_some() {
15295            return Ok(false);
15296        }
15297        let local = cache.kv[il]
15298            .as_ref()
15299            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15300        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15301            return Err(format!(
15302                "Step TP layer {il} local KV geometry k={} v={} != {}",
15303                local.kv_dim_k,
15304                local.kv_dim_v,
15305                kv_heads * head_dim
15306            )
15307            .into());
15308        }
15309        let resident_start = window
15310            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15311            .unwrap_or(0);
15312        let resident_rows = local.len - resident_start;
15313        let physical = local.physical_rows(resident_start, local.len)?;
15314        let k_rows = if resident_rows == 0 {
15315            Vec::new()
15316        } else {
15317            e.dtoh_u8_view(&e.view_u8_range(
15318                &local.k,
15319                physical.start * local.k_tok_bytes,
15320                physical.end * local.k_tok_bytes,
15321            ))?
15322        };
15323        let v_rows = if resident_rows == 0 {
15324            Vec::new()
15325        } else {
15326            e.dtoh_u8_view(&e.view_u8_range(
15327                &local.v,
15328                physical.start * local.v_tok_bytes,
15329                physical.end * local.v_tok_bytes,
15330            ))?
15331        };
15332        let mut distributed = match window {
15333            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15334                kv_heads * head_dim,
15335                kv_heads * head_dim,
15336                max_ctx,
15337                window,
15338            )?,
15339            None => tp.runtime.allocate_tp_kv_cache(
15340                kv_heads * head_dim,
15341                kv_heads * head_dim,
15342                max_ctx,
15343            )?,
15344        };
15345        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15346            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15347        {
15348            return Err(format!(
15349                "Step TP layer {il} distributed/local KV token bytes disagree: \
15350                 k={}x{ranks}/{} v={}x{ranks}/{}",
15351                distributed.k_tok_bytes(),
15352                local.k_tok_bytes,
15353                distributed.v_tok_bytes(),
15354                local.v_tok_bytes,
15355            )
15356            .into());
15357        }
15358        tp.runtime.hydrate_tp_kv_cache_from(
15359            &mut distributed,
15360            local.len,
15361            resident_start,
15362            &k_rows,
15363            &v_rows,
15364        )?;
15365        cache.tp_kv[il] = Some(distributed);
15366        Ok(true)
15367    }
15368
15369    #[allow(clippy::too_many_arguments)]
15370    fn step35_tp_prefill_attn_resident(
15371        &self,
15372        e: &Engine,
15373        fa: &FullAttnLayer,
15374        il: usize,
15375        h: &CudaSlice<f32>,
15376        pos_d: &CudaSlice<i32>,
15377        tokens: usize,
15378        cache: &mut Cache,
15379        seq_end: usize,
15380    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15381        let tp = fa
15382            .step_tp_qkv
15383            .as_ref()
15384            .ok_or("Step TP prefill lost its resident projections")?;
15385        let attention = tp
15386            .attention
15387            .as_ref()
15388            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15389        let ranks = tp.runtime.devices().len();
15390        if !step_tp_prefill_shape(
15391            true,
15392            tokens,
15393            ranks,
15394            tp.runtime.native_p2p(),
15395            true,
15396            crate::Engine::kv_fp8_on(),
15397        ) {
15398            return Err(format!(
15399                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP2/TP4 native P2P, \
15400                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15401                 native_p2p={} fp8_kv={}",
15402                tp.runtime.native_p2p(),
15403                crate::Engine::kv_fp8_on(),
15404            )
15405            .into());
15406        }
15407        for seam in [
15408            "MEMRA_STEP35_SWA_TKV",
15409            "MEMRA_PRIME_CALLLOCAL",
15410            "MEMRA_PRIME_F32CHUNK0",
15411        ] {
15412            if std::env::var(seam).as_deref() == Ok("1") {
15413                return Err(format!(
15414                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15415                )
15416                .into());
15417            }
15418        }
15419
15420        let geometry = self.step35_geom(il);
15421        let window = geometry.window.map(|window| window as usize);
15422        let head_dim = geometry.head_dim_k as usize;
15423        let heads = geometry.n_head as usize;
15424        let kv_heads = geometry.n_head_kv as usize;
15425        if heads % ranks != 0 || kv_heads % ranks != 0 {
15426            return Err(format!(
15427                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15428            )
15429            .into());
15430        }
15431        let local_heads = heads / ranks;
15432        let local_kv_heads = kv_heads / ranks;
15433        let local_kv_dim = local_kv_heads * head_dim;
15434        let hidden = self.cfg.n_embd as usize;
15435        let expected_input = tokens
15436            .checked_mul(hidden)
15437            .ok_or("Step TP prefill input size overflow")?;
15438        if h.len() < expected_input {
15439            return Err(format!(
15440                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15441                h.len()
15442            )
15443            .into());
15444        }
15445        let positions = e.dtoh_i32(pos_d)?;
15446        if positions.len() != tokens {
15447            return Err(format!(
15448                "rank-local Step prefill positions {} != tokens {tokens}",
15449                positions.len()
15450            )
15451            .into());
15452        }
15453
15454        let mut active_input = e.uninit(expected_input)?;
15455        e.copy_view_into(
15456            &mut active_input,
15457            0,
15458            &h.slice(0..expected_input),
15459            expected_input,
15460        )?;
15461        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15462        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15463        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15464        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15465        // layer-count-amplified arm of the boot flake.
15466        e.stream().synchronize()?;
15467        tp.runtime
15468            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15469        let q_raw = tp
15470            .runtime
15471            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15472        let k_raw = tp
15473            .runtime
15474            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15475        let v_raw = tp
15476            .runtime
15477            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15478        let mut q = Vec::with_capacity(ranks);
15479        let mut k = Vec::with_capacity(ranks);
15480        for rank in 0..ranks {
15481            let engine = tp
15482                .runtime
15483                .rank_engine(rank)
15484                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15485            let _main = engine.gpu.enter_main()?;
15486            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15487            engine.rms_norm(
15488                &q_raw[rank],
15489                &attention.q_norm[rank],
15490                &mut q_rank,
15491                head_dim,
15492                tokens * local_heads,
15493                self.cfg.rms_eps,
15494            )?;
15495            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15496            engine.rms_norm(
15497                &k_raw[rank],
15498                &attention.k_norm[rank],
15499                &mut k_rank,
15500                head_dim,
15501                tokens * local_kv_heads,
15502                self.cfg.rms_eps,
15503            )?;
15504            let position = engine.htod_i32(&positions)?;
15505            let rope_freqs = if geometry.rope_factors {
15506                self.step35_aux
15507                    .as_ref()
15508                    .and_then(|aux| aux.rope_freqs(engine))
15509            } else {
15510                None
15511            };
15512            engine.rope_neox2(
15513                &mut q_rank,
15514                &mut k_rank,
15515                &position,
15516                head_dim,
15517                geometry.n_rot as usize,
15518                local_heads,
15519                local_kv_heads,
15520                tokens,
15521                geometry.rope_base,
15522                1.0,
15523                rope_freqs,
15524            )?;
15525            q.push(q_rank);
15526            k.push(k_rank);
15527        }
15528
15529        let gate_weight = fa
15530            .attn_gate
15531            .as_ref()
15532            .ok_or("step35 layer is missing attn_gate.weight")?;
15533        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15534        if gate.len() != tokens * heads {
15535            return Err(format!(
15536                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15537                gate.len()
15538            )
15539            .into());
15540        }
15541
15542        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15543        let base_len = cache.kv[il]
15544            .as_ref()
15545            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15546            .len;
15547        let distributed = cache.tp_kv[il]
15548            .as_ref()
15549            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15550        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15551            return Err(format!(
15552                "Step TP layer {il} cache lengths diverged before prefill: \
15553                 local={base_len} distributed={}/{}",
15554                distributed.committed_len(),
15555                distributed.staged_len()
15556            )
15557            .into());
15558        }
15559        let target_len = base_len
15560            .checked_add(tokens)
15561            .ok_or("Step TP prefill cache length overflow")?;
15562        if target_len > cache.max_ctx {
15563            return Err(format!(
15564                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15565                cache.max_ctx
15566            )
15567            .into());
15568        }
15569        if seq_end < target_len {
15570            return Err(format!(
15571                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15572            )
15573            .into());
15574        }
15575
15576        let transaction = cache.tp_kv[il]
15577            .as_mut()
15578            .expect("distributed cache checked above")
15579            .begin_transaction()?;
15580        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15581            cache.tp_kv[il]
15582                .as_mut()
15583                .expect("distributed cache checked above"),
15584            transaction,
15585            &k,
15586            &v_raw,
15587            tokens,
15588        ) {
15589            let _ = tp.runtime.rollback_tp_kv_transaction(
15590                cache.tp_kv[il]
15591                    .as_mut()
15592                    .expect("distributed cache checked above"),
15593                transaction,
15594            );
15595            return Err(error);
15596        }
15597
15598        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15599            let distributed = cache.tp_kv[il]
15600                .as_ref()
15601                .expect("distributed cache checked above");
15602            let staged_len = distributed.staged_len();
15603            let view_start = window
15604                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15605                .unwrap_or(0);
15606            let physical = distributed.physical_range(view_start, staged_len)?;
15607            let t_kv = staged_len - view_start;
15608            let swa_naive = window.is_some_and(|window| seq_end > window);
15609            let mut gated = Vec::with_capacity(ranks);
15610            for rank in 0..ranks {
15611                let engine = tp
15612                    .runtime
15613                    .rank_engine(rank)
15614                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15615                let _main = engine.gpu.enter_main()?;
15616                let rank_cache = distributed
15617                    .rank(rank)
15618                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15619                let k_view = engine.view_u8_range(
15620                    rank_cache.k(),
15621                    physical.start * distributed.k_tok_bytes(),
15622                    physical.end * distributed.k_tok_bytes(),
15623                );
15624                let v_view = engine.view_u8_range(
15625                    rank_cache.v(),
15626                    physical.start * distributed.v_tok_bytes(),
15627                    physical.end * distributed.v_tok_bytes(),
15628                );
15629                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
15630                if swa_naive {
15631                    let window = window.expect("SWA predicate requires a window");
15632                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15633                        engine.sdpa_naive_w_quantized_view(
15634                            &q[rank],
15635                            &k_view,
15636                            &v_view,
15637                            &mut attention_out,
15638                            head_dim,
15639                            local_heads,
15640                            local_kv_heads,
15641                            tokens,
15642                            t_kv,
15643                            geometry.attention_scale(),
15644                            true,
15645                            window,
15646                            distributed.k_tok_bytes(),
15647                            distributed.v_tok_bytes(),
15648                        )?;
15649                    } else {
15650                        engine.fa_prefill_view_ws_w_hd128(
15651                            &q[rank],
15652                            &k_view,
15653                            &v_view,
15654                            &mut attention_out,
15655                            head_dim,
15656                            local_heads,
15657                            local_kv_heads,
15658                            tokens,
15659                            t_kv,
15660                            geometry.attention_scale(),
15661                            true,
15662                            window,
15663                            distributed.k_tok_bytes(),
15664                            distributed.v_tok_bytes(),
15665                        )?;
15666                    }
15667                } else if std::env::var("MEMRA_NOFA").is_ok() {
15668                    engine.sdpa_naive_quantized_view(
15669                        &q[rank],
15670                        &k_view,
15671                        &v_view,
15672                        &mut attention_out,
15673                        head_dim,
15674                        local_heads,
15675                        local_kv_heads,
15676                        tokens,
15677                        t_kv,
15678                        geometry.attention_scale(),
15679                        true,
15680                        distributed.k_tok_bytes(),
15681                        distributed.v_tok_bytes(),
15682                    )?;
15683                } else {
15684                    engine.fa_prefill_view_ws(
15685                        &q[rank],
15686                        &k_view,
15687                        &v_view,
15688                        &mut attention_out,
15689                        head_dim,
15690                        local_heads,
15691                        local_kv_heads,
15692                        tokens,
15693                        t_kv,
15694                        geometry.attention_scale(),
15695                        true,
15696                        distributed.k_tok_bytes(),
15697                        distributed.v_tok_bytes(),
15698                        false,
15699                    )?;
15700                }
15701
15702                let gate_start = rank * local_heads;
15703                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
15704                for token in 0..tokens {
15705                    let start = token * heads + gate_start;
15706                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
15707                }
15708                let gate_rank = engine.htod(&gate_rank)?;
15709                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
15710                engine.attn_head_gate(
15711                    &attention_out,
15712                    &gate_rank,
15713                    &mut gated_rank,
15714                    None,
15715                    head_dim,
15716                    local_heads,
15717                    tokens,
15718                )?;
15719                gated.push(gated_rank);
15720            }
15721            for rank in 1..ranks {
15722                let engine = tp
15723                    .runtime
15724                    .rank_engine(rank)
15725                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15726                let _main = engine.gpu.enter_main()?;
15727                engine.stream().synchronize()?;
15728            }
15729
15730            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
15731                let output = tp
15732                    .runtime
15733                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
15734                let k_shadow =
15735                    tp.runtime
15736                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
15737                let v_shadow =
15738                    tp.runtime
15739                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
15740                let root = tp
15741                    .runtime
15742                    .rank_engine(0)
15743                    .ok_or("Step TP prefill lost its root engine")?;
15744                let _main = root.gpu.enter_main()?;
15745                root.stream().synchronize()?;
15746                (output, k_shadow, v_shadow)
15747            } else {
15748                let attention = tp.runtime.gather_native_column_shards(
15749                    &gated,
15750                    tokens,
15751                    local_heads * head_dim,
15752                )?;
15753                let output = tp
15754                    .runtime
15755                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
15756                let k_shadow = tp
15757                    .runtime
15758                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
15759                let v_shadow =
15760                    tp.runtime
15761                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
15762                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
15763            };
15764            let local = cache.kv[il]
15765                .as_mut()
15766                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
15767            if local.len != base_len {
15768                return Err(format!(
15769                    "Step TP layer {il} local cache changed during prefill: \
15770                     len={} base={base_len}",
15771                    local.len
15772                )
15773                .into());
15774            }
15775            let retain_from = window
15776                .map(|window| {
15777                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
15778                    let rollback_retain =
15779                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
15780                    staged_retain.min(rollback_retain)
15781                })
15782                .unwrap_or(0);
15783            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
15784            e.append_kv_quantized_rows(
15785                &k_shadow,
15786                &v_shadow,
15787                &mut local.k,
15788                &mut local.v,
15789                write_row,
15790                tokens,
15791                local.kv_dim_k,
15792                local.kv_dim_v,
15793                local.k_tok_bytes,
15794                local.v_tok_bytes,
15795                false,
15796            )?;
15797            local.len = staged_len;
15798            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
15799            Ok(output)
15800        })();
15801
15802        let output = match staged {
15803            Ok(output) => output,
15804            Err(error) => {
15805                let _ = tp.runtime.rollback_tp_kv_transaction(
15806                    cache.tp_kv[il]
15807                        .as_mut()
15808                        .expect("distributed cache checked above"),
15809                    transaction,
15810                );
15811                if let Some(local) = cache.kv[il].as_mut() {
15812                    local.len = base_len;
15813                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
15814                }
15815                return Err(error);
15816            }
15817        };
15818        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
15819            cache.tp_kv[il]
15820                .as_mut()
15821                .expect("distributed cache checked above"),
15822            transaction,
15823            tokens,
15824        ) {
15825            let _ = tp.runtime.rollback_tp_kv_transaction(
15826                cache.tp_kv[il]
15827                    .as_mut()
15828                    .expect("distributed cache checked above"),
15829                transaction,
15830            );
15831            let local = cache.kv[il].as_mut().expect("local cache checked above");
15832            local.len = base_len;
15833            e.set_i32_one(&mut local.len_d, base_len as i32)?;
15834            return Err(error);
15835        }
15836
15837        let committed = cache.tp_kv[il]
15838            .as_ref()
15839            .expect("distributed cache checked above")
15840            .committed_len();
15841        let local_len = cache.kv[il]
15842            .as_ref()
15843            .expect("local cache checked above")
15844            .len;
15845        if committed != local_len {
15846            return Err(format!(
15847                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
15848            )
15849            .into());
15850        }
15851        eprintln!(
15852            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
15853             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
15854             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
15855             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
15856             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
15857             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
15858             output={} performance_claim=false",
15859            tp.layer,
15860            tp.devices,
15861            hydrated,
15862            if window.is_some() {
15863                "rank-local-swa-ring"
15864            } else {
15865                "rank-local-global"
15866            },
15867            tp.runtime.transport_label(),
15868            tp.runtime.bulk_p2p(),
15869            if tp.runtime.bulk_p2p() {
15870                "root-device"
15871            } else {
15872                "root-readback"
15873            },
15874        );
15875        Ok(output)
15876    }
15877
15878    fn step35_tp_decode_attn_resident(
15879        &self,
15880        e: &Engine,
15881        fa: &FullAttnLayer,
15882        il: usize,
15883        h: &CudaSlice<f32>,
15884        pos_d: &CudaSlice<i32>,
15885        cache: &mut Cache,
15886    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15887        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
15888        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
15889        // nvfp4-dev-routes counter.
15890        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15891        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15892        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15893        let started = timing.then(std::time::Instant::now);
15894        let result = if crate::tp::step_tp_decode_v2_enabled()? {
15895            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
15896        } else {
15897            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
15898        };
15899        if let Some(started) = started {
15900            use std::sync::atomic::Ordering;
15901            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
15902                + started.elapsed().as_nanos() as u64;
15903            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
15904            if calls % 430 == 0 {
15905                eprintln!(
15906                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
15907                    ns as f64 / 1.0e6,
15908                    ns as f64 / calls as f64 / 1.0e3,
15909                );
15910            }
15911        }
15912        result
15913    }
15914
15915    #[allow(clippy::too_many_arguments)]
15916    fn step35_tp_decode_attn_resident_inner(
15917        &self,
15918        e: &Engine,
15919        fa: &FullAttnLayer,
15920        il: usize,
15921        h: &CudaSlice<f32>,
15922        pos_d: &CudaSlice<i32>,
15923        cache: &mut Cache,
15924    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15925        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
15926        // drains every stream so queued async work is billed to the phase that queued it — the
15927        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
15928        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
15929        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15930        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15931        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15932        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15933        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15934        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15935        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15936        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15937        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15938        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15939        fn lap(
15940            runtime: &crate::tp::TpE4m3HostBounce,
15941            e: &Engine,
15942            timer: &std::sync::atomic::AtomicU64,
15943            started: &mut Option<std::time::Instant>,
15944        ) -> Result<(), Box<dyn std::error::Error>> {
15945            let Some(start) = started.as_mut() else {
15946                return Ok(());
15947            };
15948            for rank in 0..runtime.devices().len() {
15949                if let Some(engine) = runtime.rank_engine(rank) {
15950                    let _main = engine.gpu.enter_main()?;
15951                    engine.stream().synchronize()?;
15952                }
15953            }
15954            e.stream().synchronize()?;
15955            timer.fetch_add(
15956                start.elapsed().as_nanos() as u64,
15957                std::sync::atomic::Ordering::Relaxed,
15958            );
15959            *start = std::time::Instant::now();
15960            Ok(())
15961        }
15962        let tp = fa
15963            .step_tp_qkv
15964            .as_ref()
15965            .ok_or("Step TP decode lost its resident projections")?;
15966        let attention = tp
15967            .attention
15968            .as_ref()
15969            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
15970        if !tp.runtime.native_p2p() {
15971            return Err("rank-local Step attention requires native P2P".into());
15972        }
15973        if crate::Engine::kv_fp8_on() {
15974            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
15975        }
15976
15977        let geometry = self.step35_geom(il);
15978        let window = geometry.window.map(|window| window as usize);
15979        let ranks = tp.runtime.devices().len();
15980        let head_dim = geometry.head_dim_k as usize;
15981        let heads = geometry.n_head as usize;
15982        let kv_heads = geometry.n_head_kv as usize;
15983        if heads % ranks != 0 || kv_heads % ranks != 0 {
15984            return Err(format!(
15985                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15986            )
15987            .into());
15988        }
15989        let local_heads = heads / ranks;
15990        let local_kv_heads = kv_heads / ranks;
15991        let local_kv_dim = local_kv_heads * head_dim;
15992        let max_ctx = cache.max_ctx;
15993
15994        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15995
15996        let base_len = cache.kv[il]
15997            .as_ref()
15998            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15999            .len;
16000        let distributed = cache.tp_kv[il]
16001            .as_ref()
16002            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16003        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16004            return Err(format!(
16005                "Step TP layer {il} cache lengths diverged before decode: \
16006                 local={base_len} distributed={}/{}",
16007                distributed.committed_len(),
16008                distributed.staged_len()
16009            )
16010            .into());
16011        }
16012
16013        let mut lap_start = timing.then(std::time::Instant::now);
16014        let positions = e.dtoh_i32(pos_d)?;
16015        if positions.len() != 1 {
16016            return Err(format!(
16017                "rank-local Step decode requires one position, got {}",
16018                positions.len()
16019            )
16020            .into());
16021        }
16022        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
16023        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
16024            attention.decode_input.as_ref()
16025        {
16026            let mut decode_input = decode_input
16027                .lock()
16028                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16029            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16030            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16031            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16032            e.stream().synchronize()?;
16033            tp.runtime
16034                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16035            let q_raw = tp
16036                .runtime
16037                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16038            let k_raw = tp
16039                .runtime
16040                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16041            let v_raw = tp
16042                .runtime
16043                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16044            (q_raw, k_raw, v_raw, "root-device-replicated")
16045        } else {
16046            let activation = e.dtoh(h)?;
16047            let q_raw =
16048                tp.runtime
16049                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16050            let k_raw =
16051                tp.runtime
16052                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16053            let v_raw =
16054                tp.runtime
16055                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16056            (q_raw, k_raw, v_raw, "host-replicated")
16057        };
16058        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16059        let mut q = Vec::with_capacity(ranks);
16060        let mut k = Vec::with_capacity(ranks);
16061        for rank in 0..ranks {
16062            let engine = tp
16063                .runtime
16064                .rank_engine(rank)
16065                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16066            let _main = engine.gpu.enter_main()?;
16067            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16068            engine.rms_norm(
16069                &q_raw[rank],
16070                &attention.q_norm[rank],
16071                &mut q_rank,
16072                head_dim,
16073                local_heads,
16074                self.cfg.rms_eps,
16075            )?;
16076            let mut k_rank = engine.uninit(local_kv_dim)?;
16077            engine.rms_norm(
16078                &k_raw[rank],
16079                &attention.k_norm[rank],
16080                &mut k_rank,
16081                head_dim,
16082                local_kv_heads,
16083                self.cfg.rms_eps,
16084            )?;
16085            let position = engine.htod_i32(&positions)?;
16086            let rope_freqs = if geometry.rope_factors {
16087                self.step35_aux
16088                    .as_ref()
16089                    .and_then(|aux| aux.rope_freqs(engine))
16090            } else {
16091                None
16092            };
16093            engine.rope_neox2(
16094                &mut q_rank,
16095                &mut k_rank,
16096                &position,
16097                head_dim,
16098                geometry.n_rot as usize,
16099                local_heads,
16100                local_kv_heads,
16101                1,
16102                geometry.rope_base,
16103                1.0,
16104                rope_freqs,
16105            )?;
16106            q.push(q_rank);
16107            k.push(k_rank);
16108        }
16109        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16110
16111        let gate_weight = fa
16112            .attn_gate
16113            .as_ref()
16114            .ok_or("step35 layer is missing attn_gate.weight")?;
16115        let gate = e.matmul(gate_weight, h, 1)?;
16116        let gate = e.dtoh(&gate)?;
16117        if gate.len() != heads {
16118            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16119        }
16120        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16121
16122        let transaction = cache.tp_kv[il]
16123            .as_mut()
16124            .expect("distributed cache checked above")
16125            .begin_transaction()?;
16126        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16127            cache.tp_kv[il]
16128                .as_mut()
16129                .expect("distributed cache checked above"),
16130            transaction,
16131            &k,
16132            &v_raw,
16133            1,
16134        ) {
16135            let _ = tp.runtime.rollback_tp_kv_transaction(
16136                cache.tp_kv[il]
16137                    .as_mut()
16138                    .expect("distributed cache checked above"),
16139                transaction,
16140            );
16141            return Err(error);
16142        }
16143        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16144
16145        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16146            let distributed = cache.tp_kv[il]
16147                .as_ref()
16148                .expect("distributed cache checked above");
16149            let staged_len = distributed.staged_len();
16150            let view_start = window
16151                .map(|window| staged_len.saturating_sub(window))
16152                .unwrap_or(0);
16153            let physical = distributed.physical_range(view_start, staged_len)?;
16154            let t_kv = staged_len - view_start;
16155            let mut gated = Vec::with_capacity(ranks);
16156            for rank in 0..ranks {
16157                let engine = tp
16158                    .runtime
16159                    .rank_engine(rank)
16160                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16161                let _main = engine.gpu.enter_main()?;
16162                let rank_cache = distributed
16163                    .rank(rank)
16164                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16165                let k_view = engine.view_u8_range(
16166                    rank_cache.k(),
16167                    physical.start * distributed.k_tok_bytes(),
16168                    physical.end * distributed.k_tok_bytes(),
16169                );
16170                let v_view = engine.view_u8_range(
16171                    rank_cache.v(),
16172                    physical.start * distributed.v_tok_bytes(),
16173                    physical.end * distributed.v_tok_bytes(),
16174                );
16175                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16176                engine.fa_decode_kvmod(
16177                    &q[rank],
16178                    &k_view,
16179                    &v_view,
16180                    &mut attention_out,
16181                    head_dim,
16182                    local_heads,
16183                    local_kv_heads,
16184                    t_kv,
16185                    geometry.attention_scale(),
16186                    distributed.k_tok_bytes(),
16187                    distributed.v_tok_bytes(),
16188                    false,
16189                )?;
16190                let gate_start = rank * local_heads;
16191                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16192                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16193                engine.attn_head_gate(
16194                    &attention_out,
16195                    &gate_rank,
16196                    &mut gated_rank,
16197                    None,
16198                    head_dim,
16199                    local_heads,
16200                    1,
16201                )?;
16202                gated.push(gated_rank);
16203            }
16204            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16205
16206            let gathered =
16207                tp.runtime
16208                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16209            let output = tp
16210                .runtime
16211                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16212            let output = e.htod(&output)?;
16213            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16214
16215            let k_shadow = tp
16216                .runtime
16217                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16218            let v_shadow = tp
16219                .runtime
16220                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16221            let k_shadow = e.htod(&k_shadow)?;
16222            let v_shadow = e.htod(&v_shadow)?;
16223            let local = cache.kv[il]
16224                .as_mut()
16225                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16226            if local.len != base_len || base_len + 1 > max_ctx {
16227                return Err(format!(
16228                    "Step TP layer {il} local cache changed during decode: \
16229                     len={} base={base_len} max={max_ctx}",
16230                    local.len
16231                )
16232                .into());
16233            }
16234            let retain_from = window
16235                .map(|window| {
16236                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16237                    let rollback_retain =
16238                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16239                    staged_retain.min(rollback_retain)
16240                })
16241                .unwrap_or(0);
16242            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16243            e.append_kv_quantized(
16244                &k_shadow,
16245                &v_shadow,
16246                &mut local.k,
16247                &mut local.v,
16248                write_row,
16249                local.kv_dim_k,
16250                local.kv_dim_v,
16251                local.k_tok_bytes,
16252                local.v_tok_bytes,
16253                false,
16254            )?;
16255            local.len = base_len + 1;
16256            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16257            Ok(output)
16258        })();
16259
16260        let output = match staged {
16261            Ok(output) => output,
16262            Err(error) => {
16263                let _ = tp.runtime.rollback_tp_kv_transaction(
16264                    cache.tp_kv[il]
16265                        .as_mut()
16266                        .expect("distributed cache checked above"),
16267                    transaction,
16268                );
16269                if let Some(local) = cache.kv[il].as_mut() {
16270                    local.len = base_len;
16271                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16272                }
16273                return Err(error);
16274            }
16275        };
16276        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16277            cache.tp_kv[il]
16278                .as_mut()
16279                .expect("distributed cache checked above"),
16280            transaction,
16281            1,
16282        ) {
16283            let _ = tp.runtime.rollback_tp_kv_transaction(
16284                cache.tp_kv[il]
16285                    .as_mut()
16286                    .expect("distributed cache checked above"),
16287                transaction,
16288            );
16289            let local = cache.kv[il].as_mut().expect("local cache checked above");
16290            local.len = base_len;
16291            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16292            return Err(error);
16293        }
16294
16295        let committed = cache.tp_kv[il]
16296            .as_ref()
16297            .expect("distributed cache checked above")
16298            .committed_len();
16299        let local_len = cache.kv[il]
16300            .as_ref()
16301            .expect("local cache checked above")
16302            .len;
16303        if committed != local_len {
16304            return Err(format!(
16305                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16306            )
16307            .into());
16308        }
16309        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16310        if timing {
16311            use std::sync::atomic::Ordering;
16312            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16313            if calls % 430 == 0 {
16314                let avg = |t: &std::sync::atomic::AtomicU64| {
16315                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16316                };
16317                eprintln!(
16318                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16319                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16320                    avg(&T_POS),
16321                    avg(&T_QKV),
16322                    avg(&T_NORMROPE),
16323                    avg(&T_GATE),
16324                    avg(&T_APPEND),
16325                    avg(&T_ATTN),
16326                    avg(&T_OPROJ),
16327                    avg(&T_SHADOW),
16328                );
16329            }
16330        }
16331        eprintln!(
16332            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16333             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16334             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16335             attention_scope={} input_path={} kv_physical_rows={} \
16336             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16337             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16338             bulk_p2p={} output=root-readback performance_claim=false",
16339            tp.layer,
16340            tp.devices,
16341            hydrated,
16342            if window.is_some() {
16343                "rank-local-swa-ring"
16344            } else {
16345                "rank-local-global"
16346            },
16347            input_path,
16348            cache.tp_kv[il]
16349                .as_ref()
16350                .expect("distributed cache checked above")
16351                .physical_capacity(),
16352            tp.runtime.transport_label(),
16353            tp.runtime.bulk_p2p(),
16354        );
16355        Ok(output)
16356    }
16357
16358    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16359    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16360    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16361    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16362    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16363    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16364    #[allow(clippy::too_many_arguments)]
16365    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16366    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16367    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16368    /// the resident fused TP2 class (caller falls back to the per-row walk).
16369    pub(crate) fn step35_verify_qkv_precompute(
16370        &self,
16371        e: &Engine,
16372        il: usize,
16373        h_t: &CudaSlice<f32>,
16374        t: usize,
16375    ) -> Result<bool, Box<dyn std::error::Error>> {
16376        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16377            return Ok(false);
16378        };
16379        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16380            return Ok(false);
16381        };
16382        let Some(attention) = tp.attention.as_ref() else {
16383            return Ok(false);
16384        };
16385        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16386            return Ok(false);
16387        }
16388        let geometry = self.step35_geom(il);
16389        let heads = geometry.n_head as usize;
16390        let ws_index = tp
16391            .runtime
16392            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16393        let gate_shards = attention
16394            .gate_shards_bf16
16395            .as_deref()
16396            .map(crate::tp::StepTpGateShards::Bf16);
16397        tp.runtime.decode_v2_input_qkv_tcol(
16398            ws_index,
16399            e,
16400            h_t,
16401            t,
16402            &tp.q,
16403            &tp.k,
16404            &tp.v,
16405            gate_shards,
16406        )?;
16407        Ok(true)
16408    }
16409
16410    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16411    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16412    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16413    /// flag confirmed the defer engaged for every column.
16414    pub(crate) fn step35_verify_oproj_tcol(
16415        &self,
16416        e: &Engine,
16417        il: usize,
16418        t: usize,
16419    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16420        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16421            return Err("tcol o_proj join expects full attention".into());
16422        };
16423        let tp = fa
16424            .step_tp_qkv
16425            .as_ref()
16426            .ok_or("tcol o_proj join lost its resident projections")?;
16427        let heads = self.step35_geom(il).n_head as usize;
16428        let ws_index = tp
16429            .runtime
16430            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16431        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16432    }
16433
16434    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16435    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16436    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16437    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16438    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16439    /// walk runs the ordinary per-column program.
16440    pub(crate) fn step35_spec_fa2_precheck(
16441        &self,
16442        cache: &Cache,
16443        il: usize,
16444        pos0: usize,
16445    ) -> Result<bool, Box<dyn std::error::Error>> {
16446        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16447        // a silently-vacuous door is indistinguishable from a slow one without this.
16448        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16449            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16450            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16451            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16452                let mut seen = SEEN.lock().unwrap();
16453                if !seen.iter().any(|c| *c == clause) {
16454                    // leak: bounded by the clause-id set
16455                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16456                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16457                }
16458            }
16459            false
16460        }
16461        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16462        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16463        if let Some(only) =
16464            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16465        {
16466            if *only != il {
16467                return Ok(false);
16468            }
16469        }
16470        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16471            return Ok(nope("mixer", il, pos0));
16472        };
16473        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16474            return Ok(nope("step_tp", il, pos0));
16475        };
16476        let Some(attention) = tp.attention.as_ref() else {
16477            return Ok(nope("attention", il, pos0));
16478        };
16479        if !tp.runtime.native_p2p()
16480            || crate::Engine::kv_fp8_on()
16481            || !crate::tp::step_tp_dcw_enabled()?
16482            || !crate::tp::step_tp_qkv_fused_enabled()?
16483            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16484        {
16485            return Ok(nope("runtime-doors", il, pos0));
16486        }
16487        let geometry = self.step35_geom(il);
16488        let head_dim = geometry.head_dim_k as usize;
16489        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16490            return Ok(nope("fa-class", il, pos0));
16491        }
16492        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16493            return Ok(nope("tp-kv", il, pos0));
16494        };
16495        if distributed.staged_len() != pos0 {
16496            return Ok(nope("staged-len", il, pos0));
16497        }
16498        // Both appends must land without a ring rebase (rebase columns take the
16499        // host-row path, which cannot stash).
16500        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16501        if would_rebase {
16502            return Ok(nope("rebase", il, pos0));
16503        }
16504        let window = geometry.window.map(|w| w as usize);
16505        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16506        // shift by one key, so one shared tile grid cannot reproduce both rows'
16507        // per-column FP grouping) — and drifted verify logits change accept decisions,
16508        // breaking the spec==target contract. Engage only when BOTH rows' views start
16509        // at 0 (global, or SWA still inside its window): bitwise per row under the
16510        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16511        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16512        if let Some(w) = window {
16513            if pos0 + 2 > w {
16514                return Ok(nope("swa-capped", il, pos0));
16515            }
16516        }
16517        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16518        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16519        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16520        let (t0, t1) = (pos0 + 1, pos0 + 2);
16521        if t0 < 96 {
16522            return Ok(nope("dcw-floor", il, pos0));
16523        }
16524        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16525            return Ok(nope("vec-floor", il, pos0));
16526        }
16527        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16528        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16529        // the two rows' own launches — the joined kernel derives one grid from T1 and
16530        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16531        // the combine's merge rounding. Boundary rounds fall back per column.
16532        let ranks = tp.runtime.devices().len();
16533        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16534        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16535        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16536        if sp0 != sp1 {
16537            return Ok(nope("partition-sp", il, pos0));
16538        }
16539        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16540        if ns0 != ns1 {
16541            return Ok(nope("partition-ns", il, pos0));
16542        }
16543        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16544            return Ok(nope("partition-per", il, pos0));
16545        }
16546        Ok(true)
16547    }
16548
16549    /// T-ROW fa precheck (the rows kernel supersedes the dcw2 pair-join): every dynamic
16550    /// input of the engine-side dcw decision must hold for EVERY row — stashing is
16551    /// unrecoverable — plus the rows-launcher guards (big-rig ladder, no env split
16552    /// overrides). No partition or capped-SWA clauses: each row derives its OWN geometry.
16553    pub(crate) fn step35_fa_rows_precheck(
16554        &self,
16555        cache: &Cache,
16556        il: usize,
16557        pos0: usize,
16558        t: usize,
16559    ) -> Result<bool, Box<dyn std::error::Error>> {
16560        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16561            return Ok(false);
16562        };
16563        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16564            return Ok(false);
16565        };
16566        let Some(attention) = tp.attention.as_ref() else {
16567            return Ok(false);
16568        };
16569        if !tp.runtime.native_p2p()
16570            || crate::Engine::kv_fp8_on()
16571            || !crate::tp::step_tp_dcw_enabled()?
16572            || !crate::tp::step_tp_qkv_fused_enabled()?
16573            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16574        {
16575            return Ok(false);
16576        }
16577        let geometry = self.step35_geom(il);
16578        let head_dim = geometry.head_dim_k as usize;
16579        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16580            return Ok(false);
16581        }
16582        if crate::fa_sm_count() < 128
16583            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16584            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16585            || std::env::var("MEMRA_FA_SP16").is_ok()
16586            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16587        {
16588            return Ok(false);
16589        }
16590        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16591            return Ok(false);
16592        };
16593        if distributed.staged_len() != pos0 {
16594            return Ok(false);
16595        }
16596        let (_, would_rebase) = distributed.peek_append_ring(t)?;
16597        if would_rebase {
16598            return Ok(false);
16599        }
16600        // Row 0 sees the smallest view: its post-append effective t_kv must clear both
16601        // the dcw floor and the vec-class floor (later rows only grow).
16602        let window = geometry.window.map(|w| w as usize);
16603        let t0 = window.map(|w| (pos0 + 1).min(w)).unwrap_or(pos0 + 1);
16604        if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16605            return Ok(false);
16606        }
16607        Ok(true)
16608    }
16609
16610    /// T-ROW fa join for the verify walk (same-session rows: shared ring/len with
16611    /// len_back = t-1-r). Tables stage once per (layer, rank, ring, t) and live on the
16612    /// owning rank.
16613    pub(crate) fn step35_verify_fa_rows_join(
16614        &self,
16615        e: &Engine,
16616        il: usize,
16617        cache: &Cache,
16618        pos0: usize,
16619        t: usize,
16620    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16621        use cudarc::driver::DevicePtr;
16622        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16623            return Err("fa rows join expects full attention".into());
16624        };
16625        let tp = fa
16626            .step_tp_qkv
16627            .as_ref()
16628            .ok_or("fa rows join lost its resident projections")?;
16629        let geometry = self.step35_geom(il);
16630        let heads = geometry.n_head as usize;
16631        let head_dim = geometry.head_dim_k as usize;
16632        let window = geometry.window.map(|w| w as usize);
16633        let distributed = cache.tp_kv[il]
16634            .as_ref()
16635            .ok_or("fa rows join lost its distributed KV cache")?;
16636        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16637        // Host mirror of the kernel's big-rig ladder (launcher-guarded identical).
16638        let ladder = |t_kv: usize| -> usize {
16639            if t_kv <= 2048 {
16640                16
16641            } else if t_kv <= 16384 {
16642                64
16643            } else {
16644                128
16645            }
16646        };
16647        let mut max_ns = 1usize;
16648        for r in 0..t {
16649            let t_kv = window
16650                .map(|w| (pos0 + r + 1).min(w))
16651                .unwrap_or(pos0 + r + 1);
16652            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16653        }
16654        // Rebuild the tiny raw-pointer table from the live distributed cache immediately
16655        // before launch. A process-lifetime map cannot prove allocation generation: CUDA may
16656        // recycle len/base independently of the large K/V rings, making a pointer-key cache
16657        // hit refer to another session (Hermes `11339f5cd3c132a3`).
16658        let ranks = tp.runtime.devices().len();
16659        let mut tables = Vec::with_capacity(ranks);
16660        for rank in 0..ranks {
16661            let engine = tp
16662                .runtime
16663                .rank_engine(rank)
16664                .ok_or("fa rows join lost a rank engine")?;
16665            let rank_cache = distributed
16666                .rank(rank)
16667                .ok_or("fa rows join lost a KV cache rank")?;
16668            let _main = engine.gpu.enter_main()?;
16669            let s = engine.stream();
16670            let (kp, _g0) = rank_cache.k().device_ptr(&s);
16671            let (vp, _g1) = rank_cache.v().device_ptr(&s);
16672            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16673            let bp = match rank_cache.base_d() {
16674                Some(b) => {
16675                    let (p, _g) = b.device_ptr(&s);
16676                    p as u64
16677                }
16678                None => 0u64,
16679            };
16680            let mut host = Vec::with_capacity(t * 6);
16681            for r in 0..t {
16682                host.extend_from_slice(&[
16683                    kp as u64,
16684                    vp as u64,
16685                    lp as u64,
16686                    bp,
16687                    0u64,
16688                    (t - 1 - r) as u64,
16689                ]);
16690            }
16691            tables.push(engine.stream().clone_htod(&host)?);
16692        }
16693        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
16694        let ws_index = tp
16695            .runtime
16696            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16697        tp.runtime.decode_v2_fa_rows_join(
16698            ws_index,
16699            e,
16700            &tp.o,
16701            &tabs,
16702            t,
16703            head_dim,
16704            window.unwrap_or(0),
16705            max_ns,
16706            geometry.attention_scale(),
16707            k_tok_bytes,
16708            v_tok_bytes,
16709        )
16710    }
16711
16712    /// Multi-session t-row fa precheck (the batched serving walk): the static doors of
16713    /// the rows kernel plus per-SESSION dynamic checks — every row's own cache must be
16714    /// hydrated, in sync, rebase-free and above both floors.
16715    pub(crate) fn step35_batch_fa_rows_precheck(
16716        &self,
16717        caches: &[&mut Cache],
16718        row_to_cache: impl Fn(usize) -> usize,
16719        positions: &[i32],
16720        il: usize,
16721    ) -> Result<bool, Box<dyn std::error::Error>> {
16722        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16723            return Ok(false);
16724        };
16725        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16726            return Ok(false);
16727        };
16728        let Some(attention) = tp.attention.as_ref() else {
16729            return Ok(false);
16730        };
16731        if !tp.runtime.native_p2p()
16732            || crate::Engine::kv_fp8_on()
16733            || !crate::tp::step_tp_dcw_enabled()?
16734            || !crate::tp::step_tp_qkv_fused_enabled()?
16735            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16736        {
16737            return Ok(false);
16738        }
16739        let geometry = self.step35_geom(il);
16740        let head_dim = geometry.head_dim_k as usize;
16741        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16742            return Ok(false);
16743        }
16744        if crate::fa_sm_count() < 128
16745            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16746            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16747            || std::env::var("MEMRA_FA_SP16").is_ok()
16748            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16749        {
16750            return Ok(false);
16751        }
16752        let window = geometry.window.map(|w| w as usize);
16753        for (r, &pos) in positions.iter().enumerate() {
16754            let cache = &caches[row_to_cache(r)];
16755            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16756                return Ok(false);
16757            };
16758            if distributed.staged_len() != pos as usize {
16759                return Ok(false);
16760            }
16761            if distributed.peek_append_ring(1)?.1 {
16762                return Ok(false);
16763            }
16764            let t0 = window
16765                .map(|w| (pos as usize + 1).min(w))
16766                .unwrap_or(pos as usize + 1);
16767            if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16768                return Ok(false);
16769            }
16770        }
16771        Ok(true)
16772    }
16773
16774    /// FULL t-row attention pass for the VERIFY walk (same-session rows): rope/append +
16775    /// fa + combine + o_proj join in 3 launches/rank/layer. Row r appends at slot
16776    /// len-base+r and one last block advances len by t; the fa rows read len_back =
16777    /// t-1-r. Returns None when the fused-rope class does not hold (the walk keeps the
16778    /// per-column stash flow). Caller has passed `step35_fa_rows_precheck`.
16779    pub(crate) fn step35_verify_rope_fa_pass(
16780        &self,
16781        e: &Engine,
16782        il: usize,
16783        cache: &Cache,
16784        pos0: usize,
16785        t: usize,
16786        stage_pos: bool,
16787    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16788        use cudarc::driver::DevicePtr;
16789        if !crate::tp::fuse_rope_append_on() {
16790            return Ok(None);
16791        }
16792        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16793            return Ok(None);
16794        };
16795        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16796            return Ok(None);
16797        };
16798        let Some(attention) = tp.attention.as_ref() else {
16799            return Ok(None);
16800        };
16801        let geometry = self.step35_geom(il);
16802        let head_dim = geometry.head_dim_k as usize;
16803        if head_dim != 128 {
16804            return Ok(None);
16805        }
16806        let heads = geometry.n_head as usize;
16807        let window = geometry.window.map(|w| w as usize);
16808        let ranks = tp.runtime.devices().len();
16809        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16810            return Ok(None);
16811        };
16812        if distributed.kv_dim_k() != distributed.kv_dim_v() {
16813            return Ok(None);
16814        }
16815        {
16816            let rank0 = distributed.rank(0).ok_or("verify rope pass lost rank 0")?;
16817            if rank0.base_d().is_none()
16818                && distributed.staged_len() + t > distributed.physical_capacity()
16819            {
16820                return Ok(None);
16821            }
16822        }
16823        let mut rope_freqs = Vec::with_capacity(ranks);
16824        for rank in 0..ranks {
16825            let engine = tp
16826                .runtime
16827                .rank_engine(rank)
16828                .ok_or("verify rope pass lost a rank engine")?;
16829            rope_freqs.push(if geometry.rope_factors {
16830                match self
16831                    .step35_aux
16832                    .as_ref()
16833                    .and_then(|aux| aux.rope_freqs(engine))
16834                {
16835                    Some(f) => Some(f),
16836                    None => return Ok(None),
16837                }
16838            } else {
16839                None
16840            });
16841        }
16842        let ladder = |t_kv: usize| -> usize {
16843            if t_kv <= 2048 {
16844                16
16845            } else if t_kv <= 16384 {
16846                64
16847            } else {
16848                128
16849            }
16850        };
16851        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16852        let mut max_ns = 1usize;
16853        let mut positions = Vec::with_capacity(t);
16854        for r in 0..t {
16855            positions.push((pos0 + r) as i32);
16856            let t_kv = window
16857                .map(|w| (pos0 + r + 1).min(w))
16858                .unwrap_or(pos0 + r + 1);
16859            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16860        }
16861        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
16862        let mut tab_keys = vec![0u64; ranks];
16863        for rank in 0..ranks {
16864            let engine = tp
16865                .runtime
16866                .rank_engine(rank)
16867                .ok_or("verify rope pass lost a rank engine")?;
16868            let rank_cache = distributed
16869                .rank(rank)
16870                .ok_or("verify rope pass lost a KV cache rank")?;
16871            let _main = engine.gpu.enter_main()?;
16872            let s = engine.stream();
16873            let (kp, _g0) = rank_cache.k().device_ptr(&s);
16874            let (vp, _g1) = rank_cache.v().device_ptr(&s);
16875            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16876            let bp = match rank_cache.base_d() {
16877                Some(b) => {
16878                    let (p, _g) = b.device_ptr(&s);
16879                    p as u64
16880                }
16881                None => 0u64,
16882            };
16883            tab_keys[rank] = (kp as u64)
16884                .rotate_left(17)
16885                .wrapping_add(bp)
16886                .wrapping_add((il as u64) << 32)
16887                .wrapping_add(t as u64)
16888                .wrapping_add(1 << 63);
16889            for _r in 0..t {
16890                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
16891            }
16892        }
16893        let ws_index = tp
16894            .runtime
16895            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16896        tp.runtime
16897            .decode_v2_rope_fa_rows(
16898                ws_index,
16899                e,
16900                &tp.o,
16901                &session_parts,
16902                &tab_keys,
16903                &positions,
16904                stage_pos,
16905                true,
16906                &attention.q_norm,
16907                &attention.k_norm,
16908                &rope_freqs,
16909                t,
16910                head_dim,
16911                geometry.n_rot as usize,
16912                window.unwrap_or(0),
16913                max_ns,
16914                geometry.attention_scale(),
16915                k_tok_bytes,
16916                v_tok_bytes,
16917                self.cfg.rms_eps,
16918                geometry.rope_base,
16919            )
16920            .map(Some)
16921    }
16922
16923    /// FULL t-row attention pass for the batched walk (rope/append + fa + combine +
16924    /// o_proj join, 3 launches/rank/layer): returns None when the fused-rope class does
16925    /// not hold — the caller falls back to the per-row stash flow. The caller has
16926    /// already passed `step35_batch_fa_rows_precheck`.
16927    #[allow(clippy::too_many_arguments)]
16928    pub(crate) fn step35_batch_rope_fa_pass(
16929        &self,
16930        e: &Engine,
16931        il: usize,
16932        caches: &[&mut Cache],
16933        row_to_cache: impl Fn(usize) -> usize,
16934        positions: &[i32],
16935        t: usize,
16936        stage_pos: bool,
16937    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16938        use cudarc::driver::DevicePtr;
16939        if !crate::tp::fuse_rope_append_on() {
16940            return Ok(None);
16941        }
16942        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16943            return Ok(None);
16944        };
16945        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16946            return Ok(None);
16947        };
16948        let Some(attention) = tp.attention.as_ref() else {
16949            return Ok(None);
16950        };
16951        let geometry = self.step35_geom(il);
16952        let head_dim = geometry.head_dim_k as usize;
16953        if head_dim != 128 {
16954            return Ok(None);
16955        }
16956        let heads = geometry.n_head as usize;
16957        let window = geometry.window.map(|w| w as usize);
16958        let ranks = tp.runtime.devices().len();
16959        // The rows kernels never arm base_d; refuse once a ring could have rebased
16960        // without an armed base (the table would read base=0 after a real rebase).
16961        for r in 0..t {
16962            let cache = &caches[row_to_cache(r)];
16963            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16964                return Ok(None);
16965            };
16966            if distributed.kv_dim_k() != distributed.kv_dim_v() {
16967                return Ok(None);
16968            }
16969            let rank0 = distributed.rank(0).ok_or("rope fa pass lost rank 0")?;
16970            if rank0.base_d().is_none()
16971                && distributed.staged_len() + t > distributed.physical_capacity()
16972            {
16973                return Ok(None);
16974            }
16975        }
16976        let mut rope_freqs = Vec::with_capacity(ranks);
16977        for rank in 0..ranks {
16978            let engine = tp
16979                .runtime
16980                .rank_engine(rank)
16981                .ok_or("rope fa pass lost a rank engine")?;
16982            rope_freqs.push(if geometry.rope_factors {
16983                match self
16984                    .step35_aux
16985                    .as_ref()
16986                    .and_then(|aux| aux.rope_freqs(engine))
16987                {
16988                    Some(f) => Some(f),
16989                    None => return Ok(None),
16990                }
16991            } else {
16992                None
16993            });
16994        }
16995        let ladder = |t_kv: usize| -> usize {
16996            if t_kv <= 2048 {
16997                16
16998            } else if t_kv <= 16384 {
16999                64
17000            } else {
17001                128
17002            }
17003        };
17004        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17005        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17006        let mut tab_keys = vec![0u64; ranks];
17007        for (r, &pos) in positions.iter().enumerate().take(t) {
17008            let cache = &caches[row_to_cache(r)];
17009            let distributed = cache.tp_kv[il]
17010                .as_ref()
17011                .ok_or("rope fa pass lost a distributed KV cache")?;
17012            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17013            let t_kv = window
17014                .map(|w| (pos as usize + 1).min(w))
17015                .unwrap_or(pos as usize + 1);
17016            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17017            for rank in 0..ranks {
17018                let engine = tp
17019                    .runtime
17020                    .rank_engine(rank)
17021                    .ok_or("rope fa pass lost a rank engine")?;
17022                let rank_cache = distributed
17023                    .rank(rank)
17024                    .ok_or("rope fa pass lost a KV cache rank")?;
17025                let _main = engine.gpu.enter_main()?;
17026                let s = engine.stream();
17027                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17028                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17029                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17030                let bp = match rank_cache.base_d() {
17031                    Some(b) => {
17032                        let (p, _g) = b.device_ptr(&s);
17033                        p as u64
17034                    }
17035                    None => 0u64,
17036                };
17037                tab_keys[rank] = tab_keys[rank]
17038                    .rotate_left(9)
17039                    .wrapping_add(kp as u64)
17040                    .wrapping_add(bp)
17041                    .wrapping_add(il as u64);
17042                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17043            }
17044        }
17045        let ws_index = tp
17046            .runtime
17047            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17048        tp.runtime
17049            .decode_v2_rope_fa_rows(
17050                ws_index,
17051                e,
17052                &tp.o,
17053                &session_parts,
17054                &tab_keys,
17055                positions,
17056                stage_pos,
17057                false,
17058                &attention.q_norm,
17059                &attention.k_norm,
17060                &rope_freqs,
17061                t,
17062                head_dim,
17063                geometry.n_rot as usize,
17064                window.unwrap_or(0),
17065                max_ns,
17066                geometry.attention_scale(),
17067                k_tok_bytes,
17068                v_tok_bytes,
17069                self.cfg.rms_eps,
17070                geometry.rope_base,
17071            )
17072            .map(Some)
17073    }
17074
17075    /// Multi-session t-row fa join (batched serving): per-row table entries point at
17076    /// each row's OWN session rings/counters (len_back = 0 — every session appended
17077    /// exactly its one row). Tables stage once per (layer, rank, session-set, t).
17078    #[allow(clippy::too_many_arguments)]
17079    pub(crate) fn step35_batch_fa_rows_join(
17080        &self,
17081        e: &Engine,
17082        il: usize,
17083        caches: &[&mut Cache],
17084        row_to_cache: impl Fn(usize) -> usize,
17085        positions: &[i32],
17086        t: usize,
17087    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17088        use cudarc::driver::DevicePtr;
17089        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17090            return Err("batch fa rows join expects full attention".into());
17091        };
17092        let tp = fa
17093            .step_tp_qkv
17094            .as_ref()
17095            .ok_or("batch fa rows join lost its resident projections")?;
17096        let geometry = self.step35_geom(il);
17097        let heads = geometry.n_head as usize;
17098        let head_dim = geometry.head_dim_k as usize;
17099        let window = geometry.window.map(|w| w as usize);
17100        let ladder = |t_kv: usize| -> usize {
17101            if t_kv <= 2048 {
17102                16
17103            } else if t_kv <= 16384 {
17104                64
17105            } else {
17106                128
17107            }
17108        };
17109        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17110        for (r, &pos) in positions.iter().enumerate() {
17111            let cache = &caches[row_to_cache(r)];
17112            let distributed = cache.tp_kv[il]
17113                .as_ref()
17114                .ok_or("batch fa rows join lost a distributed KV cache")?;
17115            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17116            let t_kv = window
17117                .map(|w| (pos as usize + 1).min(w))
17118                .unwrap_or(pos as usize + 1);
17119            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17120        }
17121        // Multi-session tables also rebuild from every live K/V/len/base tuple. Keeping a
17122        // process-lifetime raw-pointer cache here omitted V and len identity and had no
17123        // allocation generation, so allocator reuse could bind one request to another.
17124        let ranks = tp.runtime.devices().len();
17125        let mut tables = Vec::with_capacity(ranks);
17126        for rank in 0..ranks {
17127            let engine = tp
17128                .runtime
17129                .rank_engine(rank)
17130                .ok_or("batch fa rows join lost a rank engine")?;
17131            let _main = engine.gpu.enter_main()?;
17132            let s = engine.stream();
17133            let mut host = Vec::with_capacity(t * 6);
17134            for r in 0..t {
17135                let cache = &caches[row_to_cache(r)];
17136                let distributed = cache.tp_kv[il]
17137                    .as_ref()
17138                    .ok_or("batch fa rows join lost a distributed KV cache")?;
17139                let rank_cache = distributed
17140                    .rank(rank)
17141                    .ok_or("batch fa rows join lost a KV cache rank")?;
17142                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17143                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17144                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17145                let bp = match rank_cache.base_d() {
17146                    Some(b) => {
17147                        let (p, _g) = b.device_ptr(&s);
17148                        p as u64
17149                    }
17150                    None => 0u64,
17151                };
17152                host.extend_from_slice(&[kp as u64, vp as u64, lp as u64, bp, 0u64, 0u64]);
17153            }
17154            tables.push(engine.stream().clone_htod(&host)?);
17155        }
17156        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17157        let ws_index = tp
17158            .runtime
17159            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17160        tp.runtime.decode_v2_fa_rows_join(
17161            ws_index,
17162            e,
17163            &tp.o,
17164            &tabs,
17165            t,
17166            head_dim,
17167            window.unwrap_or(0),
17168            max_ns,
17169            geometry.attention_scale(),
17170            k_tok_bytes,
17171            v_tok_bytes,
17172        )
17173    }
17174
17175    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
17176    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
17177    /// slab on `e`.
17178    pub(crate) fn step35_verify_spec_fa2_join(
17179        &self,
17180        e: &Engine,
17181        il: usize,
17182        cache: &Cache,
17183        pos0: usize,
17184    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17185        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17186            return Err("spec fa2 join expects full attention".into());
17187        };
17188        let tp = fa
17189            .step_tp_qkv
17190            .as_ref()
17191            .ok_or("spec fa2 join lost its resident projections")?;
17192        let geometry = self.step35_geom(il);
17193        let heads = geometry.n_head as usize;
17194        let head_dim = geometry.head_dim_k as usize;
17195        let window = geometry.window.map(|w| w as usize);
17196        // POST-append view of the second row (kernel T1 = len - lstart with len =
17197        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
17198        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
17199        let distributed = cache.tp_kv[il]
17200            .as_ref()
17201            .ok_or("spec fa2 join lost its distributed KV cache")?;
17202        let ws_index = tp
17203            .runtime
17204            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17205        tp.runtime.decode_v2_spec_fa2_join(
17206            ws_index,
17207            e,
17208            &tp.o,
17209            distributed,
17210            head_dim,
17211            window.unwrap_or(0),
17212            bucket,
17213            geometry.attention_scale(),
17214        )
17215    }
17216
17217    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
17218    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
17219    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
17220    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
17221    /// (caller falls back to the per-column walk).
17222    pub(crate) fn step35_verify_moe_tn(
17223        &self,
17224        e: &Engine,
17225        il: usize,
17226        z_t: &CudaSlice<f32>,
17227        t: usize,
17228    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17229        let layer = &self.layers[il];
17230        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
17231            return Ok(None);
17232        };
17233        let Some(tp) = m.step_tp.as_ref() else {
17234            return Ok(None);
17235        };
17236        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
17237            return Ok(None);
17238        };
17239        if !crate::tp::step_nvfp4_dev_routes_enabled()?
17240            || !crate::tp::step_tp_dev_router_enabled()?
17241            || !crate::tp::nvfp4_bank_v2_on()
17242            || bank.ep2
17243        {
17244            return Ok(None);
17245        }
17246        let cfg = &self.cfg;
17247        let Some(moe) = cfg.moe.as_ref() else {
17248            return Ok(None);
17249        };
17250        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
17251            return Ok(None);
17252        };
17253        let n_embd = cfg.n_embd as usize;
17254        let n_expert = moe.expert_count as usize;
17255        let n_used = moe.expert_used_count as usize;
17256        if t < 2 || t > 32 || z_t.len() < t * n_embd {
17257            return Err("verify moe t-row geometry".into());
17258        }
17259        let trace = std::env::var("MEMRA_TN_TRACE").as_deref() == Ok("1");
17260        if trace {
17261            eprintln!("[tn-trace] il={il} t={t} logits");
17262        }
17263        let logits = Self::moe_router_logits(e, m, z_t, t, cfg)?;
17264        // Persistent selection rows (host-op diet, same shape law as the t=1 SELW),
17265        // sized for the widest walk (t <= 8).
17266        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
17267            std::sync::Mutex::new(None);
17268        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
17269        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
17270            *selw = Some((
17271                e.ctx().ordinal(),
17272                e.htod_i32(&vec![0i32; 32 * n_used])?,
17273                e.htod(&vec![0.0f32; 32 * n_used])?,
17274            ));
17275        }
17276        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
17277        if trace {
17278            eprintln!("[tn-trace] il={il} topk logits_len={}", logits.len());
17279        }
17280        e.moe_router_sigmoid_topk_into(
17281            &logits,
17282            t,
17283            n_expert,
17284            n_used,
17285            m.active_count(),
17286            &m.exp_probs_b_dev,
17287            &m.active_experts_dev,
17288            sf,
17289            route_norm,
17290            sel_d,
17291            w_d,
17292        )?;
17293        if trace {
17294            eprintln!("[tn-trace] il={il} driver");
17295        }
17296        let mut out_t = tp
17297            .runtime
17298            .run_tensor_parallel_routes_nvfp4_device_routed_tn(
17299                bank,
17300                e,
17301                z_t,
17302                sel_d,
17303                w_d,
17304                t,
17305                n_used,
17306                tp.activation_limit,
17307            )?;
17308        if trace {
17309            eprintln!("[tn-trace] il={il} shexp out_t={}", out_t.len());
17310        }
17311        // Shared expert: ONE t-row pass through the per-row-exact twins when the bf16
17312        // dual-silu shape holds (each row's program == the t=1 fused path); otherwise the
17313        // exact t=1 program per column.
17314        if !Self::step35_shexp_rows(e, m, z_t, t, cfg, il as u16, &mut out_t)? {
17315            let mut z_row = e.uninit(n_embd)?;
17316            let mut out_row = e.uninit(n_embd)?;
17317            for c in 0..t {
17318                e.dtod_copy_view(&z_t.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
17319                e.dtod_copy_view(&out_t.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
17320                Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
17321                e.dtod_copy_into(&out_row, &mut out_t, c * n_embd)?;
17322            }
17323        }
17324        Ok(Some(out_t))
17325    }
17326
17327    /// T-ROW shared expert (spec verify / batched serving): dual-silu + down + gate +
17328    /// scaled accumulate over all rows in four launches, each the per-row-exact twin of
17329    /// the t=1 fused path. Returns false (untouched `out_t`) when the shape is ineligible.
17330    fn step35_shexp_rows(
17331        e: &Engine,
17332        m: &MoeWeights,
17333        z_t: &CudaSlice<f32>,
17334        t: usize,
17335        cfg: &ModelConfig,
17336        il: u16,
17337        out_t: &mut CudaSlice<f32>,
17338    ) -> Result<bool, Box<dyn std::error::Error>> {
17339        let n_embd = cfg.n_embd as usize;
17340        let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
17341            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
17342        else {
17343            return Ok(false);
17344        };
17345        if !crate::Engine::bf16_mmv_on() || n_embd % 8 != 0 || cfg.m3.is_some() {
17346            return Ok(false);
17347        }
17348        let (
17349            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
17350            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
17351            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
17352        ) = (gate_shexp, up_shexp, down_shexp)
17353        else {
17354            return Ok(false);
17355        };
17356        let n_ff_sh = gate_shexp.out_features();
17357        let lim = cfg.clamp_shexp_at(il as u32);
17358        // Persistent t-row buffers (widest walk t <= 8).
17359        static WS: std::sync::Mutex<Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>> =
17360            std::sync::Mutex::new(None);
17361        let mut guard = WS.lock().map_err(|_| "shexp rows ws lock is poisoned")?;
17362        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
17363        if guard
17364            .as_ref()
17365            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
17366        {
17367            *guard = Some((
17368                pins.0,
17369                pins.1,
17370                pins.2,
17371                e.uninit(32 * n_ff_sh)?,
17372                e.uninit(32 * n_embd)?,
17373            ));
17374        }
17375        let (_, _, _, act_t, sh_t) = guard.as_mut().expect("armed above");
17376        e.matvec_bf16_dual_silu_rows_into(wg, wu, z_t, act_t, n_embd, n_ff_sh, lim, t)?;
17377        e.matvec_bf16_rows_into(wd, act_t, sh_t, n_ff_sh, n_embd, t)?;
17378        // Head gate: sigmoid_dot_rows is the exact t=1 expression per row; gate-less
17379        // shexp accumulates at weight 1 (the fuse_da identity).
17380        let gate = match &m.gate_inp_shexp {
17381            Some(gate_inp_shexp) => {
17382                e.sigmoid_dot_rows(z_t, gate_inp_shexp.float_data(), n_embd, t)?
17383            }
17384            None => e.htod(&vec![1.0f32; t])?,
17385        };
17386        e.add_scaled_rows(sh_t, &gate, out_t, n_embd, t)?;
17387        Ok(true)
17388    }
17389
17390    fn step35_tp_decode_attn_resident_v2(
17391        &self,
17392        e: &Engine,
17393        fa: &FullAttnLayer,
17394        il: usize,
17395        h: &CudaSlice<f32>,
17396        pos_d: &CudaSlice<i32>,
17397        cache: &mut Cache,
17398    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17399        let tp = fa
17400            .step_tp_qkv
17401            .as_ref()
17402            .ok_or("Step TP decode lost its resident projections")?;
17403        let attention = tp
17404            .attention
17405            .as_ref()
17406            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
17407        if !tp.runtime.native_p2p() {
17408            return Err("rank-local Step attention requires native P2P".into());
17409        }
17410        if crate::Engine::kv_fp8_on() {
17411            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
17412        }
17413
17414        let geometry = self.step35_geom(il);
17415        let window = geometry.window.map(|window| window as usize);
17416        let ranks = tp.runtime.devices().len();
17417        let head_dim = geometry.head_dim_k as usize;
17418        let heads = geometry.n_head as usize;
17419        let kv_heads = geometry.n_head_kv as usize;
17420        if heads % ranks != 0 || kv_heads % ranks != 0 {
17421            return Err(format!(
17422                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
17423            )
17424            .into());
17425        }
17426        let local_heads = heads / ranks;
17427        let local_kv_heads = kv_heads / ranks;
17428        let max_ctx = cache.max_ctx;
17429
17430        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
17431
17432        let base_len = cache.kv[il]
17433            .as_ref()
17434            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
17435            .len;
17436        {
17437            let distributed = cache.tp_kv[il]
17438                .as_ref()
17439                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
17440            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
17441                return Err(format!(
17442                    "Step TP layer {il} cache lengths diverged before decode: \
17443                     local={base_len} distributed={}/{}",
17444                    distributed.committed_len(),
17445                    distributed.staged_len()
17446                )
17447                .into());
17448            }
17449        }
17450        if pos_d.len() != 1 {
17451            return Err(format!(
17452                "rank-local Step decode requires one position, got {}",
17453                pos_d.len()
17454            )
17455            .into());
17456        }
17457
17458        let decode_input = attention
17459            .decode_input
17460            .as_ref()
17461            .ok_or("Step TP decode v2 requires the replicated decode input")?;
17462        let mut decode_input = decode_input
17463            .lock()
17464            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
17465
17466        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
17467        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
17468        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
17469        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
17470        let use_gate_shards = (attention.gate_shards.is_some()
17471            || attention.gate_shards_bf16.is_some())
17472            && crate::tp::step_tp_qkv_fused_enabled()?;
17473        let gate_raw = if use_gate_shards {
17474            None
17475        } else {
17476            let gate_weight = fa
17477                .attn_gate
17478                .as_ref()
17479                .ok_or("step35 layer is missing attn_gate.weight")?;
17480            let gate_raw = e.matmul(gate_weight, h, 1)?;
17481            if gate_raw.len() != heads {
17482                return Err(format!(
17483                    "Step TP layer {il} gate output {} != {heads}",
17484                    gate_raw.len()
17485                )
17486                .into());
17487            }
17488            Some(gate_raw)
17489        };
17490
17491        let ws_index = tp
17492            .runtime
17493            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17494        let mut ws_guard = tp
17495            .runtime
17496            .decode_v2_workspace()
17497            .lock()
17498            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
17499        let ws = ws_guard
17500            .get_mut(ws_index)
17501            .ok_or("Step TP decode v2 workspace missing after ensure")?;
17502
17503        let mut rope_freqs = Vec::with_capacity(ranks);
17504        for rank in 0..ranks {
17505            let engine = tp
17506                .runtime
17507                .rank_engine(rank)
17508                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17509            rope_freqs.push(if geometry.rope_factors {
17510                self.step35_aux
17511                    .as_ref()
17512                    .and_then(|aux| aux.rope_freqs(engine))
17513            } else {
17514                None
17515            });
17516        }
17517        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
17518        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
17519        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
17520        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
17521        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
17522        // the fused rope+append+inc launch on dcw tokens.)
17523        let staged_next = base_len + 1;
17524        let t_kv_eff = window
17525            .map(|window| staged_next.min(window))
17526            .unwrap_or(staged_next);
17527        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
17528            let (write_row, would_rebase) = cache.tp_kv[il]
17529                .as_ref()
17530                .expect("distributed cache checked above")
17531                .peek_append_ring(1)?;
17532            if !would_rebase {
17533                // Arm the base mirrors on first use: base = logical staged - physical row.
17534                let base = (base_len - write_row) as i32;
17535                let distributed = cache.tp_kv[il]
17536                    .as_mut()
17537                    .expect("distributed cache checked above");
17538                for rank in 0..ranks {
17539                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
17540                        format!("Step TP layer {il} has no engine for rank {rank}")
17541                    })?;
17542                    let _main = engine.gpu.enter_main()?;
17543                    let rank_cache = distributed
17544                        .rank_mut(rank)
17545                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17546                    if rank_cache.base_d().is_none() {
17547                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
17548                    }
17549                }
17550            }
17551            !would_rebase
17552        };
17553        let fuse_rope = dcw
17554            && crate::tp::fuse_rope_append_on()
17555            && head_dim == 128
17556            && cache.tp_kv[il]
17557                .as_ref()
17558                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
17559                .unwrap_or(false);
17560
17561        let tcol_col = crate::tp::take_verify_tcol();
17562        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
17563        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
17564        // state must advance per column) but skips the fa+gate launch; post-rope q and
17565        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
17566        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
17567        // normally and the walk consumes the real output — stash flag stays unset).
17568        let fa2_col = crate::tp::take_spec_fa2_defer();
17569        tp.runtime.decode_v2_input_qkv(
17570            ws,
17571            e,
17572            h,
17573            pos_d,
17574            gate_raw.as_ref(),
17575            if !use_gate_shards {
17576                None
17577            } else if let Some(shards) = attention.gate_shards.as_deref() {
17578                Some(crate::tp::StepTpGateShards::F32(shards))
17579            } else {
17580                attention
17581                    .gate_shards_bf16
17582                    .as_deref()
17583                    .map(crate::tp::StepTpGateShards::Bf16)
17584            },
17585            &mut decode_input,
17586            &tp.q,
17587            &tp.k,
17588            &tp.v,
17589            &attention.q_norm,
17590            &attention.k_norm,
17591            head_dim,
17592            geometry.n_rot as usize,
17593            geometry.rope_base,
17594            &rope_freqs,
17595            self.cfg.rms_eps,
17596            fuse_rope,
17597            tcol_col,
17598        )?;
17599
17600        let transaction = cache.tp_kv[il]
17601            .as_mut()
17602            .expect("distributed cache checked above")
17603            .begin_transaction()?;
17604        let append_result = tp.runtime.append_tp_kv_transaction_inner(
17605            cache.tp_kv[il]
17606                .as_mut()
17607                .expect("distributed cache checked above"),
17608            transaction,
17609            &ws.k,
17610            &ws.v_raw,
17611            1,
17612            dcw,
17613        );
17614        if let Err(error) = append_result {
17615            let _ = tp.runtime.rollback_tp_kv_transaction(
17616                cache.tp_kv[il]
17617                    .as_mut()
17618                    .expect("distributed cache checked above"),
17619                transaction,
17620            );
17621            return Err(error);
17622        }
17623
17624        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17625            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
17626            // reborrows the cache mutably per rank.
17627            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
17628                let distributed = cache.tp_kv[il]
17629                    .as_ref()
17630                    .expect("distributed cache checked above");
17631                let staged_len = distributed.staged_len();
17632                let view_start = window
17633                    .map(|window| staged_len.saturating_sub(window))
17634                    .unwrap_or(0);
17635                (
17636                    staged_len,
17637                    distributed.physical_range(view_start, staged_len)?,
17638                    distributed.k_tok_bytes(),
17639                    distributed.v_tok_bytes(),
17640                    distributed.physical_capacity(),
17641                )
17642            };
17643            let view_start = window
17644                .map(|window| staged_len.saturating_sub(window))
17645                .unwrap_or(0);
17646            let t_kv = staged_len - view_start;
17647            for rank in 0..ranks {
17648                let engine = tp
17649                    .runtime
17650                    .rank_engine(rank)
17651                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17652                let _main = engine.gpu.enter_main()?;
17653                if dcw {
17654                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
17655                    // stream visit. distributed is borrowed shared here; the planes need mut —
17656                    // reborrow through the cache Option (the closure holds cache mutably).
17657                    {
17658                        let distributed_mut = cache.tp_kv[il]
17659                            .as_mut()
17660                            .expect("distributed cache checked above");
17661                        let (kv_dim_k, kv_dim_v) =
17662                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
17663                        let (k_tok_bytes, v_tok_bytes) =
17664                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
17665                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17666                            format!("Step TP layer {il} has no KV cache rank {rank}")
17667                        })?;
17668                        let (k_plane, v_plane, len_d, base_d) =
17669                            rank_cache.planes_and_counters_mut();
17670                        if fuse_rope {
17671                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
17672                            // + last-block len inc in ONE launch. Bit-identical bodies.
17673                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
17674                            let crate::tp::StepTpDecodeV2Ws {
17675                                q_raw,
17676                                k_raw,
17677                                v_raw,
17678                                q,
17679                                k,
17680                                pos,
17681                                pos_stage,
17682                                fuse_ctr,
17683                                ..
17684                            } = &mut *ws;
17685                            // Same-device rank: the staged-copy elision leaves pos[rank]
17686                            // stale — read the e-context pos stage directly (mirrors the
17687                            // rope elision in input_qkv_rank).
17688                            let pos_ref: &CudaSlice<i32> = if same_dev {
17689                                pos_stage
17690                                    .as_ref()
17691                                    .ok_or("step TP decode v2 pos stage not armed")?
17692                            } else {
17693                                &pos[rank]
17694                            };
17695                            engine.qk_norm_rope_append_inc_dcw(
17696                                &q_raw[rank],
17697                                &k_raw[rank],
17698                                &v_raw[rank],
17699                                &attention.q_norm[rank],
17700                                &attention.k_norm[rank],
17701                                &mut q[rank],
17702                                &mut k[rank],
17703                                pos_ref,
17704                                k_plane,
17705                                v_plane,
17706                                len_d,
17707                                base_d,
17708                                &mut fuse_ctr[rank],
17709                                kv_dim_k,
17710                                kv_dim_v,
17711                                k_tok_bytes,
17712                                v_tok_bytes,
17713                                head_dim,
17714                                geometry.n_rot as usize,
17715                                local_heads,
17716                                local_kv_heads,
17717                                self.cfg.rms_eps,
17718                                geometry.rope_base,
17719                                1.0,
17720                                rope_freqs[rank],
17721                            )?;
17722                        } else {
17723                            engine.append_kv_quantized_dcw(
17724                                &ws.k[rank],
17725                                &ws.v_raw[rank],
17726                                k_plane,
17727                                v_plane,
17728                                len_d,
17729                                base_d,
17730                                kv_dim_k,
17731                                kv_dim_v,
17732                                k_tok_bytes,
17733                                v_tok_bytes,
17734                            )?;
17735                        }
17736                        if !fuse_rope {
17737                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17738                                format!("Step TP layer {il} has no KV cache rank {rank}")
17739                            })?;
17740                            engine.inc_i32(rank_cache.len_d_mut())?;
17741                        }
17742                    }
17743                    let distributed = cache.tp_kv[il]
17744                        .as_ref()
17745                        .expect("distributed cache checked above");
17746                    let rank_cache = distributed
17747                        .rank(rank)
17748                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17749                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
17750                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
17751                    if fa2_col.is_some() {
17752                        // SPEC_FA2 defer: append landed above; the fa for this column
17753                        // runs in the T=2 joined launch after the pair's second append.
17754                        continue;
17755                    }
17756                    {
17757                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
17758                        // the gated output directly (bit-identical; one launch saved).
17759                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
17760                        engine.fa_decode_dcw(
17761                            &q[rank],
17762                            &k_ring,
17763                            &v_ring,
17764                            &mut gated[rank],
17765                            head_dim,
17766                            local_heads,
17767                            local_kv_heads,
17768                            rank_cache.len_d(),
17769                            rank_cache.base_d(),
17770                            window.unwrap_or(0),
17771                            t_kv,
17772                            geometry.attention_scale(),
17773                            k_tok_bytes_c,
17774                            v_tok_bytes_c,
17775                            Some(&gate[rank]),
17776                        )?;
17777                    }
17778                    continue;
17779                }
17780                let distributed = cache.tp_kv[il]
17781                    .as_ref()
17782                    .expect("distributed cache checked above");
17783                let rank_cache = distributed
17784                    .rank(rank)
17785                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17786                let k_view = engine.view_u8_range(
17787                    rank_cache.k(),
17788                    physical.start * k_tok_bytes_c,
17789                    physical.end * k_tok_bytes_c,
17790                );
17791                let v_view = engine.view_u8_range(
17792                    rank_cache.v(),
17793                    physical.start * v_tok_bytes_c,
17794                    physical.end * v_tok_bytes_c,
17795                );
17796                engine.fa_decode_kvmod(
17797                    &ws.q[rank],
17798                    &k_view,
17799                    &v_view,
17800                    &mut ws.attn_out[rank],
17801                    head_dim,
17802                    local_heads,
17803                    local_kv_heads,
17804                    t_kv,
17805                    geometry.attention_scale(),
17806                    k_tok_bytes_c,
17807                    v_tok_bytes_c,
17808                    false,
17809                )?;
17810                engine.attn_head_gate(
17811                    &ws.attn_out[rank],
17812                    &ws.gate[rank],
17813                    &mut ws.gated[rank],
17814                    None,
17815                    head_dim,
17816                    local_heads,
17817                    1,
17818                )?;
17819            }
17820
17821            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
17822            // column's `gated` rows and skip the per-column finish choreography entirely
17823            // (the batched b4_tcol + join runs after every column). The returned buffer
17824            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
17825            // stashed flag, never this buffer. Ineligible configs fall back to the
17826            // normal finish and the driver consumes the real `mixed` per column.
17827            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
17828                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
17829                // finish all run in the joined pass. Returned buffer is UNWRITTEN
17830                // (oproj-defer precedent — the walk reads the stash flag, never this).
17831                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
17832                crate::tp::set_spec_fa2_stashed();
17833                e.uninit(ws.o_out)?
17834            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
17835                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
17836                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
17837                    crate::tp::set_tcol_oproj_stashed();
17838                    e.uninit(ws.o_out)?
17839                } else {
17840                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17841                }
17842            } else {
17843                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17844            };
17845
17846            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
17847            // decode_v2_finish ordered behind the root event. Same math and cache state
17848            // transitions as v1.
17849            let local = cache.kv[il]
17850                .as_mut()
17851                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
17852            if local.len != base_len || base_len + 1 > max_ctx {
17853                return Err(format!(
17854                    "Step TP layer {il} local cache changed during decode: \
17855                     len={} base={base_len} max={max_ctx}",
17856                    local.len
17857                )
17858                .into());
17859            }
17860            if crate::tp::no_local_shadow_on() {
17861                // Lengths advance, contents stay stale (graph-door precedent: decode reads
17862                // only the distributed TP caches; local contents feed spec/MTP scratch).
17863                local.len = base_len + 1;
17864                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
17865                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
17866                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
17867                if !crate::tp::len_mirror_lazy_on() {
17868                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
17869                }
17870            } else {
17871                let retain_from = window
17872                    .map(|window| {
17873                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
17874                        let rollback_retain =
17875                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
17876                        staged_retain.min(rollback_retain)
17877                    })
17878                    .unwrap_or(0);
17879                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
17880                e.append_kv_quantized(
17881                    &ws.k_shadow,
17882                    &ws.v_shadow,
17883                    &mut local.k,
17884                    &mut local.v,
17885                    write_row,
17886                    local.kv_dim_k,
17887                    local.kv_dim_v,
17888                    local.k_tok_bytes,
17889                    local.v_tok_bytes,
17890                    false,
17891                )?;
17892                local.len = base_len + 1;
17893                e.set_i32_one(&mut local.len_d, local.len as i32)?;
17894            }
17895            Ok(output)
17896        })();
17897
17898        let output = match staged {
17899            Ok(output) => output,
17900            Err(error) => {
17901                let _ = tp.runtime.rollback_tp_kv_transaction(
17902                    cache.tp_kv[il]
17903                        .as_mut()
17904                        .expect("distributed cache checked above"),
17905                    transaction,
17906                );
17907                if let Some(local) = cache.kv[il].as_mut() {
17908                    local.len = base_len;
17909                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
17910                }
17911                return Err(error);
17912            }
17913        };
17914        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
17915        // the rank counters (same value as the absolute re-set on full accept), so commit
17916        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
17917        // keeps the absolute set (its appends do NOT inc).
17918        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
17919        if lazy_commit {
17920            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
17921                cache.tp_kv[il]
17922                    .as_mut()
17923                    .expect("distributed cache checked above"),
17924                transaction,
17925                1,
17926            ) {
17927                let _ = tp.runtime.rollback_tp_kv_transaction(
17928                    cache.tp_kv[il]
17929                        .as_mut()
17930                        .expect("distributed cache checked above"),
17931                    transaction,
17932                );
17933                let local = cache.kv[il].as_mut().expect("local cache checked above");
17934                local.len = base_len;
17935                e.set_i32_one(&mut local.len_d, base_len as i32)?;
17936                return Err(error);
17937            }
17938        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
17939            cache.tp_kv[il]
17940                .as_mut()
17941                .expect("distributed cache checked above"),
17942            transaction,
17943            1,
17944        ) {
17945            let _ = tp.runtime.rollback_tp_kv_transaction(
17946                cache.tp_kv[il]
17947                    .as_mut()
17948                    .expect("distributed cache checked above"),
17949                transaction,
17950            );
17951            let local = cache.kv[il].as_mut().expect("local cache checked above");
17952            local.len = base_len;
17953            e.set_i32_one(&mut local.len_d, base_len as i32)?;
17954            return Err(error);
17955        }
17956
17957        let committed = cache.tp_kv[il]
17958            .as_ref()
17959            .expect("distributed cache checked above")
17960            .committed_len();
17961        let local_len = cache.kv[il]
17962            .as_ref()
17963            .expect("local cache checked above")
17964            .len;
17965        if committed != local_len {
17966            return Err(format!(
17967                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
17968            )
17969            .into());
17970        }
17971        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
17972        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
17973            eprintln!(
17974                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
17975                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
17976                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
17977                 attention_tensor_parallel=true attention_scope={} \
17978                 input_path=root-device-replicated gate_tensor_parallel=false \
17979                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
17980                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
17981                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
17982                 performance_claim=false (logged once; every decode layer runs this driver)",
17983                tp.layer,
17984                tp.devices,
17985                if window.is_some() {
17986                    "rank-local-swa-ring"
17987                } else {
17988                    "rank-local-global"
17989                },
17990                tp.runtime.transport_label(),
17991                tp.runtime.bulk_p2p(),
17992            );
17993        }
17994        Ok(output)
17995    }
17996
17997    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
17998    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
17999    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
18000    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
18001    /// requiring `attn_gate`).
18002    ///
18003    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
18004    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
18005    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
18006    #[allow(clippy::too_many_arguments)]
18007    pub(crate) fn step35_decode_attn(
18008        &self,
18009        e: &Engine,
18010        fa: &FullAttnLayer,
18011        il: usize,
18012        h: &CudaSlice<f32>,
18013        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
18014        pos_d: &CudaSlice<i32>,
18015        cache: &mut Cache,
18016    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18017        if fa
18018            .step_tp_qkv
18019            .as_ref()
18020            .is_some_and(|tp| tp.attention.is_some())
18021        {
18022            if pre_q.is_some() {
18023                return Err(
18024                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
18025                     pre-quantized decode path"
18026                        .into(),
18027                );
18028            }
18029            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
18030        }
18031
18032        let geometry = self.step35_geom(il);
18033        let hd = geometry.head_dim_k as usize;
18034        let nkv = geometry.n_head_kv as usize;
18035        let nh = geometry.n_head as usize;
18036        let rbase = geometry.rope_base;
18037        let scale = geometry.attention_scale();
18038        let swa = geometry.window.is_some();
18039        let eps = self.cfg.rms_eps;
18040        let win = geometry.window.unwrap_or(0) as usize;
18041        let n_rot = geometry.n_rot as usize;
18042        let n_embd = self.cfg.n_embd as usize;
18043        let gw = fa
18044            .attn_gate
18045            .as_ref()
18046            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
18047
18048        let tp_qkv = if fa.step_tp_qkv.is_some() {
18049            if pre_q.is_some() {
18050                return Err(
18051                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
18052                     pre-quantized decode path"
18053                        .into(),
18054                );
18055            }
18056            self.step35_tp_qkv(e, fa, h, 1)?
18057        } else {
18058            None
18059        };
18060
18061        let (q0, k0, v0, gt) = match tp_qkv {
18062            Some(mut g3) => {
18063                let v = g3.pop().unwrap();
18064                let k = g3.pop().unwrap();
18065                let q = g3.pop().unwrap();
18066                let gt = e.matmul(gw, h, 1)?;
18067                (q, k, v, gt)
18068            }
18069            None => match pre_q {
18070                Some((hq, hdq)) => {
18071                    debug_assert!(
18072                        e.uses_q8_1_fast(gw),
18073                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
18074                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
18075                    );
18076                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
18077                        Some(t3) => t3,
18078                        None => (
18079                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
18080                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
18081                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
18082                        ),
18083                    };
18084                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
18085                    (a, b, c, gt)
18086                }
18087                None => {
18088                    if e.uses_q8_1_fast(&fa.wq)
18089                        && e.uses_q8_1_fast(&fa.wk)
18090                        && e.uses_q8_1_fast(&fa.wv)
18091                        && e.uses_q8_1_fast(gw)
18092                    {
18093                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
18094                        let (a, b, c) =
18095                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
18096                                Some(t3) => t3,
18097                                None => (
18098                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
18099                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
18100                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
18101                                ),
18102                            };
18103                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
18104                        (a, b, c, gt)
18105                    } else {
18106                        (
18107                            e.matmul(&fa.wq, h, 1)?,
18108                            e.matmul(&fa.wk, h, 1)?,
18109                            e.matmul(&fa.wv, h, 1)?,
18110                            e.matmul(gw, h, 1)?,
18111                        )
18112                    }
18113                }
18114            },
18115        };
18116
18117        let mut q = e.uninit(nh * hd)?;
18118        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
18119        let mut k = e.uninit(nkv * hd)?;
18120        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
18121        let ff = if swa {
18122            None
18123        } else {
18124            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
18125        };
18126        #[cfg(debug_assertions)]
18127        if let Some(ff) = ff {
18128            crate::debug_assert_tensor_stream_device(
18129                ff,
18130                &e.stream(),
18131                "step35_decode_attn.rope_freqs",
18132            );
18133        }
18134        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
18135
18136        if std::env::var("MEMRA_NOFA").is_ok() {
18137            return Err(
18138                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
18139                        cache; unset MEMRA_NOFA to use fa_decode"
18140                    .into(),
18141            );
18142        }
18143        let kvl = cache.kv[il].as_mut().unwrap();
18144        let next_len = kvl.len + 1;
18145        let (off, t_kv) = if swa && next_len > win {
18146            (next_len - win, win)
18147        } else {
18148            (0, next_len)
18149        };
18150        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
18151        e.append_kv_quantized(
18152            &k,
18153            &v0,
18154            &mut kvl.k,
18155            &mut kvl.v,
18156            write_row,
18157            kvl.kv_dim_k,
18158            kvl.kv_dim_v,
18159            kvl.k_tok_bytes,
18160            kvl.v_tok_bytes,
18161            crate::Engine::kv_fp8_on(),
18162        )?;
18163        kvl.len = next_len;
18164        let physical = kvl.physical_rows(off, off + t_kv)?;
18165        let k_view = e.view_u8_range(
18166            &kvl.k,
18167            physical.start * kvl.k_tok_bytes,
18168            physical.end * kvl.k_tok_bytes,
18169        );
18170        let v_view = e.view_u8_range(
18171            &kvl.v,
18172            physical.start * kvl.v_tok_bytes,
18173            physical.end * kvl.v_tok_bytes,
18174        );
18175        let mut attn = e.uninit(nh * hd)?;
18176        e.fa_decode_kvmod(
18177            &q,
18178            &k_view,
18179            &v_view,
18180            &mut attn,
18181            hd,
18182            nh,
18183            nkv,
18184            t_kv,
18185            scale,
18186            kvl.k_tok_bytes,
18187            kvl.v_tok_bytes,
18188            crate::Engine::kv_fp8_on(),
18189        )?;
18190
18191        let mut ag = e.uninit(nh * hd)?;
18192        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
18193        self.step35_o(e, fa, &ag, 1)
18194    }
18195}
18196
18197// ===================================================================================== //
18198//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
18199//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
18200//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
18201//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
18202//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
18203//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
18204// ===================================================================================== //
18205impl HybridModel {
18206    pub fn is_gemma4_e4b(&self) -> bool {
18207        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
18208    }
18209
18210    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
18211    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
18212    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
18213    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
18214        let g = self.cfg.gemma4.as_ref().unwrap();
18215        let swa = g.swa_pattern[il];
18216        let hd = if swa {
18217            g.key_length_swa
18218        } else {
18219            g.key_length_global
18220        } as usize;
18221        let Mixer::Full(fa) = &self.layers[il].mixer else {
18222            panic!("e4b layer {il} not full-attn")
18223        };
18224        let nh = fa.wq.out_features() / hd;
18225        let nkv = fa.wk.out_features() / hd;
18226        (
18227            hd,
18228            nkv,
18229            nh,
18230            if swa {
18231                g.rope_base_swa
18232            } else {
18233                g.rope_base_global
18234            },
18235            1.0,
18236            swa,
18237        )
18238    }
18239
18240    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
18241    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
18242        self.layers[il]
18243            .gemma4
18244            .as_ref()
18245            .and_then(|b| b.e4b.as_ref())
18246            .and_then(|e4| e4.kv_share.map(|t| t as usize))
18247    }
18248
18249    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
18250    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
18251    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
18252    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
18253    fn gemma4_e4b_inp_pl(
18254        &self,
18255        e: &Engine,
18256        tokens: &[u32],
18257        x_scaled: &CudaSlice<f32>,
18258        t: usize,
18259    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18260        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
18261        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
18262    }
18263
18264    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
18265    fn gemma4_e4b_inp_pl_dev(
18266        &self,
18267        e: &Engine,
18268        tok_d: &CudaSlice<u32>,
18269        x_scaled: &CudaSlice<f32>,
18270        t: usize,
18271    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18272        let aux = self.gemma4_aux.as_ref().unwrap();
18273        let m = aux.e4b.as_ref().unwrap();
18274        let n_embd = self.cfg.n_embd as usize;
18275        let n_layer = self.layers.len();
18276        let width = m.n_epl * n_layer;
18277        let tbl = m.tok_tbl_gpu.get_or_init(|| {
18278            e.upload_u8(&m.tok_embd_bytes)
18279                .expect("e4b per-layer token table upload")
18280        });
18281        let mut a =
18282            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
18283        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
18284        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
18285        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
18286        let mut pn = e.uninit(t * width)?;
18287        e.rms_norm(
18288            &p,
18289            m.proj_norm.float_data(),
18290            &mut pn,
18291            m.n_epl,
18292            t * n_layer,
18293            self.cfg.rms_eps,
18294        )?;
18295        let mut out = e.uninit(t * width)?;
18296        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
18297        Ok(out)
18298    }
18299
18300    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
18301    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
18302    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
18303    /// already holds this forward's rows — the target runs earlier in the stack).
18304    #[allow(clippy::too_many_arguments)]
18305    fn gemma4_e4b_attn(
18306        &self,
18307        e: &Engine,
18308        il: usize,
18309        hq: &CudaSlice<i8>,
18310        hdq: &CudaSlice<f32>,
18311        pos_d: &CudaSlice<i32>,
18312        t: usize,
18313        cache: &mut Cache,
18314        dc_bucket: Option<usize>,
18315    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18316        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
18317        let eps = self.cfg.rms_eps;
18318        let aux = self.gemma4_aux.as_ref().unwrap();
18319        let ones = aux.ones(e);
18320        #[cfg(debug_assertions)]
18321        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
18322        let Mixer::Full(fa) = &self.layers[il].mixer else {
18323            unreachable!()
18324        };
18325        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
18326        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
18327        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
18328        let h0 = e.zeros(0)?;
18329        let h = &h0;
18330
18331        let ff = if swa {
18332            None
18333        } else {
18334            Some(
18335                aux.rope_freqs(e)
18336                    .expect("e4b global rope needs rope_freqs.weight"),
18337            )
18338        };
18339        #[cfg(debug_assertions)]
18340        if let Some(ff) = ff {
18341            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
18342        }
18343        let share = self.gemma4_e4b_kv_target(il);
18344        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
18345        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
18346        let mut q;
18347        if let Some(_tgt) = share {
18348            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
18349            q = e.uninit(t * nh * hd)?;
18350            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
18351            // empty; q0 stands in for the unused k/v pointers).
18352            let mut kdummy = e.uninit(1)?;
18353            let mut vdummy = e.uninit(1)?;
18354            e.rms_norm_qkv_rope(
18355                &q0,
18356                &q0,
18357                &q0,
18358                fa.q_norm.float_data(),
18359                fa.q_norm.float_data(),
18360                ones,
18361                &mut q,
18362                &mut kdummy,
18363                &mut vdummy,
18364                hd,
18365                self.gemma4_rope_dims(il),
18366                nh * t,
18367                0,
18368                pos_d,
18369                nh,
18370                1,
18371                base,
18372                1.0,
18373                ff,
18374                eps,
18375            )?;
18376        } else {
18377            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
18378            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
18379            // q|k|v rows — the cat norm+rope twin consumes it directly.
18380            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
18381            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
18382            q = e.uninit(t * nh * hd)?;
18383            let mut k = e.uninit(t * nkv * hd)?;
18384            let mut v = e.uninit(t * nkv * hd)?;
18385            if t == 1 && cat.is_some() {
18386                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
18387                e.rms_norm_qkv_rope_cat(
18388                    &qkv0,
18389                    fa.q_norm.float_data(),
18390                    fa.k_norm.float_data(),
18391                    ones,
18392                    &mut q,
18393                    &mut k,
18394                    &mut v,
18395                    hd,
18396                    self.gemma4_rope_dims(il),
18397                    nh,
18398                    nkv,
18399                    pos_d,
18400                    nh,
18401                    nkv,
18402                    base,
18403                    1.0,
18404                    ff,
18405                    eps,
18406                )?;
18407            } else {
18408                let (q0, k0, v0) = match if t == 1 {
18409                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
18410                } else {
18411                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
18412                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
18413                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18414                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
18415                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
18416                    } else {
18417                        None
18418                    }
18419                } {
18420                    Some(triple) => triple,
18421                    None => (
18422                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
18423                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
18424                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
18425                    ), // E4B: real v (K != V)
18426                };
18427                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
18428                // the normed rows; V ones-rms, never roped).
18429                e.rms_norm_qkv_rope(
18430                    &q0,
18431                    &k0,
18432                    &v0,
18433                    fa.q_norm.float_data(),
18434                    fa.k_norm.float_data(),
18435                    ones,
18436                    &mut q,
18437                    &mut k,
18438                    &mut v,
18439                    hd,
18440                    self.gemma4_rope_dims(il),
18441                    nh * t,
18442                    nkv * t,
18443                    pos_d,
18444                    nh,
18445                    nkv,
18446                    base,
18447                    1.0,
18448                    ff,
18449                    eps,
18450                )?;
18451            }
18452            let kvl = cache.kv[il].as_mut().unwrap();
18453            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
18454            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
18455            // degenerate tok-0 stream, 2026-07-12).
18456            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18457            if dc_bucket.is_some() {
18458                // DC arm (graph serving): append at the len_d slot, advance the counter
18459                // in-stream — replay-correct, no host len in the launch args. Host mirrors
18460                // are NOT touched here (the replay loop owns them; a bump at capture-record
18461                // time would double-count the capture iteration).
18462                debug_assert!(t == 1);
18463                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
18464                e.append_kv_quantized_row_dc_inc(
18465                    &k,
18466                    &v,
18467                    &mut kvl.k,
18468                    &mut kvl.v,
18469                    &mut kvl.len_d,
18470                    kvl.kv_dim_k,
18471                    kvl.kv_dim_v,
18472                    kvl.k_tok_bytes,
18473                    kvl.v_tok_bytes,
18474                    cls,
18475                )?;
18476            } else {
18477                e.append_kv_quantized_rows(
18478                    &k,
18479                    &v,
18480                    &mut kvl.k,
18481                    &mut kvl.v,
18482                    kvl.len,
18483                    t,
18484                    kvl.kv_dim_k,
18485                    kvl.kv_dim_v,
18486                    kvl.k_tok_bytes,
18487                    kvl.v_tok_bytes,
18488                    cls,
18489                )?;
18490                kvl.len += t;
18491            }
18492            kv_f32 = Some((k, v));
18493        }
18494        // attention: per-row causal fa over the (own or target) quantized cache. The cache
18495        // already contains this forward's rows in both arms; row i attends [.., base+i].
18496        let kvl_idx = share.unwrap_or(il);
18497        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
18498        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
18499        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
18500        let mut attn = e.uninit(t * nh * hd)?;
18501        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
18502        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
18503        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
18504        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
18505        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
18506        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
18507        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
18508        //     rows (the T=K verify kernel; the target appended this forward's rows already).
18509        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
18510        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
18511        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
18512        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
18513            if let Some((kf, vf)) = &kv_f32 {
18514                if hd == 256 && t <= win {
18515                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18516                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18517                }
18518                if hd == 256 && swa && t > win {
18519                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18520                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18521                }
18522                if hd == 512 && !swa {
18523                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18524                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18525                }
18526            } else if share.is_some() {
18527                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18528                let k_view = e.view_u8(&kvl.k, kvl.k.len());
18529                let v_view = e.view_u8(&kvl.v, kvl.v.len());
18530                if hd == 256 && (!swa || t <= win) {
18531                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
18532                    e.fa_prefill_view(
18533                        &q,
18534                        &k_view,
18535                        &v_view,
18536                        &mut attn,
18537                        hd,
18538                        nh,
18539                        nkv,
18540                        t,
18541                        t,
18542                        scale,
18543                        true,
18544                        kvl.k_tok_bytes,
18545                        kvl.v_tok_bytes,
18546                        g,
18547                    )?;
18548                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18549                }
18550                // remaining shared classes (swa above the window; hd512 globals): dequant
18551                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
18552                let kv_dim = nkv * hd;
18553                let mut kf = e.uninit(t * kv_dim)?;
18554                let mut vf = e.uninit(t * kv_dim)?;
18555                e.fa_dequant_kv_view_f32(
18556                    &k_view,
18557                    &v_view,
18558                    &mut kf,
18559                    &mut vf,
18560                    kv_dim,
18561                    kv_dim,
18562                    t,
18563                    kvl.k_tok_bytes,
18564                    kvl.v_tok_bytes,
18565                    g,
18566                )?;
18567                if hd == 512 {
18568                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18569                } else {
18570                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18571                }
18572                return Ok(e.matmul(&fa.wo, &attn, t)?);
18573            }
18574        }
18575        if let Some(bucket) = dc_bucket {
18576            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
18577            // fa_decode_dc over the live counter. len_d already advanced past this token
18578            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
18579            // counter (advanced when the target ran earlier in the stack).
18580            assert!(t == 1);
18581            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
18582            // and under the window every live t_kv sits below it — cap the capture bucket
18583            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
18584            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
18585            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
18586            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
18587                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
18588            } else {
18589                bucket
18590            };
18591            let k_view = e.view_u8(&kvl.k, kvl.k.len());
18592            let v_view = e.view_u8(&kvl.v, kvl.v.len());
18593            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18594            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
18595            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
18596            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
18597            // captured into the dc graph like any other launch. Extending the cascade to
18598            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
18599            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
18600            // MEMRA_WPF=0 rollback seam.
18601            if crate::Engine::wpf_level() >= 1 {
18602                e.prefetch_weight_l2(&fa.wo)?;
18603            }
18604            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
18605            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
18606            if e.uses_q8_1_fast(&fa.wo) {
18607                let mut oq = e.alloc_i8_uninit(nh * hd)?;
18608                let mut od = e.zeros(nh * hd / 32)?;
18609                e.fa_decode_dc_q8(
18610                    &q,
18611                    &k_view,
18612                    &v_view,
18613                    &mut attn,
18614                    hd,
18615                    nh,
18616                    nkv,
18617                    &kvl.len_d,
18618                    bucket,
18619                    scale,
18620                    kvl.k_tok_bytes,
18621                    kvl.v_tok_bytes,
18622                    g,
18623                    Some((&mut oq, &mut od)),
18624                )?;
18625                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
18626            }
18627            e.fa_decode_dc(
18628                &q,
18629                &k_view,
18630                &v_view,
18631                &mut attn,
18632                hd,
18633                nh,
18634                nkv,
18635                &kvl.len_d,
18636                bucket,
18637                scale,
18638                kvl.k_tok_bytes,
18639                kvl.v_tok_bytes,
18640                g,
18641            )?;
18642            return Ok(e.matmul(&fa.wo, &attn, t)?);
18643        }
18644        for i in 0..t {
18645            let avail = base_len + i + 1;
18646            let (off_tok, t_kv) = if swa && avail > win {
18647                (avail - win, win)
18648            } else {
18649                (0, avail)
18650            };
18651            let k_view = e.view_u8_range(
18652                &kvl.k,
18653                off_tok * kvl.k_tok_bytes,
18654                (off_tok + t_kv) * kvl.k_tok_bytes,
18655            );
18656            let v_view = e.view_u8_range(
18657                &kvl.v,
18658                off_tok * kvl.v_tok_bytes,
18659                (off_tok + t_kv) * kvl.v_tok_bytes,
18660            );
18661            let qv = e.view(&q, t * nh * hd);
18662            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
18663            let mut q_one = e.uninit(nh * hd)?;
18664            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
18665            let mut a_one = e.uninit(nh * hd)?;
18666            // read class MUST match the append class (globals are e4m3 under gkv): the
18667            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
18668            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
18669            e.fa_decode_kvmod(
18670                &q_one,
18671                &k_view,
18672                &v_view,
18673                &mut a_one,
18674                hd,
18675                nh,
18676                nkv,
18677                t_kv,
18678                scale,
18679                kvl.k_tok_bytes,
18680                kvl.v_tok_bytes,
18681                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
18682            )?;
18683            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
18684        }
18685        Ok(e.matmul(&fa.wo, &attn, t)?)
18686    }
18687
18688    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
18689    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
18690    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
18691    /// layer; does NOT advance cache.pos (caller owns pos).
18692    fn gemma4_e4b_trunk(
18693        &self,
18694        e: &Engine,
18695        tokens: &[u32],
18696        pos0: usize,
18697        cache: &mut Cache,
18698        head_last: bool,
18699    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18700        let n_embd = self.cfg.n_embd as usize;
18701        let t = tokens.len();
18702        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18703        let pos_d = e.htod_i32(&pos)?;
18704        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
18705        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18706        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
18707        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
18708    }
18709
18710    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
18711    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
18712    /// eager chain by construction: SAME functions, not twins).
18713    fn gemma4_e4b_trunk_core(
18714        &self,
18715        e: &Engine,
18716        x_in: CudaSlice<f32>,
18717        inp_pl: CudaSlice<f32>,
18718        pos_d: &CudaSlice<i32>,
18719        t: usize,
18720        cache: &mut Cache,
18721        dc_bucket: Option<usize>,
18722        cap_logits: bool,
18723        head_last: bool,
18724    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18725        let n_embd = self.cfg.n_embd as usize;
18726        let eps = self.cfg.rms_eps;
18727        let n_layer = self.layers.len();
18728        let mut x = x_in;
18729        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
18730        let n_epl = aux_e4b.n_epl;
18731
18732        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
18733        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
18734        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
18735        // head rides matmul_pre too. First layer's pair comes from a standalone fused
18736        // norm+quant.
18737        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
18738        for il in 0..n_layer {
18739            let layer = &self.layers[il];
18740            let (hq, hdq) = match h_carry.take() {
18741                Some(p) => p,
18742                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
18743            };
18744            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
18745            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
18746            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
18747            let bits = layer.gemma4.as_ref().unwrap();
18748            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
18749            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
18750            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
18751            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
18752            // the fused single-phase reduction is NOT FP-order-identical to the unfused
18753            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
18754            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
18755            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
18756            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
18757            // gate dropped, decode AND verify ride the same fused chain — parity by
18758            // construction, VERIFY-GATE 0.000e0.
18759            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
18760            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
18761                e,
18762                layer,
18763                &o,
18764                &x,
18765                t,
18766                Some(layer.post_attn_norm.float_data()),
18767                fuse_exit,
18768            )?;
18769            let mut resid = e.uninit(t * n_embd)?;
18770            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
18771            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
18772            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
18773            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
18774            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
18775            let g = if fuse_exit {
18776                // sn here = RAW f0 (post_ffw deferred).
18777                let (rq, rd) = e.rms_pre_add_q8_1(
18778                    &sn,
18779                    bits.post_ffw_norm.float_data(),
18780                    &attn_out,
18781                    &mut resid,
18782                    n_embd,
18783                    t,
18784                    self.cfg.rms_eps,
18785                )?;
18786                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
18787            } else {
18788                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
18789                e.matmul(&e4b.inp_gate, &resid, t)?
18790            };
18791            let mut act = e.uninit(t * n_epl)?;
18792            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
18793                let ipv = e.view(&inp_pl, n_epl * n_layer);
18794                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
18795                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
18796                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
18797            } else {
18798                let mut inp_this = e.uninit(t * n_epl)?;
18799                e.copy_rows_strided(
18800                    &inp_pl,
18801                    &mut inp_this,
18802                    n_epl,
18803                    t,
18804                    n_epl * n_layer,
18805                    il * n_epl,
18806                )?;
18807                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
18808                e.matmul(&e4b.proj, &act, t)?
18809            };
18810            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
18811            // ONE launch (glue-fusion lane; last layer emits through output_norm).
18812            let next_norm = if il + 1 < n_layer {
18813                self.layers[il + 1].attn_norm.float_data()
18814            } else {
18815                self.output_norm.float_data()
18816            };
18817            let mut xn = e.uninit(t * n_embd)?;
18818            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
18819                &y,
18820                e4b.post_norm.float_data(),
18821                &resid,
18822                bits.layer_scale,
18823                next_norm,
18824                &mut xn,
18825                n_embd,
18826                t,
18827                eps,
18828            )?;
18829            h_carry = Some(pair);
18830            x = xn;
18831        }
18832        // the head consumes the last layer's fused (output_norm) emit. head_last callers
18833        // (prime, last_only forward) need only the final row's logits — the all-T head is
18834        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
18835        let (oq, odq) = h_carry.take().unwrap();
18836        let h0 = e.zeros(0)?;
18837        let hm = if head_last { 1 } else { t };
18838        let (hq, hd) = if head_last && t > 1 {
18839            let mut q1 = e.uninit_i8(n_embd)?;
18840            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
18841            let nb = n_embd / 32;
18842            let mut d1 = e.uninit(nb)?;
18843            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
18844            (q1, d1)
18845        } else {
18846            (oq, odq)
18847        };
18848        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
18849        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
18850        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
18851        // Logit-returning callers (host logits / spec prime) keep the capped emit.
18852        if cap_logits {
18853            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
18854            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
18855        }
18856        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
18857        Ok((ld, x))
18858    }
18859
18860    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
18861    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
18862    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
18863    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
18864    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
18865    /// covers exactly the layers that appended).
18866    pub fn gemma4_e4b_decode_step_t_am_dev(
18867        &self,
18868        e: &Engine,
18869        tok_d: &CudaSlice<u32>,
18870        t: usize,
18871        pos0: usize,
18872        cache: &mut Cache,
18873    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18874        let n_embd = self.cfg.n_embd as usize;
18875        let eps = self.cfg.rms_eps;
18876        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18877        let pos_d = e.htod_i32(&pos)?;
18878        let embd_gpu = self
18879            .embd_gpu
18880            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
18881        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
18882        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
18883        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18884        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
18885        let (ld, xp) =
18886            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
18887        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
18888        // emit is already capped, matching the eager chain bit-for-bit).
18889        let n_vocab = self.output.out_features();
18890        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
18891        for i in 0..t {
18892            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
18893        }
18894        let mut hn = e.uninit(t * n_embd)?;
18895        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18896        cache.pos += t;
18897        Ok((vam, hn))
18898    }
18899
18900    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
18901    /// prime path — mirror of `gemma4_decode_step_t_h`).
18902    pub(crate) fn gemma4_e4b_decode_step_t_h(
18903        &self,
18904        e: &Engine,
18905        tokens: &[u32],
18906        pos0: usize,
18907        cache: &mut Cache,
18908    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18909        let n_embd = self.cfg.n_embd as usize;
18910        let eps = self.cfg.rms_eps;
18911        let t = tokens.len();
18912        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
18913        let mut hn = e.uninit(t * n_embd)?;
18914        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18915        cache.pos += t;
18916        Ok((e.dtoh(&ld)?, hn))
18917    }
18918
18919    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
18920    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
18921    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
18922    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
18923    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
18924    pub fn gemma4_e4b_decode_step_dcg(
18925        &self,
18926        e: &Engine,
18927        token_d: &mut CudaSlice<u32>,
18928        pos_d: &mut CudaSlice<i32>,
18929        embd_gpu: &CudaSlice<u8>,
18930        embd_qt: i32,
18931        embd_rb: usize,
18932        cache: &mut Cache,
18933        n_vocab: usize,
18934        bucket: usize,
18935    ) -> Result<(), Box<dyn std::error::Error>> {
18936        let n_embd = self.cfg.n_embd as usize;
18937        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18938        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18939        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18940        let (ld, _x) =
18941            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
18942        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
18943        e.inc_seqlen(pos_d)?;
18944        Ok(())
18945    }
18946
18947    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
18948    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
18949    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
18950    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
18951    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
18952    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
18953    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
18954    #[allow(clippy::too_many_arguments)]
18955    pub fn gemma4_e4b_decode_step_dc(
18956        &self,
18957        e: &Engine,
18958        token_d: &CudaSlice<u32>,
18959        pos_d: &mut CudaSlice<i32>,
18960        embd_gpu: &CudaSlice<u8>,
18961        embd_qt: i32,
18962        embd_rb: usize,
18963        cache: &mut Cache,
18964        n_vocab: usize,
18965    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
18966        let n_embd = self.cfg.n_embd as usize;
18967        let eps = self.cfg.rms_eps;
18968        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18969        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18970        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18971        let (ld, _x) =
18972            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
18973        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
18974        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
18975        e.inc_seqlen(pos_d)?;
18976        cache.pos += 1;
18977        let _ = eps;
18978        Ok(tok_out)
18979    }
18980
18981    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
18982    /// pre-output_norm hidden). Advances cache.pos.
18983    pub(crate) fn gemma4_e4b_decode_step_h(
18984        &self,
18985        e: &Engine,
18986        token: u32,
18987        cache: &mut Cache,
18988    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18989        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
18990        let logits = e.dtoh(&ld)?;
18991        cache.pos += 1;
18992        Ok((logits, x))
18993    }
18994
18995    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
18996    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
18997    /// fast; the prefill fa arms come later.
18998    pub(crate) fn gemma4_e4b_prime(
18999        &self,
19000        e: &Engine,
19001        tokens: &[u32],
19002        cache: &mut Cache,
19003    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19004        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
19005        // process-kill as gemma4_prime — refuse per-request.
19006        if cache.pos != 0 {
19007            return Err(
19008                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
19009                        call or decode tokenwise"
19010                    .into(),
19011            );
19012        }
19013        let n_embd = self.cfg.n_embd as usize;
19014        let t = tokens.len();
19015        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
19016        cache.pos += t;
19017        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
19018        let xv = e.view(&x, t * n_embd);
19019        let row = xv.slice((t - 1) * n_embd..t * n_embd);
19020        let mut h_seed = e.uninit(n_embd)?;
19021        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
19022        Ok((last, h_seed, x))
19023    }
19024
19025    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
19026    pub(crate) fn gemma4_e4b_forward(
19027        &self,
19028        e: &Engine,
19029        tokens: &[u32],
19030        last_only: bool,
19031    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19032        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
19033        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
19034        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
19035    }
19036}
19037
19038#[cfg(test)]
19039mod prime_chunk_schedule_tests {
19040    use super::{
19041        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
19042        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
19043        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
19044        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
19045    };
19046
19047    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
19048        ranges.iter().map(|(start, end)| end - start).collect()
19049    }
19050
19051    fn auto_chunk(t: usize) -> usize {
19052        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
19053    }
19054
19055    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
19056    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
19057    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
19058    /// must land every boundary on it without changing coverage.
19059    #[test]
19060    fn auto_prime_ranges_align_to_the_gdn_grid() {
19061        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
19062        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
19063            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
19064            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
19065            for w in ranges.windows(2) {
19066                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
19067            }
19068            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
19069        };
19070
19071        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
19072        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
19073        let t = 9510usize;
19074        let fill = auto_chunk(t);
19075        let fixed = fixed_prime_chunk_ranges(t, fill);
19076        assert!(
19077            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
19078            "broken arm vanished: fixed auto boundaries all landed on-grid"
19079        );
19080        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
19081        assert!(
19082            dynamic[..dynamic.len() - 1]
19083                .iter()
19084                .any(|&(_, e)| e % c != 0),
19085            "broken arm vanished: dynamic auto boundaries all landed on-grid"
19086        );
19087
19088        for ranges in [&fixed, &dynamic] {
19089            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
19090            assert_covers(&aligned, t);
19091            for &(_, e) in &aligned[..aligned.len() - 1] {
19092                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
19093            }
19094            // boundaries only move DOWN, at most c-1 tokens.
19095            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
19096                assert!(a <= b && b - a < c);
19097            }
19098        }
19099
19100        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
19101        // empty range; the schedule survives degenerate short fills.
19102        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
19103        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
19104        assert_covers(&aligned, 200);
19105        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
19106
19107        // No-ops: single range, c=0 (grid off), already-aligned schedules.
19108        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
19109        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
19110        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
19111        assert_eq!(
19112            align_prime_ranges_to_gdn(&on_grid, 300, c),
19113            on_grid.as_slice()
19114        );
19115    }
19116
19117    #[test]
19118    fn active_matrix_prefix_scopes_reused_prime_slabs() {
19119        assert_eq!(
19120            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
19121            29 * 4096
19122        );
19123        assert_eq!(
19124            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
19125            29 * 4096
19126        );
19127        assert_eq!(
19128            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
19129            24 * 4096
19130        );
19131        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
19132        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
19133    }
19134
19135    #[test]
19136    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
19137        assert!(validate_step_prime_batch_modes(false, false).is_ok());
19138
19139        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
19140        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
19141
19142        for grouped in [false, true] {
19143            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
19144            assert!(err.contains("did not clear the live-server performance gate"));
19145            assert!(err.contains("per-session grouped prefill"));
19146        }
19147    }
19148
19149    #[test]
19150    fn step_grouped_path_is_eager_single_token_only() {
19151        assert!(step_grouped_decode_shape(false, 1));
19152        assert!(!step_grouped_decode_shape(true, 1));
19153        assert!(!step_grouped_decode_shape(false, 2));
19154        assert!(!step_grouped_decode_shape(true, 2));
19155    }
19156
19157    #[test]
19158    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
19159        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
19160        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
19161        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
19162        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
19163        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
19164        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
19165
19166        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
19167        assert!(step_grouped_prefill_shape(
19168            true,
19169            true,
19170            crate::cache::PRIME_CHUNK_MAX_TOKENS,
19171        ));
19172        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
19173        assert!(!step_grouped_prefill_shape(
19174            true,
19175            true,
19176            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
19177        ));
19178        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
19179        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
19180    }
19181
19182    #[test]
19183    fn step_tp_prefill_door_is_strict_and_default_off() {
19184        assert!(!parse_step_tp_prefill(None).unwrap());
19185        assert!(!parse_step_tp_prefill(Some("")).unwrap());
19186        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
19187        assert!(parse_step_tp_prefill(Some("1")).unwrap());
19188        assert!(parse_step_tp_prefill(Some("true")).is_err());
19189        assert!(parse_step_tp_prefill(Some("2")).is_err());
19190    }
19191
19192    #[test]
19193    fn step_tp_prefill_requires_a_qualified_even_rank_shape() {
19194        assert!(step_tp_prefill_shape(
19195            true,
19196            PRIME_MIN_T,
19197            4,
19198            true,
19199            true,
19200            false,
19201        ));
19202        assert!(!step_tp_prefill_shape(
19203            false,
19204            PRIME_MIN_T,
19205            4,
19206            true,
19207            true,
19208            false,
19209        ));
19210        assert!(!step_tp_prefill_shape(
19211            true,
19212            PRIME_MIN_T - 1,
19213            4,
19214            true,
19215            true,
19216            false,
19217        ));
19218        // TP2 admits (2026-08-25); odd/1-card placements still refuse.
19219        assert!(step_tp_prefill_shape(
19220            true,
19221            PRIME_MIN_T,
19222            2,
19223            true,
19224            true,
19225            false
19226        ));
19227        assert!(!step_tp_prefill_shape(
19228            true,
19229            PRIME_MIN_T,
19230            1,
19231            true,
19232            true,
19233            false
19234        ));
19235        assert!(!step_tp_prefill_shape(
19236            true,
19237            PRIME_MIN_T,
19238            3,
19239            true,
19240            true,
19241            false
19242        ));
19243        assert!(!step_tp_prefill_shape(
19244            true,
19245            PRIME_MIN_T,
19246            4,
19247            false,
19248            true,
19249            false,
19250        ));
19251        assert!(!step_tp_prefill_shape(
19252            true,
19253            PRIME_MIN_T,
19254            4,
19255            true,
19256            false,
19257            false,
19258        ));
19259        assert!(!step_tp_prefill_shape(
19260            true,
19261            PRIME_MIN_T,
19262            4,
19263            true,
19264            true,
19265            true,
19266        ));
19267    }
19268
19269    #[test]
19270    fn fixed_schedule_retains_measured_geometry() {
19271        assert_eq!(
19272            sizes(&fixed_prime_chunk_ranges(461, 128)),
19273            vec![128, 128, 128, 77]
19274        );
19275        assert_eq!(
19276            sizes(&fixed_prime_chunk_ranges(1833, 230)),
19277            vec![230, 230, 230, 230, 230, 230, 230, 223]
19278        );
19279        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
19280        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
19281        assert_eq!(capped, vec![4096, 4088, 16]);
19282        assert!(capped.iter().all(|&rows| rows <= 4096));
19283        assert_eq!(
19284            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
19285            vec![4100],
19286            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
19287        );
19288    }
19289
19290    #[test]
19291    fn dynamic_schedule_matches_registered_shapes() {
19292        let cases = [
19293            (461, vec![64, 141, 132, 124]),
19294            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
19295            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
19296        ];
19297        for (t, expected) in cases {
19298            let chunk = auto_chunk(t);
19299            let fixed = fixed_prime_chunk_ranges(t, chunk);
19300            assert_eq!(
19301                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
19302                expected
19303            );
19304        }
19305    }
19306
19307    #[test]
19308    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
19309        for t in 256..=8192 {
19310            let chunk = auto_chunk(t);
19311            let fixed = fixed_prime_chunk_ranges(t, chunk);
19312            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
19313            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
19314            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
19315            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
19316            for pair in dynamic.windows(2) {
19317                assert_eq!(pair[0].1, pair[1].0, "T={t}");
19318            }
19319            assert!(
19320                dynamic
19321                    .iter()
19322                    .all(|(start, end)| end - start >= PRIME_MIN_T),
19323                "T={t} sizes={:?}",
19324                sizes(&dynamic)
19325            );
19326            if dynamic.len() >= 3 {
19327                let chunk_sizes = sizes(&dynamic);
19328                assert!(
19329                    chunk_sizes[0] < chunk_sizes[1],
19330                    "T={t} sizes={chunk_sizes:?}"
19331                );
19332                assert!(
19333                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
19334                    "T={t} sizes={chunk_sizes:?}"
19335                );
19336            }
19337        }
19338    }
19339}
19340
19341#[cfg(test)]
19342mod page_prefetch_tests {
19343    use super::{
19344        grouped_worker_prefetch_position, page_prefetch_positions,
19345        page_prefetch_window_from_values, worker_prefetch_positions,
19346    };
19347
19348    #[test]
19349    fn page_prefetch_window_keeps_existing_opt_in_default() {
19350        assert_eq!(page_prefetch_window_from_values(false, None), 0);
19351        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
19352        assert_eq!(page_prefetch_window_from_values(true, None), 1);
19353        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
19354        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
19355        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
19356    }
19357
19358    #[test]
19359    fn rolling_page_prefetch_advises_each_future_expert_once() {
19360        let advised: Vec<_> = (0..7)
19361            .flat_map(|position| page_prefetch_positions(position, 7, 3))
19362            .collect();
19363        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
19364
19365        let one_ahead: Vec<_> = (0..4)
19366            .flat_map(|position| page_prefetch_positions(position, 4, 1))
19367            .collect();
19368        assert_eq!(one_ahead, vec![1, 2, 3]);
19369        assert!(page_prefetch_positions(0, 4, 0).is_empty());
19370    }
19371
19372    #[test]
19373    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
19374        assert_eq!(grouped_worker_prefetch_position(0, None), None);
19375        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
19376            .chain(
19377                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
19378            )
19379            .collect();
19380        assert_eq!(positions, vec![0, 1, 2, 3]);
19381        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
19382    }
19383
19384    #[test]
19385    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
19386        let queued: Vec<_> = (0..8)
19387            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
19388            .collect();
19389        assert_eq!(queued, (0..8).collect::<Vec<_>>());
19390
19391        let one_at_a_time: Vec<_> = (0..4)
19392            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
19393            .collect();
19394        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
19395        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
19396    }
19397}
19398
19399pub struct G4DcSlots {
19400    x: CudaSlice<f32>,
19401    xn: CudaSlice<f32>,
19402    cur: CudaSlice<f32>,
19403    hq: CudaSlice<i8>,
19404    hd_: CudaSlice<f32>,
19405    q0: CudaSlice<f32>,
19406    k0: CudaSlice<f32>,
19407    v0: CudaSlice<f32>,
19408    q: CudaSlice<f32>,
19409    k: CudaSlice<f32>,
19410    v: CudaSlice<f32>,
19411    attn: CudaSlice<f32>,
19412    o: CudaSlice<f32>,
19413    attn_out: CudaSlice<f32>,
19414    zsh: CudaSlice<f32>,
19415    zq: CudaSlice<i8>,
19416    zd: CudaSlice<f32>,
19417    gate: CudaSlice<f32>,
19418    up: CudaSlice<f32>,
19419    act: CudaSlice<f32>,
19420    actq: CudaSlice<i8>,
19421    actd: CudaSlice<f32>,
19422    f0: CudaSlice<f32>,
19423    sn: CudaSlice<f32>,
19424    hn: CudaSlice<f32>,
19425    logits: CudaSlice<f32>,
19426}
19427
19428/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
19429/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
19430/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
19431/// fixed logits stage the head writes.
19432pub struct Step35TokenGraphState {
19433    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
19434    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
19435    pub token_d: cudarc::driver::CudaSlice<u32>,
19436    pub pos_d: cudarc::driver::CudaSlice<i32>,
19437    pub logits_stage: cudarc::driver::CudaSlice<f32>,
19438    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
19439    /// launch, so an alloc made inside one captured child is not referable from another):
19440    /// the running residual, the post-attention pair, the shared-expert row, and the
19441    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
19442    pub x: cudarc::driver::CudaSlice<f32>,
19443    pub x1: cudarc::driver::CudaSlice<f32>,
19444    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
19445    pub sh_stage: cudarc::driver::CudaSlice<f32>,
19446    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
19447    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
19448    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
19449    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
19450    pub router_logits: cudarc::driver::CudaSlice<f32>,
19451    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
19452    pub shexp_up: cudarc::driver::CudaSlice<f32>,
19453    pub shexp_act: cudarc::driver::CudaSlice<f32>,
19454    pub gate_sig: cudarc::driver::CudaSlice<f32>,
19455    pub dense_z: cudarc::driver::CudaSlice<f32>,
19456    pub dense_gate: cudarc::driver::CudaSlice<f32>,
19457    pub dense_up: cudarc::driver::CudaSlice<f32>,
19458    pub dense_act: cudarc::driver::CudaSlice<f32>,
19459    pub hn: cudarc::driver::CudaSlice<f32>,
19460    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
19461    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
19462    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
19463    pub probe_x: cudarc::driver::CudaSlice<f32>,
19464    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
19465    /// the in-graph tail argmax chain; host reads the ring once per chunk.
19466    pub token_hist: cudarc::driver::CudaSlice<u32>,
19467    pub hist_idx: cudarc::driver::CudaSlice<i32>,
19468}
19469
19470impl HybridModel {
19471    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
19472    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
19473    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
19474    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
19475    /// needs a rebuild this token).
19476    ///
19477    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
19478    /// but not their contents under this door (the TP rank caches are fully maintained
19479    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
19480    /// must not run with the door on until the local-dcw twin lands.
19481    pub(crate) fn step35_token_graph_step(
19482        &self,
19483        e: &Engine,
19484        token: u32,
19485        cache: &mut Cache,
19486    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
19487        if !self.uses_sliding_gated_moe_program()
19488            || !crate::tp::step_tp_graph_enabled()?
19489            || !crate::tp::step_tp_dcw_enabled()?
19490            || !crate::tp::step_tp_qkv_fused_enabled()?
19491            || !crate::tp::step_tp_dev_router_enabled()?
19492            || !crate::tp::step_nvfp4_dev_routes_enabled()?
19493        {
19494            return Ok(None);
19495        }
19496        let n_embd = self.cfg.n_embd as usize;
19497        let n_vocab = self.cfg.n_vocab as usize;
19498        let eps = self.cfg.rms_eps;
19499        let n_layers = self.layers.len();
19500        let pos = cache.pos;
19501        let staged_next = pos + 1;
19502        if staged_next < 96 {
19503            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
19504        }
19505
19506        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
19507        // eager fallback for the whole token; the host path also updates base_d there).
19508        for il in 0..n_layers {
19509            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
19510                return Ok(None); // caches not hydrated yet — eager warms them
19511            };
19512            if tp_kv.peek_append_ring(1)?.1 {
19513                return Ok(None);
19514            }
19515        }
19516
19517        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
19518        // their window and share one bucket forever after ctx > window).
19519        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
19520        if !fa_vec {
19521            return Ok(None);
19522        }
19523        let sp = crate::fa_split_keys(staged_next, 8);
19524        let bucket_max = (n_splits * sp).max(staged_next);
19525
19526        let mut state_guard = self
19527            .step35_token_graph
19528            .lock()
19529            .map_err(|_| "step35 token graph lock is poisoned")?;
19530        if state_guard.is_none() {
19531            let _main = e.gpu.enter_main()?;
19532            let n_expert = self
19533                .cfg
19534                .moe
19535                .as_ref()
19536                .map(|m| m.expert_count as usize)
19537                .unwrap_or(0);
19538            let n_ff_sh = self
19539                .layers
19540                .iter()
19541                .find_map(|l| match &l.ffn {
19542                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
19543                    _ => None,
19544                })
19545                .unwrap_or(0);
19546            let n_ff_dense = self
19547                .layers
19548                .iter()
19549                .find_map(|l| match &l.ffn {
19550                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
19551                    _ => None,
19552                })
19553                .unwrap_or(0);
19554            *state_guard = Some(Step35TokenGraphState {
19555                graphs: Vec::new(),
19556                token_d: e.stream().clone_htod(&[0u32])?,
19557                pos_d: e.htod_i32(&[pos as i32])?,
19558                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
19559                x: e.htod(&vec![0.0f32; n_embd])?,
19560                x1: e.htod(&vec![0.0f32; n_embd])?,
19561                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
19562                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
19563                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19564                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19565                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
19566                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19567                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19568                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19569                gate_sig: e.htod(&vec![1.0f32; 1])?,
19570                dense_z: e.htod(&vec![0.0f32; n_embd])?,
19571                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19572                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19573                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19574                hn: e.htod(&vec![0.0f32; n_embd])?,
19575                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
19576                probe_x: e.htod(&vec![0.0f32; n_embd])?,
19577                token_hist: e.stream().clone_htod(&[0u32; 16])?,
19578                hist_idx: e.htod_i32(&[0])?,
19579            });
19580        }
19581        let state = state_guard.as_mut().expect("state armed above");
19582        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
19583        // first use, and an alloc inside a captured section is a mem node (child graphs
19584        // reject those — the tail argmax chain needs them already resident).
19585        {
19586            let _main = e.gpu.enter_main()?;
19587            let Step35TokenGraphState {
19588                logits_stage,
19589                token_d,
19590                ..
19591            } = &mut *state;
19592            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
19593        }
19594
19595        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
19596        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
19597        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
19598        // ceiling at build so the baked pointers never move.
19599        if state.graphs.is_empty() {
19600            // Build the parent at this bucket. Capture executes nothing; correctness is
19601            // pinned at replay by the token-identity gate.
19602            self.step35_token_graph_build(e, cache, state, bucket_max)?;
19603        }
19604        {
19605            let (b, g) = state.graphs.first_mut().expect("graph built above");
19606            if *b != bucket_max {
19607                g.retarget_bucket(bucket_max)?;
19608                *b = bucket_max;
19609            }
19610        }
19611        let graph = state
19612            .graphs
19613            .first()
19614            .map(|(_, g)| g)
19615            .expect("graph built above");
19616
19617        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
19618        let t_fence = tg_timing.then(std::time::Instant::now);
19619        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
19620        // queued on the rank streams, and graph children carry no ordering edge to those
19621        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
19622        // sync is a no-op between consecutive replays.
19623        {
19624            let fa0 = match &self.layers[0].mixer {
19625                Mixer::Full(fa) => fa,
19626                _ => return Err("step35 token graph expects full-attention layers".into()),
19627            };
19628            let tp0 = fa0
19629                .step_tp_qkv
19630                .as_ref()
19631                .ok_or("step35 token graph lost its TP state")?;
19632            for rank in 0..tp0.runtime.devices().len() {
19633                let engine = tp0
19634                    .runtime
19635                    .rank_engine(rank)
19636                    .ok_or("step35 token graph lost a rank engine")?;
19637                let _main = engine.gpu.enter_main()?;
19638                engine.stream().synchronize()?;
19639            }
19640        }
19641
19642        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
19643        {
19644            let _main = e.gpu.enter_main()?;
19645            e.set_u32_one(&mut state.token_d, token)?;
19646            e.set_i32_one(&mut state.pos_d, pos as i32)?;
19647        }
19648        let t_launch = tg_timing.then(std::time::Instant::now);
19649        graph.launch(e)?;
19650        let t_book = tg_timing.then(std::time::Instant::now);
19651        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
19652        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
19653        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
19654        // replay error the counters are already advanced — acceptable: the decode aborts.
19655        for il in 0..n_layers {
19656            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
19657            let transaction = tp_kv.begin_transaction()?;
19658            let fa = match &self.layers[il].mixer {
19659                Mixer::Full(fa) => fa,
19660                _ => return Err("step35 token graph expects full-attention layers".into()),
19661            };
19662            let tp = fa
19663                .step_tp_qkv
19664                .as_ref()
19665                .ok_or("step35 token graph lost its TP state")?;
19666            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
19667            // incs own the counters). Shards unused.
19668            let empty: [CudaSlice<f32>; 0] = [];
19669            tp.runtime.append_tp_kv_transaction_inner(
19670                tp_kv,
19671                transaction,
19672                &empty,
19673                &empty,
19674                1,
19675                true,
19676            )?;
19677            tp.runtime
19678                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
19679            // Local shadow: lengths advance (v1 keeps contents stale under the door).
19680            if let Some(local) = cache.kv[il].as_mut() {
19681                local.len = pos + 1;
19682                let _main = e.gpu.enter_main()?;
19683                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
19684            }
19685        }
19686        cache.pos = pos + 1;
19687        let t_sync = tg_timing.then(std::time::Instant::now);
19688        let (logits, h_seed) = {
19689            let _main = e.gpu.enter_main()?;
19690            e.stream().synchronize()?;
19691            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
19692        };
19693        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
19694            use std::sync::atomic::{AtomicU64, Ordering};
19695            static NS: [AtomicU64; 5] = [
19696                AtomicU64::new(0),
19697                AtomicU64::new(0),
19698                AtomicU64::new(0),
19699                AtomicU64::new(0),
19700                AtomicU64::new(0),
19701            ];
19702            static CALLS: AtomicU64 = AtomicU64::new(0);
19703            let now = std::time::Instant::now();
19704            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
19705            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
19706            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
19707            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
19708            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
19709            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
19710            if calls % 100 == 0 {
19711                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
19712                eprintln!(
19713                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
19714                     syncdtoh_us={:.0} total_us={:.0}",
19715                    avg(0),
19716                    avg(1),
19717                    avg(2),
19718                    avg(3),
19719                    avg(4)
19720                );
19721            }
19722        }
19723        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
19724        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
19725            use std::io::Write;
19726            let (pm, px) = {
19727                let _main = e.gpu.enter_main()?;
19728                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
19729            };
19730            for (path, data) in [
19731                ("/root/tg-probe-mixed.bin", &pm),
19732                ("/root/tg-probe-x.bin", &px),
19733            ] {
19734                let mut fo = std::fs::OpenOptions::new()
19735                    .create(true)
19736                    .append(true)
19737                    .open(path)?;
19738                for v in data {
19739                    fo.write_all(&v.to_le_bytes())?;
19740                }
19741            }
19742        }
19743        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
19744        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
19745        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
19746            let hh = {
19747                let _main = e.gpu.enter_main()?;
19748                e.dtoh(&state.hn)?
19749            };
19750            use std::io::Write;
19751            let mut fo = std::fs::OpenOptions::new()
19752                .create(true)
19753                .append(true)
19754                .open(path)?;
19755            for v in &hh {
19756                fo.write_all(&v.to_le_bytes())?;
19757            }
19758        }
19759        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
19760        // per rank per token; diagnostics only.
19761        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
19762            for il in [0usize, 1, 44] {
19763                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
19764                let host_len = tp_kv.staged_len();
19765                let fa = match &self.layers[il].mixer {
19766                    Mixer::Full(fa) => fa,
19767                    _ => continue,
19768                };
19769                let tp = fa
19770                    .step_tp_qkv
19771                    .as_ref()
19772                    .ok_or("step35 token graph lost its TP state")?;
19773                for rank in 0..tp.runtime.devices().len() {
19774                    let engine = tp
19775                        .runtime
19776                        .rank_engine(rank)
19777                        .ok_or("step35 token graph lost a rank engine")?;
19778                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
19779                    let _main = engine.gpu.enter_main()?;
19780                    engine.stream().synchronize()?;
19781                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
19782                    let base_d = match rank_cache.base_d() {
19783                        Some(b) => engine.dtoh_i32_one(b)?,
19784                        None => -1,
19785                    };
19786                    eprintln!(
19787                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
19788                         len_d={len_d} base_d={base_d}"
19789                    );
19790                }
19791            }
19792        }
19793        Ok(Some((logits, h_seed)))
19794    }
19795
19796    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
19797    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
19798    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
19799    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
19800    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
19801    pub(crate) fn head_split_matvec(
19802        &self,
19803        e: &Engine,
19804        hn: &CudaSlice<f32>,
19805    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
19806        if self.head_split_fill_device(e, hn)?.is_none() {
19807            return Ok(None);
19808        }
19809        let guard = HEAD_SPLIT_WS
19810            .lock()
19811            .map_err(|_| "head split lock is poisoned")?;
19812        let ws = guard.as_ref().expect("filled above");
19813        let _main = e.gpu.enter_main()?;
19814        Ok(Some(e.dtoh(&ws.logits_e)?))
19815    }
19816
19817    /// Compute body of the split head: arms the replica + staging on first use, then fills
19818    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
19819    /// push) and orders e's stream behind it. None = ineligible.
19820    fn head_split_fill_device(
19821        &self,
19822        e: &Engine,
19823        hn: &CudaSlice<f32>,
19824    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
19825        use cudarc::driver::DevicePtr;
19826        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
19827            return Ok(None);
19828        };
19829        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
19830            Mixer::Full(fa) => fa
19831                .step_tp_qkv
19832                .as_ref()
19833                .and_then(|tp| tp.runtime.rank_engine(1)),
19834            _ => None,
19835        }) else {
19836            return Ok(None);
19837        };
19838        let n_embd = self.cfg.n_embd as usize;
19839        let n_vocab = self.cfg.n_vocab as usize;
19840        let half = n_vocab / 2;
19841        let mut guard = HEAD_SPLIT_WS
19842            .lock()
19843            .map_err(|_| "head split lock is poisoned")?;
19844        let pin = {
19845            let _main = e.gpu.enter_main()?;
19846            let stream = e.stream();
19847            let (ptr, _g) = head.device_ptr(&stream);
19848            ptr as u64
19849        };
19850        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
19851            // One-time: upload rank1's row half + persistent staging.
19852            let hi_rows = n_vocab - half;
19853            let (w1, hn1, y1, ev_done) = {
19854                let _r1 = rank1.gpu.enter_main()?;
19855                (
19856                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
19857                    rank1.htod(&vec![0.0f32; n_embd])?,
19858                    rank1.htod(&vec![0.0f32; hi_rows])?,
19859                    rank1.ctx().new_event(None)?,
19860                )
19861            };
19862            {
19863                use cudarc::driver::sys;
19864                let src = pin + (half * n_embd * 2) as u64;
19865                let dst = {
19866                    let _r1 = rank1.gpu.enter_main()?;
19867                    let rstream = rank1.stream();
19868                    let (d, _g) = w1.device_ptr(&rstream);
19869                    d as u64
19870                };
19871                let _r1 = rank1.gpu.enter_main()?;
19872                let r = unsafe {
19873                    sys::cuMemcpyAsync(
19874                        dst as sys::CUdeviceptr,
19875                        src as sys::CUdeviceptr,
19876                        hi_rows * n_embd * 2,
19877                        rank1.stream().cu_stream() as sys::CUstream,
19878                    )
19879                };
19880                if r != sys::CUresult::CUDA_SUCCESS {
19881                    return Err(format!("head split replica upload: {r:?}").into());
19882                }
19883                rank1.stream().synchronize()?;
19884            }
19885            let (logits_e, ev_hn) = {
19886                let _main = e.gpu.enter_main()?;
19887                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
19888            };
19889            let (raw_hn1, raw_y1) = {
19890                let _r1 = rank1.gpu.enter_main()?;
19891                let rstream = rank1.stream();
19892                let (a, _g0) = hn1.device_ptr(&rstream);
19893                let (b, _g1) = y1.device_ptr(&rstream);
19894                (a as u64, b as u64)
19895            };
19896            let raw_logits_hi = {
19897                let _main = e.gpu.enter_main()?;
19898                let stream = e.stream();
19899                let (l, _g) = logits_e.device_ptr(&stream);
19900                l as u64 + (half * 4) as u64
19901            };
19902            *guard = Some(HeadSplit {
19903                pin,
19904                w1,
19905                hn1,
19906                y1,
19907                logits_e,
19908                ev_hn,
19909                ev_done,
19910                raw_hn1,
19911                raw_y1,
19912                raw_logits_hi,
19913                samp: None,
19914            });
19915        }
19916        let ws = guard.as_mut().expect("armed above");
19917        let hi_rows = n_vocab - half;
19918        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
19919        let raw_hn = {
19920            let _main = e.gpu.enter_main()?;
19921            let stream = e.stream();
19922            let (h, _g) = hn.device_ptr(&stream);
19923            ws.ev_hn.record(&stream)?;
19924            h as u64
19925        };
19926        {
19927            let _r1 = rank1.gpu.enter_main()?;
19928            rank1.stream().wait(&ws.ev_hn)?;
19929            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
19930            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
19931            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
19932            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
19933            ws.ev_done.record(&rank1.stream())?;
19934        }
19935        {
19936            let _main = e.gpu.enter_main()?;
19937            let head_lo = head.slice(0..half * n_embd * 2);
19938            let HeadSplit { logits_e, .. } = &mut *ws;
19939            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
19940            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
19941            e.stream().wait(&ws.ev_done)?;
19942            Ok(Some(()))
19943        }
19944    }
19945
19946    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
19947    /// row exactly like the host variant (identical halves, identical concat) and runs the
19948    /// device argmax into `token_d` — NO host readback. Returns false when the split is
19949    /// ineligible (caller falls back to the plain matmul head).
19950    pub(crate) fn head_split_argmax_device(
19951        &self,
19952        e: &Engine,
19953        hn: &CudaSlice<f32>,
19954        token_d: &mut CudaSlice<u32>,
19955    ) -> Result<bool, Box<dyn std::error::Error>> {
19956        if self.head_split_fill_device(e, hn)?.is_none() {
19957            return Ok(false);
19958        }
19959        let n_vocab = self.cfg.n_vocab as usize;
19960        let guard = HEAD_SPLIT_WS
19961            .lock()
19962            .map_err(|_| "head split lock is poisoned")?;
19963        let ws = guard.as_ref().expect("filled above");
19964        let _main = e.gpu.enter_main()?;
19965        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
19966        Ok(true)
19967    }
19968
19969    /// SAMPLED twin of `head_split_argmax_device`. The split head already materializes the
19970    /// full concatenated row in `ws.logits_e`, so sampling does NOT have to give up HEAD_SPLIT
19971    /// — it draws from that row on device (filter thresholds, Gumbel perturbation, argmax)
19972    /// exactly as the serve tick does. Worth ~0.2 ms/token: the post-W8 census had the
19973    /// unsplit q8 head at ~364 us against ~82 us per half.
19974    pub(crate) fn head_split_sample_device(
19975        &self,
19976        e: &Engine,
19977        hn: &CudaSlice<f32>,
19978        token_d: &mut CudaSlice<u32>,
19979        samp: &crate::decode_batch::DevSamp,
19980        ctr: u32,
19981    ) -> Result<bool, Box<dyn std::error::Error>> {
19982        if self.head_split_fill_device(e, hn)?.is_none() {
19983            return Ok(false);
19984        }
19985        let n_vocab = self.cfg.n_vocab as usize;
19986        let guard = HEAD_SPLIT_WS
19987            .lock()
19988            .map_err(|_| "head split lock is poisoned")?;
19989        let mut guard = guard;
19990        let ws = guard.as_mut().expect("filled above");
19991        let _main = e.gpu.enter_main()?;
19992        if ws.samp.is_none() {
19993            ws.samp = Some(SampScratch {
19994                pb: e.zeros(n_vocab)?,
19995                th: e.zeros(1)?,
19996                z: e.zeros(1)?,
19997                mx: e.zeros(1)?,
19998                rows: e.htod_i32(&[0i32])?,
19999            });
20000        }
20001        let filtered = samp.top_k > 0 || samp.top_p < 1.0 || samp.min_p > 0.0;
20002        let HeadSplit {
20003            logits_e,
20004            samp: scratch,
20005            ..
20006        } = &mut *ws;
20007        let sc = scratch.as_mut().expect("armed above");
20008        if filtered {
20009            e.filter_stats(
20010                logits_e, n_vocab, &sc.rows, &mut sc.th, &mut sc.z, &mut sc.mx, n_vocab, 1,
20011                samp.temp, samp.top_k, samp.top_p, samp.min_p,
20012            )?;
20013            let SampScratch { pb, th, mx, .. } = sc;
20014            e.gumbel_perturb_filtered_col(
20015                logits_e, 0, pb, n_vocab, samp.seed, ctr, samp.temp, mx, th, 0,
20016            )?;
20017        } else {
20018            e.gumbel_perturb_col(logits_e, 0, &mut sc.pb, n_vocab, samp.seed, ctr, samp.temp)?;
20019        }
20020        e.argmax_token_device_col(&sc.pb, 0, n_vocab, token_d, 0)?;
20021        Ok(true)
20022    }
20023
20024    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
20025    /// token's row).
20026    pub(crate) fn head_split_logits_dtoh(
20027        &self,
20028        e: &Engine,
20029    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
20030        let guard = HEAD_SPLIT_WS
20031            .lock()
20032            .map_err(|_| "head split lock is poisoned")?;
20033        let ws = guard.as_ref().ok_or("head split logits not armed")?;
20034        let _main = e.gpu.enter_main()?;
20035        Ok(e.dtoh(&ws.logits_e)?)
20036    }
20037
20038    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
20039    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
20040    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
20041    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
20042    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
20043    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
20044    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
20045    /// own loop re-derive hist[k-1] from the returned row.
20046    pub fn step35_token_graph_chunk(
20047        &self,
20048        e: &Engine,
20049        token: u32,
20050        k_target: usize,
20051        cache: &mut Cache,
20052    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
20053        if !self.uses_sliding_gated_moe_program()
20054            || !crate::tp::step_tp_graph_enabled()?
20055            || !crate::tp::step_tp_dcw_enabled()?
20056            || !crate::tp::step_tp_qkv_fused_enabled()?
20057            || !crate::tp::step_tp_dev_router_enabled()?
20058            || !crate::tp::step_nvfp4_dev_routes_enabled()?
20059        {
20060            return Ok(None);
20061        }
20062        let n_layers = self.layers.len();
20063        let pos = cache.pos;
20064        let staged_next = pos + 1;
20065        if staged_next < 96 {
20066            return Ok(None);
20067        }
20068        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
20069        // exec's n_splits ladder must match eager per depth).
20070        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20071        if !fa_vec {
20072            return Ok(None);
20073        }
20074        let sp = crate::fa_split_keys(staged_next, 8);
20075        let bucket_max = (n_splits * sp).max(staged_next);
20076        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
20077        let mut k = k_target.min(to_boundary).min(16);
20078        if k < 2 {
20079            return Ok(None);
20080        }
20081        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
20082        for il in 0..n_layers {
20083            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20084                return Ok(None);
20085            };
20086            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
20087                k -= 1;
20088            }
20089            if k < 2 {
20090                return Ok(None);
20091            }
20092        }
20093
20094        let mut state_guard = self
20095            .step35_token_graph
20096            .lock()
20097            .map_err(|_| "step35 token graph lock is poisoned")?;
20098        let Some(state) = state_guard.as_mut() else {
20099            return Ok(None); // per-token path arms the state + stages first
20100        };
20101        if state.graphs.is_empty() {
20102            return Ok(None);
20103        }
20104        {
20105            let (b, g) = state.graphs.first_mut().expect("checked above");
20106            if *b != bucket_max {
20107                g.retarget_bucket(bucket_max)?;
20108                *b = bucket_max;
20109            }
20110        }
20111        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
20112
20113        // Rank-stream fence (eager stragglers; see the per-token path).
20114        {
20115            let fa0 = match &self.layers[0].mixer {
20116                Mixer::Full(fa) => fa,
20117                _ => return Err("step35 token graph expects full-attention layers".into()),
20118            };
20119            let tp0 = fa0
20120                .step_tp_qkv
20121                .as_ref()
20122                .ok_or("step35 token graph lost its TP state")?;
20123            for rank in 0..tp0.runtime.devices().len() {
20124                let engine = tp0
20125                    .runtime
20126                    .rank_engine(rank)
20127                    .ok_or("step35 token graph lost a rank engine")?;
20128                let _main = engine.gpu.enter_main()?;
20129                engine.stream().synchronize()?;
20130            }
20131        }
20132
20133        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
20134        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
20135        {
20136            let _main = e.gpu.enter_main()?;
20137            e.set_u32_one(&mut state.token_d, token)?;
20138            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20139            e.set_i32_one(&mut state.hist_idx, 0)?;
20140        }
20141        for _ in 0..k {
20142            graph.launch(e)?;
20143        }
20144        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
20145        for il in 0..n_layers {
20146            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20147            let transaction = tp_kv.begin_transaction()?;
20148            let fa = match &self.layers[il].mixer {
20149                Mixer::Full(fa) => fa,
20150                _ => return Err("step35 token graph expects full-attention layers".into()),
20151            };
20152            let tp = fa
20153                .step_tp_qkv
20154                .as_ref()
20155                .ok_or("step35 token graph lost its TP state")?;
20156            let empty: [CudaSlice<f32>; 0] = [];
20157            tp.runtime.append_tp_kv_transaction_inner(
20158                tp_kv,
20159                transaction,
20160                &empty,
20161                &empty,
20162                k,
20163                true,
20164            )?;
20165            tp.runtime
20166                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
20167            if let Some(local) = cache.kv[il].as_mut() {
20168                local.len = pos + k;
20169                let _main = e.gpu.enter_main()?;
20170                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
20171            }
20172        }
20173        cache.pos = pos + k;
20174        let (hist, logits) = {
20175            let _main = e.gpu.enter_main()?;
20176            e.stream().synchronize()?;
20177            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
20178        };
20179        Ok(Some((hist[..k].to_vec(), logits)))
20180    }
20181}
20182
20183impl HybridModel {
20184    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
20185    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
20186    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
20187    /// of each phase fork in parallel and merge into the following root section.
20188    #[allow(clippy::too_many_arguments)]
20189    fn step35_token_graph_build(
20190        &self,
20191        e: &Engine,
20192        cache: &mut Cache,
20193        state: &mut Step35TokenGraphState,
20194        bucket_max: usize,
20195    ) -> Result<(), Box<dyn std::error::Error>> {
20196        use cudarc::driver::DevicePtr;
20197        let n_embd = self.cfg.n_embd as usize;
20198        let eps = self.cfg.rms_eps;
20199        let n_layers = self.layers.len();
20200        let started = std::time::Instant::now();
20201        if !crate::router_kernel_on() {
20202            return Err(
20203                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
20204            );
20205        }
20206        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
20207            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
20208        }
20209
20210        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
20211        let embd_gpu = self
20212            .embd_gpu_try(e)
20213            .ok_or("step35 token graph could not upload the device embed table")?;
20214        let embd_qtype = match self.embd.ggml_type {
20215            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
20216            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
20217            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
20218        };
20219        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
20220
20221        // Fixed-stage pointers the sections reference.
20222        let (p_mixed, p_kshadow, p_vshadow) = {
20223            let _main = e.gpu.enter_main()?;
20224            let stream = e.stream();
20225            let (a, _g) = state.mixed_stage.device_ptr(&stream);
20226            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
20227            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
20228            (a as u64, b as u64, c as u64)
20229        };
20230
20231        crate::tp::token_graph_build_begin()?;
20232        let mut group_id: u32 = 0;
20233        for il in 0..n_layers {
20234            let layer = &self.layers[il];
20235            let fa = match &layer.mixer {
20236                Mixer::Full(fa) => fa,
20237                _ => return Err("step35 token graph expects full-attention layers".into()),
20238            };
20239            let tp = fa
20240                .step_tp_qkv
20241                .as_ref()
20242                .ok_or("step35 token graph lost its TP state")?;
20243            let attention = tp
20244                .attention
20245                .as_ref()
20246                .ok_or("step35 token graph lost its attention aux")?;
20247            let geometry = self.step35_geom(il);
20248            let window = geometry.window.map(|w| w as usize);
20249            let head_dim = geometry.head_dim_k as usize;
20250            let heads = geometry.n_head as usize;
20251            let kv_heads = geometry.n_head_kv as usize;
20252            let ranks = tp.runtime.devices().len();
20253            let local_heads = heads / ranks;
20254            let local_kv_heads = kv_heads / ranks;
20255            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
20256            let use_gate_shards =
20257                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
20258            if !use_gate_shards {
20259                return Err("step35 token graph requires the fused gate shards".into());
20260            }
20261
20262            let ws_index = tp
20263                .runtime
20264                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
20265            let ws_mutex = tp.runtime.decode_v2_workspace();
20266            let mut ws_guard = ws_mutex
20267                .lock()
20268                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
20269            let ws = ws_guard
20270                .get_mut(ws_index)
20271                .ok_or("step TP decode v2 workspace missing after ensure")?;
20272            tp.runtime
20273                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
20274            let mut rope_freqs = Vec::with_capacity(ranks);
20275            for rank in 0..ranks {
20276                let engine = tp
20277                    .runtime
20278                    .rank_engine(rank)
20279                    .ok_or("step35 token graph lost a rank engine")?;
20280                rope_freqs.push(if geometry.rope_factors {
20281                    self.step35_aux
20282                        .as_ref()
20283                        .and_then(|aux| aux.rope_freqs(engine))
20284                } else {
20285                    None
20286                });
20287            }
20288            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
20289                Some(crate::tp::StepTpGateShards::F32(shards))
20290            } else {
20291                attention
20292                    .gate_shards_bf16
20293                    .as_deref()
20294                    .map(crate::tp::StepTpGateShards::Bf16)
20295            };
20296
20297            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
20298            let decode_input = attention
20299                .decode_input
20300                .as_ref()
20301                .ok_or("step35 token graph requires the replicated decode input")?;
20302            let mut decode_input = decode_input
20303                .lock()
20304                .map_err(|_| "replicated decode input lock is poisoned")?;
20305            // Stage arming happens through the eager stage flow once; require it here.
20306            if ws.h_stage.is_none() {
20307                return Err(
20308                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
20309                );
20310            }
20311            {
20312                let state_x = &mut state.x;
20313                let token_d = &state.token_d;
20314                let pos_d = &state.pos_d;
20315                crate::tp::graph_section(e, None, || {
20316                    let _main = e.gpu.enter_main()?;
20317                    if il == 0 {
20318                        e.embed_gather_device_into(
20319                            embd_gpu,
20320                            token_d,
20321                            state_x,
20322                            n_embd,
20323                            embd_qtype,
20324                            embd_row_bytes,
20325                        )?;
20326                    }
20327                    {
20328                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
20329                        e.rms_norm(
20330                            state_x,
20331                            layer.attn_norm.float_data(),
20332                            h_stage,
20333                            n_embd,
20334                            1,
20335                            eps,
20336                        )?;
20337                    }
20338                    {
20339                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
20340                        let mut dst = pos_stage.slice_mut(0..1);
20341                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
20342                    }
20343                    Ok(())
20344                })?;
20345            }
20346
20347            // ---- R0/R1 (parallel): projections + dcw attention interior ----
20348            group_id += 1;
20349            for rank in 0..ranks {
20350                let engine = tp
20351                    .runtime
20352                    .rank_engine(rank)
20353                    .ok_or("step35 token graph lost a rank engine")?;
20354                {
20355                    // fa partial pool must reach the RUN CEILING before capture — an
20356                    // in-capture grow is a mem node (child graphs reject those), and the
20357                    // retarget path (increment C) widens the baked memsets up to the ceiling
20358                    // without moving the pool pointers. Two ensures cover both sp rungs.
20359                    let ceiling = window
20360                        .map(|w| cache.max_ctx.min(w))
20361                        .unwrap_or(cache.max_ctx);
20362                    let _main = engine.gpu.enter_main()?;
20363                    engine.fa_dcw_pool_ensure(
20364                        head_dim,
20365                        local_heads,
20366                        local_kv_heads,
20367                        ceiling.min(2048),
20368                    )?;
20369                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
20370                    engine.fa_dcw_pool_ensure(
20371                        head_dim,
20372                        local_heads,
20373                        local_kv_heads,
20374                        layer_bucket,
20375                    )?;
20376                }
20377                let runtime = &tp.runtime;
20378                let q_norm = &attention.q_norm;
20379                let k_norm = &attention.k_norm;
20380                let gate_ref = gate_shards_arg.as_ref();
20381                crate::tp::graph_section(engine, Some(group_id), || {
20382                    runtime.decode_v2_input_qkv_rank(
20383                        ws,
20384                        &state.pos_d,
20385                        &mut decode_input,
20386                        &tp.q,
20387                        &tp.k,
20388                        &tp.v,
20389                        q_norm,
20390                        k_norm,
20391                        head_dim,
20392                        geometry.n_rot as usize,
20393                        geometry.rope_base,
20394                        &rope_freqs,
20395                        eps,
20396                        gate_ref,
20397                        true,
20398                        false,
20399                        rank,
20400                        None,
20401                    )?;
20402                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
20403                    // replayed values track the live counters).
20404                    let distributed = cache.tp_kv[il]
20405                        .as_mut()
20406                        .ok_or("step35 token graph lost a TP cache")?;
20407                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
20408                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
20409                    let capacity = distributed.physical_capacity();
20410                    {
20411                        let rank_cache = distributed
20412                            .rank_mut(rank)
20413                            .ok_or("step35 token graph lost a rank cache")?;
20414                        let (k_plane, v_plane, len_d, base_d) =
20415                            rank_cache.planes_and_counters_mut();
20416                        engine.append_kv_quantized_dcw(
20417                            &ws.k[rank],
20418                            &ws.v_raw[rank],
20419                            k_plane,
20420                            v_plane,
20421                            len_d,
20422                            base_d,
20423                            kv_dim_k,
20424                            kv_dim_v,
20425                            ktb,
20426                            vtb,
20427                        )?;
20428                    }
20429                    {
20430                        let rank_cache = distributed
20431                            .rank_mut(rank)
20432                            .ok_or("step35 token graph lost a rank cache")?;
20433                        engine.inc_i32(rank_cache.len_d_mut())?;
20434                    }
20435                    let rank_cache = distributed
20436                        .rank(rank)
20437                        .ok_or("step35 token graph lost a rank cache")?;
20438                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
20439                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
20440                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
20441                    // retarget addresses combine's nsp at arg slot 6, and the fused
20442                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
20443                    // only the eager arm takes FUSION #2d.
20444                    engine.fa_decode_dcw(
20445                        &ws.q[rank],
20446                        &k_ring,
20447                        &v_ring,
20448                        &mut ws.attn_out[rank],
20449                        head_dim,
20450                        local_heads,
20451                        local_kv_heads,
20452                        rank_cache.len_d(),
20453                        rank_cache.base_d(),
20454                        window.unwrap_or(0),
20455                        layer_bucket,
20456                        geometry.attention_scale(),
20457                        ktb,
20458                        vtb,
20459                        None,
20460                    )?;
20461                    engine.attn_head_gate(
20462                        &ws.attn_out[rank],
20463                        &ws.gate[rank],
20464                        &mut ws.gated[rank],
20465                        None,
20466                        head_dim,
20467                        local_heads,
20468                        1,
20469                    )?;
20470                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
20471                    Ok(())
20472                })?;
20473            }
20474
20475            // ---- ROOT: combine + shadows + e-mirrors ----
20476            {
20477                let root = tp
20478                    .runtime
20479                    .rank_engine(0)
20480                    .ok_or("step35 token graph lost the root engine")?;
20481                let runtime = &tp.runtime;
20482                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
20483            }
20484            drop(ws_guard);
20485            drop(decode_input);
20486
20487            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
20488                .ok()
20489                .and_then(|v| v.parse().ok());
20490            if probe_layer == Some(il) {
20491                let Step35TokenGraphState {
20492                    mixed_stage,
20493                    probe_mixed,
20494                    ..
20495                } = &mut *state;
20496                crate::tp::graph_section(e, None, || {
20497                    let _main = e.gpu.enter_main()?;
20498                    let mut dst = probe_mixed.slice_mut(0..n_embd);
20499                    e.stream()
20500                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
20501                    Ok(())
20502                })?;
20503            }
20504
20505            // ---- FFN half ----
20506            match &layer.ffn {
20507                crate::hybrid::Ffn::Dense {
20508                    ffn_gate,
20509                    ffn_up,
20510                    ffn_down,
20511                } => {
20512                    let n_ff = ffn_gate.out_features();
20513                    let lim = self.cfg.clamp_shexp_at(il as u32);
20514                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
20515                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
20516                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
20517                    if lim.is_some() {
20518                        return Err("step35 token graph dense FFN with clamp unsupported".into());
20519                    }
20520                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
20521                        (
20522                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
20523                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
20524                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
20525                        ) => (wg, wu, wd),
20526                        _ => {
20527                            return Err(
20528                                "step35 token graph dense FFN requires bf16-resident weights"
20529                                    .into(),
20530                            );
20531                        }
20532                    };
20533                    crate::tp::graph_section(e, None, || {
20534                        let _main = e.gpu.enter_main()?;
20535                        let Step35TokenGraphState {
20536                            x,
20537                            x1,
20538                            mixed_stage,
20539                            dense_z,
20540                            dense_gate,
20541                            dense_up,
20542                            dense_act,
20543                            sh_stage,
20544                            ..
20545                        } = &mut *state;
20546                        e.add_rms_norm(
20547                            x,
20548                            mixed_stage,
20549                            layer.post_attn_norm.float_data(),
20550                            x1,
20551                            dense_z,
20552                            n_embd,
20553                            1,
20554                            eps,
20555                        )?;
20556                        // TWO SINGLE matvecs, not the dual: eager dense rides two
20557                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
20558                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
20559                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
20560                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
20561                        Self::ffn_act_lim(
20562                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
20563                        )?;
20564                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
20565                        e.add(x1, sh_stage, x, n_embd)?;
20566                        Ok(())
20567                    })?;
20568                }
20569                crate::hybrid::Ffn::Moe(m) => {
20570                    let moe = self
20571                        .cfg
20572                        .moe
20573                        .as_ref()
20574                        .ok_or("step35 token graph needs moe cfg")?;
20575                    let n_expert = moe.expert_count as usize;
20576                    let n_used = moe.expert_used_count as usize;
20577                    let sigmoid = self
20578                        .cfg
20579                        .sigmoid_router()
20580                        .ok_or("step35 token graph needs the sigmoid router")?;
20581                    let step_tp = m
20582                        .step_tp
20583                        .as_ref()
20584                        .ok_or("step35 token graph needs TP experts")?;
20585                    let bank = match &step_tp.experts {
20586                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
20587                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
20588                    };
20589                    let routes_ws_mutex = bank.device_workspace_handle();
20590                    let mut routes_guard = routes_ws_mutex
20591                        .lock()
20592                        .map_err(|_| "routes workspace lock is poisoned")?;
20593                    let routes_ws = routes_guard
20594                        .as_mut()
20595                        .ok_or("step35 token graph requires the routes workspace warmed")?;
20596                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
20597                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
20598                    let p_z = {
20599                        let root = step_tp
20600                            .runtime
20601                            .rank_engine(0)
20602                            .ok_or("routes root engine missing")?;
20603                        let _main = root.gpu.enter_main()?;
20604                        let stream = root.stream();
20605                        let in_stage = routes_ws
20606                            .in_stage_handle()
20607                            .ok_or("routes in stage not armed")?;
20608                        let (a, _g) = in_stage.device_ptr(&stream);
20609                        a as u64
20610                    };
20611                    let local_out = bank.expert_width / ranks;
20612
20613                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
20614                    crate::tp::graph_section(e, None, || {
20615                        let _main = e.gpu.enter_main()?;
20616                        {
20617                            let in_stage = routes_ws
20618                                .in_stage_mut()
20619                                .ok_or("routes in stage not armed")?;
20620                            let Step35TokenGraphState {
20621                                x, x1, mixed_stage, ..
20622                            } = &mut *state;
20623                            e.add_rms_norm(
20624                                x,
20625                                mixed_stage,
20626                                layer.post_attn_norm.float_data(),
20627                                x1,
20628                                in_stage,
20629                                n_embd,
20630                                1,
20631                                eps,
20632                            )?;
20633                        }
20634                        {
20635                            let z_ref = routes_ws
20636                                .in_stage_handle()
20637                                .ok_or("routes in stage not armed")?;
20638                            e.router_gemv_into(
20639                                m.gate_inp.float_data(),
20640                                z_ref,
20641                                &mut state.router_logits,
20642                                n_embd,
20643                                n_expert,
20644                                1,
20645                            )?;
20646                        }
20647                        let (sel_e, w_e) = routes_ws
20648                            .dev_route_e_mut()
20649                            .ok_or("routes staging not armed")?;
20650                        e.moe_router_sigmoid_topk_into(
20651                            &state.router_logits,
20652                            1,
20653                            n_expert,
20654                            n_used,
20655                            m.active_count(),
20656                            &m.exp_probs_b_dev,
20657                            &m.active_experts_dev,
20658                            sigmoid.0,
20659                            sigmoid.1,
20660                            sel_e,
20661                            w_e,
20662                        )?;
20663                        Ok(())
20664                    })?;
20665
20666                    // ---- R0r/R1r (parallel): routes sweeps ----
20667                    group_id += 1;
20668                    for rank in 0..ranks {
20669                        let engine = step_tp
20670                            .runtime
20671                            .rank_engine(rank)
20672                            .ok_or("routes rank engine missing")?;
20673                        let runtime = &step_tp.runtime;
20674                        crate::tp::graph_section(engine, Some(group_id), || {
20675                            runtime.routes_rank_section(
20676                                bank,
20677                                routes_ws,
20678                                p_z,
20679                                local_out,
20680                                n_used,
20681                                step_tp.activation_limit,
20682                                rank,
20683                            )
20684                        })?;
20685                    }
20686
20687                    // ---- ROOTr: combine into the out stage ----
20688                    {
20689                        let root = step_tp
20690                            .runtime
20691                            .rank_engine(0)
20692                            .ok_or("routes root engine missing")?;
20693                        let runtime = &step_tp.runtime;
20694                        crate::tp::graph_section(root, None, || {
20695                            runtime.routes_root_section(bank, routes_ws)
20696                        })?;
20697                    }
20698
20699                    // ---- E3: shexp + add_shared onto the out stage + residual ----
20700                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
20701                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
20702                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
20703                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
20704                        (
20705                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
20706                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
20707                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
20708                        ) => (wg, wu, wd),
20709                        _ => {
20710                            return Err(
20711                                "step35 token graph shexp requires bf16-resident weights".into()
20712                            );
20713                        }
20714                    };
20715                    let n_ff_sh = m
20716                        .gate_shexp
20717                        .as_ref()
20718                        .expect("matched Some above")
20719                        .out_features();
20720                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
20721                    // init, reproducing eager's ones vector without a launch.
20722                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
20723                    crate::tp::graph_section(e, None, || {
20724                        let _main = e.gpu.enter_main()?;
20725                        let (z_ref, out_stage) = routes_ws
20726                            .in_and_out_stages_mut()
20727                            .ok_or("routes stages not armed")?;
20728                        let Step35TokenGraphState {
20729                            x,
20730                            x1,
20731                            sh_stage,
20732                            shexp_gate,
20733                            shexp_up,
20734                            shexp_act,
20735                            gate_sig,
20736                            ..
20737                        } = &mut *state;
20738                        e.matvec_bf16_dual_into(
20739                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
20740                        )?;
20741                        Self::ffn_act_lim(
20742                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
20743                            n_ff_sh,
20744                        )?;
20745                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
20746                        if let Some(gate_w) = gate_inp_shexp {
20747                            e.sigmoid_dot_rows_into(
20748                                z_ref,
20749                                gate_w.float_data(),
20750                                gate_sig,
20751                                n_embd,
20752                                1,
20753                            )?;
20754                        }
20755                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
20756                        e.add(x1, out_stage, x, n_embd)?;
20757                        Ok(())
20758                    })?;
20759                }
20760            }
20761            if probe_layer == Some(il) {
20762                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
20763                crate::tp::graph_section(e, None, || {
20764                    let _main = e.gpu.enter_main()?;
20765                    let mut dst = probe_x.slice_mut(0..n_embd);
20766                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
20767                    Ok(())
20768                })?;
20769            }
20770        }
20771
20772        // ---- Tail: output norm + head into the logits stage ----
20773        let head = match &self.output {
20774            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
20775            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
20776        };
20777        crate::tp::graph_section(e, None, || {
20778            let _main = e.gpu.enter_main()?;
20779            let Step35TokenGraphState {
20780                x,
20781                hn,
20782                logits_stage,
20783                token_d,
20784                pos_d,
20785                token_hist,
20786                hist_idx,
20787                ..
20788            } = &mut *state;
20789            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
20790            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
20791            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
20792            // argmax_gate-validated), the id lands in the history ring, and pos advances on
20793            // device — consecutive launches chain with NO host sync. Single-token mode
20794            // overwrites token_d/pos_d from the host before each launch, so these nodes are
20795            // harmless there.
20796            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
20797            e.u32_hist_append(token_d, token_hist, hist_idx)?;
20798            e.inc_i32(pos_d)?;
20799            Ok(())
20800        })?;
20801
20802        let graph = crate::tp::token_graph_build_finish()?;
20803        state.graphs.push((bucket_max, graph));
20804        eprintln!(
20805            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
20806             build_ms={:.0} performance_claim=false",
20807            started.elapsed().as_secs_f64() * 1e3
20808        );
20809        Ok(())
20810    }
20811}