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    enabled
162        && tokens >= PRIME_MIN_T
163        && ranks == 4
164        && native_p2p
165        && has_rank_local_attention
166        && !fp8_kv
167}
168
169fn empty_cache_layers<T>(n: usize) -> Vec<Option<T>> {
170    std::iter::repeat_with(|| None).take(n).collect()
171}
172
173/// Temporarily move a PP-2 cache's layer state into two independently-owned cache shells.
174/// The stage walkers then receive disjoint `&mut Cache` values and can run on separate host
175/// threads without aliasing. GPU buffers are moved, not copied; Drop restores every layer
176/// and publishes the last position completed by both stages.
177struct PrimeCacheStages<'a> {
178    parent: &'a mut Cache,
179    cut: usize,
180    stage0: Cache,
181    stage1: Cache,
182}
183
184impl<'a> PrimeCacheStages<'a> {
185    fn new(parent: &'a mut Cache, cut: usize) -> Self {
186        let n = parent.kv.len();
187        assert_eq!(parent.recur.len(), n, "cache layer vectors disagree");
188        assert!(cut <= n, "PP-2 cache cut {cut} exceeds {n} layers");
189        let mut kv0 = empty_cache_layers(n);
190        let mut kv1 = empty_cache_layers(n);
191        let mut tp_kv0 = empty_cache_layers(n);
192        let mut tp_kv1 = empty_cache_layers(n);
193        let mut recur0 = empty_cache_layers(n);
194        let mut recur1 = empty_cache_layers(n);
195        for i in 0..cut {
196            kv0[i] = parent.kv[i].take();
197            tp_kv0[i] = parent.tp_kv[i].take();
198            recur0[i] = parent.recur[i].take();
199        }
200        for i in cut..n {
201            kv1[i] = parent.kv[i].take();
202            tp_kv1[i] = parent.tp_kv[i].take();
203            recur1[i] = parent.recur[i].take();
204        }
205        let pos = parent.pos;
206        let max_ctx = parent.max_ctx;
207        Self {
208            parent,
209            cut,
210            stage0: Cache {
211                kv: kv0,
212                tp_kv: tp_kv0,
213                recur: recur0,
214                pos,
215                max_ctx,
216                last_logits_dev: None,
217                dflash_taps: None,
218            },
219            stage1: Cache {
220                kv: kv1,
221                tp_kv: tp_kv1,
222                recur: recur1,
223                pos,
224                max_ctx,
225                last_logits_dev: None,
226                dflash_taps: None,
227            },
228        }
229    }
230
231    fn parts(&mut self) -> (&mut Cache, &mut Cache) {
232        (&mut self.stage0, &mut self.stage1)
233    }
234}
235
236impl Drop for PrimeCacheStages<'_> {
237    fn drop(&mut self) {
238        let n = self.parent.kv.len();
239        for i in 0..n {
240            let source = if i < self.cut {
241                &mut self.stage0
242            } else {
243                &mut self.stage1
244            };
245            debug_assert!(self.parent.kv[i].is_none());
246            debug_assert!(self.parent.tp_kv[i].is_none());
247            debug_assert!(self.parent.recur[i].is_none());
248            self.parent.kv[i] = source.kv[i].take();
249            self.parent.tp_kv[i] = source.tp_kv[i].take();
250            self.parent.recur[i] = source.recur[i].take();
251        }
252        self.parent.pos = self.stage0.pos.min(self.stage1.pos);
253    }
254}
255
256/// task #18 (attn side): one sequence's pre-attention outputs (post-rope q/k, v, out-gate).
257pub(crate) struct AttnPre {
258    pub q: cudarc::driver::CudaSlice<f32>,
259    pub k: cudarc::driver::CudaSlice<f32>,
260    pub v: cudarc::driver::CudaSlice<f32>,
261    pub gate: Option<cudarc::driver::CudaSlice<f32>>,
262}
263
264/// task #18: one sequence's GDN prep outputs (the scan inputs).
265pub(crate) struct GdnPrep {
266    pub hk: usize,
267    pub q_l2: cudarc::driver::CudaSlice<f32>,
268    pub k_l2: cudarc::driver::CudaSlice<f32>,
269    pub v_g: cudarc::driver::CudaSlice<f32>,
270    pub beta: cudarc::driver::CudaSlice<f32>,
271    pub g_log: cudarc::driver::CudaSlice<f32>,
272    pub kb16: Option<cudarc::driver::CudaSlice<u8>>,
273    pub qb16: Option<cudarc::driver::CudaSlice<u8>>,
274}
275
276/// Device scratch for the burst verify stream (see `verify_stream_scratch`).
277pub(crate) struct VerifyStreamScratch {
278    pub pos_d: CudaSlice<i32>,
279    pub row_ctrs: Vec<CudaSlice<i32>>,
280}
281use crate::hybrid::{FullAttnLayer, HybridModel, LinearAttnLayer, Mixer, MoeWeights};
282
283struct MoeInputTraceWriter {
284    dir: std::path::PathBuf,
285    index: std::fs::File,
286    payloads: std::collections::HashMap<u16, (std::fs::File, u64)>,
287}
288
289static MOE_INPUT_TRACE_WRITER: std::sync::OnceLock<std::sync::Mutex<Option<MoeInputTraceWriter>>> =
290    std::sync::OnceLock::new();
291
292/// STAGE-2 GROUPED DECODE gate (MEMRA_MOE_GDEC, default ON; `=0` restores the sequential
293/// per-expert launch chain). See `moe_gdec_token`.
294fn gdec_enabled() -> bool {
295    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
296    *E.get_or_init(|| {
297        std::env::var("MEMRA_MOE_GDEC")
298            .map(|v| v != "0")
299            .unwrap_or(true)
300    })
301}
302
303/// SLAB-LOCAL RESIDENT ARM gate (lane/pp-leverb 2026-08-08, MEMRA_MOE_SLAB, default ON;
304/// `=0` restores the SLRU dispatch even when resident slabs exist). Read PER CALL, never
305/// memoized — probes A/B the two provenances in one process (the MEMRA_PRIME_PP pattern).
306/// See `moe_ffn_sequential_zq8`'s slab_local arm: the sigmoid-router archs (step35/M3/Hy3)
307/// are denied every `dev_exps` consumer (pairs/dev route softmax), so before this arm the
308/// fits-VRAM resident slabs were UPLOADED for them but never READ — the SLRU kept staging
309/// the same bytes beside a dead copy (37 GB H2D per pp4096 prime on the Step SKU, anatomy
310/// receipt). The arm reads the SAME bytes through the SAME kernels; only the pointer
311/// PROVENANCE changes (slab base + ex*stride vs SLRU slot address) — the bit-identity class
312/// `moe_ffn_dev`'s resident arm already documents against its SLRU arm.
313fn moe_slab_enabled() -> bool {
314    std::env::var("MEMRA_MOE_SLAB").as_deref() != Ok("0")
315}
316
317/// Expert-grouped dispatch remains opt-in after the local 5090 transfer gate rejected the
318/// default flip. `=0` selects the established path, while any other explicit value enables the
319/// grouped research arm for the current call.
320fn moe_grouped_enabled(_cfg: &ModelConfig, _prefill: bool) -> bool {
321    std::env::var("MEMRA_MOE_GROUPED")
322        .map(|value| value != "0")
323        .unwrap_or(false)
324}
325
326/// Deterministic in-token expert prefetch. `MEMRA_MOE_PREFETCH=1` overlaps memory-source H2D on the
327/// copy stream; selecting the opt-in worker spill backend enables the same known-next hook for disk.
328fn moe_prefetch_enabled() -> bool {
329    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
330    *E.get_or_init(|| {
331        std::env::var("MEMRA_MOE_PREFETCH").as_deref() == Ok("1")
332            || crate::spill_pread::worker_enabled()
333    })
334}
335
336/// Best-effort OS page-cache prefetch distance for mmap-backed expert ranges. Independent of the
337/// H2D copy-stream experiment so storage->RAM and RAM->HBM overlap can be measured separately.
338/// The opt-in default stays one expert to preserve the original experiment; spill rigs can widen
339/// it with `MEMRA_MOE_PAGE_PREFETCH_WINDOW` to cover NVMe latency.
340fn moe_page_prefetch_window() -> usize {
341    static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
342    *W.get_or_init(|| {
343        page_prefetch_window_from_values(
344            std::env::var("MEMRA_MOE_PAGE_PREFETCH").as_deref() == Ok("1"),
345            std::env::var("MEMRA_MOE_PAGE_PREFETCH_WINDOW")
346                .ok()
347                .as_deref(),
348        )
349    })
350}
351
352fn page_prefetch_window_from_values(enabled: bool, raw_window: Option<&str>) -> usize {
353    if !enabled {
354        return 0;
355    }
356    raw_window.and_then(|value| value.parse().ok()).unwrap_or(1)
357}
358
359/// Return only the newly exposed positions in a rolling lookahead window. Position zero seeds the
360/// full window; each later position adds one expert at the far edge. Thus widening the window does
361/// not repeatedly issue `MADV_WILLNEED` for the same range.
362fn page_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
363    if window == 0 || position >= len {
364        return len..len;
365    }
366    let (start, count) = if position == 0 {
367        (1, window)
368    } else {
369        (position.saturating_add(window), 1)
370    };
371    let start = start.min(len);
372    start..start.saturating_add(count).min(len)
373}
374
375/// Grouped worker-I/O schedule: prime the first active expert before the loop, then queue exactly
376/// one known-next expert at each iteration. Returning positions keeps expert ordering authoritative.
377fn grouped_worker_prefetch_position(order_len: usize, current: Option<usize>) -> Option<usize> {
378    let position = current.map_or(0, |position| position.saturating_add(1));
379    (position < order_len).then_some(position)
380}
381
382/// Fill the worker ring with complete experts, retaining one pinned buffer for an unexpected
383/// demand miss. Each expert has gate/up/down extents, so depth 16 admits a rolling five-expert
384/// window. Position zero primes the current expert too: its three independent reads can run in
385/// parallel instead of demand-serializing gate, up, and down before any useful GPU work exists.
386fn worker_prefetch_window() -> usize {
387    static WINDOW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
388    *WINDOW.get_or_init(|| {
389        let automatic = crate::spill_pread::configured_depth().saturating_sub(1) / 3;
390        std::env::var("MEMRA_SPILL_WORKER_EXPERT_WINDOW")
391            .ok()
392            .and_then(|value| value.parse::<usize>().ok())
393            .unwrap_or(automatic.max(1))
394    })
395}
396
397/// Return only positions newly exposed by a rolling worker-I/O window. Unlike mmap page advice,
398/// this includes the current expert when the window is seeded so all three current projections
399/// enter the CPU pool together.
400fn worker_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
401    if window == 0 || position >= len {
402        return len..len;
403    }
404    let (start, count) = if position == 0 {
405        (0, window)
406    } else {
407        (position.saturating_add(window).saturating_sub(1), 1)
408    };
409    let start = start.min(len);
410    start..start.saturating_add(count).min(len)
411}
412
413/// LAUNCH-STRUCTURE STAGE 3 gate (MEMRA_MOE_DEV, default ON; `=0` restores host routing). The
414/// zero-DtoH device-dispatch path for fully-resident layers: router top-k output stays on device,
415/// expert weight pointers come from the per-layer device table. Requires the fused router (the
416/// dev path consumes the device sel/w directly), so MEMRA_FUSED_ROUTER=0 also disables it.
417fn moe_dev_enabled() -> bool {
418    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
419    *E.get_or_init(|| {
420        std::env::var("MEMRA_MOE_DEV")
421            .map(|v| v != "0")
422            .unwrap_or(true)
423            && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0"))
424    })
425}
426
427/// Device sigmoid top-k is the default for Step-3.7 / M3 / Hy3 / GLM-DSA. `MEMRA_SIG_ROUTER=0` restores
428/// the full-logit DtoH plus `moe_route_sigmoid_host` oracle without changing expert dispatch.
429fn sigmoid_router_enabled() -> bool {
430    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
431    *E.get_or_init(|| {
432        std::env::var("MEMRA_SIG_ROUTER")
433            .map(|v| v != "0")
434            .unwrap_or(true)
435    })
436}
437
438/// MoE EXPERT dp4a gate (MEMRA_MOE_Q8, default ON; `=0` restores the Stage-A f32-dequant expert
439/// kernels). Applies when gate/up/down expert qtypes are all in the dp4a body set (IQ3_S/IQ4_XS).
440/// FP-order differs from Stage-A (int dp4a + warp tree) — argmax/run-gen/stream-identity gates
441/// arbitrate; the sequential and fused q8 paths ship as a matched pair (MEMRA_MOE_GATE contract).
442fn moe_q8_enabled() -> bool {
443    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
444    *E.get_or_init(|| {
445        std::env::var("MEMRA_MOE_Q8")
446            .map(|v| v != "0")
447            .unwrap_or(true)
448    })
449}
450
451/// gemma4 fast-arm gate: qtypes with an `expert_dot_g` dp4a body (superset used by the gelu
452/// dev arm; the qwen q8 arms keep their own battery-gated q8_expert_supported policy).
453fn expert_dp4a_supported(qt: i32) -> bool {
454    qt == crate::QT_Q4_0
455        || qt == crate::QT_IQ3_S
456        || qt == crate::QT_IQ4_XS
457        || qt == crate::QT_Q3_K
458        || qt == crate::QT_Q4_K
459        || qt == crate::QT_Q6_K
460}
461
462fn q8_expert_supported(qt: i32) -> bool {
463    // k-quant arms added 2026-07-06 (Q3_K/Q4_K/Q6_K bodies for the UD tail layers). Briefly
464    // default-excluded the same day when they appeared to break 35B real-prompt spec — the
465    // ACTUAL culprit was the MoE router's cuBLASLt n-dependence (d994271); with the router
466    // decode-exact at verify t, the k-quant arms pass the full spec battery (p1/p2/p3 + raw
467    // K=1..8) and are DEFAULT ON again (+9 tok/s: 148.9 -> 157.9). MEMRA_MOE_Q8_KQ=0 excludes.
468    static KQ: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
469    let kq = *KQ.get_or_init(|| {
470        std::env::var("MEMRA_MOE_Q8_KQ")
471            .map(|v| v != "0")
472            .unwrap_or(true)
473    });
474    // NVFP4 experts: DEFAULT ON (2026-07-17). The M3-era "decode-vs-verify MISMATCH 3.4e1"
475    // that had this excluded was the missing per-expert macro-scale fold, fixed in the
476    // dev-kernel epilogues + moe_w_scale_by_expert; the 35B ct-NVFP4 artifact now runs the
477    // q8 arm at parity with the IQ4_XS daily (174-178 tok/s, spec K=1..8 exact). M3/Hy3
478    // never reach the q8 arms regardless (sigmoid-router cfg gates on pairs/dev/gdec).
479    // MEMRA_MOE_Q8_NVFP4=0 restores the f32 arm.
480    let nvfp4_q8 = std::env::var("MEMRA_MOE_Q8_NVFP4")
481        .map(|v| v != "0")
482        .unwrap_or(true);
483    qt == crate::QT_IQ3_S
484        || qt == crate::QT_IQ4_XS
485        || (nvfp4_q8 && qt == crate::QT_NVFP4)
486        || (kq && (qt == crate::QT_Q3_K || qt == crate::QT_Q4_K || qt == crate::QT_Q6_K))
487}
488
489/// The decode-once (_dec) and IQ-MMA expert kernels dequant via IQ-specific extractors —
490/// k-quant tensors must fall to the _em dot path instead.
491fn q8_expert_dec_supported(qt: i32) -> bool {
492    qt == crate::QT_IQ3_S || qt == crate::QT_IQ4_XS || qt == crate::QT_Q4_0
493}
494
495/// Grouped-f16 door (MEMRA_MOE_F16G) per-projection admission: the qtype has a dequant-to-f16
496/// kernel in cu/moe_f16_grouped.cu AND the projection's k dimension tiles its block size.
497/// Round 49 widened coverage to q35's UD mix (gate/up IQ3_S x39 + Q3_K x1 + IQ4_XS x1; down
498/// IQ4_XS x37 + Q6_K x3 + Q4_K x1) — the round-47 IQ4_XS/Q4_0-only table admitted ~1 of 41
499/// q35 layers, which is why that cell measured FLAT.
500fn f16g_proj_ok(qt: i32, in_f: usize) -> bool {
501    match qt {
502        crate::QT_Q4_0 => in_f % 32 == 0,
503        crate::QT_IQ4_XS | crate::QT_IQ3_S | crate::QT_Q3_K | crate::QT_Q4_K | crate::QT_Q6_K => {
504            in_f % 256 == 0
505        }
506        // NVFP4 (block 64) added lane/moebatch-q35moe 2026-08-21: the ornith15 expert bank is
507        // uniform NVFP4, which passed the pairs q8 gate but missed BOTH batched doors
508        // (use_mma's dec set and this table), so 14.7k-token prefill rode the per-pair _em
509        // fallback — 88.6% of the prime wall (prime-anatomy receipt).
510        crate::QT_NVFP4 => in_f % 64 == 0,
511        _ => false,
512    }
513}
514
515/// STAGE 3 prewarm gate (MEMRA_MOE_PREWARM, default ON; `=0` leaves residency organic). One-shot
516/// per layer: force-admit every block while FREE slots cover the whole layer (never evicts).
517fn moe_prewarm_enabled() -> bool {
518    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
519    *E.get_or_init(|| {
520        std::env::var("MEMRA_MOE_PREWARM")
521            .map(|v| v != "0")
522            .unwrap_or(true)
523    })
524}
525
526/// During a discarded fixed-residency profile, admit CPU-routed misses after their current-token
527/// CPU result is complete. The current result and numeric path are unchanged; later warmup tokens
528/// can then vote for and exercise those experts on GPU before the cache is frozen.
529fn cpu_expert_profile_admit_enabled() -> bool {
530    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
531    *E.get_or_init(|| std::env::var("MEMRA_CPU_EXPERT_FREEZE_PROFILE_ADMIT").as_deref() == Ok("1"))
532}
533
534/// Minimum prompt length for the BATCHED cache prime (`prime_cache`). Below this the tokenwise
535/// decode loop wins anyway (the batched path's GEMM dispatch needs m>=16, and the stateful conv
536/// kernel needs T >= d_conv-1). Callers: generate / generate_spec.
537pub const PRIME_MIN_T: usize = 16;
538
539/// Widest tick the MoE DEV per-token program serves (lane/orndecode-20260822). PRIME_MIN_T
540/// doubled as the dev-arm's upper bound on the assumption that t==16 only ever meant real
541/// prefill; the exact-16 decode tier broke that assumption — at B=16 the MoE stage crossed
542/// onto the t>=MMA_T grouped/kq GEMM program (m_e ~1.6 rows/expert: 52.6% of the tick at
543/// ~104 us/launch) or the `_em` per-pair fallback (67.7 us), both catastrophically slower
544/// than the dev q8 kernels that serve B<=8 (8.8 us gate_up covering a token's whole expert
545/// set). Decode widths 2..=16 now ride dev; the grouped/pairs prefill programs start at 17.
546/// gate2/gate3 byte batteries at B=12/16 are the qualification (bit-checked vs isolated).
547const MOE_DEV_MAX_T: usize = 16;
548const PRIME_PIPE_MICROBATCHES: usize = 8;
549const PRIME_PIPE_MIN_CHUNK: usize = 128;
550const PRIME_PIPE_EDGE_MIN_CHUNK: usize = 64;
551const PRIME_PIPE_LINEAR_WORK: usize = 8;
552
553fn prime_pp2_auto_geometry(n_layers: usize) -> bool {
554    crate::pp::prime_pp_on()
555        && !crate::pp::pp2_streams_off()
556        && crate::pp::pp_cuts(n_layers).is_some_and(|cuts| cuts.len() == 3)
557}
558
559/// Effective internal prime chunk. An explicit MEMRA_PRIME_CHUNK is authoritative.
560/// Naked PP-2 primes use the measured pipeline geometry: up to eight microchunks, never
561/// below 128 tokens, while the legacy 4096-token cap remains the long-context bound.
562pub fn prime_chunk_tokens(t: usize, n_layers: usize) -> usize {
563    if let Ok(value) = std::env::var("MEMRA_PRIME_CHUNK") {
564        let parsed = value
565            .parse::<usize>()
566            .unwrap_or(crate::cache::PRIME_CHUNK_MAX_TOKENS);
567        return if crate::cache::swa_ring_on() {
568            if parsed == 0 {
569                crate::cache::PRIME_CHUNK_MAX_TOKENS
570            } else {
571                parsed.min(crate::cache::PRIME_CHUNK_MAX_TOKENS)
572            }
573        } else {
574            parsed
575        };
576    }
577    let chunk = crate::cache::PRIME_CHUNK_MAX_TOKENS;
578    if prime_pp2_auto_geometry(n_layers) && t >= 2 * PRIME_PIPE_MIN_CHUNK {
579        chunk.min(
580            t.div_ceil(PRIME_PIPE_MICROBATCHES)
581                .max(PRIME_PIPE_MIN_CHUNK),
582        )
583    } else {
584        chunk
585    }
586}
587
588fn fixed_prime_chunk_ranges(t: usize, chunk: usize) -> Vec<(usize, usize)> {
589    fixed_prime_chunk_ranges_for_ring(t, chunk, crate::cache::swa_ring_on())
590}
591
592fn fixed_prime_chunk_ranges_for_ring(t: usize, chunk: usize, ring_on: bool) -> Vec<(usize, usize)> {
593    if chunk == 0 || t <= chunk {
594        return vec![(0, t)];
595    }
596    let mut ranges = Vec::with_capacity(t.div_ceil(chunk));
597    let mut start = 0usize;
598    while start < t {
599        let mut end = (start + chunk).min(t);
600        if t - end > 0 && t - end < PRIME_MIN_T {
601            if ring_on {
602                let shifted = t - PRIME_MIN_T;
603                end = if shifted > start { shifted } else { t };
604            } else {
605                end = t;
606            }
607        }
608        ranges.push((start, end));
609        start = end;
610    }
611    ranges
612}
613
614fn prime_chunk_work(prefix: usize, total: usize) -> u128 {
615    let prefix = prefix as u128;
616    prefix * (prefix + (PRIME_PIPE_LINEAR_WORK as u128) * (total as u128))
617}
618
619fn dynamic_prime_chunk_ranges(
620    t: usize,
621    fixed_chunk: usize,
622    fixed: &[(usize, usize)],
623) -> Vec<(usize, usize)> {
624    let n = fixed.len();
625    if n < 3 {
626        return fixed.to_vec();
627    }
628
629    let max_first = t - (n - 1) * PRIME_MIN_T;
630    let first = fixed_chunk
631        .div_ceil(2)
632        .max(PRIME_PIPE_EDGE_MIN_CHUNK)
633        .min(max_first);
634    let mut ranges = Vec::with_capacity(n);
635    ranges.push((0, first));
636
637    let first_work = prime_chunk_work(first, t);
638    let work_span = prime_chunk_work(t, t) - first_work;
639    let denominator = (n - 1) as u128;
640    let mut previous = first;
641    for boundary in 1..n - 1 {
642        let target = first_work * denominator + work_span * (boundary as u128);
643        let remaining = n - 1 - boundary;
644        let mut low = previous + PRIME_MIN_T;
645        let mut high = t - remaining * PRIME_MIN_T;
646        while low < high {
647            let mid = low + (high - low) / 2;
648            if prime_chunk_work(mid, t) * denominator >= target {
649                high = mid;
650            } else {
651                low = mid + 1;
652            }
653        }
654        ranges.push((previous, low));
655        previous = low;
656    }
657    ranges.push((previous, t));
658    ranges
659}
660
661/// Internal prime ranges. The naked PP-2 pipeline defaults to a short-fill,
662/// equal-modeled-time schedule; MEMRA_PRIME_CHUNK_SCHED=fixed restores the measured
663/// equal-token ranges. An explicit MEMRA_PRIME_CHUNK always retains fixed semantics.
664///
665/// `gdn_grid`: the model runs the chunked GDN WY scan (`HybridModel::gdn_prime_grid_on`) —
666/// AUTO-scheduled internal boundaries are then snapped down to the WY-chunk grid
667/// (`align_prime_ranges_to_gdn`; the spec-longctx grid law, extended from serve splits to
668/// the PP prime microchunks). Explicit MEMRA_PRIME_CHUNK keeps its operator-authoritative
669/// (fixed, unaligned) semantics — the FLAGS caveat documents that identity contract.
670pub fn prime_chunk_ranges(t: usize, n_layers: usize, gdn_grid: bool) -> Vec<(usize, usize)> {
671    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
672    let chunk = prime_chunk_tokens(t, n_layers);
673    let fixed = fixed_prime_chunk_ranges(t, chunk);
674    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
675        Ok(value) => value == "dynamic",
676        Err(_) => true,
677    };
678    if explicit_chunk {
679        return fixed;
680    }
681    let ranges = if !dynamic || !prime_pp2_auto_geometry(n_layers) {
682        fixed
683    } else {
684        dynamic_prime_chunk_ranges(t, chunk, &fixed)
685    };
686    // MEMRA_PRIME_GRID_ALIGN=0 is the shared rollback seam of the grid law (same env the
687    // worker's serve-boundary alignment honors, read per call so gates can flip it
688    // in-process): the legacy off-grid auto schedule — the toothed cell's broken arm.
689    if gdn_grid && std::env::var("MEMRA_PRIME_GRID_ALIGN").as_deref() != Ok("0") {
690        align_prime_ranges_to_gdn(&ranges, t, Engine::gdn_chunk_size())
691    } else {
692        ranges
693    }
694}
695
696/// Snap AUTO prime-range internal boundaries DOWN to the GDN WY-chunk grid (lane/
697/// hermes-perf-fixes, 2026-08-23 — the missing helper the PP-auto-ranges finding names).
698///
699/// THE LAW THIS EXTENDS (measured, research/multiturn-cache-20260821/
700/// LONGCTX-EXACTNESS-20260821.md; the serve-split half already ships as the worker's
701/// `grid_align_boundary`): under the chunked WY scan a prompt primed as two calls split at
702/// L is bit-identical to the monolithic prime iff `L % gdn_chunk_size() == 0` — an off-grid
703/// call start shifts the fold grid and materializes recurrent state at a point the
704/// monolithic program never computes. The prime loop walks these ranges as separate
705/// `prime_layers` calls, so INTERNAL microchunk boundaries are the same seam: the PP-2
706/// auto geometry (`t.div_ceil(8).max(128)` fills, and every dynamic short-fill boundary)
707/// lands off the 32-token grid for most prompt lengths, which is exactly the
708/// chunk-value bit-identity the GDN lane falsified (FLAGS PRIME_CHUNK/SCHED caveat).
709///
710/// Boundaries only move DOWN (earlier is always semantically safe — same argument as the
711/// worker's alignment); a boundary that collapses onto its predecessor is dropped (ranges
712/// merge). The final range always ends at `t`. Aligning down only GROWS the tail
713/// remainder, so the fixed-schedule tail-merge rule is never re-violated. Cost bound: at
714/// most `c-1` tokens shift per boundary.
715pub fn align_prime_ranges_to_gdn(
716    ranges: &[(usize, usize)],
717    t: usize,
718    c: usize,
719) -> Vec<(usize, usize)> {
720    if c == 0 || ranges.len() < 2 {
721        return ranges.to_vec();
722    }
723    let mut out: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
724    let mut start = 0usize;
725    for (i, &(_, end)) in ranges.iter().enumerate() {
726        let e = if i + 1 == ranges.len() {
727            t
728        } else {
729            end / c * c
730        };
731        if e > start {
732            out.push((start, e));
733            start = e;
734        } // else: boundary collapsed onto its predecessor — merge into the next range
735    }
736    debug_assert_eq!(out.last().map(|&(_, e)| e), Some(t));
737    out
738}
739
740struct HeadSplit {
741    pin: u64,
742    w1: CudaSlice<u8>,
743    hn1: CudaSlice<f32>,
744    y1: CudaSlice<f32>,
745    logits_e: CudaSlice<f32>,
746    ev_hn: cudarc::driver::CudaEvent,
747    ev_done: cudarc::driver::CudaEvent,
748    raw_hn1: u64,
749    raw_y1: u64,
750    raw_logits_hi: u64,
751}
752/// HEAD-SPLIT workspace (host + device twins share it).
753static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
754
755/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
756/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
757/// input bits — rank1's local selection is bit-equal to the root's.
758#[allow(clippy::type_complexity)]
759static DEV1_ROUTER_REPS: std::sync::Mutex<
760    Option<(
761        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
762        Option<CudaSlice<f32>>,
763    )>,
764> = std::sync::Mutex::new(None);
765
766/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
767/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
768/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
769#[allow(clippy::type_complexity)]
770static SHEXP_D1_REPS: std::sync::Mutex<
771    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
772> = std::sync::Mutex::new(None);
773#[allow(clippy::type_complexity)]
774static SHEXP_D1_WS: std::sync::Mutex<
775    Option<(
776        (usize, usize),
777        CudaSlice<f32>,
778        CudaSlice<f32>,
779        CudaSlice<f32>,
780        cudarc::driver::CudaEvent,
781        cudarc::driver::CudaEvent,
782    )>,
783> = std::sync::Mutex::new(None);
784
785/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
786static SHEXP_OV_WS: std::sync::Mutex<
787    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
788> = std::sync::Mutex::new(None);
789
790impl HybridModel {
791    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
792    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
793    /// an off-grid prime-call boundary shifts the WY fold grid (see
794    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
795    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
796    pub fn gdn_prime_grid_on(&self) -> bool {
797        Engine::gdn_chunked_enabled()
798            && self
799                .layers
800                .iter()
801                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
802    }
803
804    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
805    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
806    /// (the device buffers must be addressable on both sides — the TP registry builds its
807    /// own Engine per rank, so this is a real seam, not a formality).
808    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
809        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
810    }
811
812    fn step35_tp_qkv(
813        &self,
814        e: &Engine,
815        fa: &FullAttnLayer,
816        h: &CudaSlice<f32>,
817        t: usize,
818    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
819        let Some(tp) = fa.step_tp_qkv.as_ref() else {
820            return Ok(None);
821        };
822        let values = active_matrix_values(
823            h.len(),
824            t,
825            self.cfg.n_embd as usize,
826            "Step TP QKV activation",
827        )?;
828        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
829        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
830        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
831        // round-trip on every execute that the peer transport exists to remove. The
832        // device twins are byte-identical by construction (the same bytes travel dtod
833        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
834        // The host arm below remains the transport for !native_p2p (host staging IS that
835        // transport) and for a root context this engine cannot address.
836        if Self::step35_tp_device_resident(e, tp) {
837            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
838            // on theirs (same context, different streams).
839            e.stream().synchronize()?;
840            let q = tp
841                .runtime
842                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
843            let k = tp
844                .runtime
845                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
846            let v = tp
847                .runtime
848                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
849            Self::step35_tp_log_once(tp, "qkv", "device-resident");
850            return Ok(Some(vec![q, k, v]));
851        }
852        let host = e.dtoh_view(&h.slice(0..values))?;
853        let q = if tp.runtime.native_p2p() {
854            tp.runtime
855                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
856        } else {
857            tp.runtime
858                .bf16_column_parallel_resident(&tp.q, &host, t)?
859                .gathered
860        };
861        let k = if tp.runtime.native_p2p() {
862            tp.runtime
863                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
864        } else {
865            tp.runtime
866                .bf16_column_parallel_resident(&tp.k, &host, t)?
867                .gathered
868        };
869        let v = if tp.runtime.native_p2p() {
870            tp.runtime
871                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
872        } else {
873            tp.runtime
874                .bf16_column_parallel_resident(&tp.v, &host, t)?
875                .gathered
876        };
877        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
878        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
879    }
880
881    /// One transport banner per (projection, transport) — the old per-call eprintln fired
882    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
883    /// unbouncing (the sibling grouped-EP path already learned this).
884    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
885        use std::sync::atomic::{AtomicBool, Ordering};
886        static LOGGED: [AtomicBool; 4] = [
887            AtomicBool::new(false),
888            AtomicBool::new(false),
889            AtomicBool::new(false),
890            AtomicBool::new(false),
891        ];
892        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
893        if LOGGED[idx].swap(true, Ordering::Relaxed) {
894            return;
895        }
896        eprintln!(
897            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
898             tensor_parallel=true attention_local=true kv_local=true transport={} \
899             native_p2p={} bulk_p2p={} activation={activation} \
900             output={} performance_claim=false (logged once per transport)",
901            tp.layer,
902            tp.devices,
903            tp.runtime.transport_label(),
904            tp.runtime.native_p2p(),
905            tp.runtime.bulk_p2p(),
906            if activation == "device-resident" {
907                "root-resident"
908            } else {
909                "root-readback"
910            },
911        );
912    }
913
914    fn step35_tp_o(
915        &self,
916        e: &Engine,
917        fa: &FullAttnLayer,
918        activation: &CudaSlice<f32>,
919        tokens: usize,
920    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
921        let Some(tp) = fa.step_tp_qkv.as_ref() else {
922            return Ok(None);
923        };
924        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
925        // of the attention output, no host O staging, root-resident reduction consumed in
926        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
927        if Self::step35_tp_device_resident(e, tp) {
928            e.stream().synchronize()?; // producer fence, as the QKV half
929            let output = tp
930                .runtime
931                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
932            Self::step35_tp_log_once(tp, "o", "device-resident");
933            return Ok(Some(output));
934        }
935        let host = e.dtoh(activation)?;
936        let output = if tp.runtime.native_p2p() {
937            tp.runtime
938                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
939        } else {
940            tp.runtime
941                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
942        };
943        Self::step35_tp_log_once(tp, "o", "host-canonical");
944        Ok(Some(e.htod(&output)?))
945    }
946
947    fn step35_o(
948        &self,
949        e: &Engine,
950        fa: &FullAttnLayer,
951        activation: &CudaSlice<f32>,
952        tokens: usize,
953    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
954        match self.step35_tp_o(e, fa, activation, tokens)? {
955            Some(output) => Ok(output),
956            None => e.matmul(&fa.wo, activation, tokens),
957        }
958    }
959
960    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
961    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
962    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
963    /// (it forces a dtoh + host hash per layer).
964    fn prime_trace_path() -> Option<&'static str> {
965        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
966        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
967            .as_deref()
968    }
969
970    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
971    /// each prime_layers stage and accumulates wall time per stage class, printed after
972    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
973    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
974    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
975    fn prime_anatomy_on() -> bool {
976        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
977        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
978    }
979
980    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
981        static S: [std::sync::atomic::AtomicU64; 5] = [
982            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
983            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
984            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
985            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
986            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
987        ];
988        &S
989    }
990
991    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
992    pub fn forward(
993        &self,
994        e: &Engine,
995        tokens: &[u32],
996    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
997        if self.is_gemma4_e4b() {
998            return self.gemma4_e4b_forward(e, tokens, false);
999        }
1000        if self.uses_gemma_program() {
1001            return self.gemma4_forward(e, tokens, false);
1002        }
1003        let cfg = &self.cfg;
1004        let n_embd = cfg.n_embd as usize;
1005        let t = tokens.len();
1006        let eps = cfg.rms_eps;
1007        let pos: Vec<i32> = (0..t as i32).collect();
1008        let pos_d = e.htod_i32(&pos)?;
1009
1010        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1011
1012        for (il, layer) in self.layers.iter().enumerate() {
1013            // attn_norm
1014            let mut h = e.uninit(t * n_embd)?;
1015            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1016
1017            let mixed = match &layer.mixer {
1018                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1019                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1020                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1021            };
1022
1023            // residual 1
1024            let mut x1 = e.uninit(t * n_embd)?;
1025            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1026
1027            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1028            let mut z = e.uninit(t * n_embd)?;
1029            e.rms_norm(
1030                &x1,
1031                layer.post_attn_norm.float_data(),
1032                &mut z,
1033                n_embd,
1034                t,
1035                eps,
1036            )?;
1037            let ffn_out = match &layer.ffn {
1038                crate::hybrid::Ffn::Dense {
1039                    ffn_gate,
1040                    ffn_up,
1041                    ffn_down,
1042                } => {
1043                    let n_ff = ffn_gate.out_features();
1044                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1045                    let up = g2.pop().unwrap();
1046                    let gate = g2.pop().unwrap();
1047                    let mut act = e.uninit(t * n_ff)?;
1048                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1049                    // both the dense MLP and the shared expert, and its limit is
1050                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1051                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1052                    Self::ffn_act_lim(
1053                        e,
1054                        &self.cfg,
1055                        &gate,
1056                        &up,
1057                        1.0,
1058                        1.0,
1059                        self.cfg.clamp_shexp_at(il as u32),
1060                        &mut act,
1061                        t * n_ff,
1062                    )?;
1063                    e.matmul(ffn_down, &act, t)?
1064                }
1065                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1066            };
1067            let mut x2 = e.uninit(t * n_embd)?;
1068            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1069            x = x2;
1070        }
1071
1072        let mut hn = e.uninit(t * n_embd)?;
1073        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1074        let logits = e.matmul(&self.output, &hn, t)?;
1075        Ok(e.dtoh(&logits)?)
1076    }
1077
1078    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1079    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1080    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1081    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1082    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1083    pub fn forward_last(
1084        &self,
1085        e: &Engine,
1086        tokens: &[u32],
1087    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1088        if self.uses_gemma_program() {
1089            return self.gemma4_forward(e, tokens, true);
1090        }
1091        let cfg = &self.cfg;
1092        let n_embd = cfg.n_embd as usize;
1093        let t = tokens.len();
1094        let eps = cfg.rms_eps;
1095        let pos: Vec<i32> = (0..t as i32).collect();
1096        let pos_d = e.htod_i32(&pos)?;
1097
1098        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1099        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1100        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1101        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1102        let anat = Self::prime_anatomy_on();
1103        let mut anat_last = if anat {
1104            e.stream().synchronize()?;
1105            Some(std::time::Instant::now())
1106        } else {
1107            None
1108        };
1109        macro_rules! anat_mark {
1110            ($slot:expr) => {
1111                if let Some(ts) = anat_last.as_mut() {
1112                    e.stream().synchronize()?;
1113                    Self::prime_anatomy_slots()[$slot].fetch_add(
1114                        ts.elapsed().as_nanos() as u64,
1115                        std::sync::atomic::Ordering::Relaxed,
1116                    );
1117                    *ts = std::time::Instant::now();
1118                }
1119            };
1120        }
1121        for (il, layer) in self.layers.iter().enumerate() {
1122            let mut h = e.uninit(t * n_embd)?;
1123            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1124            if probe {
1125                e.stream().synchronize()?;
1126                eprintln!("[probe] L{il} norm ok");
1127            }
1128            anat_mark!(4);
1129            let mixed = match &layer.mixer {
1130                Mixer::Full(fa) => {
1131                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1132                    anat_mark!(0);
1133                    y
1134                }
1135                Mixer::Linear(la) => {
1136                    let y = self.linear_attn(e, la, &h, t)?;
1137                    anat_mark!(1);
1138                    y
1139                }
1140                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1141            };
1142            if probe {
1143                e.stream().synchronize()?;
1144                eprintln!("[probe] L{il} mixer ok");
1145            }
1146            let mut x1 = e.uninit(t * n_embd)?;
1147            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1148            let mut z = e.uninit(t * n_embd)?;
1149            e.rms_norm(
1150                &x1,
1151                layer.post_attn_norm.float_data(),
1152                &mut z,
1153                n_embd,
1154                t,
1155                eps,
1156            )?;
1157            anat_mark!(4);
1158            let ffn_out = match &layer.ffn {
1159                crate::hybrid::Ffn::Dense {
1160                    ffn_gate,
1161                    ffn_up,
1162                    ffn_down,
1163                } => {
1164                    let n_ff = ffn_gate.out_features();
1165                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1166                    let up = g2.pop().unwrap();
1167                    let gate = g2.pop().unwrap();
1168                    let mut act = e.uninit(t * n_ff)?;
1169                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1170                    Self::ffn_act_lim(
1171                        e,
1172                        &self.cfg,
1173                        &gate,
1174                        &up,
1175                        1.0,
1176                        1.0,
1177                        self.cfg.clamp_shexp_at(il as u32),
1178                        &mut act,
1179                        t * n_ff,
1180                    )?;
1181                    let y = e.matmul(ffn_down, &act, t)?;
1182                    anat_mark!(3);
1183                    y
1184                }
1185                crate::hybrid::Ffn::Moe(m) => {
1186                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1187                    anat_mark!(2);
1188                    y
1189                }
1190            };
1191            if probe {
1192                e.stream().synchronize()?;
1193                eprintln!("[probe] L{il} ffn ok");
1194            }
1195            let mut x2 = e.uninit(t * n_embd)?;
1196            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1197            x = x2;
1198        }
1199        if anat {
1200            let s = Self::prime_anatomy_slots();
1201            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1202            eprintln!(
1203                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1204                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1205                ms(0),
1206                ms(1),
1207                ms(2),
1208                ms(3),
1209                ms(4)
1210            );
1211        }
1212        // norm over all T, then slice the LAST row and run lm_head on that single row.
1213        let mut hn = e.uninit(t * n_embd)?;
1214        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1215        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1216        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1217        let mut hlast = e.uninit(n_embd)?;
1218        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1219        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1220        Ok(e.dtoh(&logits)?)
1221    }
1222
1223    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1224    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1225    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1226    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1227    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1228    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1229    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1230    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1231    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1232    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1233    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1234    ///   (c) `cache.pos`/KV len/len_d advance by T.
1235    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1236    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1237    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1238    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1239    ///
1240    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1241    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1242    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1243    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1244    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1245    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1246    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1247    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1248    /// differently under load — research/tick-seg-20260807, receipt in
1249    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1250    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1251    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1252    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1253    /// caller that SPLITS one request across calls passes the remainder.
1254    pub fn prime_cache(
1255        &self,
1256        e: &Engine,
1257        tokens: &[u32],
1258        cache: &mut Cache,
1259        queued_after: usize,
1260    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1261        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1262    }
1263
1264    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1265    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1266    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1267    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1268    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1269    pub fn prime_cache_overlaid(
1270        &self,
1271        e: &Engine,
1272        tokens: &[u32],
1273        cache: &mut Cache,
1274        queued_after: usize,
1275        overlay: Option<&crate::vision::EmbedOverlay>,
1276    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1277        let n_embd = self.cfg.n_embd as usize;
1278        let t = tokens.len();
1279        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1280        // session cache — every chunk (including the first) takes the continuation arm
1281        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1282        assert!(
1283            t >= PRIME_MIN_T,
1284            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1285        );
1286        assert!(
1287            cache.pos + t <= cache.max_ctx,
1288            "prime_cache: prompt exceeds cache max_ctx"
1289        );
1290
1291        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1292        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1293        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1294        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1295        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1296        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1297        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1298        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1299        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1300        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1301        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1302        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1303            if self.is_gemma4_e4b() {
1304                if overlay.is_some() {
1305                    return Err(
1306                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1307                    );
1308                }
1309                return self.gemma4_e4b_prime(e, tokens, cache);
1310            }
1311            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1312            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1313            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1314            // spans become bidirectional attention islands (lane/gemma-vision).
1315            return self.gemma4_prime(e, tokens, cache, overlay);
1316        }
1317        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1318        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1319        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1320        // the prefill's ARITHMETIC, so two rigs with different values produced different
1321        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1322        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1323        // (VERDICT.md) — and it is NOT what docs originally said:
1324        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1325        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1326        //     output head), so growing a chunk cannot move an existing row's value.
1327        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1328        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1329        //     not describe our leak.
1330        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1331        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1332        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1333        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1334        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1335        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1336        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1337        // the source — every row is in one numeric class, so the chunk size no longer steers
1338        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1339        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1340        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1341        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1342        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1343        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1344        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1345        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1346        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1347        // across calls, the request still ends at the same absolute position, whatever the tick
1348        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1349        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1350        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1351        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1352        // on in a measured default run.
1353        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1354        let seq_end = if legacy_calllocal {
1355            cache.pos + t
1356        } else {
1357            cache.pos + t + queued_after
1358        };
1359        if ranges.len() == 1 {
1360            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1361        }
1362        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1363        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1364        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1365        // this lane owns the balanced two-stage schedule only.
1366        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1367            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1368                if overlay.is_some() {
1369                    return Err(
1370                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1371                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1372                            .into(),
1373                    );
1374                }
1375                if crate::pp::pp_multi_stream_same_device() {
1376                    return Err(
1377                        "prime chunk pipeline refused with 2 stage streams on one device — \
1378                         that concurrent-stream placement remains quarantined by the deferred \
1379                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1380                         the serial split."
1381                            .into(),
1382                    );
1383                }
1384                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1385            }
1386        }
1387        let mut hiddens = e.uninit(t * n_embd)?;
1388        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1389        for &(start, end) in &ranges {
1390            // chunked prime writes tap rows at the chunk's absolute offset
1391            if let Some(taps) = cache.dflash_taps.as_mut() {
1392                taps.base = start;
1393            }
1394            let (l, hs, x) =
1395                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1396            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1397            last = Some((l, hs));
1398        }
1399        let (logits, h_seed) = last.unwrap();
1400        Ok((logits, h_seed, hiddens))
1401    }
1402
1403    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1404    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1405    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1406    /// norm, lm head, and caller hidden-stack copy as the serial split.
1407    fn prime_cache_pp2_pipelined(
1408        &self,
1409        e: &Engine,
1410        tokens: &[u32],
1411        cache: &mut Cache,
1412        seq_end: usize,
1413        ranges: &[(usize, usize)],
1414        fence: &[usize],
1415    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1416        debug_assert_eq!(fence.len(), 3);
1417        debug_assert!(ranges.len() >= 2);
1418        let rt = crate::pp::PpNRt::get(e)?;
1419        assert_eq!(
1420            rt.n_stages(),
1421            2,
1422            "prime pipeline requires exactly two PP stages"
1423        );
1424        let n_embd = self.cfg.n_embd as usize;
1425        let t = tokens.len();
1426        let initial_base = cache.pos;
1427        let caller_stream = e.stream();
1428
1429        // #87 reverse publication before any new stage allocation, then prewarm both
1430        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1431        // after stage 1(N) is queued would synchronize that stream and erase the first
1432        // overlap on a two-chunk prompt.
1433        rt.fence_stages_behind(&caller_stream)?;
1434        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1435        rt.prepare_overlap_slots(0, max_payload)?;
1436
1437        let mut hiddens = e.uninit(t * n_embd)?;
1438        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1439        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1440        let (cache0, cache1) = stage_caches.parts();
1441        let (first_start, first_end) = ranges[0];
1442        let mut slot = self.prime_pp2_stage0_enqueue(
1443            e,
1444            rt,
1445            &tokens[first_start..first_end],
1446            cache0,
1447            seq_end,
1448            fence,
1449            initial_base + first_start,
1450            true,
1451        )?;
1452        cache0.pos = initial_base + first_end;
1453
1454        for (i, &(start, end)) in ranges.iter().enumerate() {
1455            let base = initial_base + start;
1456            debug_assert_eq!(
1457                cache1.pos, base,
1458                "stage 1 must drain chunks in original position order"
1459            );
1460            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1461                let next_base = initial_base + next_start;
1462                debug_assert_eq!(
1463                    cache0.pos, next_base,
1464                    "stage 0 must issue chunks in original position order"
1465                );
1466                let cache0_stage = &mut *cache0;
1467                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1468                // on one host thread therefore serialize even if the calls are ordered as
1469                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1470                // stage 1 consumes slot N while stage 0 produces slot N+1.
1471                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1472                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1473                        let next = self
1474                            .prime_pp2_stage0_enqueue(
1475                                e,
1476                                rt,
1477                                &tokens[next_start..next_end],
1478                                cache0_stage,
1479                                seq_end,
1480                                fence,
1481                                next_base,
1482                                true,
1483                            )
1484                            .map_err(|err| err.to_string())?;
1485                        cache0_stage.pos = initial_base + next_end;
1486                        Ok(next)
1487                    });
1488                    let x = self.prime_pp2_stage1_enqueue(
1489                        e,
1490                        rt,
1491                        slot,
1492                        end - start,
1493                        cache1,
1494                        seq_end,
1495                        fence,
1496                        base,
1497                        true,
1498                    )?;
1499                    let out = {
1500                        rt.bind_stage(1)?;
1501                        let _st1 = rt.enter(1);
1502                        let e1 = rt.engine(1, e);
1503                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1504                    };
1505                    let next = stage0
1506                        .join()
1507                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1508                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1509                    Ok((out, Some(next)))
1510                })?
1511            } else {
1512                let x = self.prime_pp2_stage1_enqueue(
1513                    e,
1514                    rt,
1515                    slot,
1516                    end - start,
1517                    cache1,
1518                    seq_end,
1519                    fence,
1520                    base,
1521                    true,
1522                )?;
1523                let out = {
1524                    rt.bind_stage(1)?;
1525                    let _st1 = rt.enter(1);
1526                    let e1 = rt.engine(1, e);
1527                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1528                };
1529                (out, None)
1530            };
1531
1532            rt.publish_to(1, &caller_stream)?;
1533            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1534            last = Some((out.0, out.1));
1535            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1536
1537            if let Some(next) = next_slot {
1538                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1539                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1540                // Stage 0(N+1) is already queued before this wait is appended, so its
1541                // overlap with stage 1(N) is preserved.
1542                rt.fence_stages_behind(&caller_stream)?;
1543                slot = next;
1544            }
1545        }
1546
1547        debug_assert_eq!(cache0.pos, initial_base + t);
1548        debug_assert_eq!(cache1.pos, initial_base + t);
1549        let (logits, h_seed) = last.unwrap();
1550        Ok((logits, h_seed, hiddens))
1551    }
1552
1553    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1554    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1555    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1556    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1557    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1558    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1559    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1560        if Engine::gdn_db_on()
1561            && Engine::gdn_chunked_enabled()
1562            && t >= 16
1563            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1564            && num_k * 2 == num_v
1565        {
1566            num_k
1567        } else {
1568            num_v
1569        }
1570    }
1571
1572    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1573    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1574    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1575    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1576    fn f16out_on(e: &Engine, t: usize) -> bool {
1577        crate::f16_ffi::pp_f16_enabled()
1578            && t >= 16
1579            && !e.verify_exact_on()
1580            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1581    }
1582
1583    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1584    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1585    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1586    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1587    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1588    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1589    /// see one entry, byte-identical behavior.
1590    pub fn prime_slabs_get(
1591        &self,
1592        e: &Engine,
1593        t: usize,
1594        n_embd: usize,
1595        n_ff_max: usize,
1596    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1597        let mut slabs = self.prime_slabs.lock().unwrap();
1598        let dev = e.ctx().ordinal();
1599        let need_new = match slabs.get(&dev) {
1600            None => true,
1601            Some(sl) => sl.lock().unwrap().t_cap < t,
1602        };
1603        if need_new {
1604            slabs.insert(
1605                dev,
1606                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1607                    t_cap: t,
1608                    h: e.uninit(t * n_embd)?,
1609                    x1: e.uninit(t * n_embd)?,
1610                    z: e.uninit(t * n_embd)?,
1611                    act: e.uninit(t * n_ff_max)?,
1612                    xa: e.uninit(t * n_embd)?,
1613                    xb: e.uninit(t * n_embd)?,
1614                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1615                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1616                    gate: e.uninit(t * n_ff_max)?,
1617                    up: e.uninit(t * n_ff_max)?,
1618                    ffn_out: e.uninit(t * n_embd)?,
1619                    seg_glue: Vec::new(),
1620                    mixed: e.uninit(t * n_embd)?,
1621                    seg_mid: Vec::new(),
1622                    seg_t: 0,
1623                })),
1624            );
1625        }
1626        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1627    }
1628
1629    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1630    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1631    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1632    fn prime_chunk(
1633        &self,
1634        e: &Engine,
1635        tokens: &[u32],
1636        cache: &mut Cache,
1637        seq_end: usize,
1638        chunk_off: usize,
1639        overlay: Option<&crate::vision::EmbedOverlay>,
1640    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1641        if crate::pp::pp_host_bounce_active()
1642            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1643        {
1644            return Err(
1645                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1646                 has no active prime stage split and would peer-read remote weights; keep \
1647                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1648                    .into(),
1649            );
1650        }
1651        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1652        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1653        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1654        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1655        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1656        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1657        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1658        // loader is off and there is nothing remote to split for.
1659        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1660            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1661                if overlay.is_some() {
1662                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1663                         run single-device or MEMRA_PRIME_PP=0"
1664                        .into());
1665                }
1666                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1667            }
1668        }
1669        if crate::pp::pp_host_bounce_active() {
1670            return Err(
1671                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1672                 refusing an unsplit remote-weight walk"
1673                    .into(),
1674            );
1675        }
1676        let t = tokens.len();
1677        let base = cache.pos;
1678        debug_assert!(
1679            seq_end >= base + t,
1680            "prime_chunk: seq_end must cover this chunk"
1681        );
1682        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1683        let pos_d = e.htod_i32(&pos)?;
1684
1685        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1686        if let Some(ov) = overlay {
1687            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1688            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1689            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1690            let n_embd = self.cfg.n_embd as usize;
1691            for &(pos, row_off, n_rows) in &ov.spans {
1692                let lo = pos.max(chunk_off);
1693                let hi = (pos + n_rows).min(chunk_off + t);
1694                if lo < hi {
1695                    let src_row = row_off + (lo - pos);
1696                    let view = ov
1697                        .rows
1698                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1699                    e.copy_view_into(
1700                        &mut x_embed,
1701                        (lo - chunk_off) * n_embd,
1702                        &view,
1703                        (hi - lo) * n_embd,
1704                    )?;
1705                }
1706            }
1707        }
1708        let x = self.prime_layers(
1709            e,
1710            x_embed,
1711            0,
1712            self.layers.len(),
1713            &pos_d,
1714            t,
1715            base,
1716            cache,
1717            seq_end,
1718        )?;
1719        self.prime_chunk_epilogue(e, x, t, cache)
1720    }
1721
1722    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1723    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1724    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1725    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1726    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1727    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1728    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1729    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1730    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1731    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1732    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1733    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1734    ///     each stage walks through its own resident transients;
1735    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1736    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1737    #[allow(clippy::too_many_arguments)]
1738    fn prime_layers(
1739        &self,
1740        e: &Engine,
1741        x_in: CudaSlice<f32>,
1742        lo: usize,
1743        hi: usize,
1744        pos_d: &CudaSlice<i32>,
1745        t: usize,
1746        base: usize,
1747        cache: &mut Cache,
1748        seq_end: usize,
1749    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1750        let cfg = &self.cfg;
1751        let n_embd = cfg.n_embd as usize;
1752        let eps = cfg.rms_eps;
1753        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1754        // standalone convert launches). Only when the f16 lane serves and T reaches the
1755        // GEMM tier; bit-identical either way.
1756        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1757        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1758        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1759        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1760        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1761        // capacity tail must stay behind checked views. The hidden-stack return clones the
1762        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1763        let n_ff_max = self
1764            .layers
1765            .iter()
1766            .map(|l| match &l.ffn {
1767                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1768                _ => n_embd,
1769            })
1770            .max()
1771            .unwrap_or(n_embd)
1772            .max(n_embd);
1773        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1774        let slab = if use_slabs {
1775            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1776        } else {
1777            None
1778        };
1779        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1780        let mut x_own; // fallback storage when slabs are off
1781        type SlabRefs<'a> = (
1782            &'a mut CudaSlice<f32>,
1783            &'a mut CudaSlice<f32>,
1784            &'a mut CudaSlice<f32>,
1785            &'a mut CudaSlice<f32>,
1786            &'a mut CudaSlice<u8>,
1787            &'a mut CudaSlice<u8>,
1788            &'a mut CudaSlice<f32>,
1789            &'a mut CudaSlice<f32>,
1790            &'a mut CudaSlice<f32>,
1791        );
1792        let (mut x_cur, mut x_nxt, sl): (
1793            &mut CudaSlice<f32>,
1794            &mut CudaSlice<f32>,
1795            Option<SlabRefs>,
1796        );
1797        let mut seg: Option<(
1798            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1799            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1800            &mut CudaSlice<f32>,
1801            &mut usize,
1802        )> = None;
1803        let mut x_own2;
1804        match slab_guard.as_mut() {
1805            Some(g) => {
1806                let slabs = &mut **g;
1807                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1808                let PrimeSlabs {
1809                    xa,
1810                    xb,
1811                    h,
1812                    x1,
1813                    z,
1814                    act,
1815                    h16,
1816                    z16,
1817                    gate,
1818                    up,
1819                    ffn_out,
1820                    seg_glue,
1821                    mixed,
1822                    seg_mid,
1823                    seg_t,
1824                    ..
1825                } = slabs;
1826                x_cur = xa;
1827                x_nxt = xb;
1828                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1829                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1830            }
1831            None => {
1832                x_own = x_in;
1833                x_own2 = e.uninit(t * n_embd)?;
1834                x_cur = &mut x_own;
1835                x_nxt = &mut x_own2;
1836                sl = None;
1837            }
1838        }
1839        let mut alloc_h;
1840        let mut alloc_x1;
1841        let mut alloc_z;
1842        let mut alloc_act;
1843        let mut alloc_h16;
1844        let mut alloc_z16;
1845        let mut alloc_gate;
1846        let mut alloc_up;
1847        let mut alloc_fo;
1848        let (h, x1, z, act): (
1849            &mut CudaSlice<f32>,
1850            &mut CudaSlice<f32>,
1851            &mut CudaSlice<f32>,
1852            &mut CudaSlice<f32>,
1853        );
1854        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
1855        let (sl_gate, sl_up, sl_fo): (
1856            &mut CudaSlice<f32>,
1857            &mut CudaSlice<f32>,
1858            &mut CudaSlice<f32>,
1859        );
1860        match sl {
1861            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
1862                h = a;
1863                x1 = b;
1864                z = c;
1865                act = d;
1866                h16 = e16;
1867                z16 = f16b;
1868                sl_gate = g;
1869                sl_up = u;
1870                sl_fo = fo;
1871            }
1872            None => {
1873                alloc_h = e.uninit(t * n_embd)?;
1874                alloc_x1 = e.uninit(t * n_embd)?;
1875                alloc_z = e.uninit(t * n_embd)?;
1876                alloc_act = e.uninit(t * n_ff_max)?;
1877                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1878                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1879                alloc_gate = e.uninit(t * n_ff_max)?;
1880                alloc_up = e.uninit(t * n_ff_max)?;
1881                alloc_fo = e.uninit(t * n_embd)?;
1882                h = &mut alloc_h;
1883                x1 = &mut alloc_x1;
1884                z = &mut alloc_z;
1885                act = &mut alloc_act;
1886                h16 = &mut alloc_h16;
1887                z16 = &mut alloc_z16;
1888                sl_gate = &mut alloc_gate;
1889                sl_up = &mut alloc_up;
1890                sl_fo = &mut alloc_fo;
1891            }
1892        }
1893        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
1894        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
1895        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
1896        // first prime at this t (capture does not execute -> launch right after).
1897        let n_layers = self.layers.len();
1898        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
1899        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
1900        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
1901        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
1902        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
1903        // machinery stays (byte-identical) as their foundation.
1904        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
1905        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
1906        // step35 rides its own mixer through the normal per-layer arm below.
1907        let use_seg = f16fuse
1908            && seg.is_some()
1909            && !self.uses_sliding_gated_moe_program()
1910            && lo == 0
1911            && hi == n_layers
1912            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
1913        if let Some((sg, sm, _, st)) = seg.as_mut() {
1914            if **st != t {
1915                sg.clear();
1916                sg.extend((0..n_layers).map(|_| None));
1917                sm.clear();
1918                sm.extend((0..n_layers).map(|_| None));
1919                **st = t;
1920            }
1921        }
1922        {
1923            let layer_lo = &self.layers[lo];
1924            if f16fuse {
1925                e.rms_norm_f16out(
1926                    x_cur,
1927                    layer_lo.attn_norm.float_data(),
1928                    h,
1929                    h16,
1930                    n_embd,
1931                    t,
1932                    eps,
1933                )?;
1934            } else {
1935                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
1936            }
1937        }
1938        let anat = Self::prime_anatomy_on();
1939        let mut anat_last = if anat {
1940            e.stream().synchronize()?;
1941            Some(std::time::Instant::now())
1942        } else {
1943            None
1944        };
1945        // Closes the region that just ENDED into `slot`, restarting the clock.
1946        macro_rules! anat_mark {
1947            ($slot:expr) => {
1948                if let Some(ts) = anat_last.as_mut() {
1949                    e.stream().synchronize()?;
1950                    Self::prime_anatomy_slots()[$slot].fetch_add(
1951                        ts.elapsed().as_nanos() as u64,
1952                        std::sync::atomic::Ordering::Relaxed,
1953                    );
1954                    *ts = std::time::Instant::now();
1955                }
1956            };
1957        }
1958        for il in lo..hi {
1959            let layer = &self.layers[il];
1960            let hx16 = if f16fuse { Some(&*h16) } else { None };
1961            if use_seg {
1962                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
1963                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
1964                let (pre, pre16, w_out) = match &layer.mixer {
1965                    Mixer::Full(fa) => {
1966                        let g3 = match hx16 {
1967                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
1968                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
1969                        };
1970                        let (pre, pre16) =
1971                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
1972                        (pre, pre16, &fa.wo)
1973                    }
1974                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1975                    Mixer::Linear(la) => {
1976                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
1977                        let g4 = match hx16 {
1978                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
1979                            None => e.matmul_group(&ws, h, t)?,
1980                        };
1981                        let (pre, pre16) =
1982                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
1983                        (pre, pre16, &la.ssm_out)
1984                    }
1985                };
1986                {
1987                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
1988                    let pre_n = pre.len() / t;
1989                    let xh_pre = match pre16 {
1990                        Some(x) => x,
1991                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
1992                    };
1993                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
1994                        let y = e.matmul(w_out, &pre, t)?;
1995                        e.copy_into(mslab, 0, &y, t * n_embd)?;
1996                    }
1997                    if sm[il].is_none() {
1998                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
1999                        let w_post = layer.post_attn_norm.float_data();
2000                        e.stream().synchronize()?;
2001                        e.stream()
2002                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2003                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2004                            e.add(x_cur, mslab, x1, t * n_embd)?;
2005                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2006                            Ok(())
2007                        })();
2008                        let g = e.stream().end_capture(
2009                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2010                        r?;
2011                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2012                    }
2013                    sm[il].as_ref().unwrap().launch()?;
2014                }
2015            } else {
2016                let mixed = match &layer.mixer {
2017                    Mixer::Full(fa) => {
2018                        let y =
2019                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2020                        anat_mark!(0);
2021                        y
2022                    }
2023                    Mixer::Linear(la) => {
2024                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2025                        anat_mark!(1);
2026                        y
2027                    }
2028                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2029                };
2030                if f16fuse {
2031                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2032                    // bit-identical) — the standalone add pass disappears.
2033                    e.add_rms_norm_f16out(
2034                        x_cur,
2035                        &mixed,
2036                        layer.post_attn_norm.float_data(),
2037                        x1,
2038                        z,
2039                        z16,
2040                        n_embd,
2041                        t,
2042                        eps,
2043                    )?;
2044                } else {
2045                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2046                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2047                }
2048                anat_mark!(4);
2049            }
2050            let zx16 = if f16fuse { Some(&*z16) } else { None };
2051            match &layer.ffn {
2052                crate::hybrid::Ffn::Dense {
2053                    ffn_gate,
2054                    ffn_up,
2055                    ffn_down,
2056                } => {
2057                    let n_ff = ffn_gate.out_features();
2058                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2059                    // the allocating group + copy when a mirror is missing.
2060                    let mut into_ok = false;
2061                    if let Some(xh) = zx16 {
2062                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2063                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2064                    }
2065                    if !into_ok {
2066                        let mut g2 = match zx16 {
2067                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2068                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2069                        };
2070                        let up_y = g2.pop().unwrap();
2071                        let gate_y = g2.pop().unwrap();
2072                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2073                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2074                    }
2075                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2076                    // operand in-epilogue; non-silu activations keep the standalone convert.
2077                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2078                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2079                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2080                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2081                    {
2082                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2083                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2084                        Some(a16)
2085                    } else {
2086                        Self::ffn_act_lim(
2087                            e,
2088                            &self.cfg,
2089                            sl_gate,
2090                            sl_up,
2091                            1.0,
2092                            1.0,
2093                            d_lim,
2094                            act,
2095                            t * n_ff,
2096                        )?;
2097                        None
2098                    };
2099                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2100                    let xh_act = match act16 {
2101                        Some(x) => x,
2102                        None => e.f16_act(act, t * n_ff, n_ff)?,
2103                    };
2104                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2105                        let y = e.matmul(ffn_down, &*act, t)?;
2106                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2107                    }
2108                }
2109                crate::hybrid::Ffn::Moe(m) => {
2110                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2111                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2112                    anat_mark!(2);
2113                }
2114            }
2115            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2116                anat_mark!(3);
2117            }
2118            if use_seg && il + 1 < hi {
2119                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2120                let w_next = self.layers[il + 1].attn_norm.float_data();
2121                let (sg, _, _, _) = seg.as_mut().unwrap();
2122                if sg[il].is_none() {
2123                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2124                    e.stream().synchronize()?;
2125                    e.stream()
2126                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2127                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2128                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2129                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2130                        Ok(())
2131                    })();
2132                    let g = e.stream().end_capture(
2133                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2134                    );
2135                    r?;
2136                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2137                }
2138                sg[il].as_ref().unwrap().launch()?;
2139            } else {
2140                if il + 1 < hi {
2141                    let w_next = self.layers[il + 1].attn_norm.float_data();
2142                    if f16fuse {
2143                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2144                    } else {
2145                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2146                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2147                    }
2148                } else {
2149                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2150                }
2151            }
2152            anat_mark!(4);
2153            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2154            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2155            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2156            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2157            // unset (the default) costs one OnceLock read per layer.
2158            if let Some(path) = Self::prime_trace_path() {
2159                let row = (base + t - 1) as usize;
2160                let host = e.dtoh(x_nxt)?;
2161                let last = &host[(t - 1) * n_embd..t * n_embd];
2162                use std::io::Write as _;
2163                let mut f = std::fs::OpenOptions::new()
2164                    .create(true)
2165                    .append(true)
2166                    .open(path)?;
2167                let mut h64: u64 = 0xcbf29ce484222325;
2168                for v in last {
2169                    h64 ^= v.to_bits() as u64;
2170                    h64 = h64.wrapping_mul(0x100000001b3);
2171                }
2172                writeln!(
2173                    f,
2174                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2175                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2176                    last[0], last[1], last[2]
2177                )?;
2178            }
2179            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2180            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2181            self.dflash_tap(e, cache, il, x_nxt, t)?;
2182            std::mem::swap(&mut x_cur, &mut x_nxt);
2183        }
2184        if anat {
2185            let s = Self::prime_anatomy_slots();
2186            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2187            eprintln!(
2188                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2189                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2190                ms(0),
2191                ms(1),
2192                ms(2),
2193                ms(3),
2194                ms(4)
2195            );
2196        }
2197        // hidden-stack return: clone the final x out of the slab
2198        let mut x = e.uninit(t * n_embd)?;
2199        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2200        drop(slab_guard);
2201        Ok(x)
2202    }
2203
2204    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2205    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2206    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2207    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2208    fn prime_chunk_epilogue(
2209        &self,
2210        e: &Engine,
2211        x: CudaSlice<f32>,
2212        t: usize,
2213        cache: &mut Cache,
2214    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2215        let n_embd = self.cfg.n_embd as usize;
2216        let eps = self.cfg.rms_eps;
2217        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2218        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2219        // the post-norm copy happens after hn exists).
2220        let mut h_seed = e.uninit(n_embd)?;
2221        if !crate::spec::spec_hpost() {
2222            e.copy_view_into(
2223                &mut h_seed,
2224                0,
2225                &x.slice((t - 1) * n_embd..t * n_embd),
2226                n_embd,
2227            )?;
2228        }
2229        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2230        let mut hn = e.uninit(t * n_embd)?;
2231        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2232        if crate::spec::spec_hpost() {
2233            e.copy_view_into(
2234                &mut h_seed,
2235                0,
2236                &hn.slice((t - 1) * n_embd..t * n_embd),
2237                n_embd,
2238            )?;
2239        }
2240        let last = e.view(&hn, t * n_embd);
2241        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2242        let mut hlast = e.uninit(n_embd)?;
2243        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2244        let logits = e.matmul(&self.output, &hlast, 1)?;
2245        cache.pos += t;
2246        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2247        // post-norm stack hn (MEMRA_SPEC_HPOST).
2248        Ok((
2249            e.dtoh(&logits)?,
2250            h_seed,
2251            if crate::spec::spec_hpost() { hn } else { x },
2252        ))
2253    }
2254
2255    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2256    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2257    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2258    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2259    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2260    /// prefill kernels. Structure mirrors the verify split exactly:
2261    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2262    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2263    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2264    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2265    ///                  there via the sharded loader) → `publish_to`
2266    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2267    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2268    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2269    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2270    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2271    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2272    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2273    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2274    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2275    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2276    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2277    /// and its liveness counter is bumped here — the gate goes green with this function.
2278    fn prime_chunk_ppn(
2279        &self,
2280        e: &Engine,
2281        tokens: &[u32],
2282        cache: &mut Cache,
2283        seq_end: usize,
2284        fence: &[usize],
2285    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2286        let rt = crate::pp::PpNRt::get(e)?;
2287        let n_st = fence.len() - 1;
2288        assert_eq!(
2289            rt.n_stages(),
2290            n_st,
2291            "PpNRt stage count {} != fence stages {n_st}",
2292            rt.n_stages()
2293        );
2294        let n_embd = self.cfg.n_embd as usize;
2295        let t = tokens.len();
2296        let base = cache.pos;
2297        debug_assert!(
2298            seq_end >= base + t,
2299            "prime_chunk_ppn: seq_end must cover this chunk"
2300        );
2301        let payload = t * n_embd;
2302        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2303        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2304        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2305        let caller_stream = e.stream();
2306        rt.fence_stages_behind(&caller_stream)?;
2307
2308        if n_st == 2 {
2309            let slot =
2310                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2311            let x =
2312                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2313            let out = {
2314                rt.bind_stage(1)?;
2315                let _st1 = rt.enter(1);
2316                let e1 = rt.engine(1, e);
2317                self.prime_chunk_epilogue(e1, x, t, cache)?
2318            };
2319            rt.publish_to(1, &caller_stream)?;
2320            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2321            return Ok(out);
2322        }
2323
2324        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2325
2326        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2327        let mut slot = {
2328            let _st0 = rt.enter(0);
2329            let e0 = rt.engine(0, e);
2330            let pos_d = e0.htod_i32(&pos)?;
2331            let x = self.embed(e0, tokens)?;
2332            let x =
2333                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2334            rt.tx(0, &x, payload)?
2335            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2336        };
2337
2338        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2339        for s in 1..n_st - 1 {
2340            let _st = rt.enter(s);
2341            let es = rt.engine(s, e);
2342            let pos_d = es.htod_i32(&pos)?;
2343            let x = rt.rx(s - 1, slot, payload)?;
2344            let x = self.prime_layers(
2345                es,
2346                x,
2347                fence[s],
2348                fence[s + 1],
2349                &pos_d,
2350                t,
2351                base,
2352                cache,
2353                seq_end,
2354            )?;
2355            slot = rt.tx(s, &x, payload)?;
2356        }
2357
2358        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2359        let _stl = rt.enter(n_st - 1);
2360        let el = rt.engine(n_st - 1, e);
2361        let pos_d = el.htod_i32(&pos)?;
2362        let x = rt.rx(n_st - 2, slot, payload)?;
2363        let x = self.prime_layers(
2364            el,
2365            x,
2366            fence[n_st - 1],
2367            fence[n_st],
2368            &pos_d,
2369            t,
2370            base,
2371            cache,
2372            seq_end,
2373        )?;
2374        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2375        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2376        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2377        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2378        // stage stream host-side, but the law is stated in events, not in a dtoh side
2379        // effect a later deferred form would remove.
2380        rt.publish_to(n_st - 1, &caller_stream)?;
2381        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2382        Ok(out)
2383    }
2384
2385    fn prime_pp2_stage0_enqueue(
2386        &self,
2387        e: &Engine,
2388        rt: &crate::pp::PpNRt,
2389        tokens: &[u32],
2390        cache: &mut Cache,
2391        seq_end: usize,
2392        fence: &[usize],
2393        base: usize,
2394        pipelined: bool,
2395    ) -> Result<usize, Box<dyn std::error::Error>> {
2396        let t = tokens.len();
2397        let n_embd = self.cfg.n_embd as usize;
2398        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2399        rt.bind_stage(0)?;
2400        let _st0 = rt.enter(0);
2401        let e0 = rt.engine(0, e);
2402        let pos_d = e0.htod_i32(&pos)?;
2403        let x = self.embed(e0, tokens)?;
2404        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2405        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2406        if pipelined {
2407            rt.tx_pipelined(0, &x, t * n_embd)
2408        } else {
2409            rt.tx(0, &x, t * n_embd)
2410        }
2411    }
2412
2413    fn prime_pp2_stage1_enqueue(
2414        &self,
2415        e: &Engine,
2416        rt: &crate::pp::PpNRt,
2417        slot: usize,
2418        t: usize,
2419        cache: &mut Cache,
2420        seq_end: usize,
2421        fence: &[usize],
2422        base: usize,
2423        pipelined: bool,
2424    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
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(1)?;
2428        let _st1 = rt.enter(1);
2429        let e1 = rt.engine(1, e);
2430        let pos_d = e1.htod_i32(&pos)?;
2431        let x = rt.rx(0, slot, t * n_embd)?;
2432        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2433        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2434    }
2435
2436    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2437    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2438    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2439    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2440    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2441    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2442    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2443    /// bookkeeping still runs on the host per call — the real replay path moves the write
2444    /// slot to the len_d device counter (increment 3).
2445    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2446    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2447    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2448    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2449    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2450    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2451    pub fn prime_chunk_captured(
2452        &self,
2453        e: &Engine,
2454        x_in: &CudaSlice<f32>,
2455        pos_d: &CudaSlice<i32>,
2456        t: usize,
2457        cache: &mut Cache,
2458        len_d: &CudaSlice<i32>,
2459        logits_out: &mut CudaSlice<f32>,
2460        h_seed_out: &mut CudaSlice<f32>,
2461    ) -> Result<(), Box<dyn std::error::Error>> {
2462        let cfg = &self.cfg;
2463        let n_embd = cfg.n_embd as usize;
2464        let eps = cfg.rms_eps;
2465        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2466        let mut x = e.uninit(t * n_embd)?;
2467        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2468        for (il, layer) in self.layers.iter().enumerate() {
2469            let mut h = e.uninit(t * n_embd)?;
2470            let mut hx16: Option<CudaSlice<u8>> = None;
2471            if f16fuse {
2472                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2473                e.rms_norm_f16out(
2474                    &x,
2475                    layer.attn_norm.float_data(),
2476                    &mut h,
2477                    &mut b16,
2478                    n_embd,
2479                    t,
2480                    eps,
2481                )?;
2482                hx16 = Some(b16);
2483            } else {
2484                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2485            }
2486            let mixed = match &layer.mixer {
2487                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2488                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2489                // come from the caller (see step35_attn_pre_wo's doc note).
2490                Mixer::Full(fa) => {
2491                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2492                }
2493                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2494                Mixer::Linear(la) => {
2495                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2496                    let g4 = match hx16.as_ref() {
2497                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2498                        None => e.matmul_group(&ws, &h, t)?,
2499                    };
2500                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2501                }
2502            };
2503            let mut x1 = e.uninit(t * n_embd)?;
2504            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2505            let mut z = e.uninit(t * n_embd)?;
2506            let mut zx16: Option<CudaSlice<u8>> = None;
2507            if f16fuse {
2508                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2509                e.rms_norm_f16out(
2510                    &x1,
2511                    layer.post_attn_norm.float_data(),
2512                    &mut z,
2513                    &mut b16,
2514                    n_embd,
2515                    t,
2516                    eps,
2517                )?;
2518                zx16 = Some(b16);
2519            } else {
2520                e.rms_norm(
2521                    &x1,
2522                    layer.post_attn_norm.float_data(),
2523                    &mut z,
2524                    n_embd,
2525                    t,
2526                    eps,
2527                )?;
2528            }
2529            let ffn_out = match &layer.ffn {
2530                crate::hybrid::Ffn::Dense {
2531                    ffn_gate,
2532                    ffn_up,
2533                    ffn_down,
2534                } => {
2535                    let n_ff = ffn_gate.out_features();
2536                    let mut g2 = match &zx16 {
2537                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2538                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2539                    };
2540                    let up = g2.pop().unwrap();
2541                    let gate = g2.pop().unwrap();
2542                    let mut act = e.uninit(t * n_ff)?;
2543                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2544                    Self::ffn_act_lim(
2545                        e,
2546                        &self.cfg,
2547                        &gate,
2548                        &up,
2549                        1.0,
2550                        1.0,
2551                        self.cfg.clamp_shexp_at(il as u32),
2552                        &mut act,
2553                        t * n_ff,
2554                    )?;
2555                    e.matmul(ffn_down, &act, t)?
2556                }
2557                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2558            };
2559            let mut x2 = e.uninit(t * n_embd)?;
2560            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2561            x = x2;
2562        }
2563        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2564        if !crate::spec::spec_hpost() {
2565            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2566        }
2567        let mut hn = e.uninit(t * n_embd)?;
2568        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2569        if crate::spec::spec_hpost() {
2570            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2571        }
2572        let mut hlast = e.uninit(n_embd)?;
2573        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2574        let logits = e.matmul(&self.output, &hlast, 1)?;
2575        let nv = logits.len();
2576        e.copy_into(logits_out, 0, &logits, nv)?;
2577        Ok(())
2578    }
2579
2580    fn step35_prime_batch_on() -> bool {
2581        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2582    }
2583
2584    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2585    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2586    #[allow(clippy::too_many_arguments)]
2587    fn step35_prime_batch_layers(
2588        &self,
2589        e: &Engine,
2590        mut x: CudaSlice<f32>,
2591        lo: usize,
2592        hi: usize,
2593        ts: &[usize],
2594        offs: &[usize],
2595        pos_ds: &[CudaSlice<i32>],
2596        caches: &mut [&mut Cache],
2597    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2598        let cfg = &self.cfg;
2599        let n_embd = cfg.n_embd as usize;
2600        let eps = cfg.rms_eps;
2601        let b = ts.len();
2602        let total: usize = ts.iter().sum();
2603        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2604
2605        let split = |e: &Engine,
2606                     y: &CudaSlice<f32>,
2607                     dim: usize|
2608         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2609            let mut out = Vec::with_capacity(b);
2610            for s in 0..b {
2611                let mut ys = e.uninit(ts[s] * dim)?;
2612                e.copy_view_into(
2613                    &mut ys,
2614                    0,
2615                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2616                    ts[s] * dim,
2617                )?;
2618                out.push(ys);
2619            }
2620            Ok(out)
2621        };
2622
2623        for il in lo..hi {
2624            let layer = &self.layers[il];
2625            let Mixer::Full(fa) = &layer.mixer else {
2626                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2627            };
2628
2629            let mut h = e.uninit(total * n_embd)?;
2630            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2631            if f16fuse {
2632                e.rms_norm_f16out(
2633                    &x,
2634                    layer.attn_norm.float_data(),
2635                    &mut h,
2636                    &mut hx16,
2637                    n_embd,
2638                    total,
2639                    eps,
2640                )?;
2641            } else {
2642                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2643            }
2644
2645            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2646            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2647            // application stay verbatim.
2648            let gate_w = fa
2649                .attn_gate
2650                .as_ref()
2651                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2652            let mut g4 = if f16fuse {
2653                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2654            } else {
2655                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2656            };
2657            let gate = g4.pop().unwrap();
2658            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2659                (0..b).map(|_| Vec::with_capacity(3)).collect();
2660            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2661                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2662                    parts[s].push(ys);
2663                }
2664            }
2665            let gates = split(e, &gate, gate_w.out_features())?;
2666            let geometry = self.step35_geom(il);
2667            let hd = geometry.head_dim_k as usize;
2668            let nh = geometry.n_head as usize;
2669            let mut ag_cat = e.uninit(total * nh * hd)?;
2670            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2671                let ag = self.step35_attn_pre_wo(
2672                    e,
2673                    fa,
2674                    g3s,
2675                    None,
2676                    Some(&gate),
2677                    &pos_ds[s],
2678                    ts[s],
2679                    Some(&mut *caches[s]),
2680                    il,
2681                    ts[s],
2682                )?;
2683                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2684            }
2685            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2686
2687            let mut x1 = e.uninit(total * n_embd)?;
2688            let mut z = e.uninit(total * n_embd)?;
2689            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2690            if f16fuse {
2691                e.add_rms_norm_f16out(
2692                    &x,
2693                    &mixed,
2694                    layer.post_attn_norm.float_data(),
2695                    &mut x1,
2696                    &mut z,
2697                    &mut zx16,
2698                    n_embd,
2699                    total,
2700                    eps,
2701                )?;
2702            } else {
2703                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2704                e.rms_norm(
2705                    &x1,
2706                    layer.post_attn_norm.float_data(),
2707                    &mut z,
2708                    n_embd,
2709                    total,
2710                    eps,
2711                )?;
2712            }
2713
2714            let ffn_out = match &layer.ffn {
2715                crate::hybrid::Ffn::Dense {
2716                    ffn_gate,
2717                    ffn_up,
2718                    ffn_down,
2719                } => {
2720                    let n_ff = ffn_gate.out_features();
2721                    let mut g2 = if f16fuse {
2722                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2723                    } else {
2724                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2725                    };
2726                    let up = g2.pop().unwrap();
2727                    let gate = g2.pop().unwrap();
2728                    let mut act = e.uninit(total * n_ff)?;
2729                    let d_lim = cfg.clamp_shexp_at(il as u32);
2730                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2731                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2732                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2733                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2734                            Some(y) => y,
2735                            None => e.matmul(ffn_down, &act, total)?,
2736                        }
2737                    } else {
2738                        Self::ffn_act_lim(
2739                            e,
2740                            cfg,
2741                            &gate,
2742                            &up,
2743                            1.0,
2744                            1.0,
2745                            d_lim,
2746                            &mut act,
2747                            total * n_ff,
2748                        )?;
2749                        e.matmul(ffn_down, &act, total)?
2750                    }
2751                }
2752                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2753            };
2754            let mut x2 = e.uninit(total * n_embd)?;
2755            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2756            x = x2;
2757        }
2758        Ok(x)
2759    }
2760
2761    fn step35_prime_batch_epilogue(
2762        &self,
2763        e: &Engine,
2764        x: CudaSlice<f32>,
2765        ts: &[usize],
2766        offs: &[usize],
2767        caches: &mut [&mut Cache],
2768    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2769        let n_embd = self.cfg.n_embd as usize;
2770        let total: usize = ts.iter().sum();
2771        let mut hn = e.uninit(total * n_embd)?;
2772        e.rms_norm(
2773            &x,
2774            self.output_norm.float_data(),
2775            &mut hn,
2776            n_embd,
2777            total,
2778            self.cfg.rms_eps,
2779        )?;
2780
2781        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2782        let mut out = Vec::with_capacity(ts.len());
2783        for s in 0..ts.len() {
2784            let mut hidden = e.uninit(ts[s] * n_embd)?;
2785            e.copy_view_into(
2786                &mut hidden,
2787                0,
2788                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
2789                ts[s] * n_embd,
2790            )?;
2791            let last0 = (offs[s] + ts[s] - 1) * n_embd;
2792            let mut h_seed = e.uninit(n_embd)?;
2793            e.copy_view_into(
2794                &mut h_seed,
2795                0,
2796                &hidden_src.slice(last0..last0 + n_embd),
2797                n_embd,
2798            )?;
2799            // Exactness-first: the serial reference runs the output head at m=1.
2800            let mut hlast = e.uninit(n_embd)?;
2801            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
2802            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
2803            caches[s].pos += ts[s];
2804            out.push((logits, h_seed, hidden));
2805        }
2806        Ok(out)
2807    }
2808
2809    fn step35_prime_cache_batch(
2810        &self,
2811        e: &Engine,
2812        prompts: &[&[u32]],
2813        caches: &mut [&mut Cache],
2814    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2815        validate_step_prime_batch_modes(
2816            step_tp_prefill_enabled()?,
2817            step_ep_grouped_prefill_enabled()?,
2818        )?;
2819        if crate::pp::pp_host_bounce_active()
2820            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
2821        {
2822            return Err(
2823                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
2824                 stage split; refusing an unsplit remote-weight walk"
2825                    .into(),
2826            );
2827        }
2828        if !Self::step35_prime_batch_on() {
2829            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
2830        }
2831        if caches.iter().any(|c| c.pos != 0) {
2832            return Err(
2833                "step35 batched prime currently supports complete fresh prompts only; \
2834                 continuation/tick chunks require per-request queued_after"
2835                    .into(),
2836            );
2837        }
2838
2839        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2840        for &t in &ts {
2841            assert!(
2842                t >= PRIME_MIN_T,
2843                "step35 batched prime needs T >= {PRIME_MIN_T}"
2844            );
2845        }
2846        for (s, c) in caches.iter().enumerate() {
2847            assert!(
2848                ts[s] <= c.max_ctx,
2849                "step35 batched prime exceeds cache max_ctx"
2850            );
2851        }
2852        let offs: Vec<usize> = ts
2853            .iter()
2854            .scan(0usize, |a, &t| {
2855                let o = *a;
2856                *a += t;
2857                Some(o)
2858            })
2859            .collect();
2860        let total: usize = ts.iter().sum();
2861        let payload = total * self.cfg.n_embd as usize;
2862        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2863        let positions: Vec<Vec<i32>> = ts.iter().map(|&t| (0..t as i32).collect()).collect();
2864        let upload_positions =
2865            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
2866                positions
2867                    .iter()
2868                    .map(|p| e.htod_i32(p))
2869                    .collect::<Result<_, _>>()
2870            };
2871
2872        static ONCE: std::sync::Once = std::sync::Once::new();
2873        ONCE.call_once(|| {
2874            eprintln!(
2875                "[step35-prime-batch] first concat prime: B={} tokens={total}",
2876                prompts.len()
2877            );
2878        });
2879
2880        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
2881            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
2882                let rt = crate::pp::PpNRt::get(e)?;
2883                let n_st = fence.len() - 1;
2884                assert_eq!(
2885                    rt.n_stages(),
2886                    n_st,
2887                    "step35 prime batch stage count mismatch"
2888                );
2889                let caller_stream = e.stream();
2890                rt.fence_stages_behind(&caller_stream)?;
2891
2892                let mut slot = {
2893                    let _st0 = rt.enter(0);
2894                    let e0 = rt.engine(0, e);
2895                    let pos_ds = upload_positions(e0)?;
2896                    let x = self.embed(e0, &cat_tokens)?;
2897                    let x = self.step35_prime_batch_layers(
2898                        e0, x, fence[0], fence[1], &ts, &offs, &pos_ds, caches,
2899                    )?;
2900                    rt.tx(0, &x, payload)?
2901                };
2902                for s in 1..n_st - 1 {
2903                    let _st = rt.enter(s);
2904                    let es = rt.engine(s, e);
2905                    let pos_ds = upload_positions(es)?;
2906                    let x = rt.rx(s - 1, slot, payload)?;
2907                    let x = self.step35_prime_batch_layers(
2908                        es,
2909                        x,
2910                        fence[s],
2911                        fence[s + 1],
2912                        &ts,
2913                        &offs,
2914                        &pos_ds,
2915                        caches,
2916                    )?;
2917                    slot = rt.tx(s, &x, payload)?;
2918                }
2919
2920                let _stl = rt.enter(n_st - 1);
2921                let el = rt.engine(n_st - 1, e);
2922                let pos_ds = upload_positions(el)?;
2923                let x = rt.rx(n_st - 2, slot, payload)?;
2924                let x = self.step35_prime_batch_layers(
2925                    el,
2926                    x,
2927                    fence[n_st - 1],
2928                    fence[n_st],
2929                    &ts,
2930                    &offs,
2931                    &pos_ds,
2932                    caches,
2933                )?;
2934                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
2935                rt.publish_to(n_st - 1, &caller_stream)?;
2936                crate::pp::STEP35_PRIME_BATCH_SPLITS
2937                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2938                out
2939            } else {
2940                let pos_ds = upload_positions(e)?;
2941                let x = self.embed(e, &cat_tokens)?;
2942                let x = self.step35_prime_batch_layers(
2943                    e,
2944                    x,
2945                    0,
2946                    self.layers.len(),
2947                    &ts,
2948                    &offs,
2949                    &pos_ds,
2950                    caches,
2951                )?;
2952                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2953            }
2954        } else {
2955            let pos_ds = upload_positions(e)?;
2956            let x = self.embed(e, &cat_tokens)?;
2957            let x = self.step35_prime_batch_layers(
2958                e,
2959                x,
2960                0,
2961                self.layers.len(),
2962                &ts,
2963                &offs,
2964                &pos_ds,
2965                caches,
2966            )?;
2967            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2968        };
2969        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2970        Ok(out)
2971    }
2972
2973    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
2974    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
2975    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
2976    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
2977    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
2978    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
2979    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
2980    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
2981    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
2982    /// over the quantized past; Linear: the stateful pad_view twin — the same state
2983    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
2984    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
2985    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
2986    /// back to single-chunk serving).
2987    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
2988    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
2989    pub fn prime_cache_batch(
2990        &self,
2991        e: &Engine,
2992        prompts: &[&[u32]],
2993        caches: &mut [&mut Cache],
2994    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2995        if crate::pp::pp_cuts(self.layers.len()).is_some()
2996            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
2997        {
2998            return Err("pipeline rewrite is not qualified for batched prime".into());
2999        }
3000        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3001            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3002                return Err("neither batched-prime nor eager rewrite is qualified".into());
3003            }
3004            if prompts.len() != caches.len() {
3005                return Err("prime fallback prompt/cache shape mismatch".into());
3006            }
3007            static ONCE: std::sync::Once = std::sync::Once::new();
3008            ONCE.call_once(|| {
3009                eprintln!(
3010                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3011                );
3012            });
3013            return prompts
3014                .iter()
3015                .copied()
3016                .zip(caches.iter_mut())
3017                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3018                .collect();
3019        }
3020        let cfg = &self.cfg;
3021        let n_embd = cfg.n_embd as usize;
3022        let eps = cfg.rms_eps;
3023        let b = prompts.len();
3024        assert!(b >= 1 && b == caches.len());
3025        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3026        let carried = pos0s.iter().any(|&p| p > 0);
3027        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3028        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3029        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3030        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3031        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3032        if self.uses_gemma_program() {
3033            return Err(
3034                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3035                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3036                    .into(),
3037            );
3038        }
3039        // Step35 has a dedicated concat walk: the generic core below cannot express its
3040        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3041        if self.uses_sliding_gated_moe_program() {
3042            return self.step35_prime_cache_batch(e, prompts, caches);
3043        }
3044        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3045        for &t in &ts {
3046            assert!(
3047                t >= PRIME_MIN_T,
3048                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3049            );
3050        }
3051        for (s, c) in caches.iter().enumerate() {
3052            assert!(
3053                c.pos + ts[s] <= c.max_ctx,
3054                "prime_cache_batch: prompt exceeds cache max_ctx"
3055            );
3056        }
3057        let total: usize = ts.iter().sum();
3058        let offs: Vec<usize> = ts
3059            .iter()
3060            .scan(0usize, |a, &t| {
3061                let o = *a;
3062                *a += t;
3063                Some(o)
3064            })
3065            .collect();
3066        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3067        let pos_ds: Vec<CudaSlice<i32>> = ts
3068            .iter()
3069            .zip(&pos0s)
3070            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3071            .collect::<Result<_, _>>()?;
3072        // split a concat [total, dim] buffer into per-seq copies
3073        let split = |e: &Engine,
3074                     y: &CudaSlice<f32>,
3075                     dim: usize|
3076         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3077            let mut out = Vec::with_capacity(b);
3078            for s in 0..b {
3079                let mut ys = e.uninit(ts[s] * dim)?;
3080                e.copy_view_into(
3081                    &mut ys,
3082                    0,
3083                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3084                    ts[s] * dim,
3085                )?;
3086                out.push(ys);
3087            }
3088            Ok(out)
3089        };
3090
3091        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3092        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3093        for (il, layer) in self.layers.iter().enumerate() {
3094            let mut h = e.uninit(total * n_embd)?;
3095            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3096            e.rms_norm_f16out(
3097                &x,
3098                layer.attn_norm.float_data(),
3099                &mut h,
3100                &mut hx16,
3101                n_embd,
3102                total,
3103                eps,
3104            )?;
3105            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3106            let mut mixed = e.uninit(total * n_embd)?;
3107            match &layer.mixer {
3108                Mixer::Full(fa) => {
3109                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3110                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3111                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3112                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3113                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3114                    // back to the per-seq dispatch.
3115                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3116                    let (n_head, n_head_kv, head_dim) = (
3117                        geometry.n_head as usize,
3118                        geometry.n_head_kv as usize,
3119                        geometry.head_dim_k as usize,
3120                    );
3121                    let fa_scale = geometry.attention_scale();
3122                    let use_favl = !carried
3123                        && (2..=8).contains(&b)
3124                        && (head_dim == 256 || head_dim == 128)
3125                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3126                        && std::env::var("MEMRA_NOFA").is_err()
3127                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3128                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3129                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3130                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3131                    if use_favl {
3132                        let (qf_w, kf_w, vf_w) = (
3133                            fa.wq.out_features(),
3134                            fa.wk.out_features(),
3135                            fa.wv.out_features(),
3136                        );
3137                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3138                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3139                        // cannot check its own extents; `qf_w` is the wq out-features that set
3140                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3141                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3142                        struct APre {
3143                            q: CudaSlice<f32>,
3144                            gate: Option<CudaSlice<f32>>,
3145                            qn: CudaSlice<f32>,
3146                            kn: CudaSlice<f32>,
3147                        }
3148                        let mut aps = Vec::with_capacity(b);
3149                        for &t in ts.iter().take(b) {
3150                            aps.push(APre {
3151                                q: e.uninit(t * n_head * head_dim)?,
3152                                gate: Some(e.uninit(t * n_head * head_dim)?),
3153                                qn: e.uninit(t * n_head * head_dim)?,
3154                                kn: e.uninit(t * n_head_kv * head_dim)?,
3155                            });
3156                        }
3157                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3158                            let kvl = caches[0].kv[il].as_ref().unwrap();
3159                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3160                        };
3161                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3162                            .map(|s| {
3163                                let (o, t) = (offs[s], ts[s]);
3164                                let kvl = caches[s].kv[il].as_ref().unwrap();
3165                                assert!(
3166                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3167                                    "prime_cache_batch attn vl: fresh + capacity"
3168                                );
3169                                crate::AttnPreVl {
3170                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3171                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3172                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3173                                    q: e.addr_f32(&aps[s].q),
3174                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3175                                    qn: e.addr_f32(&aps[s].qn),
3176                                    kn: e.addr_f32(&aps[s].kn),
3177                                    kc: e.addr_u8(&kvl.k),
3178                                    vc: e.addr_u8(&kvl.v),
3179                                    t: t as i32,
3180                                    pad: 0,
3181                                }
3182                            })
3183                            .collect();
3184                        e.attn_pre_vl8(
3185                            &pargs,
3186                            fa.q_norm.float_data(),
3187                            fa.k_norm.float_data(),
3188                            head_dim,
3189                            geometry.n_rot as usize,
3190                            n_head,
3191                            n_head_kv,
3192                            self.cfg.rms_eps,
3193                            geometry.rope_base,
3194                            1.0,
3195                            kv_dim_k,
3196                            kv_dim_v,
3197                            ktb,
3198                            vtb,
3199                        )?;
3200                        for s in 0..b {
3201                            let kvl = caches[s].kv[il].as_mut().unwrap();
3202                            kvl.len += ts[s];
3203                            let new_len = kvl.len as i32;
3204                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3205                        }
3206                        let mut attns = Vec::with_capacity(b);
3207                        let mut mirrors = Vec::with_capacity(b);
3208                        for &t in ts.iter().take(b) {
3209                            attns.push(e.uninit(t * n_head * head_dim)?);
3210                            let n = t * n_head_kv * head_dim;
3211                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3212                        }
3213                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3214                        // promoted single-seq config is on; else the mma favl.
3215                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3216                            Ok("0") => false,
3217                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3218                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3219                            // portable build.
3220                            Ok("1") => {
3221                                crate::refuse_portable_force(
3222                                    "MEMRA_FA3=1",
3223                                    "the sm_90a fa3/bf16 kernels",
3224                                );
3225                                true
3226                            }
3227                            _ => cfg!(memra_hopper_mma),
3228                        };
3229                        if fa3_on {
3230                            let mut q16s = Vec::with_capacity(b);
3231                            let mut v16s = Vec::with_capacity(b);
3232                            for s in 0..b {
3233                                let t = ts[s];
3234                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3235                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3236                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3237                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3238                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3239                                e.f32_to_bf16_v(
3240                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3241                                    &mut v16,
3242                                    t * n_head_kv * head_dim,
3243                                )?;
3244                                q16s.push(q16);
3245                                v16s.push((k16, v16));
3246                            }
3247                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3248                            let mut kp = qp;
3249                            let mut vp = qp;
3250                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3251                            let mut tsv = [0i32; 8];
3252                            for s in 0..b {
3253                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3254                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3255                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3256                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3257                                tsv[s] = ts[s] as i32;
3258                            }
3259                            let rc = unsafe {
3260                                crate::fa3_vl_raw(
3261                                    qp.as_ptr(),
3262                                    kp.as_ptr(),
3263                                    vp.as_ptr(),
3264                                    op.as_ptr(),
3265                                    tsv.as_ptr(),
3266                                    b as i32,
3267                                    n_head as i32,
3268                                    n_head_kv as i32,
3269                                    head_dim as i32,
3270                                    fa_scale,
3271                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3272                                )
3273                            };
3274                            if rc != 0 {
3275                                return Err(format!("memra_fa3_vl rc={rc}").into());
3276                            }
3277                        } else {
3278                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3279                                .map(|s| crate::FaSeqVl {
3280                                    q: e.addr_f32(&aps[s].qn),
3281                                    k16: e.addr_u8(&mirrors[s].0),
3282                                    v16: e.addr_u8(&mirrors[s].1),
3283                                    o: e.addr_f32(&attns[s]),
3284                                    kf: e.addr_f32(&aps[s].kn),
3285                                    vf: e.addr_f32v(
3286                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3287                                    ),
3288                                    t: ts[s] as i32,
3289                                    pad: 0,
3290                                })
3291                                .collect();
3292                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3293                        }
3294                        for (s, attn) in attns.into_iter().enumerate() {
3295                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3296                                e,
3297                                attn,
3298                                &aps[s].gate,
3299                                ts[s],
3300                                n_head,
3301                                head_dim,
3302                            )?;
3303                            let mut done = false;
3304                            if let Some(xh) = &ag16 {
3305                                done = e.try_f16_gemm_pre_into_off(
3306                                    &fa.wo,
3307                                    xh,
3308                                    ts[s],
3309                                    &mut mixed,
3310                                    offs[s] * n_embd,
3311                                )?;
3312                            }
3313                            if !done {
3314                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3315                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3316                            }
3317                        }
3318                    } else {
3319                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3320                            (0..b).map(|_| Vec::new()).collect();
3321                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3322                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3323                                parts[s].push(ys);
3324                            }
3325                        }
3326                        for (s, g3s) in parts.into_iter().enumerate() {
3327                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3328                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3329                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
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                    }
3347                }
3348                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3349                Mixer::Linear(la) => {
3350                    // task #16: NO split copies (cores read row-offset views of the concat
3351                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3352                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3353                    // varlen K5 launch for all sequences.
3354                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3355                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3356                    let outs =
3357                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3358                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3359                        let (o, t) = (offs[s], ts[s]);
3360                        let mut done = false;
3361                        if let Some(xh) = &gn16 {
3362                            done = e.try_f16_gemm_pre_into_off(
3363                                &la.ssm_out,
3364                                xh,
3365                                t,
3366                                &mut mixed,
3367                                o * n_embd,
3368                            )?;
3369                        }
3370                        if !done {
3371                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3372                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3373                        }
3374                    }
3375                }
3376            }
3377            let mut x1 = e.uninit(total * n_embd)?;
3378            let mut z = e.uninit(total * n_embd)?;
3379            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3380            e.add_rms_norm_f16out(
3381                &x,
3382                &mixed,
3383                layer.post_attn_norm.float_data(),
3384                &mut x1,
3385                &mut z,
3386                &mut zx16,
3387                n_embd,
3388                total,
3389                eps,
3390            )?;
3391            let ffn_out = match &layer.ffn {
3392                crate::hybrid::Ffn::Dense {
3393                    ffn_gate,
3394                    ffn_up,
3395                    ffn_down,
3396                } => {
3397                    let n_ff = ffn_gate.out_features();
3398                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3399                    let up = g2.pop().unwrap();
3400                    let gate = g2.pop().unwrap();
3401                    let mut act = e.uninit(total * n_ff)?;
3402                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3403                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3404                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3405                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3406                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3407                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3408                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3409                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3410                            Some(y) => y,
3411                            None => e.matmul(ffn_down, &act, total)?,
3412                        }
3413                    } else {
3414                        Self::ffn_act_lim(
3415                            e,
3416                            &self.cfg,
3417                            &gate,
3418                            &up,
3419                            1.0,
3420                            1.0,
3421                            d_lim,
3422                            &mut act,
3423                            total * n_ff,
3424                        )?;
3425                        e.matmul(ffn_down, &act, total)?
3426                    }
3427                }
3428                crate::hybrid::Ffn::Moe(m) => {
3429                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3430                }
3431            };
3432            let mut x2 = e.uninit(total * n_embd)?;
3433            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3434            x = x2;
3435        }
3436        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3437        let mut hn = e.uninit(total * n_embd)?;
3438        e.rms_norm(
3439            &x,
3440            self.output_norm.float_data(),
3441            &mut hn,
3442            n_embd,
3443            total,
3444            eps,
3445        )?;
3446        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3447        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3448        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3449        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3450        // argmax battery arbitrates, same as every other prefill GEMM change.
3451        let mut hcat = e.uninit(b * n_embd)?;
3452        for s in 0..b {
3453            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3454            e.copy_view_into(
3455                &mut hcat,
3456                s * n_embd,
3457                &hn.slice(last0..last0 + n_embd),
3458                n_embd,
3459            )?;
3460        }
3461        let logits_cat = if b >= 2 {
3462            e.try_f16_gemm(&self.output, &hcat, b)?
3463        } else {
3464            None
3465        };
3466        let logits_host: Option<Vec<f32>> = match &logits_cat {
3467            Some(lc) => Some(e.dtoh(lc)?),
3468            None => None,
3469        };
3470        let n_vocab = self.output.out_features();
3471        let mut hidden_all = if crate::spec::spec_hpost() {
3472            split(e, &hn, n_embd)?
3473        } else {
3474            split(e, &x, n_embd)?
3475        };
3476        let mut out = Vec::with_capacity(b);
3477        for s in 0..b {
3478            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3479            let mut h_seed = e.uninit(n_embd)?;
3480            if !crate::spec::spec_hpost() {
3481                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3482            } else {
3483                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3484            }
3485            let logits = match &logits_host {
3486                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3487                None => {
3488                    let mut hlast = e.uninit(n_embd)?;
3489                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3490                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3491                }
3492            };
3493            caches[s].pos += ts[s];
3494            out.push((logits, h_seed, hidden_all.remove(0)));
3495        }
3496        Ok(out)
3497    }
3498
3499    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3500    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3501    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3502    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3503    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3504    ///
3505    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3506    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3507    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3508    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3509    #[allow(clippy::too_many_arguments)]
3510    fn full_attn_prime(
3511        &self,
3512        e: &Engine,
3513        fa: &FullAttnLayer,
3514        h: &CudaSlice<f32>,
3515        hx: Option<&CudaSlice<u8>>,
3516        pos_d: &CudaSlice<i32>,
3517        t: usize,
3518        cache: &mut Cache,
3519        il: usize,
3520        seq_end: usize,
3521    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3522        if self.uses_sliding_gated_moe_program() {
3523            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3524        }
3525        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3526        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3527        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3528        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3529        let g3 = match hx {
3530            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3531            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3532        };
3533        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3534    }
3535
3536    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3537    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3538    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3539    fn full_attn_prime_core(
3540        &self,
3541        e: &Engine,
3542        fa: &FullAttnLayer,
3543        g3: Vec<CudaSlice<f32>>,
3544        pos_d: &CudaSlice<i32>,
3545        t: usize,
3546        cache: &mut Cache,
3547        il: usize,
3548    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3549        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3550        if let Some(xh) = &ag16 {
3551            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3552                return Ok(y);
3553            }
3554        }
3555        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3556    }
3557
3558    fn full_attn_prime_core_inner(
3559        &self,
3560        e: &Engine,
3561        fa: &FullAttnLayer,
3562        g3: Vec<CudaSlice<f32>>,
3563        pos_d: &CudaSlice<i32>,
3564        t: usize,
3565        cache: &mut Cache,
3566        il: usize,
3567    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3568        let cfg = &self.cfg;
3569        let geometry = cfg.full_attention_geometry_at(il as u32);
3570        let n_head = geometry.n_head as usize;
3571        let n_head_kv = geometry.n_head_kv as usize;
3572        let head_dim = geometry.head_dim_k as usize;
3573        let scale = geometry.attention_scale();
3574        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3575        let AttnPre { q, k, v, gate } = pre;
3576        let mut attn = e.uninit(t * n_head * head_dim)?;
3577        self.full_attn_prime_fa_dispatch(
3578            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3579        )?;
3580        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3581    }
3582
3583    /// task #18 (attn side): projections tail through KV append — everything before the
3584    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3585    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3586    #[allow(clippy::type_complexity)]
3587    fn full_attn_prime_pre_fa(
3588        &self,
3589        e: &Engine,
3590        fa: &FullAttnLayer,
3591        mut g3: Vec<CudaSlice<f32>>,
3592        pos_d: &CudaSlice<i32>,
3593        t: usize,
3594        cache: &mut Cache,
3595        il: usize,
3596    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3597        let cfg = &self.cfg;
3598        let geometry = cfg.full_attention_geometry_at(il as u32);
3599        let n_head = geometry.n_head as usize;
3600        let n_head_kv = geometry.n_head_kv as usize;
3601        let head_dim = geometry.head_dim_k as usize;
3602        let eps = cfg.rms_eps;
3603
3604        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3605        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3606        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3607        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3608        let v = g3.pop().unwrap();
3609        let mut k = g3.pop().unwrap();
3610        let qf = g3.pop().unwrap();
3611        let (mut q, gate) = if gated {
3612            let mut q = e.uninit(t * n_head * head_dim)?;
3613            let mut gate = e.uninit(t * n_head * head_dim)?;
3614            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3615            (q, Some(gate))
3616        } else {
3617            (qf, None)
3618        };
3619
3620        let mut qn = e.uninit(t * n_head * head_dim)?;
3621        e.rms_norm(
3622            &q,
3623            fa.q_norm.float_data(),
3624            &mut qn,
3625            head_dim,
3626            n_head * t,
3627            eps,
3628        )?;
3629        q = qn;
3630        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3631        e.rms_norm(
3632            &k,
3633            fa.k_norm.float_data(),
3634            &mut kn,
3635            head_dim,
3636            n_head_kv * t,
3637            eps,
3638        )?;
3639        k = kn;
3640        let rope_dims = geometry.n_rot as usize;
3641        e.rope_neox(
3642            &mut q,
3643            pos_d,
3644            head_dim,
3645            rope_dims,
3646            n_head,
3647            t,
3648            geometry.rope_base,
3649            1.0,
3650        )?;
3651        e.rope_neox(
3652            &mut k,
3653            pos_d,
3654            head_dim,
3655            rope_dims,
3656            n_head_kv,
3657            t,
3658            geometry.rope_base,
3659            1.0,
3660        )?;
3661
3662        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3663        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3664        {
3665            let kvl = cache.kv[il].as_mut().unwrap();
3666            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3667            e.append_kv_quantized_rows(
3668                &k,
3669                &v,
3670                &mut kvl.k,
3671                &mut kvl.v,
3672                kvl.len,
3673                t,
3674                kvl.kv_dim_k,
3675                kvl.kv_dim_v,
3676                kvl.k_tok_bytes,
3677                kvl.v_tok_bytes,
3678                crate::Engine::kv_fp8_on(),
3679            )?;
3680            kvl.len += t;
3681            let new_len = kvl.len as i32;
3682            e.set_i32_one(&mut kvl.len_d, new_len)?;
3683        }
3684
3685        let base_len = {
3686            let kvl = cache.kv[il].as_ref().unwrap();
3687            kvl.len - t // KV rows present BEFORE this chunk's append above
3688        };
3689        Ok((AttnPre { q, k, v, gate }, base_len))
3690    }
3691
3692    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3693    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3694    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3695    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3696    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3697    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3698    #[allow(clippy::too_many_arguments)]
3699    fn full_attn_prime_fa_dispatch(
3700        &self,
3701        e: &Engine,
3702        q: &CudaSlice<f32>,
3703        k: &CudaSlice<f32>,
3704        v: &CudaSlice<f32>,
3705        attn: &mut CudaSlice<f32>,
3706        base_len: usize,
3707        t: usize,
3708        cache: &mut Cache,
3709        il: usize,
3710        head_dim: usize,
3711        n_head: usize,
3712        n_head_kv: usize,
3713        scale: f32,
3714    ) -> Result<(), Box<dyn std::error::Error>> {
3715        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3716        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3717        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3718        // attend through the quantized cache exactly like every later chunk (quantize-then-
3719        // attend). One numeric class for every row => the chunk size cannot decide where a
3720        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3721        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
3722        // pin-the-boundary approach).
3723        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
3724        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
3725        // with the fix unconditional, only re-introducing the class edge can prove the gate
3726        // still detects the mechanism. Never on in a measured default run.
3727        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
3728            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3729                e.sdpa_naive(
3730                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3731                )?;
3732            } else {
3733                e.fa_prefill(
3734                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3735                )?;
3736            }
3737            return Ok(());
3738        }
3739        let kvl = cache.kv[il].as_ref().unwrap();
3740        let t_kv = base_len + t;
3741        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
3742        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
3743        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
3744        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
3745        // same numeric class, so the uniform contract holds on the fallback too.
3746        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3747            e.sdpa_naive_quantized_view(
3748                q,
3749                &k_view,
3750                &v_view,
3751                attn,
3752                head_dim,
3753                n_head,
3754                n_head_kv,
3755                t,
3756                t_kv,
3757                scale,
3758                true,
3759                kvl.k_tok_bytes,
3760                kvl.v_tok_bytes,
3761            )?;
3762            return Ok(());
3763        }
3764        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
3765        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
3766        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
3767        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
3768        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
3769        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
3770        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
3771        let deqw = std::env::var("MEMRA_PRIME_DEQW")
3772            .map(|v| v != "0")
3773            .unwrap_or(true);
3774        if deqw {
3775            e.fa_prefill_view_ws(
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                crate::Engine::kv_fp8_on(),
3790            )?;
3791        } else {
3792            e.fa_prefill_view(
3793                q,
3794                &k_view,
3795                &v_view,
3796                attn,
3797                head_dim,
3798                n_head,
3799                n_head_kv,
3800                t,
3801                t_kv,
3802                scale,
3803                true,
3804                kvl.k_tok_bytes,
3805                kvl.v_tok_bytes,
3806                crate::Engine::kv_fp8_on(),
3807            )?;
3808        }
3809        Ok(())
3810    }
3811
3812    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
3813    /// (bit-identical composition) and hands wo its fp16 operand directly.
3814    fn full_attn_prime_post_fa(
3815        &self,
3816        e: &Engine,
3817        attn: CudaSlice<f32>,
3818        gate: &Option<CudaSlice<f32>>,
3819        t: usize,
3820        n_head: usize,
3821        head_dim: usize,
3822    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3823        let (attn_g, ag16) = match gate {
3824            Some(gate) => {
3825                let n = t * n_head * head_dim;
3826                let mut ag = e.uninit(n)?;
3827                if Self::f16out_on(e, t) {
3828                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
3829                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
3830                    (ag, Some(a16))
3831                } else {
3832                    let mut gsig = e.uninit(n)?;
3833                    e.sigmoid(gate, &mut gsig, n)?;
3834                    e.mul(&attn, &gsig, &mut ag, n)?;
3835                    (ag, None)
3836                }
3837            }
3838            None => (attn, None),
3839        };
3840        Ok((attn_g, ag16))
3841    }
3842
3843    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
3844    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
3845    /// carried THROUGH the cache like the spec verify does: carried-ring conv
3846    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
3847    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
3848    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
3849    fn linear_attn_prime(
3850        &self,
3851        e: &Engine,
3852        la: &LinearAttnLayer,
3853        h: &CudaSlice<f32>,
3854        hx: Option<&CudaSlice<u8>>,
3855        t: usize,
3856        cache: &mut Cache,
3857        il: usize,
3858    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3859        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
3860        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3861        let g4 = match hx {
3862            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
3863            None => e.matmul_group(&ws, h, t)?,
3864        };
3865        self.linear_attn_prime_core(e, la, g4, t, cache, il)
3866    }
3867
3868    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
3869    fn linear_attn_prime_core(
3870        &self,
3871        e: &Engine,
3872        la: &LinearAttnLayer,
3873        mut g4: Vec<CudaSlice<f32>>,
3874        t: usize,
3875        cache: &mut Cache,
3876        il: usize,
3877    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3878        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
3879    }
3880
3881    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
3882    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
3883    /// conv ring writes back from the true tail. None = classic path, byte-identical.
3884    #[allow(clippy::too_many_arguments)]
3885    fn linear_attn_prime_core_pad_inner(
3886        &self,
3887        e: &Engine,
3888        la: &LinearAttnLayer,
3889        mut g4: Vec<CudaSlice<f32>>,
3890        t: usize,
3891        cache: &mut Cache,
3892        il: usize,
3893        pad_len: Option<&CudaSlice<i32>>,
3894    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3895        // shim over the view twin (task #16): full-range views of the owned buffers.
3896        let geometry = la.geometry;
3897        let d_state = geometry.key_head_dim as usize;
3898        let num_k = geometry.key_heads as usize;
3899        let num_v = geometry.value_heads as usize;
3900        let key_dim = d_state * num_k;
3901        let value_dim = geometry.value_head_dim as usize * num_v;
3902        let conv_dim = key_dim * 2 + value_dim;
3903        let alpha = g4.pop().unwrap(); // [T, num_v]
3904        let beta_raw = g4.pop().unwrap(); // [T, num_v]
3905        let z = g4.pop().unwrap(); // [T, value_dim]
3906        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
3907        self.linear_attn_prime_core_pad_view(
3908            e,
3909            la,
3910            &qkv_mixed.slice(0..t * conv_dim),
3911            &z.slice(0..t * value_dim),
3912            &beta_raw.slice(0..t * num_v),
3913            &alpha.slice(0..t * num_v),
3914            t,
3915            cache,
3916            il,
3917            pad_len,
3918        )
3919    }
3920
3921    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
3922    /// shared verbatim by the per-seq scan path and the varlen batched path.
3923    #[allow(clippy::too_many_arguments)]
3924    fn linear_attn_gdn_prep(
3925        &self,
3926        e: &Engine,
3927        la: &LinearAttnLayer,
3928        qkv_mixed: &cudarc::driver::CudaView<f32>,
3929        beta_raw: &cudarc::driver::CudaView<f32>,
3930        alpha: &cudarc::driver::CudaView<f32>,
3931        t: usize,
3932        cache: &mut Cache,
3933        il: usize,
3934        pad_len: Option<&CudaSlice<i32>>,
3935    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
3936        let cfg = &self.cfg;
3937        let geometry = la.geometry;
3938        let d_state = geometry.key_head_dim as usize;
3939        let num_k = geometry.key_heads as usize;
3940        let num_v = geometry.value_heads as usize;
3941        let d_conv = geometry.conv_kernel as usize;
3942        let key_dim = d_state * num_k; // 2048
3943        let value_dim = geometry.value_head_dim as usize * num_v;
3944        let conv_dim = key_dim * 2 + value_dim; // 8192
3945        let eps = cfg.rms_eps;
3946        debug_assert!(
3947            t >= d_conv - 1,
3948            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
3949        );
3950
3951        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
3952        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
3953        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
3954        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
3955        let rl = cache.recur[il].as_mut().unwrap();
3956        let hk = Self::gdn_hk(e, t, num_v, num_k);
3957        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
3958        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
3959        let mut q_g = e.uninit(d_state * hk * t)?;
3960        let mut k_g = e.uninit(d_state * hk * t)?;
3961        let mut v_g = e.uninit(d_state * num_v * t)?;
3962        if conv_fuse {
3963            e.ssm_conv1d_gdn_state_pad(
3964                qkv_mixed,
3965                &mut rl.conv_state,
3966                la.ssm_conv1d.float_data(),
3967                &mut q_g,
3968                &mut k_g,
3969                &mut v_g,
3970                conv_dim,
3971                t,
3972                d_conv,
3973                d_state,
3974                num_v,
3975                num_k,
3976                key_dim,
3977                hk,
3978                pad_len,
3979            )?;
3980        } else {
3981            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
3982            e.ssm_conv1d_tm_state_pad_v(
3983                qkv_mixed,
3984                &mut rl.conv_state,
3985                la.ssm_conv1d.float_data(),
3986                &mut conv_out,
3987                conv_dim,
3988                t,
3989                d_conv,
3990                pad_len,
3991            )?;
3992            e.qkv_to_gdn_repack(
3993                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
3994            )?;
3995        }
3996        let mut q_l2 = e.uninit(d_state * hk * t)?;
3997        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
3998        // Emitted only where a consumer exists (the wgmma config) — on other arches the
3999        // alloc + epilogue stores would be pure waste.
4000        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4001            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4002            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4003            Some(qb)
4004        } else {
4005            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4006            None
4007        };
4008        let mut k_l2 = e.uninit(d_state * hk * t)?;
4009        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4010        let kb16 = if Engine::l2_v2_on(d_state) {
4011            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4012            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4013            Some(kb)
4014        } else {
4015            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4016            None
4017        };
4018        let mut beta = e.uninit(t * num_v)?;
4019        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4020        let mut g_log = e.uninit(t * num_v)?;
4021        e.gdn_glog_v(
4022            alpha,
4023            la.ssm_dt.float_data(),
4024            la.ssm_a.float_data(),
4025            &mut g_log,
4026            num_v,
4027            t,
4028        )?;
4029        if let Some(len_d) = pad_len {
4030            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4031        }
4032        Ok(GdnPrep {
4033            hk,
4034            q_l2,
4035            k_l2,
4036            v_g,
4037            beta,
4038            g_log,
4039            kb16,
4040            qb16,
4041        })
4042    }
4043
4044    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4045    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4046    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4047    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4048    #[allow(clippy::too_many_arguments)]
4049    fn linear_attn_prime_core_batch(
4050        &self,
4051        e: &Engine,
4052        la: &LinearAttnLayer,
4053        g4: &[CudaSlice<f32>],
4054        offs: &[usize],
4055        ts: &[usize],
4056        caches: &mut [&mut Cache],
4057        il: usize,
4058    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4059        let geometry = la.geometry;
4060        let d_state = geometry.key_head_dim as usize;
4061        let num_k = geometry.key_heads as usize;
4062        let num_v = geometry.value_heads as usize;
4063        let d_conv = geometry.conv_kernel as usize;
4064        let key_dim = d_state * num_k;
4065        let value_dim = geometry.value_head_dim as usize * num_v;
4066        let conv_dim = key_dim * 2 + value_dim;
4067        let eps = self.cfg.rms_eps;
4068        let scale = 1.0 / (d_state as f32).sqrt();
4069        let b = ts.len();
4070        let c = Engine::gdn_chunk_size();
4071        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4072        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4073        let carried = caches.iter().any(|c| c.pos > 0);
4074        let use_vl = !carried
4075            && (2..=8).contains(&b)
4076            && Engine::gdn_chunked_enabled()
4077            && ts.iter().all(|&t| t >= 16)
4078            && e.gdn_mma_enabled(c)
4079            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4080        if !use_vl {
4081            return (0..b)
4082                .map(|s| {
4083                    let (o, t) = (offs[s], ts[s]);
4084                    self.linear_attn_prime_core_pad_view(
4085                        e,
4086                        la,
4087                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4088                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4089                        &g4[2].slice(o * num_v..(o + t) * num_v),
4090                        &g4[3].slice(o * num_v..(o + t) * num_v),
4091                        t,
4092                        caches[s],
4093                        il,
4094                        None,
4095                    )
4096                })
4097                .collect();
4098        }
4099        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4100        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4101        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4102        struct SeqBufs {
4103            conv_out: CudaSlice<f32>,
4104            q_g: CudaSlice<f32>,
4105            k_g: CudaSlice<f32>,
4106            v_g: CudaSlice<f32>,
4107            q_l2: CudaSlice<f32>,
4108            k_l2: CudaSlice<f32>,
4109            beta: CudaSlice<f32>,
4110            g_log: CudaSlice<f32>,
4111            gn: CudaSlice<f32>,
4112            gn16: CudaSlice<u8>,
4113        }
4114        let f16o = Self::f16out_on(e, 16);
4115        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4116        let mut sb = Vec::with_capacity(b);
4117        let mut pres = Vec::with_capacity(b);
4118        for &t in ts.iter().take(b) {
4119            sb.push(SeqBufs {
4120                conv_out: e.uninit(conv_dim * t)?,
4121                q_g: e.uninit(d_state * hk * t)?,
4122                k_g: e.uninit(d_state * hk * t)?,
4123                v_g: e.uninit(d_state * num_v * t)?,
4124                q_l2: e.uninit(d_state * hk * t)?,
4125                k_l2: e.uninit(d_state * hk * t)?,
4126                beta: e.uninit(t * num_v)?,
4127                g_log: e.uninit(t * num_v)?,
4128                gn: e.uninit(d_state * num_v * t)?,
4129                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4130            });
4131            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4132        }
4133        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4134            .map(|s| {
4135                let (o, t) = (offs[s], ts[s]);
4136                let rl = caches[s].recur[il].as_ref().unwrap();
4137                crate::GdnPrepVl {
4138                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4139                    conv_state: e.addr_f32(&rl.conv_state),
4140                    conv_out: e.addr_f32(&sb[s].conv_out),
4141                    q_g: e.addr_f32(&sb[s].q_g),
4142                    k_g: e.addr_f32(&sb[s].k_g),
4143                    v_g: e.addr_f32(&sb[s].v_g),
4144                    q_l2: e.addr_f32(&sb[s].q_l2),
4145                    k_l2: e.addr_f32(&sb[s].k_l2),
4146                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4147                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4148                    beta: e.addr_f32(&sb[s].beta),
4149                    g_log: e.addr_f32(&sb[s].g_log),
4150                    o: e.addr_f32(&pres[s].o),
4151                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4152                    gn: e.addr_f32(&sb[s].gn),
4153                    gn16: e.addr_u8(&sb[s].gn16),
4154                    kb16: if Engine::l2_v2_on(d_state) {
4155                        e.addr_u8(&pres[s].kb16)
4156                    } else {
4157                        0
4158                    },
4159                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4160                        e.addr_u8(&pres[s].qb16)
4161                    } else {
4162                        0
4163                    },
4164                    t: t as i32,
4165                    pad: 0,
4166                }
4167            })
4168            .collect();
4169        let args: Vec<crate::GdnSeqVl> = (0..b)
4170            .map(|s| {
4171                let rl = caches[s].recur[il].as_ref().unwrap();
4172                crate::GdnSeqVl {
4173                    kb16: e.addr_u8(&pres[s].kb16),
4174                    gcum: e.addr_f32(&pres[s].gcum),
4175                    beta: e.addr_f32(&sb[s].beta),
4176                    u: e.addr_f32(&pres[s].u),
4177                    wb16: e.addr_u8(&pres[s].wb16),
4178                    y: e.addr_u8(&pres[s].y16),
4179                    ssnap: e.addr_u8(&pres[s].ssnap16),
4180                    state_in: e.addr_f32(&rl.ssm_state),
4181                    state_out: e.addr_f32(&rl.ssm_state_alt),
4182                    q: e.addr_f32(&sb[s].q_l2),
4183                    p: e.addr_f32(&pres[s].p),
4184                    o: e.addr_f32(&pres[s].o),
4185                    k: e.addr_f32(&sb[s].k_l2),
4186                    v: e.addr_f32(&sb[s].v_g),
4187                    g: e.addr_f32(&sb[s].g_log),
4188                    a: e.addr_f32(&pres[s].a),
4189                    w: e.addr_f32(&pres[s].w),
4190                    t: ts[s] as i32,
4191                    nc: pres[s].nc as i32,
4192                }
4193            })
4194            .collect();
4195        e.gdn_prep_vl8(
4196            &prep_args,
4197            la.ssm_conv1d.float_data(),
4198            la.ssm_dt.float_data(),
4199            la.ssm_a.float_data(),
4200            conv_dim,
4201            d_conv,
4202            d_state,
4203            num_v,
4204            num_k,
4205            key_dim,
4206            hk,
4207            eps,
4208        )?;
4209        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4210        // both standalone mirror launches vanish on the default config.
4211        if !Engine::l2_v2_on(d_state) {
4212            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4213        }
4214        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4215        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4216            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4217            if !Engine::l2_v2_on(d_state) {
4218                for s in 0..b {
4219                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4220                }
4221            }
4222            let mut wa = [crate::GdnWVl::default(); 8];
4223            for s in 0..b {
4224                wa[s] = crate::GdnWVl {
4225                    qb16: e.addr_u8(&pres[s].qb16),
4226                    pb16: e.addr_u8(&pres[s].pb16),
4227                };
4228            }
4229            Some(crate::GdnWVl8(wa))
4230        } else {
4231            None
4232        };
4233        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4234        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4235        if f16o {
4236            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4237        }
4238        // per-seq state swap (+ non-f16out tail fallback)
4239        let mut out = Vec::with_capacity(b);
4240        for (s, bufs) in sb.into_iter().enumerate() {
4241            let rl = caches[s].recur[il].as_mut().unwrap();
4242            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4243            let (o, t) = (offs[s], ts[s]);
4244            let SeqBufs { mut gn, gn16, .. } = bufs;
4245            if f16o {
4246                out.push((gn, Some(gn16)));
4247            } else {
4248                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4249                e.gated_rmsnorm_zv(
4250                    &pres[s].o,
4251                    la.ssm_norm.float_data(),
4252                    &z_v,
4253                    &mut gn,
4254                    d_state,
4255                    num_v * t,
4256                    eps,
4257                )?;
4258                out.push((gn, None));
4259            }
4260        }
4261        Ok(out)
4262    }
4263
4264    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4265    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4266    /// Same kernels, same values, byte-identical to the Vec shim above.
4267    #[allow(clippy::too_many_arguments)]
4268    fn linear_attn_prime_core_pad_view(
4269        &self,
4270        e: &Engine,
4271        la: &LinearAttnLayer,
4272        qkv_mixed: &cudarc::driver::CudaView<f32>,
4273        z: &cudarc::driver::CudaView<f32>,
4274        beta_raw: &cudarc::driver::CudaView<f32>,
4275        alpha: &cudarc::driver::CudaView<f32>,
4276        t: usize,
4277        cache: &mut Cache,
4278        il: usize,
4279        pad_len: Option<&CudaSlice<i32>>,
4280    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4281        let cfg = &self.cfg;
4282        let geometry = la.geometry;
4283        let d_state = geometry.key_head_dim as usize;
4284        let num_v = geometry.value_heads as usize;
4285        let eps = cfg.rms_eps;
4286        let scale = 1.0 / (d_state as f32).sqrt();
4287
4288        let prep =
4289            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4290
4291        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4292        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4293        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4294        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4295        // verify keep the sequential kernel).
4296        let mut o = e.uninit(d_state * num_v * t)?;
4297        let rl = cache.recur[il].as_mut().unwrap();
4298        {
4299            let crate::cache::RecurLayer {
4300                ssm_state,
4301                ssm_state_alt,
4302                ..
4303            } = rl;
4304            e.gdn_scan_prefill(
4305                &prep.q_l2,
4306                &prep.k_l2,
4307                &prep.v_g,
4308                &prep.g_log,
4309                &prep.beta,
4310                prep.kb16.as_ref(),
4311                prep.qb16.as_ref(),
4312                ssm_state,
4313                ssm_state_alt,
4314                &mut o,
4315                num_v,
4316                t,
4317                scale,
4318                prep.hk,
4319            )?;
4320        }
4321        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4322
4323        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4324        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4325        let mut gn = e.uninit(d_state * num_v * t)?;
4326        let gn16 = if Self::f16out_on(e, t) {
4327            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4328            e.gated_rmsnorm_f16out_zv(
4329                &o,
4330                la.ssm_norm.float_data(),
4331                z,
4332                &mut gn,
4333                &mut g16,
4334                d_state,
4335                num_v * t,
4336                eps,
4337            )?;
4338            Some(g16)
4339        } else {
4340            e.gated_rmsnorm_zv(
4341                &o,
4342                la.ssm_norm.float_data(),
4343                z,
4344                &mut gn,
4345                d_state,
4346                num_v * t,
4347                eps,
4348            )?;
4349            None
4350        };
4351        Ok((gn, gn16))
4352    }
4353
4354    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4355    #[allow(clippy::too_many_arguments)]
4356    fn linear_attn_prime_core_pad(
4357        &self,
4358        e: &Engine,
4359        la: &LinearAttnLayer,
4360        g4: Vec<CudaSlice<f32>>,
4361        t: usize,
4362        cache: &mut Cache,
4363        il: usize,
4364        pad_len: Option<&CudaSlice<i32>>,
4365    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4366        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4367        if let Some(xh) = &gn16 {
4368            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4369                return Ok(y);
4370            }
4371        }
4372        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4373    }
4374
4375    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4376    ///
4377    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4378    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4379    pub fn full_attn(
4380        &self,
4381        e: &Engine,
4382        fa: &FullAttnLayer,
4383        h: &CudaSlice<f32>,
4384        pos_d: &CudaSlice<i32>,
4385        t: usize,
4386        il: usize,
4387    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4388        if self.uses_sliding_gated_moe_program() {
4389            return self.step35_attn(e, fa, h, pos_d, t, il);
4390        }
4391        let cfg = &self.cfg;
4392        let _n_embd = cfg.n_embd as usize;
4393        let geometry = cfg.full_attention_geometry_at(il as u32);
4394        let n_head = geometry.n_head as usize;
4395        let n_head_kv = geometry.n_head_kv as usize;
4396        let head_dim = geometry.head_dim_k as usize;
4397        let eps = cfg.rms_eps;
4398        let scale = geometry.attention_scale();
4399
4400        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4401        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4402        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4403        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4404        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4405        let v = g3.pop().unwrap();
4406        let mut k = g3.pop().unwrap();
4407        let qf = g3.pop().unwrap();
4408        let (mut q, gate) = if gated {
4409            let mut q = e.uninit(t * n_head * head_dim)?;
4410            let mut gate = e.uninit(t * n_head * head_dim)?;
4411            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4412            (q, Some(gate))
4413        } else {
4414            (qf, None)
4415        };
4416
4417        // QK-norm (per head_dim row), then partial RoPE.
4418        let mut qn = e.uninit(t * n_head * head_dim)?;
4419        e.rms_norm(
4420            &q,
4421            fa.q_norm.float_data(),
4422            &mut qn,
4423            head_dim,
4424            n_head * t,
4425            eps,
4426        )?;
4427        q = qn;
4428        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4429        e.rms_norm(
4430            &k,
4431            fa.k_norm.float_data(),
4432            &mut kn,
4433            head_dim,
4434            n_head_kv * t,
4435            eps,
4436        )?;
4437        k = kn;
4438        let rope_dims = geometry.n_rot as usize;
4439        e.rope_neox(
4440            &mut q,
4441            pos_d,
4442            head_dim,
4443            rope_dims,
4444            n_head,
4445            t,
4446            geometry.rope_base,
4447            1.0,
4448        )?;
4449        e.rope_neox(
4450            &mut k,
4451            pos_d,
4452            head_dim,
4453            rope_dims,
4454            n_head_kv,
4455            t,
4456            geometry.rope_base,
4457            1.0,
4458        )?;
4459
4460        // SDPA
4461        let mut attn = e.uninit(t * n_head * head_dim)?;
4462        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4463        // falls back to naive sdpa.
4464        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4465            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4466            e.sdpa_naive(
4467                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4468            )?;
4469        } else {
4470            e.fa_prefill(
4471                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4472            )?;
4473        }
4474
4475        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4476        let attn_g = match &gate {
4477            Some(gate) => {
4478                let mut gsig = e.uninit(t * n_head * head_dim)?;
4479                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4480                let mut ag = e.uninit(t * n_head * head_dim)?;
4481                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4482                ag
4483            }
4484            None => attn,
4485        };
4486
4487        // o projection
4488        let o = e.matmul(&fa.wo, &attn_g, t)?;
4489        Ok(o)
4490    }
4491
4492    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4493    pub fn linear_attn(
4494        &self,
4495        e: &Engine,
4496        la: &LinearAttnLayer,
4497        h: &CudaSlice<f32>,
4498        t: usize,
4499    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4500        let cfg = &self.cfg;
4501        let _n_embd = cfg.n_embd as usize;
4502        let geometry = la.geometry;
4503        let d_state = geometry.key_head_dim as usize;
4504        let num_k = geometry.key_heads as usize;
4505        let num_v = geometry.value_heads as usize;
4506        let d_conv = geometry.conv_kernel as usize;
4507        let head_k = d_state;
4508        let head_v = geometry.value_head_dim as usize;
4509        let key_dim = head_k * num_k; // 2048
4510        let value_dim = head_v * num_v; // 4096
4511        let conv_dim = key_dim * 2 + value_dim; // 8192
4512        let eps = cfg.rms_eps;
4513        let scale = 1.0 / (d_state as f32).sqrt();
4514
4515        // projections
4516        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4517        let mut g4 = e.matmul_group(
4518            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4519            h,
4520            t,
4521        )?;
4522        let alpha = g4.pop().unwrap(); // [T, num_v]
4523        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4524        let z = g4.pop().unwrap(); // [T, value_dim]
4525        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4526
4527        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4528        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4529        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4530        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4531        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4532        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4533        let _ = (head_k, head_v);
4534        let mut q_g = e.uninit(d_state * num_v * t)?;
4535        let mut k_g = e.uninit(d_state * num_v * t)?;
4536        let mut v_g = e.uninit(d_state * num_v * t)?;
4537        e.ssm_conv1d_gdn(
4538            &qkv_mixed,
4539            la.ssm_conv1d.float_data(),
4540            &mut q_g,
4541            &mut k_g,
4542            &mut v_g,
4543            conv_dim,
4544            t,
4545            d_conv,
4546            d_state,
4547            num_v,
4548            num_k,
4549            key_dim,
4550        )?;
4551        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4552        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4553        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4554        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4555        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4556        let v_gd = v_g;
4557
4558        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4559        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4560        let mut beta = e.uninit(t * num_v)?;
4561        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4562        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4563        let mut g_log = e.uninit(t * num_v)?;
4564        e.gdn_glog(
4565            &alpha,
4566            la.ssm_dt.float_data(),
4567            la.ssm_a.float_data(),
4568            &mut g_log,
4569            num_v,
4570            t,
4571        )?;
4572
4573        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4574        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4575        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4576        let mut o = e.uninit(d_state * num_v * t)?;
4577        e.gdn_scan_prefill(
4578            &q_l2,
4579            &k_l2,
4580            &v_gd,
4581            &g_log,
4582            &beta,
4583            None,
4584            None,
4585            &state_in,
4586            &mut state_out,
4587            &mut o,
4588            num_v,
4589            t,
4590            scale,
4591            num_v,
4592        )?;
4593
4594        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4595        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4596        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4597        // o rows are (t*num_v+vh) too. Good.
4598        let mut gn = e.uninit(d_state * num_v * t)?;
4599        e.gated_rmsnorm(
4600            &o,
4601            la.ssm_norm.float_data(),
4602            &z,
4603            &mut gn,
4604            d_state,
4605            num_v * t,
4606            eps,
4607        )?;
4608
4609        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4610        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4611        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4612        let out = e.matmul(&la.ssm_out, &gn, t)?;
4613        Ok(out)
4614    }
4615}
4616
4617impl HybridModel {
4618    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4619    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4620    ///
4621    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4622    /// different 860160-byte block than the same expert of layer 7).
4623    ///
4624    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4625    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4626    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4627    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4628    pub fn moe_ffn_il(
4629        &self,
4630        e: &Engine,
4631        m: &MoeWeights,
4632        z: &CudaSlice<f32>,
4633        t: usize,
4634        il: u16,
4635    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4636        Self::moe_ffn_inner(
4637            e,
4638            m,
4639            z,
4640            None,
4641            t,
4642            &self.cfg,
4643            il,
4644            self.max_moe_block(),
4645            false,
4646            None,
4647            self.uses_sliding_gated_moe_program(),
4648        )
4649    }
4650
4651    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4652    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4653    pub fn moe_ffn_il_prefill(
4654        &self,
4655        e: &Engine,
4656        m: &MoeWeights,
4657        z: &CudaSlice<f32>,
4658        t: usize,
4659        il: u16,
4660    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4661        Self::moe_ffn_inner(
4662            e,
4663            m,
4664            z,
4665            None,
4666            t,
4667            &self.cfg,
4668            il,
4669            self.max_moe_block(),
4670            true,
4671            Some(&self.step_grouped_prefill),
4672            self.uses_sliding_gated_moe_program(),
4673        )
4674    }
4675
4676    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4677    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4678    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4679    pub fn moe_ffn_il_zq8(
4680        &self,
4681        e: &Engine,
4682        m: &MoeWeights,
4683        z: &CudaSlice<f32>,
4684        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4685        t: usize,
4686        il: u16,
4687    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4688        Self::moe_ffn_inner(
4689            e,
4690            m,
4691            z,
4692            zq8,
4693            t,
4694            &self.cfg,
4695            il,
4696            self.max_moe_block(),
4697            false,
4698            None,
4699            self.uses_sliding_gated_moe_program(),
4700        )
4701    }
4702
4703    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4704    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4705    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4706    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4707    ///
4708    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4709    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4710    pub(crate) fn moe_ffn(
4711        e: &Engine,
4712        m: &MoeWeights,
4713        z: &CudaSlice<f32>,
4714        t: usize,
4715        cfg: &ModelConfig,
4716        il: u16,
4717        max_block: usize,
4718    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4719        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4720    }
4721
4722    #[allow(clippy::too_many_arguments)]
4723    pub(crate) fn moe_ffn_inner(
4724        e: &Engine,
4725        m: &MoeWeights,
4726        z: &CudaSlice<f32>,
4727        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4728        t: usize,
4729        cfg: &ModelConfig,
4730        il: u16,
4731        max_block: usize,
4732        prefill: bool,
4733        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
4734        sliding_gated_moe: bool,
4735    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4736        let worker_io = crate::spill_pread::worker_enabled();
4737        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
4738        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
4739            e.with_moe_cache(max_block, |cache, _| {
4740                cache.begin_forward_epoch(il, t);
4741                if worker_io {
4742                    cache.begin_worker_scope();
4743                }
4744                Ok(())
4745            })?;
4746        }
4747        if m.step_ep.is_some() || m.step_tp.is_some() {
4748            let moe = cfg
4749                .moe
4750                .as_ref()
4751                .ok_or("Step distributed execution requires MoE model metadata")?;
4752            let n_embd = cfg.n_embd as usize;
4753            let n_expert = moe.expert_count as usize;
4754            let n_used = moe.expert_used_count as usize;
4755            let sigmoid = cfg
4756                .sigmoid_router()
4757                .ok_or("Step distributed execution requires the Step sigmoid router")?;
4758            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
4759            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
4760            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
4761            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
4762                return Err(
4763                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
4764                );
4765            }
4766            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
4767                return Err(format!(
4768                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
4769                    PRIME_MIN_T,
4770                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
4771                )
4772                .into());
4773            }
4774            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
4775            let grouped_prefill_shape =
4776                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
4777            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
4778                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
4779            }) {
4780                let (selected, route_weights) =
4781                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
4782                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
4783                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
4784                Self::trace_moe_input(e, il, t, n_embd, z)?;
4785                let selected = selected
4786                    .iter()
4787                    .map(|&expert| expert as usize)
4788                    .collect::<Vec<_>>();
4789
4790                // The narrow route readback above orders the owning-stage producer. The grouped
4791                // runtime then copies the resident root activation into its persistent rank inputs.
4792                e.stream().synchronize()?;
4793                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
4794                    state.projection.set_activation_limit(ep.activation_limit)?;
4795                    ep.runtime
4796                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
4797                            ep.experts.e4m3()?,
4798                            &mut state.projection,
4799                            z,
4800                            t,
4801                            &selected,
4802                        )?;
4803                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
4804                        &state.projection,
4805                        &mut state.combine,
4806                        &route_weights,
4807                    )?;
4808                    ep.runtime.execute_step_grouped_expert_parallel_gate(
4809                        ep.experts.e4m3()?,
4810                        &mut state.projection,
4811                    )?;
4812                    ep.runtime.execute_step_grouped_expert_parallel_combine(
4813                        &state.projection,
4814                        &mut state.combine,
4815                    )?;
4816                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
4817                        &state.projection,
4818                        &state.combine,
4819                        e,
4820                    )?;
4821                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
4822                    if prefill {
4823                        // A shared plan may be reused by the next layer on a different runtime
4824                        // stream. Complete the owning-stage copy before its source is overwritten.
4825                        e.stream().synchronize()?;
4826                    }
4827                    eprintln!(
4828                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
4829                         attention_layout=tensor-parallel expert_layout=expert-parallel \
4830                         expert_transport={} native_p2p=true route_control=host-narrow \
4831                         input=root-device projection_workspaces=persistent \
4832                         combine=root-device output=owning-stage-device \
4833                         prefill={prefill} batched_decode=false capacity={} \
4834                         performance_claim=false",
4835                        ep.devices,
4836                        ep.runtime.transport_label(),
4837                        state.projection.max_tokens(),
4838                    );
4839                    Ok::<_, Box<dyn std::error::Error>>(output)
4840                };
4841
4842                if grouped_prefill_shape {
4843                    let grouped_prefill = grouped_prefill
4844                        .ok_or("Step grouped prefill has no model-scoped executor")?;
4845                    let mut shared = grouped_prefill
4846                        .lock()
4847                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
4848                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
4849                        state.devices != ep.devices
4850                            || state.grouped.projection.max_tokens() < t
4851                            || state.grouped.projection.input_width() != n_embd
4852                            || state.grouped.projection.expert_width()
4853                                != moe.expert_ff_length as usize
4854                    });
4855                    if needs_prepare {
4856                        let seed_input = vec![0.0f32; n_embd];
4857                        let seed_selected = &selected[..n_used];
4858                        let seed_weights = &route_weights[..n_used];
4859                        let projection = ep
4860                            .runtime
4861                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
4862                                ep.experts.e4m3()?,
4863                                &seed_input,
4864                                1,
4865                                seed_selected,
4866                                ep.activation_limit,
4867                                t,
4868                            )?;
4869                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
4870                            &projection,
4871                            seed_weights,
4872                        )?;
4873                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
4874                            devices: ep.devices.clone(),
4875                            grouped: crate::hybrid::StepEpGroupedDecode {
4876                                projection,
4877                                combine,
4878                            },
4879                        });
4880                        eprintln!(
4881                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
4882                             shared_across_layers=true performance_claim=false",
4883                            ep.devices,
4884                        );
4885                    }
4886                    return execute(
4887                        &mut shared
4888                            .state
4889                            .as_mut()
4890                            .expect("Step grouped prefill state prepared above")
4891                            .grouped,
4892                    );
4893                }
4894
4895                let mut grouped = ep
4896                    .grouped_decode
4897                    .as_ref()
4898                    .expect("grouped decode presence checked above")
4899                    .lock()
4900                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
4901                return execute(&mut grouped);
4902            }
4903            if grouped_prefill_shape {
4904                return Err(
4905                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
4906                        .into(),
4907                );
4908            }
4909            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
4910            // expert program — the per-layer host logits readback (the last per-layer host
4911            // sync) disappears. Selection tie-breaking may differ from the host router:
4912            // numeric-class door, run-gen argmax gate + boot battery.
4913            if t == 1
4914                && crate::tp::step_nvfp4_dev_routes_enabled()?
4915                && crate::tp::step_tp_dev_router_enabled()?
4916            {
4917                if let Some(tp) = &m.step_tp {
4918                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
4919                        let (sf, route_norm) = sigmoid;
4920                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
4921                        // before the router — the rank streams overlap the gemv+topk.
4922                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
4923                        // from its own z copy (replicated deterministic router — identical
4924                        // bits in, identical sel/w out) and starts its sweep without
4925                        // waiting the root's sel broadcast.
4926                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4927                        let d1_router = *D1_ROUTER.get_or_init(|| {
4928                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
4929                        });
4930                        if d1_router {
4931                            let (sf_h, rn_h) = sigmoid;
4932                            let n_ex = m.gate_exps.n_expert;
4933                            let act_ct = m.active_count();
4934                            let _ = tp.runtime.nvfp4_routes_prestage_with(
4935                                bank,
4936                                e,
4937                                z,
4938                                |rank1, in1, sel1, w1| {
4939                                    let mut guard = DEV1_ROUTER_REPS
4940                                        .lock()
4941                                        .map_err(|_| "dev1 router replica lock")?;
4942                                    let (reps, scratch) =
4943                                        guard.get_or_insert_with(|| (Default::default(), None));
4944                                    if !reps.contains_key(&il) {
4945                                        use cudarc::driver::DevicePtr;
4946                                        let (g1, p1, a1) = (
4947                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
4948                                            rank1.htod(&vec![0.0f32; n_ex])?,
4949                                            rank1.alloc_u8_uninit(n_ex)?,
4950                                        );
4951                                        for (src, dst_len, dst) in [
4952                                            (
4953                                                {
4954                                                    let s = e.stream();
4955                                                    let (p, _g) =
4956                                                        m.gate_inp.float_data().device_ptr(&s);
4957                                                    p as u64
4958                                                },
4959                                                n_ex * n_embd * 4,
4960                                                {
4961                                                    let s = rank1.stream();
4962                                                    let (p, _g) = g1.device_ptr(&s);
4963                                                    p as u64
4964                                                },
4965                                            ),
4966                                            (
4967                                                {
4968                                                    let s = e.stream();
4969                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
4970                                                    p as u64
4971                                                },
4972                                                n_ex * 4,
4973                                                {
4974                                                    let s = rank1.stream();
4975                                                    let (p, _g) = p1.device_ptr(&s);
4976                                                    p as u64
4977                                                },
4978                                            ),
4979                                            (
4980                                                {
4981                                                    let s = e.stream();
4982                                                    let (p, _g) =
4983                                                        m.active_experts_dev.device_ptr(&s);
4984                                                    p as u64
4985                                                },
4986                                                n_ex,
4987                                                {
4988                                                    let s = rank1.stream();
4989                                                    let (p, _g) = a1.device_ptr(&s);
4990                                                    p as u64
4991                                                },
4992                                            ),
4993                                        ] {
4994                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
4995                                        }
4996                                        rank1.stream().synchronize()?;
4997                                        reps.insert(il, (g1, p1, a1));
4998                                    }
4999                                    if scratch.is_none() {
5000                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5001                                    }
5002                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5003                                    let logits1 = scratch.as_mut().expect("armed above");
5004                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5005                                    rank1.moe_router_sigmoid_topk_into(
5006                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5007                                        w1,
5008                                    )?;
5009                                    Ok(true)
5010                                },
5011                            )?;
5012                        } else {
5013                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5014                        }
5015                        // Persistent selection buffers: the allocating topk built two fresh
5016                        // slices per layer; sel/w land in process-static rows instead
5017                        // (host-op diet — same kernel, same bytes).
5018                        static SELW: std::sync::Mutex<
5019                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5020                        > = std::sync::Mutex::new(None);
5021                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5022                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5023                            *selw = Some((
5024                                e.ctx().ordinal(),
5025                                e.htod_i32(&vec![0i32; n_used])?,
5026                                e.htod(&vec![0.0f32; n_used])?,
5027                            ));
5028                        }
5029                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5030                        e.moe_router_sigmoid_topk_into(
5031                            &logits,
5032                            t,
5033                            n_expert,
5034                            n_used,
5035                            m.active_count(),
5036                            &m.exp_probs_b_dev,
5037                            &m.active_experts_dev,
5038                            sf,
5039                            route_norm,
5040                            sel_d,
5041                            w_d,
5042                        )?;
5043                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5044                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5045                        // PREJOIN hook so it executes while the peer rank drains its sweep
5046                        // (fills dev0's join wait); apply adds the identical values after.
5047                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5048                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5049                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5050                        });
5051                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5052                        // expert runs on rank1 — the idle device — same kernels, same
5053                        // split program, down row root-resident: bit-identical.
5054                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5055                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5056                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5057                        }) && tp.runtime.rank_engine(1).is_some();
5058                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5059                        // overlap ws + ones row and hand their RAW pointers to the routed
5060                        // run — the join add folds the shexp apply into one launch.
5061                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5062                        let tail3 = *TAIL3
5063                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5064                        let mut ov_issued = false;
5065                        let mut d1_issued = false;
5066                        let mut tail_folded = false;
5067                        let mut output = if shexp_d1 {
5068                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5069                            tp.runtime
5070                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5071                                    bank,
5072                                    e,
5073                                    z,
5074                                    &sel_d,
5075                                    &w_d,
5076                                    n_used,
5077                                    tp.activation_limit,
5078                                    || {
5079                                        d1_issued = Self::shexp_dev1_issue(
5080                                            e, rank1, m, z, cfg, il, n_embd,
5081                                        )?;
5082                                        Ok(())
5083                                    },
5084                                )?
5085                        } else if shexp_ov {
5086                            // Raw sh/ones pointers for the fused tail (persistent statics;
5087                            // pointers stable, no lock held across the routed call). The
5088                            // sh CONTENT is written by the prejoin-issued kernels earlier
5089                            // on e's stream — stream order covers the fused add.
5090                            let post_add = if tail3 {
5091                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5092                            } else {
5093                                None
5094                            };
5095                            let used_post = post_add.is_some();
5096                            let out = tp
5097                                .runtime
5098                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5099                                    bank,
5100                                    e,
5101                                    z,
5102                                    &sel_d,
5103                                    &w_d,
5104                                    n_used,
5105                                    tp.activation_limit,
5106                                    || {
5107                                        ov_issued =
5108                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5109                                        Ok(())
5110                                    },
5111                                    post_add,
5112                                )?;
5113                            // ov_issued false with post_add armed = an early-return arm
5114                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5115                            // fall through to the normal shexp add (battery v22 receipt:
5116                            // the strict error here failed every graph-door boot).
5117                            if used_post && ov_issued {
5118                                tail_folded = true; // apply folded into the join add
5119                            }
5120                            out
5121                        } else {
5122                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5123                                bank,
5124                                e,
5125                                z,
5126                                &sel_d,
5127                                &w_d,
5128                                n_used,
5129                                tp.activation_limit,
5130                            )?
5131                        };
5132                        if output.len() != t * n_embd {
5133                            return Err(format!(
5134                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5135                                output.len()
5136                            )
5137                            .into());
5138                        }
5139                        if tail_folded {
5140                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5141                        } else if d1_issued {
5142                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5143                        } else if ov_issued {
5144                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5145                        } else {
5146                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5147                        }
5148                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5149                            std::sync::atomic::AtomicU64::new(0);
5150                        let layer_bit = 1u64 << (il as u64 % 64);
5151                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5152                            & layer_bit
5153                            == 0
5154                        {
5155                            eprintln!(
5156                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5157                                 expert_transport={} native_p2p={} router=device \
5158                                 activation=host-canonical accumulation=host-canonical \
5159                                 output=e-device io=device performance_claim=false \
5160                                 (logged once per layer)",
5161                                tp.devices,
5162                                tp.runtime.transport_label(),
5163                                tp.runtime.native_p2p(),
5164                            );
5165                        }
5166                        return Ok(output);
5167                    }
5168                }
5169            }
5170            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5171            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5172            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5173            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5174            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5175            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5176            let route_started = route_timing.then(std::time::Instant::now);
5177            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5178                e,
5179                &logits,
5180                z,
5181                t,
5182                n_embd,
5183                n_expert,
5184                n_used,
5185                m.exp_probs_b.as_deref(),
5186                sigmoid,
5187                m.active_experts.as_deref(),
5188            )?;
5189            if let Some(started) = route_started {
5190                use std::sync::atomic::Ordering;
5191                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5192                    + started.elapsed().as_nanos() as u64;
5193                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5194                if calls % 430 == 0 {
5195                    eprintln!(
5196                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5197                        ns as f64 / 1.0e6,
5198                        ns as f64 / calls as f64 / 1.0e3,
5199                    );
5200                }
5201            }
5202            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5203            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5204            Self::trace_moe_input(e, il, t, n_embd, z)?;
5205            let selected = selected
5206                .iter()
5207                .map(|&expert| expert as usize)
5208                .collect::<Vec<_>>();
5209            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5210            // combined output comes back as an e-context row — no host round-trip, no host
5211            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5212            // both preserve f32 bits), gated by greedy token identity.
5213            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5214                if let Some(tp) = &m.step_tp {
5215                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5216                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5217                            bank,
5218                            e,
5219                            z,
5220                            &selected,
5221                            &route_weights,
5222                            n_used,
5223                            tp.activation_limit,
5224                        )?;
5225                        if output.len() != t * n_embd {
5226                            return Err(format!(
5227                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5228                                output.len()
5229                            )
5230                            .into());
5231                        }
5232                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5233                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5234                            std::sync::atomic::AtomicU64::new(0);
5235                        let layer_bit = 1u64 << (il as u64 % 64);
5236                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5237                            & layer_bit
5238                            == 0
5239                        {
5240                            eprintln!(
5241                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5242                                 expert_transport={} native_p2p={} activation=host-canonical \
5243                                 accumulation=host-canonical output=e-device io=device \
5244                                 performance_claim=false (logged once per layer)",
5245                                tp.devices,
5246                                tp.runtime.transport_label(),
5247                                tp.runtime.native_p2p(),
5248                            );
5249                        }
5250                        return Ok(output);
5251                    }
5252                }
5253            }
5254            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5255                (
5256                    match &tp.experts {
5257                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5258                            tp.runtime.run_tensor_parallel_routes(
5259                                bank,
5260                                &input,
5261                                t,
5262                                &selected,
5263                                &route_weights,
5264                                n_used,
5265                            )?
5266                        }
5267                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5268                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5269                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5270                                    bank,
5271                                    &input,
5272                                    &selected,
5273                                    &route_weights,
5274                                    n_used,
5275                                    tp.activation_limit,
5276                                )?
5277                            } else {
5278                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5279                                    bank,
5280                                    &input,
5281                                    t,
5282                                    &selected,
5283                                    &route_weights,
5284                                    n_used,
5285                                    tp.activation_limit,
5286                                )?
5287                            }
5288                        }
5289                    },
5290                    "tp",
5291                    &tp.devices,
5292                    tp.runtime.transport_label(),
5293                    tp.runtime.native_p2p(),
5294                )
5295            } else {
5296                let ep = m
5297                    .step_ep
5298                    .as_ref()
5299                    .ok_or("Step distributed runtime has no EP or TP state")?;
5300                (
5301                    match &ep.experts {
5302                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5303                            ep.runtime.run_routed_experts(
5304                                bank,
5305                                &input,
5306                                t,
5307                                &selected,
5308                                &route_weights,
5309                                n_used,
5310                                ep.activation_limit,
5311                            )?
5312                        }
5313                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5314                            ep.runtime.run_routed_experts_nvfp4(
5315                                bank,
5316                                &input,
5317                                t,
5318                                &selected,
5319                                &route_weights,
5320                                n_used,
5321                                ep.activation_limit,
5322                            )?
5323                        }
5324                    },
5325                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5326                    &ep.devices,
5327                    ep.runtime.transport_label(),
5328                    ep.runtime.native_p2p(),
5329                )
5330            };
5331            if routed.len() != t * n_embd {
5332                return Err(format!(
5333                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5334                    routed.len()
5335                )
5336                .into());
5337            }
5338            let mut output = e.htod(&routed)?;
5339            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5340            // Once per layer per process: the topology contract line is a boot receipt, not a
5341            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5342            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5343            let layer_bit = 1u64 << (il as u64 % 64);
5344            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5345                == 0
5346            {
5347                eprintln!(
5348                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5349                     expert_transport={transport} native_p2p={native_p2p} \
5350                     activation={} accumulation={} output={} \
5351                     performance_claim=false (logged once per layer)",
5352                    if let Some(ep) = &m.step_ep {
5353                        ep.runtime.expert_activation_label()
5354                    } else {
5355                        "host-canonical"
5356                    },
5357                    if let Some(ep) = &m.step_ep {
5358                        ep.runtime.expert_accumulation_label()
5359                    } else {
5360                        "host-canonical"
5361                    },
5362                    if let Some(ep) = &m.step_ep {
5363                        ep.runtime.expert_output_label()
5364                    } else {
5365                        "host-accumulated"
5366                    },
5367                );
5368                if let Some(ep) = &m.step_ep {
5369                    if let Some(limit) = ep.activation_limit {
5370                        eprintln!(
5371                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5372                             formula=min-silu-times-clamped-up performance_claim=false"
5373                        );
5374                    }
5375                }
5376            }
5377            return Ok(output);
5378        }
5379        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5380            let moe = cfg.moe.as_ref().unwrap();
5381            let n_expert = moe.expert_count as usize;
5382            let n_used = moe.expert_used_count as usize;
5383            let sigmoid = cfg.sigmoid_router().unwrap();
5384            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5385            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5386            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5387        }
5388        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5389        // current caller into this research arm; the naked default stays on the established path.
5390        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5391            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5392            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5393            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5394            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5395            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5396            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5397                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5398                let g_host = e.dtoh(&grouped_out)?;
5399                let s_host = e.dtoh(&seq_out)?;
5400                let g_bytes: &[u8] = unsafe {
5401                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5402                };
5403                let s_bytes: &[u8] = unsafe {
5404                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5405                };
5406                if g_bytes == s_bytes {
5407                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5408                } else {
5409                    let diffs = g_host
5410                        .iter()
5411                        .zip(s_host.iter())
5412                        .enumerate()
5413                        .filter(|(_, (a, b))| a != b)
5414                        .count();
5415                    let maxdiff = g_host
5416                        .iter()
5417                        .zip(s_host.iter())
5418                        .map(|(a, b)| (a - b).abs())
5419                        .fold(0.0f32, f32::max);
5420                    panic!(
5421                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5422                        g_host.len()
5423                    );
5424                }
5425            }
5426            return Ok(grouped_out);
5427        }
5428        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5429    }
5430
5431    fn sigmoid_resident_dev_eligible(
5432        e: &Engine,
5433        m: &MoeWeights,
5434        cfg: &ModelConfig,
5435        sliding_gated_moe: bool,
5436    ) -> bool {
5437        let Some(moe) = cfg.moe.as_ref() else {
5438            return false;
5439        };
5440        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5441        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5442        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5443        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5444            std::env::var("MEMRA_MOE_STATS").is_ok()
5445                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5446                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5447                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5448                || std::env::var("MEMRA_MOE_GATE").is_ok()
5449        });
5450        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5451            if dev.dev != e.ctx().ordinal() {
5452                return false;
5453            }
5454            let q8 = moe_q8_enabled()
5455                && q8_expert_supported(m.gate_exps.qtype)
5456                && q8_expert_supported(m.up_exps.qtype)
5457                && q8_expert_supported(m.down_exps.qtype);
5458            let fp8 = dev.fp8_blk.is_some()
5459                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5460                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5461                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5462            q8 || fp8
5463        });
5464        sliding_gated_moe
5465            && sigmoid_router_enabled()
5466            && moe_dev_enabled()
5467            && moe_slab_enabled()
5468            && !observation_mode
5469            && moe.expert_used_count <= 8
5470            && m.has_uniform_expert_layout()
5471            && m.gate_exps.macros.is_none()
5472            && m.up_exps.macros.is_none()
5473            && m.down_exps.macros.is_none()
5474            && !m.has_macros
5475            && resident_layout_supported
5476    }
5477
5478    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5479    pub(crate) fn moe_ffn_sequential(
5480        e: &Engine,
5481        m: &MoeWeights,
5482        z: &CudaSlice<f32>,
5483        t: usize,
5484        cfg: &ModelConfig,
5485        il: u16,
5486        max_block: usize,
5487    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5488        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5489    }
5490
5491    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5492    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5493    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5494    fn moe_router_logits(
5495        e: &Engine,
5496        m: &MoeWeights,
5497        z: &CudaSlice<f32>,
5498        t: usize,
5499        cfg: &ModelConfig,
5500    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5501        if t < PRIME_MIN_T {
5502            // Decode and speculative verify use one fixed per-row reduction program.
5503            if crate::router_kernel_on() {
5504                e.router_gemv(
5505                    m.gate_inp.float_data(),
5506                    z,
5507                    cfg.n_embd as usize,
5508                    m.gate_exps.n_expert,
5509                    t,
5510                )
5511            } else {
5512                e.matmul_decode_exact(&m.gate_inp, z, t)
5513            }
5514        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5515            e.router_gemv(
5516                m.gate_inp.float_data(),
5517                z,
5518                cfg.n_embd as usize,
5519                m.gate_exps.n_expert,
5520                t,
5521            )
5522        } else {
5523            e.matmul(&m.gate_inp, z, t)
5524        }
5525    }
5526
5527    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5528    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5529    /// trace is independent of the dispatch optimization selected for the forward.
5530    fn trace_moe_routes(
5531        il: u16,
5532        t: usize,
5533        sel_all: &[u32],
5534        weights: &[f32],
5535    ) -> Result<(), Box<dyn std::error::Error>> {
5536        use std::io::Write as _;
5537        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5538            let mut f = std::fs::OpenOptions::new()
5539                .create(true)
5540                .append(true)
5541                .open(path)?;
5542            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5543            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5544        }
5545        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5546            let mut f = std::fs::OpenOptions::new()
5547                .create(true)
5548                .append(true)
5549                .open(path)?;
5550            let pairs: Vec<String> = sel_all
5551                .iter()
5552                .zip(weights)
5553                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5554                .collect();
5555            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5556        }
5557        Ok(())
5558    }
5559
5560    #[allow(clippy::too_many_arguments)]
5561    fn trace_sigmoid_router_logits(
5562        e: &Engine,
5563        il: u16,
5564        t: usize,
5565        n_expert: usize,
5566        n_used: usize,
5567        logits: &CudaSlice<f32>,
5568        m: &MoeWeights,
5569        (scaling_factor, route_norm): (f32, bool),
5570    ) -> Result<(), Box<dyn std::error::Error>> {
5571        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5572            return Ok(());
5573        }
5574        let logits = e.dtoh(logits)?;
5575        let active: Vec<u8> = m
5576            .active_experts
5577            .as_ref()
5578            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5579            .unwrap_or_else(|| vec![1; n_expert]);
5580        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5581        crate::sigrouter_contract::capture_served_logits(
5582            il as u32,
5583            t,
5584            n_expert,
5585            n_used,
5586            scaling_factor,
5587            route_norm,
5588            &active,
5589            &bias,
5590            &logits,
5591        )?;
5592        Ok(())
5593    }
5594
5595    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
5596    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
5597    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
5598    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
5599    fn trace_moe_input(
5600        e: &Engine,
5601        il: u16,
5602        t: usize,
5603        n_embd: usize,
5604        z: &CudaSlice<f32>,
5605    ) -> Result<(), Box<dyn std::error::Error>> {
5606        use std::io::Write as _;
5607        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
5608            return Ok(());
5609        };
5610        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
5611        let host = e.dtoh_view(&z.slice(0..values))?;
5612        let bytes = unsafe {
5613            std::slice::from_raw_parts(
5614                host.as_ptr().cast::<u8>(),
5615                host.len() * std::mem::size_of::<f32>(),
5616            )
5617        };
5618        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
5619        let mut state = state
5620            .lock()
5621            .map_err(|_| "MoE input trace writer lock is poisoned")?;
5622        if state.is_none() {
5623            let dir = std::path::PathBuf::from(&dir);
5624            std::fs::create_dir_all(&dir)?;
5625            let index = std::fs::OpenOptions::new()
5626                .create(true)
5627                .append(true)
5628                .open(dir.join("index.jsonl"))?;
5629            *state = Some(MoeInputTraceWriter {
5630                dir,
5631                index,
5632                payloads: std::collections::HashMap::new(),
5633            });
5634        }
5635        let writer = state.as_mut().unwrap();
5636        if writer.dir != std::path::Path::new(&dir) {
5637            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
5638        }
5639        let file_name = format!("layer-{il:03}.f32");
5640        if !writer.payloads.contains_key(&il) {
5641            let payload = std::fs::OpenOptions::new()
5642                .create(true)
5643                .append(true)
5644                .open(writer.dir.join(&file_name))?;
5645            let offset = payload.metadata()?.len();
5646            writer.payloads.insert(il, (payload, offset));
5647        }
5648        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
5649        let row_offset = *offset;
5650        payload.write_all(bytes)?;
5651        *offset += bytes.len() as u64;
5652        writeln!(
5653            writer.index,
5654            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
5655             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
5656             \"payload_bytes\":{}}}",
5657            bytes.len()
5658        )?;
5659        Ok(())
5660    }
5661
5662    #[allow(clippy::too_many_arguments)]
5663    pub(crate) fn moe_ffn_sequential_zq8(
5664        e: &Engine,
5665        m: &MoeWeights,
5666        z: &CudaSlice<f32>,
5667        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5668        t: usize,
5669        cfg: &ModelConfig,
5670        il: u16,
5671        max_block: usize,
5672    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5673        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
5674        let moe = cfg.moe.as_ref().unwrap();
5675        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
5676        let n_expert = moe.expert_count as usize; // 256
5677        let n_used = moe.expert_used_count as usize; // 8
5678        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
5679
5680        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
5681        debug_assert_eq!(m.gate_exps.in_f, n_embd);
5682        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
5683        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
5684        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
5685        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
5686
5687        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
5688        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
5689        let lim_exp = cfg.clamp_exp_at(il as u32);
5690        let lim_shexp = cfg.clamp_shexp_at(il as u32);
5691        let use_cache = Engine::moe_cache_enabled();
5692        let uniform_experts = m.has_uniform_expert_layout();
5693        let moe_q8 = uniform_experts
5694            && moe_q8_enabled()
5695            && q8_expert_supported(m.gate_exps.qtype)
5696            && q8_expert_supported(m.up_exps.qtype)
5697            && q8_expert_supported(m.down_exps.qtype);
5698        // Experimental secondary backend: complete experts already resident in the SLRU stay on
5699        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
5700        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
5701        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
5702        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
5703        // commands and CI have no llama.cpp or OpenMP dependency.
5704        let cpu_expert_requested = crate::cpu_experts::configured();
5705        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
5706            return Err(std::io::Error::other(
5707                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
5708            )
5709            .into());
5710        }
5711        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
5712        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
5713        // Those backends are each deterministic but are different numeric configurations, so a
5714        // later prefill eviction can change greedy output. Freeze after the first real prefill;
5715        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
5716        // staging below and cannot change backend assignment.
5717        let freeze_cpu_residency = cpu_expert_requested
5718            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
5719        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
5720            .ok()
5721            .and_then(|value| value.parse::<usize>().ok())
5722            .is_some_and(|tokens| tokens > 0);
5723        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
5724            e.freeze_moe_cache();
5725        }
5726        let cache_frozen = use_cache && e.moe_cache_frozen();
5727        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
5728
5729        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
5730        // cannot change logits, selected expert ids, or routing weights.
5731        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5732        if let Some(sig) = cfg.sigmoid_router() {
5733            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
5734        }
5735
5736        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
5737        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
5738        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
5739        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
5740        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
5741        // per-token host stall that dominated the 35B decode wall after stages 1+2.
5742        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
5743        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
5744        // only difference is where sel/w/pointers are READ from (device instead of params).
5745        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
5746        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
5747        // Any non-resident layer falls through to host routing + the gdec/sequential path.
5748        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
5749        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
5750        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
5751        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
5752        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
5753        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
5754        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
5755        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
5756        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
5757        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
5758        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
5759        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
5760        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
5761        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
5762        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
5763        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
5764        // now rides the dev loop below (same kernels per token as decode); pairs serves real
5765        // prefill (t >= 16, where spec never verifies).
5766        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
5767        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
5768        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
5769        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
5770        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
5771        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
5772        // ride the macro-aware sequential/staged paths below or every expert output is off by
5773        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
5774        let no_exp_macros = m.gate_exps.macros.is_none()
5775            && m.up_exps.macros.is_none()
5776            && m.down_exps.macros.is_none();
5777        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
5778        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
5779        // so it cannot even see the per-layer limit.
5780        if cfg.sigmoid_router().is_none()
5781            && cfg.m3.is_none()
5782            && cfg.hy3.is_none()
5783            && !cfg.swiglu_clamped_at(il as u32)
5784            && no_exp_macros
5785            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
5786            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
5787            // pairs serves real prefill from 17 up.
5788            && t > MOE_DEV_MAX_T
5789            && m.dev_exps.is_some()
5790            && moe_q8_enabled()
5791            && q8_expert_supported(m.gate_exps.qtype)
5792            && q8_expert_supported(m.up_exps.qtype)
5793            && q8_expert_supported(m.down_exps.qtype)
5794            && std::env::var("MEMRA_MOE_PAIRS")
5795                .map(|v| v != "0")
5796                .unwrap_or(true)
5797            && std::env::var("MEMRA_MOE_STATS").is_err()
5798        {
5799            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
5800        }
5801
5802        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
5803        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
5804        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
5805        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
5806        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
5807        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
5808        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
5809        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
5810        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
5811        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
5812        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
5813        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
5814        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
5815        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
5816        // Keyed off sigmoid_router() so arch #4 is denied by construction.
5817        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
5818        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
5819        let dev_ok = uniform_experts
5820            && cfg.sigmoid_router().is_none()
5821            && cfg.m3.is_none()
5822            && cfg.hy3.is_none()
5823            && !cfg.swiglu_clamped_at(il as u32);
5824        // Observation modes must route through the host-visible selection below. Otherwise a fully
5825        // resident layer returns through device dispatch before its trace/stats row is recorded,
5826        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
5827        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
5828            || std::env::var("MEMRA_MOE_TRACE").is_ok()
5829            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5830            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
5831        if dev_ok
5832            && t <= MOE_DEV_MAX_T
5833            && m.dev_exps.is_some()
5834            && n_used <= 8
5835            && moe_dev_enabled()
5836            && !observe_routes
5837        {
5838            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5839        }
5840        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
5841            let row_ok = e.with_moe_cache(max_block, |c, eng| {
5842                if moe_prewarm_enabled() {
5843                    c.prewarm_layer(il, m, eng)?;
5844                }
5845                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
5846            })?;
5847            if row_ok {
5848                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5849            }
5850        }
5851
5852        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
5853        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
5854            if cpu_hybrid {
5855                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
5856                    e,
5857                    &logits,
5858                    z,
5859                    t,
5860                    n_embd,
5861                    n_expert,
5862                    n_used,
5863                    m.exp_probs_b.as_deref(),
5864                    sig,
5865                    m.active_experts.as_deref(),
5866                )?;
5867                (sel, w, Some(input))
5868            } else {
5869                let (sel, w) =
5870                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
5871                (sel, w, None)
5872            }
5873        } else {
5874            let (sel, w) =
5875                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
5876            (sel, w, None)
5877        };
5878        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
5879
5880        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
5881        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
5882        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
5883        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
5884        Self::trace_moe_input(e, il, t, n_embd, z)?;
5885
5886        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
5887        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
5888        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
5889        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
5890        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
5891        // wait for each pending block, so later copies can overlap the earlier expert kernels while
5892        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
5893        // T=1; batched forwards can have token-local consumers still in flight between selections.
5894        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
5895        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
5896        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
5897        let worker_disk_prefetch =
5898            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
5899        let promote_worker_h2d =
5900            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
5901        if promote_worker_h2d {
5902            let mut selected_blocks = Vec::with_capacity(n_used * 3);
5903            for &ex in sel_all.iter().take(n_used) {
5904                let ex = ex as u16;
5905                selected_blocks.extend([
5906                    BlockId::new(il, PROJ_GATE, ex),
5907                    BlockId::new(il, PROJ_UP, ex),
5908                    BlockId::new(il, PROJ_DOWN, ex),
5909                ]);
5910            }
5911            for &ex in sel_all.iter().take(n_used) {
5912                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
5913            }
5914            e.with_moe_cache(max_block, |cache, eng| {
5915                cache.promote_worker_reads_at_safe_boundary(
5916                    &selected_blocks,
5917                    &selected_blocks,
5918                    eng,
5919                )?;
5920                Ok(())
5921            })?;
5922        }
5923
5924        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
5925        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
5926        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
5927            let mut cnt = vec![0u32; n_expert];
5928            for &s in sel_all.iter() {
5929                cnt[s as usize] += 1;
5930            }
5931            let total = sel_all.len() as f64;
5932            let mut h = 0.0f64;
5933            let mut active = 0usize;
5934            for &c in &cnt {
5935                if c > 0 {
5936                    active += 1;
5937                    let p = c as f64 / total;
5938                    h -= p * p.log2();
5939                }
5940            }
5941            let maxc = cnt.iter().copied().max().unwrap_or(0);
5942            println!(
5943                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
5944                il,
5945                t,
5946                sel_all.len(),
5947                active,
5948                n_expert,
5949                h,
5950                (n_expert as f64).log2(),
5951                total / active.max(1) as f64,
5952                maxc
5953            );
5954        }
5955
5956        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
5957        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
5958        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
5959        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
5960        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
5961        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
5962        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
5963        // zeroed-then-accumulated exactly as before (fallback).
5964        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
5965        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
5966        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
5967        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
5968        let gdec_may_fire = uniform_experts
5969            && use_cache
5970            && n_used <= 8
5971            && gdec_enabled()
5972            && !cfg.swiglu_clamped_at(il as u32);
5973        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
5974        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
5975        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
5976        // archs the slabs were uploaded but never read, and every expert went through the
5977        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
5978        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
5979        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
5980        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
5981        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
5982        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
5983        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
5984        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
5985        // strictly worse than staging); under PP-2 without the prime walker this admits
5986        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
5987        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
5988        let slab_local = m
5989            .dev_exps
5990            .as_ref()
5991            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
5992        let slab_bases = slab_local.map(|d| {
5993            use cudarc::driver::DevicePtr;
5994            let s = e.stream();
5995            let (pg, _g0) = d.gate.device_ptr(&s);
5996            let (pu, _g1) = d.up.device_ptr(&s);
5997            let (pd, _g2) = d.down.device_ptr(&s);
5998            (pg as u64, pu as u64, pd as u64)
5999        });
6000        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6001        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6002        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6003        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6004        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6005        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6006        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6007        // all-resident tokens, staged loop for misses), which is a dispatch-class
6008        // comparison, not a provenance one.
6009        let slab_fused_may_fire = slab_bases.is_some()
6010            && n_used <= 8
6011            && gdec_enabled()
6012            && !cfg.swiglu_clamped_at(il as u32)
6013            && cfg.m3.is_none()
6014            && no_exp_macros
6015            && moe_q8;
6016        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6017        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6018        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6019            e.uninit(t * n_embd)?
6020        } else {
6021            e.zeros(t * n_embd)?
6022        };
6023        // The router readback above already established a host boundary. Copy each small-t hidden
6024        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6025        let cpu_input = if cpu_hybrid {
6026            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6027        } else {
6028            None
6029        };
6030
6031        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6032        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6033        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6034        // measured ~123 memsets/token of the decode wall).
6035        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6036        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6037        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6038        let mut scratch_g: Option<CudaSlice<u8>> = None;
6039        let mut scratch_u: Option<CudaSlice<u8>> = None;
6040        let mut scratch_d: Option<CudaSlice<u8>> = None;
6041        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6042        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6043
6044        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6045        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6046        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6047        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6048        let page_window = moe_page_prefetch_window();
6049
6050        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6051        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6052        for tok in 0..t {
6053            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6054            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6055            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6056            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6057
6058            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6059            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6060            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6061            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6062            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6063            // miss falls through to the sequential loop below, which admits as before. In steady
6064            // state on a fully-resident rig every token-layer takes the grouped path.
6065            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6066            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6067            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6068            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6069            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6070            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6071            let no_macros = m.gate_exps.macros.is_none()
6072                && m.up_exps.macros.is_none()
6073                && m.down_exps.macros.is_none();
6074            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6075            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6076            // with pointers computed from the resident slab base + ex*stride instead of
6077            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6078            // slab holds every expert by construction, so this arm never falls through
6079            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6080            // staging both die). Bit-identity class: pointer provenance only, the same
6081            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6082            // slab exists it is strictly better (no lock, no miss).
6083            if slab_fused_may_fire {
6084                let (pg, pu, pd) = slab_bases.unwrap();
6085                let mut gp = [0u64; 8];
6086                let mut up = [0u64; 8];
6087                let mut dp = [0u64; 8];
6088                for (j, &ex) in sel.iter().enumerate() {
6089                    let ex = ex as usize;
6090                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6091                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6092                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6093                }
6094                let mut wv = [0f32; 8];
6095                wv[..n_used].copy_from_slice(w);
6096                if tok_q8.is_none() {
6097                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6098                }
6099                let (zq, zd) = tok_q8.as_ref().unwrap();
6100                let act = e.moe_gate_up_silu8_q8(
6101                    crate::WPtr8(gp),
6102                    crate::WPtr8(up),
6103                    zq,
6104                    zd,
6105                    n_embd,
6106                    n_ff_exp,
6107                    n_used,
6108                    m.gate_exps.qtype,
6109                    m.up_exps.qtype,
6110                    m.gate_exps.row_bytes,
6111                    m.up_exps.row_bytes,
6112                )?;
6113                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6114                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6115                e.moe_down8_fma_q8(
6116                    crate::WPtr8(dp),
6117                    crate::F32x8(wv),
6118                    &aq2,
6119                    &ad2,
6120                    &mut dst,
6121                    n_ff_exp,
6122                    n_embd,
6123                    n_used,
6124                    m.down_exps.qtype,
6125                    m.down_exps.row_bytes,
6126                )?;
6127                continue;
6128            }
6129            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6130                if tok_q8.is_none() {
6131                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6132                }
6133                let (zq, zd) = tok_q8.as_ref().unwrap();
6134                if Self::moe_gdec_token_q8(
6135                    e,
6136                    m,
6137                    il,
6138                    max_block,
6139                    zq,
6140                    zd,
6141                    sel,
6142                    w,
6143                    &mut moe_out,
6144                    tok,
6145                    n_embd,
6146                    n_ff_exp,
6147                    n_used,
6148                )? {
6149                    continue;
6150                }
6151            } else if gdec_may_fire
6152                && cfg.m3.is_none()
6153                && no_macros
6154                && Self::moe_gdec_token(
6155                    e,
6156                    m,
6157                    il,
6158                    max_block,
6159                    &zt,
6160                    sel,
6161                    w,
6162                    &mut moe_out,
6163                    tok,
6164                    n_embd,
6165                    n_ff_exp,
6166                    n_used,
6167                )?
6168            {
6169                continue;
6170            }
6171
6172            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6173            // slab pair could fire. This token fell through to a sequential axpy loop, which
6174            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6175            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6176            // has no fallible predicate), included for the allocation invariant's symmetry.
6177            if gdec_may_fire || slab_fused_may_fire {
6178                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6179                e.memset_zeros_view(&mut row)?;
6180            }
6181
6182            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6183            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6184            // stall this path exists to remove, while mixing projections would require another
6185            // activation round-trip. Weight addresses remain valid until this worker is joined at
6186            // the bottom of the token scope.
6187            let mut cpu_mask = vec![false; sel.len()];
6188            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6189                let gpu_resident = if use_cache {
6190                    e.with_moe_cache(max_block, |cache, _| {
6191                        Ok(sel
6192                            .iter()
6193                            .map(|&expert| {
6194                                let expert = expert as u16;
6195                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6196                                    .into_iter()
6197                                    .filter(|&projection| {
6198                                        cache
6199                                            .resident(BlockId::new(il, projection, expert))
6200                                            .is_some()
6201                                    })
6202                                    .count()
6203                            })
6204                            .collect::<Vec<_>>())
6205                    })?
6206                } else {
6207                    vec![0; sel.len()]
6208                };
6209                let mut cpu_selected = Vec::new();
6210                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6211                    if gpu_resident[index] != 3 {
6212                        cpu_mask[index] = true;
6213                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6214                        let expert = expert as usize;
6215                        cpu_selected.push((expert, route_weight));
6216                    }
6217                }
6218                if crate::cpu_experts::predictor_enabled() {
6219                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6220                    // from this layer's MoE input and prefetches predicted-and-missing
6221                    // experts into the companion RAM cache. Never blocks this thread.
6222                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6223                    crate::cpu_experts::predictor_submit(il, row);
6224                }
6225                if cpu_selected.is_empty() {
6226                    None
6227                } else {
6228                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6229                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6230                        .map_err(std::io::Error::other)?;
6231                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6232                }
6233            } else {
6234                None
6235            };
6236
6237            let worker_window = worker_disk_prefetch
6238                .then(worker_prefetch_window)
6239                .unwrap_or(0);
6240            for (j, &ex) in sel.iter().enumerate() {
6241                if cpu_mask[j] {
6242                    continue;
6243                }
6244                let ex = ex as usize;
6245                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6246                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6247                // fused form) and macro-carrying artifacts — still have their bytes in the
6248                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6249                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6250                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6251                if let Some(d) = slab_local {
6252                    let gl = m.gate_exps.expert_layout(ex);
6253                    let ul = m.up_exps.expert_layout(ex);
6254                    let dl = m.down_exps.expert_layout(ex);
6255                    let (g0, u0, d0) = (
6256                        ex * m.gate_exps.expert_stride,
6257                        ex * m.up_exps.expert_stride,
6258                        ex * m.down_exps.expert_stride,
6259                    );
6260                    let (gate, up) = if moe_q8 {
6261                        if tok_q8.is_none() {
6262                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6263                        }
6264                        let (zq, zd) = tok_q8.as_ref().unwrap();
6265                        (
6266                            e.qmatvec_expert_q8(
6267                                &d.gate,
6268                                g0..g0 + gl.len,
6269                                zq,
6270                                zd,
6271                                1,
6272                                m.gate_exps.in_f,
6273                                m.gate_exps.out_f,
6274                                gl.qtype,
6275                                gl.row_bytes,
6276                            )?,
6277                            e.qmatvec_expert_q8(
6278                                &d.up,
6279                                u0..u0 + ul.len,
6280                                zq,
6281                                zd,
6282                                1,
6283                                m.up_exps.in_f,
6284                                m.up_exps.out_f,
6285                                ul.qtype,
6286                                ul.row_bytes,
6287                            )?,
6288                        )
6289                    } else {
6290                        (
6291                            e.qmatvec_view(
6292                                &d.gate,
6293                                g0..g0 + gl.len,
6294                                &zt,
6295                                1,
6296                                m.gate_exps.in_f,
6297                                m.gate_exps.out_f,
6298                                gl.qtype,
6299                                gl.row_bytes,
6300                            )?,
6301                            e.qmatvec_view(
6302                                &d.up,
6303                                u0..u0 + ul.len,
6304                                &zt,
6305                                1,
6306                                m.up_exps.in_f,
6307                                m.up_exps.out_f,
6308                                ul.qtype,
6309                                ul.row_bytes,
6310                            )?,
6311                        )
6312                    };
6313                    let mut act = e.uninit(n_ff_exp)?;
6314                    Self::ffn_act_lim(
6315                        e,
6316                        cfg,
6317                        &gate,
6318                        &up,
6319                        m.gate_exps.macro_scale(ex),
6320                        m.up_exps.macro_scale(ex),
6321                        lim_exp,
6322                        &mut act,
6323                        n_ff_exp,
6324                    )?;
6325                    let y = if moe_q8 {
6326                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6327                        e.qmatvec_expert_q8(
6328                            &d.down,
6329                            d0..d0 + dl.len,
6330                            &aq2,
6331                            &ad2,
6332                            1,
6333                            m.down_exps.in_f,
6334                            m.down_exps.out_f,
6335                            dl.qtype,
6336                            dl.row_bytes,
6337                        )?
6338                    } else {
6339                        let actv = act.slice(0..n_ff_exp);
6340                        e.qmatvec_view(
6341                            &d.down,
6342                            d0..d0 + dl.len,
6343                            &actv,
6344                            1,
6345                            m.down_exps.in_f,
6346                            m.down_exps.out_f,
6347                            dl.qtype,
6348                            dl.row_bytes,
6349                        )?
6350                    };
6351                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6352                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6353                    continue;
6354                }
6355                for next in page_prefetch_positions(j, sel.len(), page_window) {
6356                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6357                }
6358                let keep = [
6359                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6360                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6361                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6362                ];
6363                if worker_disk_prefetch && worker_window > 0 {
6364                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6365                        Self::moe_prefetch_disk_expert(
6366                            e,
6367                            il,
6368                            sel[next] as usize,
6369                            m,
6370                            max_block,
6371                            &keep,
6372                        )?;
6373                    }
6374                } else if cache_dispatch
6375                    && !cpu_hybrid
6376                    && moe_prefetch_enabled()
6377                    && j + 1 < sel.len()
6378                {
6379                    let next = sel[j + 1] as usize;
6380                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6381                }
6382                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6383                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6384                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6385                    // layouts stay on the metadata-aware f32 path.
6386                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6387                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6388                    }
6389                    let gate = if gate_q8 {
6390                        let (zq, zd) = tok_q8.as_ref().unwrap();
6391                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6392                    } else {
6393                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6394                    };
6395                    let up = if up_q8 {
6396                        let (zq, zd) = tok_q8.as_ref().unwrap();
6397                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6398                    } else {
6399                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6400                    };
6401                    let mut act = e.uninit(n_ff_exp)?;
6402                    Self::ffn_act_lim(
6403                        e,
6404                        cfg,
6405                        &gate,
6406                        &up,
6407                        m.gate_exps.macro_scale(ex),
6408                        m.up_exps.macro_scale(ex),
6409                        lim_exp,
6410                        &mut act,
6411                        n_ff_exp,
6412                    )?;
6413                    let y = if down_q8 {
6414                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6415                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6416                    } else {
6417                        let actv = act.slice(0..n_ff_exp);
6418                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6419                    };
6420                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6421                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6422                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6423                } else if cache_dispatch {
6424                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6425                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6426                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6427                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6428                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6429                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6430                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6431                    Self::ffn_act_lim(
6432                        e,
6433                        cfg,
6434                        &gate,
6435                        &up,
6436                        m.gate_exps.macro_scale(ex),
6437                        m.up_exps.macro_scale(ex),
6438                        lim_exp,
6439                        &mut act,
6440                        n_ff_exp,
6441                    )?;
6442                    let actv = act.slice(0..n_ff_exp);
6443                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6444                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6445                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6446                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6447                } else if cache_frozen {
6448                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6449                    // first prime. Reuse every fixed resident projection directly and stage only a
6450                    // true miss through the ordinary scratch slot. This preserves the established
6451                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6452                    let gate = Self::moe_frozen_gemm(
6453                        e,
6454                        il,
6455                        PROJ_GATE,
6456                        ex,
6457                        m,
6458                        max_block,
6459                        &zt,
6460                        &mut scratch_g,
6461                        g_len,
6462                    )?;
6463                    let up = Self::moe_frozen_gemm(
6464                        e,
6465                        il,
6466                        PROJ_UP,
6467                        ex,
6468                        m,
6469                        max_block,
6470                        &zt,
6471                        &mut scratch_u,
6472                        u_len,
6473                    )?;
6474                    let mut act = e.uninit(n_ff_exp)?;
6475                    Self::ffn_act_lim(
6476                        e,
6477                        cfg,
6478                        &gate,
6479                        &up,
6480                        m.gate_exps.macro_scale(ex),
6481                        m.up_exps.macro_scale(ex),
6482                        lim_exp,
6483                        &mut act,
6484                        n_ff_exp,
6485                    )?;
6486                    let actv = act.slice(0..n_ff_exp);
6487                    let y = Self::moe_frozen_gemm(
6488                        e,
6489                        il,
6490                        PROJ_DOWN,
6491                        ex,
6492                        m,
6493                        max_block,
6494                        &actv,
6495                        &mut scratch_d,
6496                        d_len,
6497                    )?;
6498                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6499                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6500                } else {
6501                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6502                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6503                    // fully overwrites the byte range the GEMM reads).
6504                    if scratch_g.is_none() {
6505                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6506                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6507                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6508                    }
6509                    let (sg, su, sd) = (
6510                        scratch_g.as_mut().unwrap(),
6511                        scratch_u.as_mut().unwrap(),
6512                        scratch_d.as_mut().unwrap(),
6513                    );
6514                    let gl = m.gate_exps.expert_layout(ex);
6515                    let ul = m.up_exps.expert_layout(ex);
6516                    let dl = m.down_exps.expert_layout(ex);
6517                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6518                    let gate = e.qmatvec_view(
6519                        sg,
6520                        0..gl.len,
6521                        &zt,
6522                        1,
6523                        m.gate_exps.in_f,
6524                        m.gate_exps.out_f,
6525                        gl.qtype,
6526                        gl.row_bytes,
6527                    )?;
6528
6529                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6530                    let up = e.qmatvec_view(
6531                        su,
6532                        0..ul.len,
6533                        &zt,
6534                        1,
6535                        m.up_exps.in_f,
6536                        m.up_exps.out_f,
6537                        ul.qtype,
6538                        ul.row_bytes,
6539                    )?;
6540
6541                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6542                    Self::ffn_act_lim(
6543                        e,
6544                        cfg,
6545                        &gate,
6546                        &up,
6547                        m.gate_exps.macro_scale(ex),
6548                        m.up_exps.macro_scale(ex),
6549                        lim_exp,
6550                        &mut act,
6551                        n_ff_exp,
6552                    )?;
6553
6554                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6555                    let actv = act.slice(0..n_ff_exp);
6556                    let y = e.qmatvec_view(
6557                        sd,
6558                        0..dl.len,
6559                        &actv,
6560                        1,
6561                        m.down_exps.in_f,
6562                        m.down_exps.out_f,
6563                        dl.qtype,
6564                        dl.row_bytes,
6565                    )?;
6566
6567                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6568                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6569                }
6570            }
6571            if let Some(worker) = cpu_worker {
6572                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6573                let cpu_output = e.htod(&cpu_output)?;
6574                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6575                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6576            }
6577            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6578                for (j, &ex) in sel.iter().enumerate() {
6579                    if cpu_mask[j] {
6580                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6581                    }
6582                }
6583            }
6584        }
6585
6586        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6587        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6588        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6589        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6590        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6591            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
6592        {
6593            let n_ff_sh = gate_shexp.out_features(); // 512
6594            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
6595            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
6596            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
6597            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
6598            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
6599            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
6600            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
6601            let verify_t = t > 1 && t < PRIME_MIN_T;
6602            let (sg_gate, sg_up) = if t == 1 {
6603                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
6604            } else if verify_t {
6605                (
6606                    e.matmul_decode_exact(gate_shexp, z, t)?,
6607                    e.matmul_decode_exact(up_shexp, z, t)?,
6608                )
6609            } else {
6610                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
6611            };
6612            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
6613            Self::ffn_act_lim(
6614                e,
6615                cfg,
6616                &sg_gate,
6617                &sg_up,
6618                1.0,
6619                1.0,
6620                lim_shexp,
6621                &mut sa,
6622                t * n_ff_sh,
6623            )?;
6624            let sh = if verify_t {
6625                e.matmul_decode_exact(down_shexp, &sa, t)?
6626            } else {
6627                e.matmul(down_shexp, &sa, t)?
6628            }; // [T, n_embd]
6629
6630            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
6631            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
6632            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
6633            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
6634            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
6635            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
6636            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
6637            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
6638            // expert's contribution into every token's residual, so under cross-request
6639            // concat prefill a session's hidden state depended on its co-arrivals' token
6640            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
6641            let g = match &m.gate_inp_shexp {
6642                Some(gate_inp_shexp) => {
6643                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
6644                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
6645                    } else {
6646                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
6647                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
6648                        e.sigmoid(&gs, &mut g, t)?;
6649                        g
6650                    }
6651                }
6652                None => e.htod(&vec![1.0f32; t])?,
6653            };
6654            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
6655            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
6656        }
6657
6658        Ok(moe_out)
6659    }
6660
6661    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
6662    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
6663    pub fn stage1_h2d_per_token(&self) -> u64 {
6664        use crate::hybrid::Ffn;
6665        let n_used = self
6666            .cfg
6667            .moe
6668            .as_ref()
6669            .map(|m| m.expert_used_count as u64)
6670            .unwrap_or(0);
6671        let mut bytes = 0u64;
6672        for l in self.layers.iter() {
6673            if let Ffn::Moe(m) = &l.ffn {
6674                bytes += n_used
6675                    * (m.gate_exps.max_expert_bytes()
6676                        + m.up_exps.max_expert_bytes()
6677                        + m.down_exps.max_expert_bytes()) as u64;
6678            }
6679        }
6680        bytes
6681    }
6682
6683    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
6684    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
6685    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
6686    pub(crate) fn max_moe_block(&self) -> usize {
6687        use crate::hybrid::Ffn;
6688        let mut mx = 0usize;
6689        let mut scan = |ffn: &Ffn| {
6690            if let Ffn::Moe(m) = ffn {
6691                mx = mx
6692                    .max(m.gate_exps.max_expert_bytes())
6693                    .max(m.up_exps.max_expert_bytes())
6694                    .max(m.down_exps.max_expert_bytes());
6695            }
6696        };
6697        for l in self.layers.iter() {
6698            scan(&l.ffn);
6699        }
6700        if let Some(mtp) = self.mtp.as_ref() {
6701            scan(&mtp.ffn);
6702        }
6703        mx
6704    }
6705
6706    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
6707    /// but have no bytes and therefore consume no residency slot.
6708    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
6709        use crate::hybrid::Ffn;
6710        let mut sizes = Vec::new();
6711        let mut scan = |ffn: &Ffn| {
6712            let Ffn::Moe(m) = ffn else { return };
6713            for ex in 0..m.gate_exps.n_expert {
6714                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
6715                    continue;
6716                }
6717                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
6718                    let len = exps.expert_layout(ex).len;
6719                    if len > 0 {
6720                        sizes.push(len);
6721                    }
6722                }
6723            }
6724        };
6725        for layer in &self.layers {
6726            scan(&layer.ffn);
6727        }
6728        if let Some(mtp) = &self.mtp {
6729            scan(&mtp.ffn);
6730        }
6731        sizes
6732    }
6733
6734    /// Persist the frozen residency set so a later process can restage it directly and skip
6735    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
6736    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
6737    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
6738    /// post-freeze argmax gate still validates the serving assignment.
6739    pub fn save_cpu_expert_residency_profile(
6740        &self,
6741        e: &Engine,
6742        path: &std::path::Path,
6743    ) -> Result<(), Box<dyn std::error::Error>> {
6744        let Some(ids) = e.export_moe_residency() else {
6745            return Err("no MoE residency cache to persist".into());
6746        };
6747        let mut body = format!(
6748            "memra-freeze-profile v1 max_block={} blocks={}\n",
6749            self.max_moe_block(),
6750            ids.len()
6751        );
6752        for (layer, proj, ex) in &ids {
6753            body.push_str(&format!("{layer} {proj} {ex}\n"));
6754        }
6755        let tmp = path.with_extension("tmp");
6756        std::fs::write(&tmp, body)?;
6757        std::fs::rename(&tmp, path)?;
6758        println!(
6759            "[moe-cache] freeze profile saved: {} blocks -> {}",
6760            ids.len(),
6761            path.display()
6762        );
6763        Ok(())
6764    }
6765
6766    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
6767    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
6768    /// missing or its header does not match this model's slot geometry.
6769    pub fn restore_cpu_expert_residency_profile(
6770        &self,
6771        e: &Engine,
6772        path: &std::path::Path,
6773    ) -> Result<bool, Box<dyn std::error::Error>> {
6774        use crate::hybrid::Ffn;
6775        use crate::moe_cache::BlockId;
6776        let Ok(content) = std::fs::read_to_string(path) else {
6777            return Ok(false);
6778        };
6779        let mut lines = content.lines();
6780        let Some(header) = lines.next() else {
6781            return Ok(false);
6782        };
6783        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
6784        if !header.starts_with(&expected) {
6785            println!(
6786                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
6787                path.display()
6788            );
6789            return Ok(false);
6790        }
6791        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
6792            std::collections::HashMap::new();
6793        for line in lines {
6794            let mut fields = line.split_whitespace();
6795            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
6796            else {
6797                continue;
6798            };
6799            let (Ok(layer), Ok(proj), Ok(ex)) =
6800                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
6801            else {
6802                continue;
6803            };
6804            by_layer
6805                .entry(layer)
6806                .or_default()
6807                .push(BlockId::new(layer, proj, ex));
6808        }
6809        let requested: usize = by_layer.values().map(Vec::len).sum();
6810        if requested == 0 {
6811            return Ok(false);
6812        }
6813        let max_block = self.max_moe_block();
6814        let mut restaged = 0usize;
6815        let mut stage_layer =
6816            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
6817                let Ffn::Moe(m) = ffn else { return Ok(()) };
6818                let Some(ids) = by_layer.get(&layer_index) else {
6819                    return Ok(());
6820                };
6821                e.with_moe_cache(max_block, |cache, eng| {
6822                    for id in ids {
6823                        if cache.restage_block(*id, m, eng)? {
6824                            restaged += 1;
6825                        }
6826                    }
6827                    Ok(())
6828                })
6829            };
6830        for (index, layer) in self.layers.iter().enumerate() {
6831            stage_layer(index as u16, &layer.ffn)?;
6832        }
6833        if let Some(mtp) = self.mtp.as_ref() {
6834            stage_layer(u16::MAX, &mtp.ffn)?;
6835        }
6836        e.freeze_moe_cache();
6837        println!(
6838            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
6839            path.display()
6840        );
6841        Ok(true)
6842    }
6843
6844    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
6845    pub fn freeze_cpu_expert_residency(
6846        &self,
6847        e: &Engine,
6848    ) -> Result<(), Box<dyn std::error::Error>> {
6849        e.freeze_moe_cache();
6850        Ok(())
6851    }
6852
6853    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
6854    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
6855    /// the model's activation exactly.
6856    ///
6857    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
6858    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
6859    /// form for anything that can land on a clamped layer.
6860    pub fn ffn_act(
6861        e: &Engine,
6862        cfg: &ModelConfig,
6863        gate: &CudaSlice<f32>,
6864        up: &CudaSlice<f32>,
6865        act: &mut CudaSlice<f32>,
6866        n: usize,
6867    ) -> Result<(), Box<dyn std::error::Error>> {
6868        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
6869    }
6870
6871    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
6872    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
6873    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
6874    #[allow(clippy::too_many_arguments)]
6875    pub(crate) fn ffn_act_scaled(
6876        e: &Engine,
6877        cfg: &ModelConfig,
6878        gate: &CudaSlice<f32>,
6879        up: &CudaSlice<f32>,
6880        gs: f32,
6881        us: f32,
6882        act: &mut CudaSlice<f32>,
6883        n: usize,
6884    ) -> Result<(), Box<dyn std::error::Error>> {
6885        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
6886    }
6887
6888    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
6889    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
6890    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
6891    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
6892    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
6893    ///                 arrays are SEPARATE and a layer can have one without the other.
6894    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
6895    /// already known live.
6896    #[allow(clippy::too_many_arguments)]
6897    pub(crate) fn ffn_act_lim(
6898        e: &Engine,
6899        cfg: &ModelConfig,
6900        gate: &CudaSlice<f32>,
6901        up: &CudaSlice<f32>,
6902        gs: f32,
6903        us: f32,
6904        limit: Option<f32>,
6905        act: &mut CudaSlice<f32>,
6906        n: usize,
6907    ) -> Result<(), Box<dyn std::error::Error>> {
6908        if let Some(m3) = cfg.m3.as_ref() {
6909            debug_assert!(
6910                limit.is_none(),
6911                "m3 swigluoai and step35 clamp are different archs"
6912            );
6913            return e.swigluoai_mul_scaled(
6914                gate,
6915                up,
6916                gs,
6917                us,
6918                m3.swiglu_alpha,
6919                m3.swiglu_limit,
6920                act,
6921                n,
6922            );
6923        }
6924        if let Some(l) = limit {
6925            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
6926        }
6927        if gs == 1.0 && us == 1.0 {
6928            return e.silu_mul(gate, up, act, n);
6929        }
6930        e.silu_mul_scaled(gate, up, gs, us, act, n)
6931    }
6932
6933    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
6934    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
6935    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
6936    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
6937    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
6938    fn moe_route(
6939        e: &Engine,
6940        logits: &CudaSlice<f32>,
6941        t: usize,
6942        n_expert: usize,
6943        n_used: usize,
6944    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6945        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
6946    }
6947
6948    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
6949    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
6950    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
6951    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
6952    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
6953    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
6954    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
6955    #[allow(clippy::too_many_arguments)]
6956    fn moe_route_sigmoid_cfg(
6957        e: &Engine,
6958        logits: &CudaSlice<f32>,
6959        t: usize,
6960        n_expert: usize,
6961        n_used: usize,
6962        m: &MoeWeights,
6963        (sf, route_norm): (f32, bool),
6964    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6965        if sigmoid_router_enabled() {
6966            return e.moe_router_sigmoid_topk_host(
6967                logits,
6968                t,
6969                n_expert,
6970                n_used,
6971                m.active_count(),
6972                &m.exp_probs_b_dev,
6973                &m.active_experts_dev,
6974                sf,
6975                route_norm,
6976            );
6977        }
6978        let lg = e.dtoh(logits)?;
6979        Self::moe_route_sigmoid_host(
6980            &lg,
6981            t,
6982            n_expert,
6983            n_used,
6984            m.exp_probs_b.as_deref(),
6985            sf,
6986            route_norm,
6987            m.active_experts.as_deref(),
6988        )
6989    }
6990
6991    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
6992    /// the existing softmax device kernel has no mask input.
6993    fn moe_route_cfg(
6994        e: &Engine,
6995        logits: &CudaSlice<f32>,
6996        t: usize,
6997        n_expert: usize,
6998        n_used: usize,
6999        active: Option<&[bool]>,
7000    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7001        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7002        // rollback) via the single-sync pinned readback — softmax arch only.
7003        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7004            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7005        }
7006        // Host oracle (the §D bit-identity reference).
7007        let lg = e.dtoh(logits)?; // [T*n_expert] host
7008        let mut sel = vec![0u32; t * n_used];
7009        let mut w_out = vec![0f32; t * n_used];
7010        for tok in 0..t {
7011            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7012            // softmax over ALL n_expert (stable: subtract max)
7013            let maxl = row
7014                .iter()
7015                .enumerate()
7016                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7017                .map(|(_, &x)| x)
7018                .fold(f32::NEG_INFINITY, f32::max);
7019            let mut probs = vec![0f32; n_expert];
7020            let mut den = 0f32;
7021            for i in 0..n_expert {
7022                if active.is_some_and(|mask| !mask[i]) {
7023                    continue;
7024                }
7025                let x = (row[i] - maxl).exp();
7026                probs[i] = x;
7027                den += x;
7028            }
7029            for p in probs.iter_mut() {
7030                *p /= den;
7031            }
7032            // stable DESC sort: prob DESC, ascending-index tiebreak.
7033            let mut idx: Vec<usize> = (0..n_expert)
7034                .filter(|&i| active.is_none_or(|mask| mask[i]))
7035                .collect();
7036            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7037            let sl = &idx[..n_used];
7038            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7039            let mut ws: f32 = wv.iter().sum();
7040            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7041            for x in wv.iter_mut() {
7042                *x /= ws;
7043            }
7044            for j in 0..n_used {
7045                sel[tok * n_used + j] = sl[j] as u32;
7046                w_out[tok * n_used + j] = wv[j];
7047            }
7048        }
7049        Ok((sel, w_out))
7050    }
7051
7052    #[allow(clippy::too_many_arguments)]
7053    fn moe_route_sigmoid_with_input(
7054        e: &Engine,
7055        logits: &CudaSlice<f32>,
7056        input: &CudaSlice<f32>,
7057        t: usize,
7058        in_features: usize,
7059        n_expert: usize,
7060        n_used: usize,
7061        bias: Option<&[f32]>,
7062        (sf, route_norm): (f32, bool),
7063        active: Option<&[bool]>,
7064    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7065        let logit_values =
7066            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7067        let input_values =
7068            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7069        let (lg, input) = e.dtoh_pair_views(
7070            &logits.slice(0..logit_values),
7071            &input.slice(0..input_values),
7072        )?;
7073        let (sel, w) =
7074            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7075        Ok((sel, w, input))
7076    }
7077
7078    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7079    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7080    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7081    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7082    pub fn start_moe_prefetch_predictor(
7083        &self,
7084        e: &Engine,
7085        cfg: &ModelConfig,
7086    ) -> Result<(), Box<dyn std::error::Error>> {
7087        use crate::hybrid::Ffn;
7088        let Some(sig) = cfg.sigmoid_router() else {
7089            return Err("prefetch predictor requires a sigmoid-router arch".into());
7090        };
7091        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7092            .export_moe_residency()
7093            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7094            .into_iter()
7095            .collect();
7096        let mut layers = Vec::new();
7097        for (index, layer) in self.layers.iter().enumerate() {
7098            let Ffn::Moe(m) = &layer.ffn else { continue };
7099            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7100                continue;
7101            };
7102            let router = e.dtoh(data)?;
7103            let n_expert = m.gate_exps.n_expert;
7104            let n_embd = m.gate_exps.in_f;
7105            if router.len() != n_embd * n_expert {
7106                continue;
7107            }
7108            let build = |exps: &crate::model::HostExps| {
7109                (0..n_expert)
7110                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7111                    .collect::<Vec<_>>()
7112            };
7113            layers.push((
7114                index as u16,
7115                crate::cpu_experts::PredictLayerInit {
7116                    router,
7117                    bias: m.exp_probs_b.clone(),
7118                    active: m.active_experts.clone(),
7119                    n_embd,
7120                    n_used: cfg
7121                        .moe
7122                        .as_ref()
7123                        .map(|moe| moe.expert_used_count as usize)
7124                        .ok_or("prefetch predictor requires MoE config")?,
7125                    sig,
7126                    weights_n_expert: n_expert,
7127                    gate: build(&m.gate_exps),
7128                    up: build(&m.up_exps),
7129                    down: build(&m.down_exps),
7130                },
7131            ));
7132        }
7133        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7134    }
7135
7136    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7137    /// selection math to the rollback runtime, applied to host-computed logits.
7138    #[allow(clippy::too_many_arguments)]
7139    pub fn moe_route_sigmoid_host_public(
7140        logits: &[f32],
7141        t: usize,
7142        n_expert: usize,
7143        n_used: usize,
7144        bias: Option<&[f32]>,
7145        sf: f32,
7146        route_norm: bool,
7147        active: Option<&[bool]>,
7148    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7149        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7150    }
7151
7152    #[allow(clippy::too_many_arguments)]
7153    fn moe_route_sigmoid_host(
7154        lg: &[f32],
7155        t: usize,
7156        n_expert: usize,
7157        n_used: usize,
7158        bias: Option<&[f32]>,
7159        sf: f32,
7160        route_norm: bool,
7161        active: Option<&[bool]>,
7162    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7163        let active_count = active
7164            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7165            .unwrap_or(n_expert);
7166        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7167        if lg.len() != t * n_expert {
7168            return Err(format!(
7169                "sigmoid router logits length mismatch: got {}, expected {}",
7170                lg.len(),
7171                t * n_expert,
7172            )
7173            .into());
7174        }
7175        let mut sel = vec![0u32; t * n_used];
7176        let mut w_out = vec![0f32; t * n_used];
7177        for tok in 0..t {
7178            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7179            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7180            // selection score = sigmoid + bias; weight = plain sigmoid.
7181            let selsc: Vec<f32> = match bias {
7182                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7183                None => scores.clone(),
7184            };
7185            let mut idx: Vec<usize> = (0..n_expert)
7186                .filter(|&i| active.is_none_or(|mask| mask[i]))
7187                .collect();
7188            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7189            let sl = &idx[..n_used];
7190            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7191            if route_norm {
7192                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7193                for x in wv.iter_mut() {
7194                    *x = *x / ws * sf;
7195                }
7196            } else {
7197                for x in wv.iter_mut() {
7198                    *x *= sf;
7199                }
7200            }
7201            for j in 0..n_used {
7202                sel[tok * n_used + j] = sl[j] as u32;
7203                w_out[tok * n_used + j] = wv[j];
7204            }
7205        }
7206        Ok((sel, w_out))
7207    }
7208
7209    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7210    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7211    /// macro-scaled experts, and observation modes are denied by the caller.
7212    #[allow(clippy::too_many_arguments)]
7213    fn moe_ffn_sigmoid_dev(
7214        e: &Engine,
7215        m: &MoeWeights,
7216        z: &CudaSlice<f32>,
7217        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7218        logits: &CudaSlice<f32>,
7219        t: usize,
7220        cfg: &ModelConfig,
7221        il: u16,
7222        (scaling_factor, route_norm): (f32, bool),
7223    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7224        let moe = cfg.moe.as_ref().unwrap();
7225        let n_embd = cfg.n_embd as usize;
7226        let n_expert = moe.expert_count as usize;
7227        let n_used = moe.expert_used_count as usize;
7228        let n_ff_exp = moe.expert_ff_length as usize;
7229        let dev = m.dev_exps.as_ref().unwrap();
7230        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7231        debug_assert!(m.has_uniform_expert_layout());
7232        debug_assert!(!m.has_macros);
7233
7234        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7235            logits,
7236            t,
7237            n_expert,
7238            n_used,
7239            m.active_count(),
7240            &m.exp_probs_b_dev,
7241            &m.active_experts_dev,
7242            scaling_factor,
7243            route_norm,
7244        )?;
7245        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7246        if let Some(fp8) = dev.fp8_blk.as_ref() {
7247            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7248            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7249            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7250            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7251            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7252            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7253
7254            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7255            // activations with block-128 E4M3 weights. This deliberately
7256            // simple resident reference is the correctness oracle for later
7257            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7258            // load-time Q8 diagnostic representation, so one process never
7259            // crosses between numerical programs.
7260            let selected = e.dtoh_i32(&sel_d)?;
7261            let route_weights = e.dtoh(&w_d)?;
7262            let mut moe_out = e.zeros(t * n_embd)?;
7263            for tok in 0..t {
7264                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7265                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7266                for j in 0..n_used {
7267                    let pair = tok * n_used + j;
7268                    let expert = selected[pair] as usize;
7269                    let gate = Self::moe_resident_fp8_e4m3(
7270                        e,
7271                        &m.gate_exps,
7272                        &dev.gate,
7273                        &fp8.gate,
7274                        expert,
7275                        &zt,
7276                        1,
7277                    )?;
7278                    let up = Self::moe_resident_fp8_e4m3(
7279                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7280                    )?;
7281                    let mut act = e.uninit(n_ff_exp)?;
7282                    Self::ffn_act_lim(
7283                        e,
7284                        cfg,
7285                        &gate,
7286                        &up,
7287                        1.0,
7288                        1.0,
7289                        cfg.clamp_exp_at(il as u32),
7290                        &mut act,
7291                        n_ff_exp,
7292                    )?;
7293                    let act = act.slice(0..n_ff_exp);
7294                    let down = Self::moe_resident_fp8_e4m3(
7295                        e,
7296                        &m.down_exps,
7297                        &dev.down,
7298                        &fp8.down,
7299                        expert,
7300                        &act,
7301                        1,
7302                    )?;
7303                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7304                }
7305            }
7306            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7307                eprintln!(
7308                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7309                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7310                    cfg.clamp_exp_at(il as u32).is_some(),
7311                );
7312            }
7313            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7314            return Ok(moe_out);
7315        }
7316        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7317            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7318            (combined, combined)
7319        } else {
7320            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7321        };
7322        let (zq, zd) = match (t, zq8) {
7323            (1, Some((q, d))) => (q.clone(), d.clone()),
7324            _ => e.quantize_q8_1(z, t, n_embd)?,
7325        };
7326        let n_pairs = t * n_used;
7327        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7328            // The final Step layers retain the established separate gate/up -> clamp -> down
7329            // arithmetic. Pair rows are derived from token position; selected expert ids and
7330            // routing weights remain the device router's buffers throughout.
7331            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7332            let pair_tok_d = e.htod_i32(&pair_tok)?;
7333            let gate = e.moe_pairs_matvec_q8(
7334                &dev.ptr_row,
7335                0,
7336                &pair_tok_d,
7337                &sel_d,
7338                &zq,
7339                &zd,
7340                n_embd,
7341                n_ff_exp,
7342                n_expert,
7343                n_pairs,
7344                m.gate_exps.qtype,
7345                gate_row_bytes,
7346            )?;
7347            let up = e.moe_pairs_matvec_q8(
7348                &dev.ptr_row,
7349                1,
7350                &pair_tok_d,
7351                &sel_d,
7352                &zq,
7353                &zd,
7354                n_embd,
7355                n_ff_exp,
7356                n_expert,
7357                n_pairs,
7358                m.up_exps.qtype,
7359                up_row_bytes,
7360            )?;
7361            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7362            Self::ffn_act_lim(
7363                e,
7364                cfg,
7365                &gate,
7366                &up,
7367                1.0,
7368                1.0,
7369                cfg.clamp_exp_at(il as u32),
7370                &mut act,
7371                n_pairs * n_ff_exp,
7372            )?;
7373            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7374            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7375            let pair_self_d = e.htod_i32(&pair_self)?;
7376            let down = e.moe_pairs_matvec_q8(
7377                &dev.ptr_row,
7378                2,
7379                &pair_self_d,
7380                &sel_d,
7381                &aq2,
7382                &ad2,
7383                n_ff_exp,
7384                n_embd,
7385                n_expert,
7386                n_pairs,
7387                m.down_exps.qtype,
7388                m.down_exps.row_bytes,
7389            )?;
7390            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7391            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7392            let tok_off_d = e.htod_i32(&tok_off)?;
7393            let tok_ids_d = e.htod_i32(&tok_ids)?;
7394            let mut output = e.uninit(t * n_embd)?;
7395            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7396            output
7397        } else {
7398            let act = e.moe_gate_up_silu8_dev_q8_rows(
7399                &dev.ptr_row,
7400                &sel_d,
7401                &zq,
7402                &zd,
7403                t,
7404                n_embd,
7405                n_ff_exp,
7406                n_used,
7407                n_expert,
7408                m.gate_exps.qtype,
7409                m.up_exps.qtype,
7410                gate_row_bytes,
7411                up_row_bytes,
7412                &m.dev_macros,
7413            )?;
7414            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7415            let mut output = e.uninit(t * n_embd)?;
7416            e.moe_down8_fma_dev_q8_rows_g(
7417                &dev.ptr_row,
7418                &sel_d,
7419                &w_d,
7420                &aq2,
7421                &ad2,
7422                &mut output,
7423                t,
7424                n_ff_exp,
7425                n_embd,
7426                n_used,
7427                n_expert,
7428                m.down_exps.qtype,
7429                m.down_exps.row_bytes,
7430            )?;
7431            output
7432        };
7433
7434        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7435            eprintln!(
7436                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7437                cfg.clamp_exp_at(il as u32).is_some(),
7438                dev.gu_il,
7439            );
7440        }
7441        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7442        Ok(moe_out)
7443    }
7444
7445    #[allow(clippy::too_many_arguments)]
7446    fn moe_resident_fp8_e4m3(
7447        e: &Engine,
7448        exps: &crate::model::HostExps,
7449        bytes: &CudaSlice<u8>,
7450        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7451        expert: usize,
7452        x: &cudarc::driver::CudaView<f32>,
7453        m: usize,
7454    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7455        let layout = exps.expert_layout(expert);
7456        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7457        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7458        let byte_start = expert * exps.expert_stride;
7459        let scale_start = expert * scales.expert_stride;
7460        let weight = bytes.slice(byte_start..byte_start + layout.len);
7461        let scale = scales
7462            .scales
7463            .slice(scale_start..scale_start + scales.expert_stride);
7464        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7465    }
7466
7467    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7468    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7469    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7470    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7471    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7472    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7473    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7474    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7475    fn moe_ffn_pairs(
7476        e: &Engine,
7477        m: &MoeWeights,
7478        z: &CudaSlice<f32>,
7479        logits: &CudaSlice<f32>,
7480        t: usize,
7481        cfg: &ModelConfig,
7482    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7483        let moe = cfg.moe.as_ref().unwrap();
7484        let n_embd = cfg.n_embd as usize;
7485        let n_expert = moe.expert_count as usize;
7486        let n_used = moe.expert_used_count as usize;
7487        let n_ff_exp = moe.expert_ff_length as usize;
7488        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7489        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7490        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7491        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7492        debug_assert!(
7493            !cfg.swiglu_clamped_anywhere(),
7494            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7495        );
7496        let dev = m.dev_exps.as_ref().unwrap();
7497        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7498        let (rbg_d, rbu_d) = if dev.gu_il {
7499            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7500            (sxx, sxx)
7501        } else {
7502            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7503        };
7504
7505        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7506        let n_pairs = t * n_used;
7507        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7508        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7509        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7510        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7511        let pair_w: Vec<f32> = w_all.clone();
7512        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7513        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7514        let pt = e.htod_i32(&pair_tok)?;
7515        let px = e.htod_i32(&pair_ex)?;
7516        let pw = e.htod(&pair_w)?;
7517        let toff = e.htod_i32(&tok_off)?;
7518        let tids = e.htod_i32(&tok_ids)?;
7519
7520        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7521        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7522        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7523        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7524        for p in 0..n_pairs {
7525            by_ex[pair_ex[p] as usize].push(p as i32);
7526        }
7527        let mut ex_ids: Vec<i32> = Vec::new();
7528        let mut ex_off: Vec<i32> = vec![0];
7529        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7530        for (ex, list) in by_ex.iter().enumerate() {
7531            if list.is_empty() {
7532                continue;
7533            }
7534            ex_ids.push(ex as i32);
7535            ex_pairs.extend_from_slice(list);
7536            ex_off.push(ex_pairs.len() as i32);
7537        }
7538        let n_active = ex_ids.len();
7539        let exi = e.htod_i32(&ex_ids)?;
7540        let exo = e.htod_i32(&ex_off)?;
7541        let exp_d = e.htod_i32(&ex_pairs)?;
7542        let _ = &px; // pair-major twin keeps it; em path uses CSR
7543
7544        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7545        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7546        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7547        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7548        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7549        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7550        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7551        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7552        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7553        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7554        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7555        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7556        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7557        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7558        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7559        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7560        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7561        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7562        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7563        let mma_t = *MMA_T.get_or_init(|| {
7564            std::env::var("MEMRA_MOE_MMA_T")
7565                .ok()
7566                .and_then(|v| v.parse().ok())
7567                .unwrap_or(16)
7568        });
7569        let use_mma = std::env::var("MEMRA_MOE_MMA")
7570            .map(|v| v != "0")
7571            .unwrap_or(true)
7572            && t >= mma_t
7573            && q8_expert_dec_supported(m.gate_exps.qtype)
7574            && q8_expert_dec_supported(m.up_exps.qtype)
7575            && q8_expert_dec_supported(m.down_exps.qtype)
7576            && n_embd % 256 == 0
7577            && n_ff_exp % 256 == 0;
7578        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7579        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7580        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7581        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7582        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7583        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7584        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7585        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7586        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7587        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7588        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7589        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7590        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7591        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
7592        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
7593        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
7594            && q8_expert_dec_supported(m.up_exps.qtype)
7595            && q8_expert_dec_supported(m.down_exps.qtype)
7596            && n_embd % 256 == 0
7597            && n_ff_exp % 256 == 0;
7598        let f16g_mode = crate::moe_f16g_mode();
7599        let f16g = f16g_mode != 0
7600            && t >= mma_t
7601            && (f16g_mode != 3 || !mma_capable)
7602            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
7603            && f16g_proj_ok(m.up_exps.qtype, n_embd)
7604            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
7605        if use_mma || f16g {
7606            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
7607            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
7608            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
7609            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
7610            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
7611            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
7612            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
7613            let y_down = if f16g {
7614                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
7615                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
7616                // permute at the very end back to pair-id order for the scatter.
7617                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
7618                let csr_tok_d = e.htod_i32(&csr_tok)?;
7619                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
7620                let g_csr = e.moe_f16_grouped(
7621                    &dev.ptr_row,
7622                    0,
7623                    n_expert,
7624                    &exi,
7625                    &ex_off,
7626                    &exo,
7627                    &z_f16,
7628                    &z_s,
7629                    n_embd,
7630                    n_ff_exp,
7631                    n_active,
7632                    n_pairs,
7633                    m.gate_exps.qtype,
7634                    rbg_d,
7635                )?;
7636                let u_csr = e.moe_f16_grouped(
7637                    &dev.ptr_row,
7638                    1,
7639                    n_expert,
7640                    &exi,
7641                    &ex_off,
7642                    &exo,
7643                    &z_f16,
7644                    &z_s,
7645                    n_embd,
7646                    n_ff_exp,
7647                    n_active,
7648                    n_pairs,
7649                    m.up_exps.qtype,
7650                    rbu_d,
7651                )?;
7652                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
7653                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
7654                let d_csr = e.moe_f16_grouped(
7655                    &dev.ptr_row,
7656                    2,
7657                    n_expert,
7658                    &exi,
7659                    &ex_off,
7660                    &exo,
7661                    &a_f16,
7662                    &a_s,
7663                    n_ff_exp,
7664                    n_embd,
7665                    n_active,
7666                    n_pairs,
7667                    m.down_exps.qtype,
7668                    m.down_exps.row_bytes,
7669                )?;
7670                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
7671            } else {
7672                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
7673                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
7674                let gate = e.mmq_iq_experts(
7675                    &dev.ptr_row,
7676                    0,
7677                    n_expert,
7678                    &exi,
7679                    &exo,
7680                    &exp_d,
7681                    &pt,
7682                    &z_scr,
7683                    n_embd,
7684                    n_ff_exp,
7685                    n_active,
7686                    n_pairs,
7687                    t,
7688                    m.gate_exps.qtype,
7689                    rbg_d,
7690                )?;
7691                let up = e.mmq_iq_experts(
7692                    &dev.ptr_row,
7693                    1,
7694                    n_expert,
7695                    &exi,
7696                    &exo,
7697                    &exp_d,
7698                    &pt,
7699                    &z_scr,
7700                    n_embd,
7701                    n_ff_exp,
7702                    n_active,
7703                    n_pairs,
7704                    t,
7705                    m.up_exps.qtype,
7706                    rbu_d,
7707                )?;
7708                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
7709                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
7710                // registers and writes ONLY the quantized scratch — the two-pass chain
7711                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
7712                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
7713                let a_scr = if crate::moe_fuse_actq_on() {
7714                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
7715                } else {
7716                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7717                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
7718                };
7719                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7720                let pself = e.htod_i32(&pair_self)?;
7721                e.mmq_iq_experts(
7722                    &dev.ptr_row,
7723                    2,
7724                    n_expert,
7725                    &exi,
7726                    &exo,
7727                    &exp_d,
7728                    &pself,
7729                    &a_scr,
7730                    n_ff_exp,
7731                    n_embd,
7732                    n_active,
7733                    n_pairs,
7734                    n_pairs,
7735                    m.down_exps.qtype,
7736                    m.down_exps.row_bytes,
7737                )?
7738            };
7739            let mut moe_out = e.uninit(t * n_embd)?;
7740            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7741            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7742                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7743            {
7744                let n_ff_sh = gate_shexp.out_features();
7745                let sg_gate = e.matmul(gate_shexp, z, t)?;
7746                let sg_up = e.matmul(up_shexp, z, t)?;
7747                let mut sa = e.uninit(t * n_ff_sh)?;
7748                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7749                let sh = e.matmul(down_shexp, &sa, t)?;
7750                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7751                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
7752                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
7753                // i.e. the one real prefill actually takes on a resident-expert MoE model,
7754                // so the concat-prime isolation fix has to land here as well.
7755                let g = match &m.gate_inp_shexp {
7756                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7757                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7758                    }
7759                    Some(gate_inp_shexp) => {
7760                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7761                        let mut g = e.uninit(t)?;
7762                        e.sigmoid(&gs, &mut g, t)?;
7763                        g
7764                    }
7765                    None => e.htod(&vec![1.0f32; t])?,
7766                };
7767                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7768            }
7769            return Ok(moe_out);
7770        }
7771
7772        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
7773        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
7774        let dec = std::env::var("MEMRA_MOE_DEC")
7775            .map(|v| v != "0")
7776            .unwrap_or(true);
7777        let matvec = |proj,
7778                      exi: &_,
7779                      exo: &_,
7780                      exp_d: &_,
7781                      pt: &_,
7782                      aq: &_,
7783                      ad: &_,
7784                      inf,
7785                      outf,
7786                      qtype,
7787                      rb|
7788         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7789            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
7790            let dec = dec && q8_expert_dec_supported(qtype);
7791            if dec {
7792                e.moe_pairs_matvec_q8_dec(
7793                    &dev.ptr_row,
7794                    proj,
7795                    exi,
7796                    exo,
7797                    exp_d,
7798                    pt,
7799                    aq,
7800                    ad,
7801                    inf,
7802                    outf,
7803                    n_expert,
7804                    n_active,
7805                    n_pairs,
7806                    qtype,
7807                    rb,
7808                )
7809            } else {
7810                e.moe_pairs_matvec_q8_em(
7811                    &dev.ptr_row,
7812                    proj,
7813                    exi,
7814                    exo,
7815                    exp_d,
7816                    pt,
7817                    aq,
7818                    ad,
7819                    inf,
7820                    outf,
7821                    n_expert,
7822                    n_active,
7823                    n_pairs,
7824                    qtype,
7825                    rb,
7826                )
7827            }
7828        };
7829        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7830        let gate = matvec(
7831            0,
7832            &exi,
7833            &exo,
7834            &exp_d,
7835            &pt,
7836            &zq,
7837            &zd,
7838            n_embd,
7839            n_ff_exp,
7840            m.gate_exps.qtype,
7841            rbg_d,
7842        )?;
7843        let up = matvec(
7844            1,
7845            &exi,
7846            &exo,
7847            &exp_d,
7848            &pt,
7849            &zq,
7850            &zd,
7851            n_embd,
7852            n_ff_exp,
7853            m.up_exps.qtype,
7854            rbu_d,
7855        )?;
7856        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7857        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7858        // down consumes PAIR-major activation rows: pair_tok = identity.
7859        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7860        let pself = e.htod_i32(&pair_self)?;
7861        let y_down = matvec(
7862            2,
7863            &exi,
7864            &exo,
7865            &exp_d,
7866            &pself,
7867            &aq2,
7868            &ad2,
7869            n_ff_exp,
7870            n_embd,
7871            m.down_exps.qtype,
7872            m.down_exps.row_bytes,
7873        )?;
7874        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
7875        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7876
7877        // SHARED EXPERT epilogue — same as the other paths.
7878        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7879        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7880        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7881            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7882        {
7883            let n_ff_sh = gate_shexp.out_features();
7884            // These decode-exact forms are required by the new Step resident arm. Keep the
7885            // established grouped shared-expert program for every other architecture: widening
7886            // this to Gemma changed its speculative acceptance despite green argmax gates.
7887            let step_exact = true;
7888            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
7889            let (sg_gate, sg_up) = if step_exact && t == 1 {
7890                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
7891            } else if verify_t {
7892                let mut fused = None;
7893                if crate::spec::spec_fused_t()
7894                    && (2..=4).contains(&t)
7895                    && e.uses_q8_1_fast(gate_shexp)
7896                    && e.uses_q8_1_fast(up_shexp)
7897                {
7898                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7899                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
7900                }
7901                match fused {
7902                    Some(pair) => pair,
7903                    None => (
7904                        e.matmul_decode_exact(gate_shexp, z, t)?,
7905                        e.matmul_decode_exact(up_shexp, z, t)?,
7906                    ),
7907                }
7908            } else {
7909                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
7910            };
7911            let mut sa = e.uninit(t * n_ff_sh)?;
7912            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7913            let sh = if verify_t {
7914                e.matmul_decode_exact(down_shexp, &sa, t)?
7915            } else {
7916                e.matmul(down_shexp, &sa, t)?
7917            };
7918            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7919            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
7920            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
7921            // dispatch choice cannot change bits.
7922            let g = match &m.gate_inp_shexp {
7923                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7924                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7925                }
7926                Some(gate_inp_shexp) => {
7927                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7928                    let mut g = e.uninit(t)?;
7929                    e.sigmoid(&gs, &mut g, t)?;
7930                    g
7931                }
7932                None => e.htod(&vec![1.0f32; t])?,
7933            };
7934            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7935        }
7936        Ok(moe_out)
7937    }
7938
7939    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
7940    #[allow(clippy::too_many_arguments)]
7941    #[allow(clippy::too_many_arguments)]
7942    fn moe_ffn_dev(
7943        e: &Engine,
7944        m: &MoeWeights,
7945        z: &CudaSlice<f32>,
7946        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7947        logits: &CudaSlice<f32>,
7948        t: usize,
7949        cfg: &ModelConfig,
7950        il: u16,
7951        max_block: usize,
7952    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7953        let moe = cfg.moe.as_ref().unwrap();
7954        let n_embd = cfg.n_embd as usize;
7955        let n_expert = moe.expert_count as usize;
7956        let n_used = moe.expert_used_count as usize;
7957        let n_ff_exp = moe.expert_ff_length as usize;
7958        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
7959        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
7960        // clamped layers; assert both so a future caller that skips the gate fails loudly.
7961        debug_assert!(
7962            cfg.sigmoid_router().is_none(),
7963            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
7964        );
7965        debug_assert!(
7966            !cfg.swiglu_clamped_at(il as u32),
7967            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
7968        );
7969
7970        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
7971        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
7972        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
7973        // skipped entirely for macro-free experts (every k-quant GGUF).
7974        if m.has_macros {
7975            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
7976        }
7977
7978        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
7979        let mut moe_out = e.uninit(t * n_embd)?;
7980
7981        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
7982        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
7983        if let Some(dev) = m.dev_exps.as_ref() {
7984            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
7985            // the combined stride; up's base is offset in the ptr table. Down unchanged.
7986            let (rbg_d, rbu_d) = if dev.gu_il {
7987                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7988                (sxx, sxx)
7989            } else {
7990                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7991            };
7992            let q8 = moe_q8_enabled()
7993                && q8_expert_supported(m.gate_exps.qtype)
7994                && q8_expert_supported(m.up_exps.qtype)
7995                && q8_expert_supported(m.down_exps.qtype);
7996            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
7997            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
7998            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
7999            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8000            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8001            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8002            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8003            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8004            let rows_arm = q8
8005                && t > 1
8006                && crate::spec::spec_m2()
8007                && n_ff_exp == 512
8008                && n_used <= 8
8009                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8010                    .map(|v| v.is_empty() || v == "v")
8011                    .unwrap_or(true)
8012                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8013                    .map(|v| v.is_empty() || v == "w8h2v")
8014                    .unwrap_or(true);
8015            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8016            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8017            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8018            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8019            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8020            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8021            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8022            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8023            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8024                .ok()
8025                .and_then(|v| v.parse::<i32>().ok())
8026                .unwrap_or(1);
8027            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8028            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8029            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8030            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8031            // axis. Three chain-pinning attempts did not close it (receipts,
8032            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8033            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8034            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8035            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8036            // never decode-batch-gate at B=8 on the MoE model itself.
8037            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8038            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8039            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8040            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8041            // de-admission verdict above stands until those gates are GREEN on the MoE
8042            // artifact; this door must never default on.
8043            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8044            let csr_qt = |qt: i32| {
8045                qt == crate::QT_IQ4_XS
8046                    || qt == crate::QT_IQ3_S
8047                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8048            };
8049            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8050            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8051            let csr_arm = rows_arm
8052                && csr_mode > 0
8053                && t <= csr_t_max
8054                && csr_uniform
8055                && csr_qt(m.gate_exps.qtype)
8056                && csr_qt(m.up_exps.qtype)
8057                && csr_qt(m.down_exps.qtype);
8058            if csr_arm {
8059                if csr_mode == 2 {
8060                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8061                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8062                }
8063                let n_pairs = t * n_used;
8064                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8065                let act = e.moe_gate_up_silu8_dev_q8_csr(
8066                    &dev.ptr_row,
8067                    &sel_d,
8068                    &zq,
8069                    &zd,
8070                    n_pairs,
8071                    n_embd,
8072                    n_ff_exp,
8073                    n_used,
8074                    n_expert,
8075                    m.gate_exps.qtype,
8076                    m.up_exps.qtype,
8077                    rbg_d,
8078                    rbu_d,
8079                )?;
8080                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8081                // down stays on the _rows twin — BOTH CSR down variants measured negative
8082                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8083                // 16-group rows have too little decode to amortize any dedup structure.
8084                e.moe_down8_fma_dev_q8_rows(
8085                    &dev.ptr_row,
8086                    &sel_d,
8087                    &w_d,
8088                    &aq2,
8089                    &ad2,
8090                    &mut moe_out,
8091                    t,
8092                    n_ff_exp,
8093                    n_embd,
8094                    n_used,
8095                    n_expert,
8096                    m.down_exps.qtype,
8097                    m.down_exps.row_bytes,
8098                )?;
8099                if csr_mode == 2 {
8100                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8101                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8102                        &dev.ptr_row,
8103                        &sel_d,
8104                        &zq,
8105                        &zd,
8106                        t,
8107                        n_embd,
8108                        n_ff_exp,
8109                        n_used,
8110                        n_expert,
8111                        m.gate_exps.qtype,
8112                        m.up_exps.qtype,
8113                        rbg_d,
8114                        rbu_d,
8115                        &m.dev_macros,
8116                    )?;
8117                    let mut out_r = e.uninit(t * n_embd)?;
8118                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8119                    e.moe_down8_fma_dev_q8_rows(
8120                        &dev.ptr_row,
8121                        &sel_d,
8122                        &w_d,
8123                        &aq2r,
8124                        &ad2r,
8125                        &mut out_r,
8126                        t,
8127                        n_ff_exp,
8128                        n_embd,
8129                        n_used,
8130                        n_expert,
8131                        m.down_exps.qtype,
8132                        m.down_exps.row_bytes,
8133                    )?;
8134                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8135                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8136                    let ba = a1
8137                        .iter()
8138                        .zip(&a2)
8139                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8140                        .count();
8141                    let bo = o1
8142                        .iter()
8143                        .zip(&o2)
8144                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8145                        .count();
8146                    if ba + bo > 0 {
8147                        eprintln!(
8148                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8149                            a1.len(),
8150                            o1.len()
8151                        );
8152                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8153                        let sel_h = e.dtoh_i32(&sel_d)?;
8154                        let mut shown = 0;
8155                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8156                            if x.to_bits() != y.to_bits() && shown < 4 {
8157                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8158                                let ex = sel_h[p];
8159                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8160                                eprintln!(
8161                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8162                                );
8163                                shown += 1;
8164                            }
8165                        }
8166                        std::process::exit(3);
8167                    }
8168                }
8169            } else if rows_arm {
8170                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8171                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8172                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8173                    use std::sync::atomic::{AtomicU64, Ordering};
8174                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8175                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8176                    static CALLS: AtomicU64 = AtomicU64::new(0);
8177                    let sel_h = e.dtoh_i32(&sel_d)?;
8178                    let mut u: Vec<i32> = sel_h.clone();
8179                    u.sort_unstable();
8180                    u.dedup();
8181                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8182                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8183                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8184                    if c % 480 == 0 {
8185                        let p = PAIRS.load(Ordering::Relaxed);
8186                        let q = UNIQ.load(Ordering::Relaxed);
8187                        eprintln!(
8188                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8189                            q as f64 / p as f64
8190                        );
8191                    }
8192                }
8193                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8194                let act = e.moe_gate_up_silu8_dev_q8_rows(
8195                    &dev.ptr_row,
8196                    &sel_d,
8197                    &zq,
8198                    &zd,
8199                    t,
8200                    n_embd,
8201                    n_ff_exp,
8202                    n_used,
8203                    n_expert,
8204                    m.gate_exps.qtype,
8205                    m.up_exps.qtype,
8206                    rbg_d,
8207                    rbu_d,
8208                    &m.dev_macros,
8209                )?;
8210                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8211                e.moe_down8_fma_dev_q8_rows(
8212                    &dev.ptr_row,
8213                    &sel_d,
8214                    &w_d,
8215                    &aq2,
8216                    &ad2,
8217                    &mut moe_out,
8218                    t,
8219                    n_ff_exp,
8220                    n_embd,
8221                    n_used,
8222                    n_expert,
8223                    m.down_exps.qtype,
8224                    m.down_exps.row_bytes,
8225                )?;
8226            } else {
8227                for tok in 0..t {
8228                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8229                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8230                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8231                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8232                    if q8 {
8233                        let (zq, zd) = match (t, zq8) {
8234                            (1, Some((q, d))) => (q.clone(), d.clone()),
8235                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8236                        };
8237                        let act = e.moe_gate_up_silu8_dev_q8(
8238                            &dev.ptr_row,
8239                            &selt,
8240                            &zq,
8241                            &zd,
8242                            n_embd,
8243                            n_ff_exp,
8244                            n_used,
8245                            n_expert,
8246                            m.gate_exps.qtype,
8247                            m.up_exps.qtype,
8248                            rbg_d,
8249                            rbu_d,
8250                            &m.dev_macros,
8251                        )?;
8252                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8253                        e.moe_down8_fma_dev_q8(
8254                            &dev.ptr_row,
8255                            &selt,
8256                            &wt,
8257                            &aq2,
8258                            &ad2,
8259                            &mut dst,
8260                            n_ff_exp,
8261                            n_embd,
8262                            n_used,
8263                            n_expert,
8264                            m.down_exps.qtype,
8265                            m.down_exps.row_bytes,
8266                        )?;
8267                    } else {
8268                        let act = e.moe_gate_up_silu8_dev(
8269                            &dev.ptr_row,
8270                            &selt,
8271                            &zt,
8272                            n_embd,
8273                            n_ff_exp,
8274                            n_used,
8275                            n_expert,
8276                            m.gate_exps.qtype,
8277                            m.up_exps.qtype,
8278                            rbg_d,
8279                            rbu_d,
8280                            &m.dev_macros,
8281                        )?;
8282                        e.moe_down8_fma_dev(
8283                            &dev.ptr_row,
8284                            &selt,
8285                            &wt,
8286                            &act,
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                    }
8296                }
8297            }
8298        } else {
8299            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8300            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8301            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8302            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8303            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8304            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8305            let q8 = moe_q8_enabled()
8306                && q8_expert_supported(m.gate_exps.qtype)
8307                && q8_expert_supported(m.up_exps.qtype)
8308                && q8_expert_supported(m.down_exps.qtype);
8309            e.with_moe_cache(max_block, |c, eng| {
8310                let row = c
8311                    .layer_dev_row(il, n_expert, eng)?
8312                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8313                for tok in 0..t {
8314                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8315                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8316                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8317                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8318                    if q8 {
8319                        let (zq, zd) = match (t, zq8) {
8320                            (1, Some((q, d))) => (q.clone(), d.clone()),
8321                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8322                        };
8323                        let act = eng.moe_gate_up_silu8_dev_q8(
8324                            row,
8325                            &selt,
8326                            &zq,
8327                            &zd,
8328                            n_embd,
8329                            n_ff_exp,
8330                            n_used,
8331                            n_expert,
8332                            m.gate_exps.qtype,
8333                            m.up_exps.qtype,
8334                            m.gate_exps.row_bytes,
8335                            m.up_exps.row_bytes,
8336                            &m.dev_macros,
8337                        )?;
8338                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8339                        eng.moe_down8_fma_dev_q8(
8340                            row,
8341                            &selt,
8342                            &wt,
8343                            &aq2,
8344                            &ad2,
8345                            &mut dst,
8346                            n_ff_exp,
8347                            n_embd,
8348                            n_used,
8349                            n_expert,
8350                            m.down_exps.qtype,
8351                            m.down_exps.row_bytes,
8352                        )?;
8353                    } else {
8354                        let act = eng.moe_gate_up_silu8_dev(
8355                            row,
8356                            &selt,
8357                            &zt,
8358                            n_embd,
8359                            n_ff_exp,
8360                            n_used,
8361                            n_expert,
8362                            m.gate_exps.qtype,
8363                            m.up_exps.qtype,
8364                            m.gate_exps.row_bytes,
8365                            m.up_exps.row_bytes,
8366                            &m.dev_macros,
8367                        )?;
8368                        eng.moe_down8_fma_dev(
8369                            row,
8370                            &selt,
8371                            &wt,
8372                            &act,
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                    }
8382                }
8383                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8384                c.hits += (t * 3 * n_used) as u64;
8385                Ok(())
8386            })?;
8387        }
8388
8389        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8390        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8391        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8392        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8393        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8394            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8395        {
8396            let n_ff_sh = gate_shexp.out_features();
8397            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8398            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8399            let verify_t = t > 1 && t < PRIME_MIN_T;
8400            let (sg_gate, sg_up) = if t == 1 {
8401                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8402            } else if verify_t {
8403                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8404                // rides one shared quantize + one fused2 batched launch instead of two
8405                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8406                let mut fused = None;
8407                if crate::spec::spec_fused_t()
8408                    && (2..=4).contains(&t)
8409                    && e.uses_q8_1_fast(gate_shexp)
8410                    && e.uses_q8_1_fast(up_shexp)
8411                {
8412                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8413                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8414                }
8415                match fused {
8416                    Some(pair) => pair,
8417                    None => (
8418                        e.matmul_decode_exact(gate_shexp, z, t)?,
8419                        e.matmul_decode_exact(up_shexp, z, t)?,
8420                    ),
8421                }
8422            } else {
8423                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8424            };
8425            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8426            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8427            let sh = if verify_t {
8428                e.matmul_decode_exact(down_shexp, &sa, t)?
8429            } else {
8430                e.matmul(down_shexp, &sa, t)?
8431            };
8432            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8433            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8434            // between the two arms; prefill keeps the batched cuBLASLt linear).
8435            let g = match &m.gate_inp_shexp {
8436                Some(gate_inp_shexp) => {
8437                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8438                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8439                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8440                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8441                    } else {
8442                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8443                        let mut g = e.uninit(t)?;
8444                        e.sigmoid(&gs, &mut g, t)?;
8445                        g
8446                    }
8447                }
8448                None => e.htod(&vec![1.0f32; t])?,
8449            };
8450            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8451        }
8452
8453        Ok(moe_out)
8454    }
8455
8456    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8457    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8458    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8459    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8460    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8461    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8462    /// the collected raw pointers cannot move between collection and launch (single-threaded
8463    /// decode; the lock is held only for collection, launches are stream-ordered after any
8464    /// prior same-stream staging writes).
8465    #[allow(clippy::too_many_arguments)]
8466    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8467    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8468    #[allow(clippy::too_many_arguments)]
8469    fn moe_gdec_token_q8(
8470        e: &Engine,
8471        m: &MoeWeights,
8472        il: u16,
8473        max_block: usize,
8474        zq: &CudaSlice<i8>,
8475        zd: &CudaSlice<f32>,
8476        sel: &[u32],
8477        w: &[f32],
8478        moe_out: &mut CudaSlice<f32>,
8479        tok: usize,
8480        n_embd: usize,
8481        n_ff_exp: usize,
8482        n_used: usize,
8483    ) -> Result<bool, Box<dyn std::error::Error>> {
8484        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8485        use cudarc::driver::DevicePtr;
8486        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8487            let mut g = [0u64; 8];
8488            let mut u = [0u64; 8];
8489            let mut d = [0u64; 8];
8490            for (j, &ex) in sel.iter().enumerate() {
8491                let ex = ex as u16;
8492                let (Some(sg), Some(su), Some(sd)) = (
8493                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8494                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8495                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8496                ) else {
8497                    return Ok(None);
8498                };
8499                let __s = eng.stream();
8500                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8501                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8502                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8503                g[j] = pg as u64;
8504                u[j] = pu as u64;
8505                d[j] = pd as u64;
8506            }
8507            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8508                for &ex in sel {
8509                    let ex = ex as u16;
8510                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8511                        c.note_profile_hit(BlockId::new(il, proj, ex));
8512                    }
8513                }
8514            }
8515            c.hits += (3 * n_used) as u64;
8516            Ok(Some((g, u, d)))
8517        })?;
8518        let Some((g, u, d)) = ptrs else {
8519            return Ok(false);
8520        };
8521        let mut wv = [0f32; 8];
8522        wv[..n_used].copy_from_slice(w);
8523        let act = e.moe_gate_up_silu8_q8(
8524            crate::WPtr8(g),
8525            crate::WPtr8(u),
8526            zq,
8527            zd,
8528            n_embd,
8529            n_ff_exp,
8530            n_used,
8531            m.gate_exps.qtype,
8532            m.up_exps.qtype,
8533            m.gate_exps.row_bytes,
8534            m.up_exps.row_bytes,
8535        )?;
8536        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8537        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8538        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8539        e.moe_down8_fma_q8(
8540            crate::WPtr8(d),
8541            crate::F32x8(wv),
8542            &aq2,
8543            &ad2,
8544            &mut dst,
8545            n_ff_exp,
8546            n_embd,
8547            n_used,
8548            m.down_exps.qtype,
8549            m.down_exps.row_bytes,
8550        )?;
8551        Ok(true)
8552    }
8553
8554    fn moe_gdec_token(
8555        e: &Engine,
8556        m: &MoeWeights,
8557        il: u16,
8558        max_block: usize,
8559        zt: &cudarc::driver::CudaView<f32>,
8560        sel: &[u32],
8561        w: &[f32],
8562        moe_out: &mut CudaSlice<f32>,
8563        tok: usize,
8564        n_embd: usize,
8565        n_ff_exp: usize,
8566        n_used: usize,
8567    ) -> Result<bool, Box<dyn std::error::Error>> {
8568        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8569        use cudarc::driver::DevicePtr;
8570        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8571        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8572            let mut g = [0u64; 8];
8573            let mut u = [0u64; 8];
8574            let mut d = [0u64; 8];
8575            for (j, &ex) in sel.iter().enumerate() {
8576                let ex = ex as u16;
8577                let (Some(sg), Some(su), Some(sd)) = (
8578                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8579                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8580                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8581                ) else {
8582                    return Ok(None);
8583                };
8584                let __s = eng.stream();
8585                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8586                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8587                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8588                g[j] = pg as u64;
8589                u[j] = pu as u64;
8590                d[j] = pd as u64;
8591            }
8592            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8593                for &ex in sel {
8594                    let ex = ex as u16;
8595                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8596                        c.note_profile_hit(BlockId::new(il, proj, ex));
8597                    }
8598                }
8599            }
8600            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
8601            Ok(Some((g, u, d)))
8602        })?;
8603        let Some((g, u, d)) = ptrs else {
8604            return Ok(false);
8605        };
8606        let mut wv = [0f32; 8];
8607        wv[..n_used].copy_from_slice(w);
8608        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
8609        let act = e.moe_gate_up_silu8(
8610            crate::WPtr8(g),
8611            crate::WPtr8(u),
8612            zt,
8613            n_embd,
8614            n_ff_exp,
8615            n_used,
8616            m.gate_exps.qtype,
8617            m.up_exps.qtype,
8618            m.gate_exps.row_bytes,
8619            m.up_exps.row_bytes,
8620        )?;
8621        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8622        e.moe_down8_fma_into(
8623            crate::WPtr8(d),
8624            crate::F32x8(wv),
8625            &act,
8626            &mut dst,
8627            n_ff_exp,
8628            n_embd,
8629            n_used,
8630            m.down_exps.qtype,
8631            m.down_exps.row_bytes,
8632        )?;
8633        Ok(true)
8634    }
8635
8636    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
8637    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
8638    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
8639    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
8640    fn moe_cached_gemm_q8(
8641        e: &Engine,
8642        il: u16,
8643        proj: u8,
8644        ex: usize,
8645        m: &MoeWeights,
8646        max_block: usize,
8647        aq: &CudaSlice<i8>,
8648        ad: &CudaSlice<f32>,
8649    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8650        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8651        let exps = match proj {
8652            PROJ_GATE => &m.gate_exps,
8653            PROJ_UP => &m.up_exps,
8654            _ => &m.down_exps,
8655        };
8656        let layout = exps.expert_layout(ex);
8657        let id = BlockId::new(il, proj, ex as u16);
8658        let source = exps.expert_source(ex);
8659        e.with_moe_cache(max_block, |c, eng| {
8660            let slot = c.dispatch_source(id, source, eng)?;
8661            let DispatchSlot::Resident(sl) = slot;
8662            let buf = c.slot(sl);
8663            eng.qmatvec_expert_q8(
8664                buf,
8665                0..layout.len,
8666                aq,
8667                ad,
8668                1,
8669                exps.in_f,
8670                exps.out_f,
8671                layout.qtype,
8672                layout.row_bytes,
8673            )
8674        })
8675    }
8676
8677    fn moe_cached_gemm(
8678        e: &Engine,
8679        il: u16,
8680        proj: u8,
8681        ex: usize,
8682        m: &MoeWeights,
8683        max_block: usize,
8684        x: &cudarc::driver::CudaView<f32>,
8685    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8686        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8687        let exps = match proj {
8688            PROJ_GATE => &m.gate_exps,
8689            PROJ_UP => &m.up_exps,
8690            _ => &m.down_exps,
8691        };
8692        let layout = exps.expert_layout(ex);
8693        let id = BlockId::new(il, proj, ex as u16);
8694        let source = exps.expert_source(ex);
8695        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
8696        e.with_moe_cache(max_block, |c, eng| {
8697            let slot = c.dispatch_source(id, source, eng)?;
8698            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
8699            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
8700            let DispatchSlot::Resident(sl) = slot;
8701            let buf = c.slot(sl);
8702            eng.qmatvec_view(
8703                buf,
8704                0..layout.len,
8705                x,
8706                1,
8707                exps.in_f,
8708                exps.out_f,
8709                layout.qtype,
8710                layout.row_bytes,
8711            )
8712        })
8713    }
8714
8715    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
8716    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
8717    /// so the current forward's backend assignment and output remain unchanged.
8718    fn moe_profile_admit_expert(
8719        e: &Engine,
8720        il: u16,
8721        ex: usize,
8722        m: &MoeWeights,
8723        max_block: usize,
8724    ) -> Result<(), Box<dyn std::error::Error>> {
8725        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8726        e.with_moe_cache(max_block, |cache, eng| {
8727            for (proj, exps) in [
8728                (PROJ_GATE, &m.gate_exps),
8729                (PROJ_UP, &m.up_exps),
8730                (PROJ_DOWN, &m.down_exps),
8731            ] {
8732                let id = BlockId::new(il, proj, ex as u16);
8733                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
8734            }
8735            Ok(())
8736        })
8737    }
8738
8739    /// Read a projection from the immutable residency set when present; otherwise use one
8740    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
8741    #[allow(clippy::too_many_arguments)]
8742    fn moe_frozen_gemm(
8743        e: &Engine,
8744        il: u16,
8745        proj: u8,
8746        ex: usize,
8747        m: &MoeWeights,
8748        max_block: usize,
8749        x: &cudarc::driver::CudaView<f32>,
8750        scratch: &mut Option<CudaSlice<u8>>,
8751        scratch_len: usize,
8752    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8753        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
8754        let exps = match proj {
8755            PROJ_GATE => &m.gate_exps,
8756            PROJ_UP => &m.up_exps,
8757            _ => &m.down_exps,
8758        };
8759        let layout = exps.expert_layout(ex);
8760        let id = BlockId::new(il, proj, ex as u16);
8761        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
8762            let Some(slot) = cache.resident(id) else {
8763                return Ok(None);
8764            };
8765            let buf = cache.slot(slot);
8766            Ok(Some(eng.qmatvec_view(
8767                buf,
8768                0..layout.len,
8769                x,
8770                1,
8771                exps.in_f,
8772                exps.out_f,
8773                layout.qtype,
8774                layout.row_bytes,
8775            )?))
8776        })? {
8777            return Ok(output);
8778        }
8779        if scratch.is_none() {
8780            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
8781        }
8782        let scratch = scratch.as_mut().unwrap();
8783        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
8784        e.qmatvec_view(
8785            scratch,
8786            0..layout.len,
8787            x,
8788            1,
8789            exps.in_f,
8790            exps.out_f,
8791            layout.qtype,
8792            layout.row_bytes,
8793        )
8794    }
8795
8796    fn moe_prefetch_expert(
8797        e: &Engine,
8798        il: u16,
8799        ex: usize,
8800        m: &MoeWeights,
8801        max_block: usize,
8802        keep: &[crate::moe_cache::BlockId],
8803    ) -> Result<(), Box<dyn std::error::Error>> {
8804        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8805        e.with_moe_cache(max_block, |c, eng| {
8806            for (proj, exps) in [
8807                (PROJ_GATE, &m.gate_exps),
8808                (PROJ_UP, &m.up_exps),
8809                (PROJ_DOWN, &m.down_exps),
8810            ] {
8811                let id = BlockId::new(il, proj, ex as u16);
8812                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
8813            }
8814            Ok(())
8815        })
8816    }
8817
8818    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
8819    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
8820    fn moe_prefetch_disk_expert(
8821        e: &Engine,
8822        il: u16,
8823        ex: usize,
8824        m: &MoeWeights,
8825        max_block: usize,
8826        keep: &[crate::moe_cache::BlockId],
8827    ) -> Result<(), Box<dyn std::error::Error>> {
8828        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8829        e.with_moe_cache(max_block, |c, eng| {
8830            for (proj, exps) in [
8831                (PROJ_GATE, &m.gate_exps),
8832                (PROJ_UP, &m.up_exps),
8833                (PROJ_DOWN, &m.down_exps),
8834            ] {
8835                let source = exps.expert_source(ex);
8836                if let crate::model::ExpertSource::Disk { .. } = &source {
8837                    let id = BlockId::new(il, proj, ex as u16);
8838                    let _ = c.prefetch_source(id, source, keep, eng)?;
8839                }
8840            }
8841            Ok(())
8842        })
8843    }
8844
8845    #[inline]
8846    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
8847        let _ = m.gate_exps.prefetch_expert_pages(ex);
8848        let _ = m.up_exps.prefetch_expert_pages(ex);
8849        let _ = m.down_exps.prefetch_expert_pages(ex);
8850    }
8851}
8852
8853// ================================================================================================
8854// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
8855//
8856// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
8857// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
8858// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
8859//
8860// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
8861// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
8862// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
8863// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
8864// identical to the per-token loop regardless of expert processing order.
8865//
8866// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
8867// ================================================================================================
8868
8869impl HybridModel {
8870    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
8871    /// sequential fused q8 program over the token axis; clamped layers use the separate
8872    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
8873    #[allow(clippy::too_many_arguments)]
8874    fn moe_ffn_grouped_resident_q8(
8875        e: &Engine,
8876        m: &MoeWeights,
8877        z: &CudaSlice<f32>,
8878        t: usize,
8879        cfg: &ModelConfig,
8880        il: u16,
8881        sel_all: &[u32],
8882        w_all: &[f32],
8883        table: &CudaSlice<u64>,
8884        gu_il: bool,
8885    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8886        let moe = cfg.moe.as_ref().unwrap();
8887        let n_embd = cfg.n_embd as usize;
8888        let n_expert = moe.expert_count as usize;
8889        let n_used = moe.expert_used_count as usize;
8890        let n_ff_exp = moe.expert_ff_length as usize;
8891        let n_pairs = t * n_used;
8892        debug_assert_eq!(sel_all.len(), n_pairs);
8893        debug_assert_eq!(w_all.len(), n_pairs);
8894        debug_assert!(
8895            m.gate_exps.macros.is_none()
8896                && m.up_exps.macros.is_none()
8897                && m.down_exps.macros.is_none(),
8898            "resident grouped q8 does not fold per-expert macro scales",
8899        );
8900
8901        // The rows twins run the resident sequential program verbatim on grid.z = token:
8902        // fused gate/up/SiLU per slot, batched activation quantization, then the original
8903        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
8904        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
8905        // never enter the softmax router.
8906        if !cfg.swiglu_clamped_at(il as u32) {
8907            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8908            let sel_d = e.htod_i32(&sel)?;
8909            let w_d = e.htod(w_all)?;
8910            let (gate_row_bytes, up_row_bytes) = if gu_il {
8911                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8912                (combined, combined)
8913            } else {
8914                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8915            };
8916            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8917            let act = e.moe_gate_up_silu8_dev_q8_rows(
8918                table,
8919                &sel_d,
8920                &zq,
8921                &zd,
8922                t,
8923                n_embd,
8924                n_ff_exp,
8925                n_used,
8926                n_expert,
8927                m.gate_exps.qtype,
8928                m.up_exps.qtype,
8929                gate_row_bytes,
8930                up_row_bytes,
8931                &m.dev_macros,
8932            )?;
8933            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8934            let mut moe_out = e.uninit(t * n_embd)?;
8935            e.moe_down8_fma_dev_q8_rows_g(
8936                table,
8937                &sel_d,
8938                &w_d,
8939                &aq2,
8940                &ad2,
8941                &mut moe_out,
8942                t,
8943                n_ff_exp,
8944                n_embd,
8945                n_used,
8946                n_expert,
8947                m.down_exps.qtype,
8948                m.down_exps.row_bytes,
8949            )?;
8950
8951            if std::env::var("MEMRA_MOE_STATS").is_ok() {
8952                let mut counts = vec![0usize; n_expert];
8953                for &expert in sel_all {
8954                    counts[expert as usize] += 1;
8955                }
8956                let mut sizes: Vec<usize> =
8957                    counts.into_iter().filter(|&count| count != 0).collect();
8958                sizes.sort_unstable();
8959                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
8960                println!(
8961                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
8962                     m_e: min={} median={} mean={mean:.1} max={}",
8963                    sizes.len(),
8964                    n_expert,
8965                    sizes.first().copied().unwrap_or(0),
8966                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
8967                    sizes.last().copied().unwrap_or(0),
8968                );
8969            }
8970            return Ok(moe_out);
8971        }
8972
8973        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
8974        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
8975        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
8976        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
8977        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8978        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
8979        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
8980
8981        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
8982        for (pair, &expert) in pair_ex.iter().enumerate() {
8983            by_expert[expert as usize].push(pair as i32);
8984        }
8985
8986        let pair_tok_d = e.htod_i32(&pair_tok)?;
8987        let pair_ex_d = e.htod_i32(&pair_ex)?;
8988        let pair_w_d = e.htod(w_all)?;
8989        let tok_off_d = e.htod_i32(&tok_off)?;
8990        let tok_ids_d = e.htod_i32(&tok_ids)?;
8991
8992        let matvec = |proj: i32,
8993                      pair_rows: &CudaSlice<i32>,
8994                      aq: &CudaSlice<i8>,
8995                      ad: &CudaSlice<f32>,
8996                      in_f: usize,
8997                      out_f: usize,
8998                      qtype: i32,
8999                      row_bytes: usize|
9000         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9001            e.moe_pairs_matvec_q8(
9002                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9003                row_bytes,
9004            )
9005        };
9006
9007        let (gate_row_bytes, up_row_bytes) = if gu_il {
9008            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9009            (combined, combined)
9010        } else {
9011            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9012        };
9013        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9014        let gate = matvec(
9015            0,
9016            &pair_tok_d,
9017            &zq,
9018            &zd,
9019            n_embd,
9020            n_ff_exp,
9021            m.gate_exps.qtype,
9022            gate_row_bytes,
9023        )?;
9024        let up = matvec(
9025            1,
9026            &pair_tok_d,
9027            &zq,
9028            &zd,
9029            n_embd,
9030            n_ff_exp,
9031            m.up_exps.qtype,
9032            up_row_bytes,
9033        )?;
9034        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9035        Self::ffn_act_lim(
9036            e,
9037            cfg,
9038            &gate,
9039            &up,
9040            1.0,
9041            1.0,
9042            cfg.clamp_exp_at(il as u32),
9043            &mut act,
9044            n_pairs * n_ff_exp,
9045        )?;
9046        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9047        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9048        let pair_self_d = e.htod_i32(&pair_self)?;
9049        let down = matvec(
9050            2,
9051            &pair_self_d,
9052            &aq2,
9053            &ad2,
9054            n_ff_exp,
9055            n_embd,
9056            m.down_exps.qtype,
9057            m.down_exps.row_bytes,
9058        )?;
9059        let mut moe_out = e.uninit(t * n_embd)?;
9060        e.moe_pairs_scatter(
9061            &down,
9062            &pair_w_d,
9063            &tok_off_d,
9064            &tok_ids_d,
9065            &mut moe_out,
9066            t,
9067            n_embd,
9068        )?;
9069
9070        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9071            let mut sizes: Vec<usize> = by_expert
9072                .iter()
9073                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9074                .collect();
9075            sizes.sort_unstable();
9076            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9077            println!(
9078                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9079                 m_e: min={} median={} mean={mean:.1} max={}",
9080                sizes.len(),
9081                n_expert,
9082                sizes.first().copied().unwrap_or(0),
9083                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9084                sizes.last().copied().unwrap_or(0),
9085            );
9086        }
9087        Ok(moe_out)
9088    }
9089
9090    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9091    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9092    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9093    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9094    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9095    #[allow(clippy::too_many_arguments)]
9096    fn shexp_split_matvec(
9097        e: &Engine,
9098        rank1: &Engine,
9099        wg: &CudaSlice<u8>,
9100        wu: &CudaSlice<u8>,
9101        wd: &CudaSlice<u8>,
9102        z: &CudaSlice<f32>,
9103        lim: Option<f32>,
9104        cfg: &ModelConfig,
9105        il: u16,
9106        n_embd: usize,
9107        n_ff_sh: usize,
9108    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9109        use cudarc::driver::DevicePtr;
9110        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9111            return Ok(None);
9112        }
9113        let hf = n_ff_sh / 2;
9114        let nd = n_embd / 2;
9115        struct Rep {
9116            wg1: CudaSlice<u8>,
9117            wu1: CudaSlice<u8>,
9118            wd1: CudaSlice<u8>,
9119        }
9120        struct SplitWs {
9121            pin_dev: usize,
9122            // e side
9123            gate0: CudaSlice<f32>,
9124            up0: CudaSlice<f32>,
9125            act: CudaSlice<f32>,
9126            sh_buf: CudaSlice<f32>,
9127            ev_z: cudarc::driver::CudaEvent,
9128            ev_act0: cudarc::driver::CudaEvent,
9129            // rank1 side
9130            z1: CudaSlice<f32>,
9131            g1: CudaSlice<f32>,
9132            u1: CudaSlice<f32>,
9133            a1h: CudaSlice<f32>,
9134            act1: CudaSlice<f32>,
9135            y1: CudaSlice<f32>,
9136            ev_act1: cudarc::driver::CudaEvent,
9137            ev_y1: cudarc::driver::CudaEvent,
9138            raw_act_e: u64,
9139            raw_sh_e: u64,
9140            raw_z1: u64,
9141            raw_a1h: u64,
9142            raw_act1: u64,
9143            raw_y1: u64,
9144        }
9145        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9146        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9147            std::sync::Mutex::new(None);
9148        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9149        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9150        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9151        let pins = e.ctx().ordinal();
9152        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9153            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9154                let _m = e.gpu.enter_main()?;
9155                (
9156                    e.htod(&vec![0.0f32; hf])?,
9157                    e.htod(&vec![0.0f32; hf])?,
9158                    e.htod(&vec![0.0f32; n_ff_sh])?,
9159                    e.htod(&vec![0.0f32; n_embd])?,
9160                    e.ctx().new_event(None)?,
9161                    e.ctx().new_event(None)?,
9162                )
9163            };
9164            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9165                let _r = rank1.gpu.enter_main()?;
9166                (
9167                    rank1.htod(&vec![0.0f32; n_embd])?,
9168                    rank1.htod(&vec![0.0f32; hf])?,
9169                    rank1.htod(&vec![0.0f32; hf])?,
9170                    rank1.htod(&vec![0.0f32; hf])?,
9171                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9172                    rank1.htod(&vec![0.0f32; nd])?,
9173                    rank1.ctx().new_event(None)?,
9174                    rank1.ctx().new_event(None)?,
9175                )
9176            };
9177            let (raw_act_e, raw_sh_e) = {
9178                let _m = e.gpu.enter_main()?;
9179                let stream = e.stream();
9180                let (a, _g0) = act.device_ptr(&stream);
9181                let (b, _g1) = sh_buf.device_ptr(&stream);
9182                (a as u64, b as u64)
9183            };
9184            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9185                let _r = rank1.gpu.enter_main()?;
9186                let rs = rank1.stream();
9187                let (a, _g0) = z1.device_ptr(&rs);
9188                let (b, _g1) = a1h.device_ptr(&rs);
9189                let (c, _g2) = act1.device_ptr(&rs);
9190                let (d, _g3) = y1.device_ptr(&rs);
9191                (a as u64, b as u64, c as u64, d as u64)
9192            };
9193            *guard = Some(SplitWs {
9194                pin_dev: pins,
9195                gate0,
9196                up0,
9197                act,
9198                sh_buf,
9199                ev_z,
9200                ev_act0,
9201                z1,
9202                g1,
9203                u1,
9204                a1h,
9205                act1,
9206                y1,
9207                ev_act1,
9208                ev_y1,
9209                raw_act_e,
9210                raw_sh_e,
9211                raw_z1,
9212                raw_a1h,
9213                raw_act1,
9214                raw_y1,
9215            });
9216        }
9217        let ws = guard.as_mut().expect("armed above");
9218        let wg_pin = {
9219            let _m = e.gpu.enter_main()?;
9220            let stream = e.stream();
9221            let (p, _g) = wg.device_ptr(&stream);
9222            p as u64
9223        };
9224        if !reps.contains_key(&wg_pin) {
9225            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9226            let mut up = |src: &CudaSlice<u8>,
9227                          off_bytes: usize,
9228                          len: usize|
9229             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9230                use cudarc::driver::sys;
9231                let sptr = {
9232                    let _m = e.gpu.enter_main()?;
9233                    let stream = e.stream();
9234                    let (p, _g) = src.device_ptr(&stream);
9235                    p as u64 + off_bytes as u64
9236                };
9237                let dst = {
9238                    let _r = rank1.gpu.enter_main()?;
9239                    rank1.alloc_u8_uninit(len)?
9240                };
9241                let dptr = {
9242                    let _r = rank1.gpu.enter_main()?;
9243                    let rs = rank1.stream();
9244                    let (p, _g) = dst.device_ptr(&rs);
9245                    p as u64
9246                };
9247                let _r = rank1.gpu.enter_main()?;
9248                let r = unsafe {
9249                    sys::cuMemcpyAsync(
9250                        dptr as sys::CUdeviceptr,
9251                        sptr as sys::CUdeviceptr,
9252                        len,
9253                        rank1.stream().cu_stream() as sys::CUstream,
9254                    )
9255                };
9256                if r != sys::CUresult::CUDA_SUCCESS {
9257                    return Err(format!("shexp split replica upload: {r:?}").into());
9258                }
9259                rank1.stream().synchronize()?;
9260                Ok(dst)
9261            };
9262            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9263            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9264            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9265            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9266        }
9267        let _ = il;
9268        // Per token, evented split flow.
9269        let raw_z = {
9270            let _m = e.gpu.enter_main()?;
9271            let stream = e.stream();
9272            let (p, _g) = z.device_ptr(&stream);
9273            ws.ev_z.record(&stream)?;
9274            p as u64
9275        };
9276        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9277        {
9278            let rep = reps.get(&wg_pin).expect("uploaded above");
9279            let _r = rank1.gpu.enter_main()?;
9280            rank1.stream().wait(&ws.ev_z)?;
9281            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9282            let SplitWs {
9283                z1, g1, u1, a1h, ..
9284            } = &mut *ws;
9285            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9286            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9287            // local place into act1[hf..] + P2P push into e's act[hf..]
9288            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9289            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9290            ws.ev_act1.record(&rank1.stream())?;
9291        }
9292        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9293        {
9294            let _m = e.gpu.enter_main()?;
9295            let SplitWs {
9296                gate0, up0, act, ..
9297            } = &mut *ws;
9298            let wg_lo = wg.slice(0..hf * n_embd * 2);
9299            let wu_lo = wu.slice(0..hf * n_embd * 2);
9300            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9301            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9302            ws.ev_act0.record(&e.stream())?;
9303        }
9304        // rank1: pull e's act half into act1[0..hf], run down hi, push y 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_act0)?;
9309            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9310            let SplitWs { act1, y1, .. } = &mut *ws;
9311            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9312            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9313            ws.ev_y1.record(&rank1.stream())?;
9314        }
9315        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9316        {
9317            let _m = e.gpu.enter_main()?;
9318            e.stream().wait(&ws.ev_act1)?;
9319            let SplitWs { act, sh_buf, .. } = &mut *ws;
9320            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9321            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9322            e.stream().wait(&ws.ev_y1)?;
9323            let mut sh = e.uninit(n_embd)?;
9324            {
9325                let mut dst = sh.slice_mut(0..n_embd);
9326                e.stream()
9327                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9328            }
9329            Ok(Some(sh))
9330        }
9331    }
9332
9333    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9334    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9335    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9336    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9337    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9338    /// the join with the exact add_scaled_rows expression: values unchanged.
9339    fn shexp_overlap_issue(
9340        e: &Engine,
9341        m: &MoeWeights,
9342        z: &CudaSlice<f32>,
9343        cfg: &ModelConfig,
9344        il: u16,
9345        n_embd: usize,
9346    ) -> Result<bool, Box<dyn std::error::Error>> {
9347        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9348            return Ok(false);
9349        }
9350        let (
9351            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9352            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9353            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9354        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9355        else {
9356            return Ok(false);
9357        };
9358        let n_ff_sh = m
9359            .gate_shexp
9360            .as_ref()
9361            .expect("matched Some above")
9362            .out_features();
9363        let lim = cfg.clamp_shexp_at(il as u32);
9364        let mut guard = SHEXP_OV_WS
9365            .lock()
9366            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9367        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9368        if guard
9369            .as_ref()
9370            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9371        {
9372            *guard = Some((
9373                pins.0,
9374                pins.1,
9375                pins.2,
9376                e.uninit(n_ff_sh)?,
9377                e.uninit(n_embd)?,
9378            ));
9379        }
9380        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9381        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9382        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9383        drop(guard);
9384        Ok(true)
9385    }
9386
9387    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9388    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9389    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9390    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9391    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9392    #[allow(clippy::too_many_arguments)]
9393    fn shexp_dev1_issue(
9394        e: &Engine,
9395        rank1: &Engine,
9396        m: &MoeWeights,
9397        z: &CudaSlice<f32>,
9398        cfg: &ModelConfig,
9399        il: u16,
9400        n_embd: usize,
9401    ) -> Result<bool, Box<dyn std::error::Error>> {
9402        use cudarc::driver::DevicePtr;
9403        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9404            return Ok(false);
9405        }
9406        let (
9407            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9408            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9409            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9410        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9411        else {
9412            return Ok(false);
9413        };
9414        let n_ff_sh = m
9415            .gate_shexp
9416            .as_ref()
9417            .expect("matched Some above")
9418            .out_features();
9419        let lim = cfg.clamp_shexp_at(il as u32);
9420        // Shared scratch, geometry-keyed.
9421        let mut ws_guard = SHEXP_D1_WS
9422            .lock()
9423            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9424        if ws_guard
9425            .as_ref()
9426            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9427        {
9428            let (act1, z1, ev_done) = {
9429                let _r1 = rank1.gpu.enter_main()?;
9430                (
9431                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9432                    rank1.htod(&vec![0.0f32; n_embd])?,
9433                    rank1.ctx().new_event(None)?,
9434                )
9435            };
9436            let (sh_root, ev_z) = {
9437                let _main = e.gpu.enter_main()?;
9438                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9439            };
9440            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9441        }
9442        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9443        let mut reps_guard = SHEXP_D1_REPS
9444            .lock()
9445            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9446        let reps = reps_guard.get_or_insert_with(Default::default);
9447        if !reps.contains_key(&il) {
9448            let (wg1, wu1, wd1) = {
9449                let _r1 = rank1.gpu.enter_main()?;
9450                (
9451                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9452                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9453                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9454                )
9455            };
9456            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9457                let s_ptr = {
9458                    let _main = e.gpu.enter_main()?;
9459                    let stream = e.stream();
9460                    let (p, _g) = src.device_ptr(&stream);
9461                    p as u64
9462                };
9463                let d_ptr = {
9464                    let _r1 = rank1.gpu.enter_main()?;
9465                    let stream = rank1.stream();
9466                    let (p, _g) = dst.device_ptr(&stream);
9467                    p as u64
9468                };
9469                let _r1 = rank1.gpu.enter_main()?;
9470                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9471            }
9472            {
9473                let _r1 = rank1.gpu.enter_main()?;
9474                rank1.stream().synchronize()?;
9475            }
9476            reps.insert(il, (wg1, wu1, wd1));
9477        }
9478        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9479        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9480        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9481        // row root-side (single store pass), rings ev_done.
9482        let (raw_z, raw_sh) = {
9483            let _main = e.gpu.enter_main()?;
9484            let stream = e.stream();
9485            let (a, _g0) = z.device_ptr(&stream);
9486            let (b, _g1) = sh_root.device_ptr(&stream);
9487            ev_z.record(&stream)?;
9488            (a as u64, b as u64)
9489        };
9490        {
9491            let _r1 = rank1.gpu.enter_main()?;
9492            rank1.stream().wait(ev_z)?;
9493            let raw_z1 = {
9494                let stream = rank1.stream();
9495                let (p, _g) = z1.device_ptr(&stream);
9496                p as u64
9497            };
9498            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9499            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9500            // down writes the ROOT-resident row over P2P via the raw-output twin of
9501            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9502            // cross-device, so launch on the raw pointer.
9503            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9504            ev_done.record(&rank1.stream())?;
9505        }
9506        Ok(true)
9507    }
9508
9509    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9510    fn shexp_dev1_apply(
9511        e: &Engine,
9512        output: &mut CudaSlice<f32>,
9513        n_embd: usize,
9514    ) -> Result<(), Box<dyn std::error::Error>> {
9515        let guard = SHEXP_D1_WS
9516            .lock()
9517            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9518        let (pin, _, _, sh_root, _, ev_done) =
9519            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9520        if pin.0 != n_embd {
9521            return Err("shexp dev1 width drifted".into());
9522        }
9523        let _main = e.gpu.enter_main()?;
9524        e.stream().wait(ev_done)?;
9525        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9526            std::sync::Mutex::new(None);
9527        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9528        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9529            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9530        }
9531        let ones = &og.as_ref().expect("armed above").1;
9532        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9533        Ok(())
9534    }
9535
9536    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9537    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9538    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9539    fn shexp_overlap_tail_ptrs(
9540        e: &Engine,
9541        m: &MoeWeights,
9542        cfg: &ModelConfig,
9543        n_embd: usize,
9544    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9545        use cudarc::driver::DevicePtr;
9546        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9547            return Ok(None);
9548        }
9549        let (
9550            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9551            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9552            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9553        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9554        else {
9555            return Ok(None);
9556        };
9557        let n_ff_sh = m
9558            .gate_shexp
9559            .as_ref()
9560            .expect("matched Some above")
9561            .out_features();
9562        let mut guard = SHEXP_OV_WS
9563            .lock()
9564            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9565        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9566        if guard
9567            .as_ref()
9568            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9569        {
9570            *guard = Some((
9571                pins.0,
9572                pins.1,
9573                pins.2,
9574                e.uninit(n_ff_sh)?,
9575                e.uninit(n_embd)?,
9576            ));
9577        }
9578        let sh_raw = {
9579            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9580            let stream = e.stream();
9581            let (p, _g) = sh.device_ptr(&stream);
9582            p as u64
9583        };
9584        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9585            std::sync::Mutex::new(None);
9586        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9587        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9588            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9589        }
9590        let ones_raw = {
9591            let stream = e.stream();
9592            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
9593            p as u64
9594        };
9595        Ok(Some((sh_raw, ones_raw)))
9596    }
9597
9598    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
9599    /// add_scaled_rows program the split path used (persistent ones row, no htod).
9600    fn shexp_overlap_apply(
9601        e: &Engine,
9602        output: &mut CudaSlice<f32>,
9603        n_embd: usize,
9604    ) -> Result<(), Box<dyn std::error::Error>> {
9605        let guard = SHEXP_OV_WS
9606            .lock()
9607            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9608        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
9609        if *ne != n_embd {
9610            return Err("shexp overlap width drifted".into());
9611        }
9612        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9613            std::sync::Mutex::new(None);
9614        let mut og = ONES_OV.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 = &og.as_ref().expect("armed above").1;
9619        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
9620        Ok(())
9621    }
9622
9623    fn moe_ffn_grouped_add_shared(
9624        e: &Engine,
9625        m: &MoeWeights,
9626        z: &CudaSlice<f32>,
9627        t: usize,
9628        cfg: &ModelConfig,
9629        il: u16,
9630        moe_out: &mut CudaSlice<f32>,
9631    ) -> Result<(), Box<dyn std::error::Error>> {
9632        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
9633        // queued matmuls here rather than at the next host readback).
9634        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9635        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9636        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9637        let shexp_started = shexp_timing.then(std::time::Instant::now);
9638        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
9639        if let Some(started) = shexp_started {
9640            use std::sync::atomic::Ordering;
9641            e.stream().synchronize()?;
9642            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9643                + started.elapsed().as_nanos() as u64;
9644            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9645            if calls % 430 == 0 {
9646                eprintln!(
9647                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9648                    ns as f64 / 1.0e6,
9649                    ns as f64 / calls as f64 / 1.0e3,
9650                );
9651            }
9652        }
9653        result
9654    }
9655
9656    #[allow(clippy::too_many_arguments)]
9657    fn moe_ffn_grouped_add_shared_inner(
9658        e: &Engine,
9659        m: &MoeWeights,
9660        z: &CudaSlice<f32>,
9661        t: usize,
9662        cfg: &ModelConfig,
9663        il: u16,
9664        moe_out: &mut CudaSlice<f32>,
9665    ) -> Result<(), Box<dyn std::error::Error>> {
9666        let n_embd = cfg.n_embd as usize;
9667        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
9668            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9669        {
9670            let n_ff_sh = gate_shexp.out_features();
9671            let lim = cfg.clamp_shexp_at(il as u32);
9672            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
9673            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
9674            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
9675            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
9676            // operand pre-quantized (kernel_check-proven identities). This path measured
9677            // 167us/layer as separate matmuls + 5 allocs at decode.
9678            let fused = t == 1
9679                && lim.is_none()
9680                && cfg.m3.is_none()
9681                && e.uses_q8_1_fast(gate_shexp)
9682                && e.uses_q8_1_fast(up_shexp);
9683            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
9684            // the two matvec_bf16 launches matmul would issue).
9685            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
9686                match (gate_shexp, up_shexp) {
9687                    (
9688                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
9689                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
9690                    ) => Some((wg, wu)),
9691                    _ => None,
9692                }
9693            } else {
9694                None
9695            };
9696            let sh = if let Some((wg, wu)) = bf16_dual {
9697                // Persistent shared-expert workspace: sizes are constant across every MoE
9698                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
9699                // the four per-layer allocations. Buffers are fully overwritten each call.
9700                static SHEXP_WS: std::sync::Mutex<
9701                    Option<(
9702                        usize,
9703                        usize,
9704                        usize,
9705                        CudaSlice<f32>,
9706                        CudaSlice<f32>,
9707                        CudaSlice<f32>,
9708                        CudaSlice<f32>,
9709                    )>,
9710                > = std::sync::Mutex::new(None);
9711                let down_bf16 = match down_shexp {
9712                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9713                    _ => None,
9714                };
9715                let mut guard = SHEXP_WS
9716                    .lock()
9717                    .map_err(|_| "shexp workspace lock is poisoned")?;
9718                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9719                if guard
9720                    .as_ref()
9721                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9722                {
9723                    *guard = Some((
9724                        pins.0,
9725                        pins.1,
9726                        pins.2,
9727                        e.uninit(n_ff_sh)?,
9728                        e.uninit(n_ff_sh)?,
9729                        e.uninit(n_ff_sh)?,
9730                        e.uninit(n_embd)?,
9731                    ));
9732                }
9733                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
9734                // through to the single-device arm when ineligible.
9735                {
9736                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9737                    let split_on = *ON
9738                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
9739                    if split_on {
9740                        if let (Some(wd), Some(rank1)) = (
9741                            match down_shexp {
9742                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9743                                _ => None,
9744                            },
9745                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
9746                        ) {
9747                            if let Some(sh) = Self::shexp_split_matvec(
9748                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
9749                            )? {
9750                                drop(guard);
9751                                let gate = match &m.gate_inp_shexp {
9752                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
9753                                        z,
9754                                        gate_inp_shexp.float_data(),
9755                                        n_embd,
9756                                        t,
9757                                    )?,
9758                                    None => e.htod(&vec![1.0f32; t])?,
9759                                };
9760                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9761                                return Ok(());
9762                            }
9763                        }
9764                    }
9765                }
9766                let (_, _, _, gate, up, act, sh_buf) =
9767                    guard.as_mut().expect("shexp workspace initialized above");
9768                if cfg.m3.is_none() {
9769                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
9770                    // per-row program + exact silu/clamped expression, bit-identical.
9771                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9772                    let _ = (&gate, &up);
9773                } else {
9774                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
9775                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
9776                }
9777                if let Some(down) = down_bf16 {
9778                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
9779                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
9780                    // exact f32acc per-row program + the exact add_scaled_rows expression
9781                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
9782                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
9783                    // accumulate consumes the same f32 the split path stored and reloaded.
9784                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9785                    let fuse_da = *FUSE_DA.get_or_init(|| {
9786                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
9787                    });
9788                    if fuse_da && m.gate_inp_shexp.is_none() {
9789                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9790                            std::sync::Mutex::new(None);
9791                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
9792                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9793                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9794                        }
9795                        let ones = &og.as_ref().expect("armed above").1;
9796                        e.matvec_bf16_down_addscale_into(
9797                            down, act, ones, moe_out, n_ff_sh, n_embd,
9798                        )?;
9799                        return Ok(());
9800                    }
9801                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
9802                    let sh = e.uninit(n_embd)?;
9803                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
9804                    let mut sh = sh;
9805                    {
9806                        let mut dst = sh.slice_mut(0..n_embd);
9807                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
9808                    }
9809                    sh
9810                } else {
9811                    e.matmul(down_shexp, act, 1)?
9812                }
9813            } else if fused {
9814                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
9815                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
9816                    Some((gate, up)) => Some((gate, up)),
9817                    None => {
9818                        match (
9819                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
9820                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
9821                        ) {
9822                            (Some(gate), Some(up)) => Some((gate, up)),
9823                            _ => None,
9824                        }
9825                    }
9826                };
9827                match pair {
9828                    Some(((gate, gs), (up, us))) => {
9829                        if e.uses_q8_1_fast(down_shexp) {
9830                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
9831                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
9832                        } else {
9833                            let mut act = e.uninit(n_ff_sh)?;
9834                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
9835                            e.matmul(down_shexp, &act, 1)?
9836                        }
9837                    }
9838                    None => {
9839                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
9840                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
9841                        let mut act = e.uninit(n_ff_sh)?;
9842                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
9843                        e.matmul(down_shexp, &act, 1)?
9844                    }
9845                }
9846            } else {
9847                let sg_gate = e.matmul(gate_shexp, z, t)?;
9848                let sg_up = e.matmul(up_shexp, z, t)?;
9849                let mut sa = e.uninit(t * n_ff_sh)?;
9850                Self::ffn_act_lim(
9851                    e,
9852                    cfg,
9853                    &sg_gate,
9854                    &sg_up,
9855                    1.0,
9856                    1.0,
9857                    lim,
9858                    &mut sa,
9859                    t * n_ff_sh,
9860                )?;
9861                e.matmul(down_shexp, &sa, t)?
9862            };
9863            let gate = match &m.gate_inp_shexp {
9864                Some(gate_inp_shexp) => {
9865                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
9866                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
9867                    } else {
9868                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
9869                        let mut gate = e.uninit(t)?;
9870                        e.sigmoid(&raw, &mut gate, t)?;
9871                        gate
9872                    }
9873                }
9874                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
9875                // synchronizes the stream — measured as the biggest per-layer host gap
9876                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
9877                // device serves every layer; larger t (prefill) keeps the plain htod.
9878                None if t == 1 => {
9879                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9880                        std::sync::Mutex::new(None);
9881                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
9882                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9883                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9884                    }
9885                    let ones = &guard.as_ref().expect("armed above").1;
9886                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
9887                    return Ok(());
9888                }
9889                None => e.htod(&vec![1.0f32; t])?,
9890            };
9891            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9892        }
9893        Ok(())
9894    }
9895
9896    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
9897    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
9898    pub(crate) fn moe_ffn_grouped(
9899        e: &Engine,
9900        m: &MoeWeights,
9901        z: &CudaSlice<f32>,
9902        t: usize,
9903        cfg: &ModelConfig,
9904        il: u16,
9905        max_block: usize,
9906    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9907        let moe = cfg.moe.as_ref().unwrap();
9908        let n_embd = cfg.n_embd as usize;
9909        let n_expert = moe.expert_count as usize;
9910        let n_used = moe.expert_used_count as usize;
9911        let n_ff_exp = moe.expert_ff_length as usize;
9912        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
9913        let lim_exp = cfg.clamp_exp_at(il as u32);
9914
9915        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
9916        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
9917        // enters the softmax-only pairs/dev router.
9918        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
9919        if let Some(sig) = cfg.sigmoid_router() {
9920            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
9921        }
9922        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
9923            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
9924        } else {
9925            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
9926        };
9927        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
9928        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
9929        Self::trace_moe_input(e, il, t, n_embd, z)?;
9930
9931        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
9932        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
9933        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
9934        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
9935        let no_exp_macros = m.gate_exps.macros.is_none()
9936            && m.up_exps.macros.is_none()
9937            && m.down_exps.macros.is_none();
9938        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
9939            m.has_uniform_expert_layout()
9940                && no_exp_macros
9941                && moe_q8_enabled()
9942                && q8_expert_supported(m.gate_exps.qtype)
9943                && q8_expert_supported(m.up_exps.qtype)
9944                && q8_expert_supported(m.down_exps.qtype)
9945                && moe_slab_enabled()
9946                && dev.dev == e.ctx().ordinal()
9947        });
9948        if let Some(dev) = resident_q8 {
9949            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
9950                e,
9951                m,
9952                z,
9953                t,
9954                cfg,
9955                il,
9956                &sel_all,
9957                &w_all,
9958                &dev.ptr_row,
9959                dev.gu_il,
9960            )?;
9961            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
9962            return Ok(moe_out);
9963        }
9964
9965        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
9966        // For each expert e, we need: which tokens use it, their positions in z, their top-k
9967        // slot index (for bit-identical accumulation), and their weights.
9968        struct ExpertGroup {
9969            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
9970            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
9971            weights: Vec<f32>,      // renormalized weight for that token-expert pair
9972        }
9973        let mut groups: Vec<ExpertGroup> = (0..n_expert)
9974            .map(|_| ExpertGroup {
9975                tok_indices: Vec::new(),
9976                slot_indices: Vec::new(),
9977                weights: Vec::new(),
9978            })
9979            .collect();
9980
9981        for tok in 0..t {
9982            for j in 0..n_used {
9983                let ex = sel_all[tok * n_used + j] as usize;
9984                let w = w_all[tok * n_used + j];
9985                groups[ex].tok_indices.push(tok as i32);
9986                groups[ex].slot_indices.push(j as i32);
9987                groups[ex].weights.push(w);
9988            }
9989        }
9990
9991        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
9992        // Each token's 8 expert contributions land in their respective slots.
9993        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
9994        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
9995
9996        // Expert weight dimensions (used in both cache and staging paths).
9997        let g_len = m.gate_exps.max_expert_bytes();
9998        let u_len = m.up_exps.max_expert_bytes();
9999        let d_len = m.down_exps.max_expert_bytes();
10000        let moe_q8 = m.has_uniform_expert_layout()
10001            && moe_q8_enabled()
10002            && q8_expert_supported(m.gate_exps.qtype)
10003            && q8_expert_supported(m.up_exps.qtype)
10004            && q8_expert_supported(m.down_exps.qtype);
10005        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10006        // Interleaved GU slabs require the pointer-table fast path above.
10007        let slab_local = m
10008            .dev_exps
10009            .as_ref()
10010            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10011        let use_cache =
10012            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10013        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10014        // also does: a local resident slab or a live SLRU dispatch.
10015        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10016
10017        // GPU scratch for staging (only allocated without a local slab or cache).
10018        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10019            (
10020                Some(e.alloc_u8(g_len)?),
10021                Some(e.alloc_u8(u_len)?),
10022                Some(e.alloc_u8(d_len)?),
10023            )
10024        } else {
10025            (None, None, None)
10026        };
10027
10028        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10029        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10030        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10031        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10032        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10033        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10034        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10035        // at long prompts where every expert stages regardless. Order is FREE to change without
10036        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10037        // regardless of expert processing order (the whole point of the slots).
10038        let mut order: Vec<usize> = (0..n_expert)
10039            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10040            .collect();
10041        order.sort_by(|&a, &b| {
10042            groups[b]
10043                .tok_indices
10044                .len()
10045                .cmp(&groups[a].tok_indices.len())
10046                .then(a.cmp(&b))
10047        });
10048        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10049        let page_window = moe_page_prefetch_window();
10050        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10051        if worker_disk_prefetch {
10052            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10053                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10054            }
10055        }
10056        for (order_pos, &ex) in order.iter().enumerate() {
10057            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10058                Self::moe_prefetch_host_expert(order[next], m);
10059            }
10060            if worker_disk_prefetch {
10061                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10062                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10063                    let keep = [
10064                        BlockId::new(il, PROJ_GATE, ex as u16),
10065                        BlockId::new(il, PROJ_UP, ex as u16),
10066                        BlockId::new(il, PROJ_DOWN, ex as u16),
10067                    ];
10068                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10069                }
10070            }
10071            let grp = &groups[ex];
10072            let m_e = grp.tok_indices.len();
10073            m_dist.push(m_e);
10074            let gl = m.gate_exps.expert_layout(ex);
10075            let ul = m.up_exps.expert_layout(ex);
10076            let dl = m.down_exps.expert_layout(ex);
10077
10078            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10079            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10080            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10081            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10082            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10083            let dmac = m.down_exps.macro_scale(ex);
10084            let weight_d = if dmac == 1.0 {
10085                e.htod(&grp.weights)?
10086            } else {
10087                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10088                e.htod(&scaled)?
10089            };
10090
10091            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10092            let mut gathered = e.zeros(m_e * n_embd)?;
10093            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10094            let gv = gathered.slice(0..m_e * n_embd);
10095
10096            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10097            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10098            let y = if let Some(dev) = slab_local {
10099                let gate_start = ex * m.gate_exps.expert_stride;
10100                let up_start = ex * m.up_exps.expert_stride;
10101                let down_start = ex * m.down_exps.expert_stride;
10102                if grouped_q8 {
10103                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10104                    let gate = e.qmatvec_expert_q8(
10105                        &dev.gate,
10106                        gate_start..gate_start + gl.len,
10107                        &zq,
10108                        &zd,
10109                        m_e,
10110                        m.gate_exps.in_f,
10111                        m.gate_exps.out_f,
10112                        gl.qtype,
10113                        gl.row_bytes,
10114                    )?;
10115                    let up = e.qmatvec_expert_q8(
10116                        &dev.up,
10117                        up_start..up_start + ul.len,
10118                        &zq,
10119                        &zd,
10120                        m_e,
10121                        m.up_exps.in_f,
10122                        m.up_exps.out_f,
10123                        ul.qtype,
10124                        ul.row_bytes,
10125                    )?;
10126                    let mut act = e.uninit(m_e * n_ff_exp)?;
10127                    Self::ffn_act_lim(
10128                        e,
10129                        cfg,
10130                        &gate,
10131                        &up,
10132                        m.gate_exps.macro_scale(ex),
10133                        m.up_exps.macro_scale(ex),
10134                        lim_exp,
10135                        &mut act,
10136                        m_e * n_ff_exp,
10137                    )?;
10138                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10139                    e.qmatvec_expert_q8(
10140                        &dev.down,
10141                        down_start..down_start + dl.len,
10142                        &aq2,
10143                        &ad2,
10144                        m_e,
10145                        m.down_exps.in_f,
10146                        m.down_exps.out_f,
10147                        dl.qtype,
10148                        dl.row_bytes,
10149                    )?
10150                } else {
10151                    let gate = e.qmatvec_view(
10152                        &dev.gate,
10153                        gate_start..gate_start + gl.len,
10154                        &gv,
10155                        m_e,
10156                        m.gate_exps.in_f,
10157                        m.gate_exps.out_f,
10158                        gl.qtype,
10159                        gl.row_bytes,
10160                    )?;
10161                    let up = e.qmatvec_view(
10162                        &dev.up,
10163                        up_start..up_start + ul.len,
10164                        &gv,
10165                        m_e,
10166                        m.up_exps.in_f,
10167                        m.up_exps.out_f,
10168                        ul.qtype,
10169                        ul.row_bytes,
10170                    )?;
10171                    let mut act = e.uninit(m_e * n_ff_exp)?;
10172                    Self::ffn_act_lim(
10173                        e,
10174                        cfg,
10175                        &gate,
10176                        &up,
10177                        m.gate_exps.macro_scale(ex),
10178                        m.up_exps.macro_scale(ex),
10179                        lim_exp,
10180                        &mut act,
10181                        m_e * n_ff_exp,
10182                    )?;
10183                    let actv = act.slice(0..m_e * n_ff_exp);
10184                    e.qmatvec_view(
10185                        &dev.down,
10186                        down_start..down_start + dl.len,
10187                        &actv,
10188                        m_e,
10189                        m.down_exps.in_f,
10190                        m.down_exps.out_f,
10191                        dl.qtype,
10192                        dl.row_bytes,
10193                    )?
10194                }
10195            } else if use_cache {
10196                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10197                if grouped_q8 {
10198                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10199                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10200                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10201                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10202                        eng.qmatvec_expert_q8(
10203                            cache.buf(slot),
10204                            0..gl.len,
10205                            &zq,
10206                            &zd,
10207                            m_e,
10208                            m.gate_exps.in_f,
10209                            m.gate_exps.out_f,
10210                            gl.qtype,
10211                            gl.row_bytes,
10212                        )
10213                    })?;
10214                    let up = e.with_moe_cache(max_block, |cache, eng| {
10215                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10216                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10217                        eng.qmatvec_expert_q8(
10218                            cache.buf(slot),
10219                            0..ul.len,
10220                            &zq,
10221                            &zd,
10222                            m_e,
10223                            m.up_exps.in_f,
10224                            m.up_exps.out_f,
10225                            ul.qtype,
10226                            ul.row_bytes,
10227                        )
10228                    })?;
10229                    let mut act = e.uninit(m_e * n_ff_exp)?;
10230                    Self::ffn_act_lim(
10231                        e,
10232                        cfg,
10233                        &gate,
10234                        &up,
10235                        m.gate_exps.macro_scale(ex),
10236                        m.up_exps.macro_scale(ex),
10237                        lim_exp,
10238                        &mut act,
10239                        m_e * n_ff_exp,
10240                    )?;
10241                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10242                    e.with_moe_cache(max_block, |cache, eng| {
10243                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10244                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10245                        eng.qmatvec_expert_q8(
10246                            cache.buf(slot),
10247                            0..dl.len,
10248                            &aq2,
10249                            &ad2,
10250                            m_e,
10251                            m.down_exps.in_f,
10252                            m.down_exps.out_f,
10253                            dl.qtype,
10254                            dl.row_bytes,
10255                        )
10256                    })?
10257                } else {
10258                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10259                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10260                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10261                        eng.qmatvec_view(
10262                            cache.buf(slot),
10263                            0..gl.len,
10264                            &gv,
10265                            m_e,
10266                            m.gate_exps.in_f,
10267                            m.gate_exps.out_f,
10268                            gl.qtype,
10269                            gl.row_bytes,
10270                        )
10271                    })?;
10272                    let up = e.with_moe_cache(max_block, |cache, eng| {
10273                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10274                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10275                        eng.qmatvec_view(
10276                            cache.buf(slot),
10277                            0..ul.len,
10278                            &gv,
10279                            m_e,
10280                            m.up_exps.in_f,
10281                            m.up_exps.out_f,
10282                            ul.qtype,
10283                            ul.row_bytes,
10284                        )
10285                    })?;
10286                    let mut act = e.uninit(m_e * n_ff_exp)?;
10287                    Self::ffn_act_lim(
10288                        e,
10289                        cfg,
10290                        &gate,
10291                        &up,
10292                        m.gate_exps.macro_scale(ex),
10293                        m.up_exps.macro_scale(ex),
10294                        lim_exp,
10295                        &mut act,
10296                        m_e * n_ff_exp,
10297                    )?;
10298                    let actv = act.slice(0..m_e * n_ff_exp);
10299                    e.with_moe_cache(max_block, |cache, eng| {
10300                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10301                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10302                        eng.qmatvec_view(
10303                            cache.buf(slot),
10304                            0..dl.len,
10305                            &actv,
10306                            m_e,
10307                            m.down_exps.in_f,
10308                            m.down_exps.out_f,
10309                            dl.qtype,
10310                            dl.row_bytes,
10311                        )
10312                    })?
10313                }
10314            } else {
10315                let sg = scratch_g.as_mut().unwrap();
10316                let su = scratch_u.as_mut().unwrap();
10317                let sd = scratch_d.as_mut().unwrap();
10318                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10319                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10320                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10321                if grouped_q8 {
10322                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10323                    let gate = e.qmatvec_expert_q8(
10324                        sg,
10325                        0..gl.len,
10326                        &zq,
10327                        &zd,
10328                        m_e,
10329                        m.gate_exps.in_f,
10330                        m.gate_exps.out_f,
10331                        gl.qtype,
10332                        gl.row_bytes,
10333                    )?;
10334                    let up = e.qmatvec_expert_q8(
10335                        su,
10336                        0..ul.len,
10337                        &zq,
10338                        &zd,
10339                        m_e,
10340                        m.up_exps.in_f,
10341                        m.up_exps.out_f,
10342                        ul.qtype,
10343                        ul.row_bytes,
10344                    )?;
10345                    let mut act = e.uninit(m_e * n_ff_exp)?;
10346                    Self::ffn_act_lim(
10347                        e,
10348                        cfg,
10349                        &gate,
10350                        &up,
10351                        m.gate_exps.macro_scale(ex),
10352                        m.up_exps.macro_scale(ex),
10353                        lim_exp,
10354                        &mut act,
10355                        m_e * n_ff_exp,
10356                    )?;
10357                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10358                    e.qmatvec_expert_q8(
10359                        sd,
10360                        0..dl.len,
10361                        &aq2,
10362                        &ad2,
10363                        m_e,
10364                        m.down_exps.in_f,
10365                        m.down_exps.out_f,
10366                        dl.qtype,
10367                        dl.row_bytes,
10368                    )?
10369                } else {
10370                    let gate = e.qmatvec_view(
10371                        sg,
10372                        0..gl.len,
10373                        &gv,
10374                        m_e,
10375                        m.gate_exps.in_f,
10376                        m.gate_exps.out_f,
10377                        gl.qtype,
10378                        gl.row_bytes,
10379                    )?;
10380                    let up = e.qmatvec_view(
10381                        su,
10382                        0..ul.len,
10383                        &gv,
10384                        m_e,
10385                        m.up_exps.in_f,
10386                        m.up_exps.out_f,
10387                        ul.qtype,
10388                        ul.row_bytes,
10389                    )?;
10390                    let mut act = e.uninit(m_e * n_ff_exp)?;
10391                    Self::ffn_act_lim(
10392                        e,
10393                        cfg,
10394                        &gate,
10395                        &up,
10396                        m.gate_exps.macro_scale(ex),
10397                        m.up_exps.macro_scale(ex),
10398                        lim_exp,
10399                        &mut act,
10400                        m_e * n_ff_exp,
10401                    )?;
10402                    let actv = act.slice(0..m_e * n_ff_exp);
10403                    e.qmatvec_view(
10404                        sd,
10405                        0..dl.len,
10406                        &actv,
10407                        m_e,
10408                        m.down_exps.in_f,
10409                        m.down_exps.out_f,
10410                        dl.qtype,
10411                        dl.row_bytes,
10412                    )?
10413                }
10414            };
10415
10416            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10417            e.scatter_slot(
10418                &y,
10419                &tok_idx_d,
10420                &slot_idx_d,
10421                &weight_d,
10422                &mut slot_buf,
10423                &mut wbuf,
10424                n_embd,
10425                n_used,
10426                m_e,
10427            )?;
10428        }
10429
10430        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10431        let mut moe_out = e.zeros(t * n_embd)?;
10432        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10433
10434        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10435        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10436            m_dist.sort_unstable();
10437            let active = m_dist.len();
10438            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10439            let median = m_dist[active / 2];
10440            let max_m = *m_dist.last().unwrap();
10441            let min_m = m_dist[0];
10442            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10443            println!(
10444                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10445                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10446                      above_gemm_threshold(>=16)={above16}/{active}"
10447            );
10448        }
10449
10450        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10451        Ok(moe_out)
10452    }
10453
10454    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10455    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10456    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10457    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10458    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10459    /// expert-sum order identical to the sequential path.
10460    pub(crate) fn moe_ffn_lockstep(
10461        &self,
10462        e: &Engine,
10463        m: &MoeWeights,
10464        zbatch: &CudaSlice<f32>,
10465        mrows: usize,
10466        il: u16,
10467        max_block: usize,
10468    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10469        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10470        let cfg = &self.cfg;
10471        let moe = cfg.moe.as_ref().unwrap();
10472        let n_embd = cfg.n_embd as usize;
10473        let n_expert = moe.expert_count as usize;
10474        let n_used = moe.expert_used_count as usize;
10475        let n_ff_exp = moe.expert_ff_length as usize;
10476        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10477        let lim_exp = cfg.clamp_exp_at(il as u32);
10478        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10479
10480        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10481        if let Some(sig) = cfg.sigmoid_router() {
10482            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10483        }
10484        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10485            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10486        } else {
10487            Self::moe_route_cfg(
10488                e,
10489                &logits,
10490                mrows,
10491                n_expert,
10492                n_used,
10493                m.active_experts.as_deref(),
10494            )?
10495        };
10496        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10497
10498        // Residency split at whole-expert granularity against the (frozen) cache.
10499        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10500            Ok((0..n_expert)
10501                .map(|ex| {
10502                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10503                        .into_iter()
10504                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10505                })
10506                .collect())
10507        })?;
10508
10509        struct Group {
10510            rows: Vec<i32>,
10511            slots: Vec<i32>,
10512            weights: Vec<f32>,
10513        }
10514        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10515        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10516        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10517            Default::default();
10518        for row in 0..mrows {
10519            for j in 0..n_used {
10520                let ex = sel_all[row * n_used + j] as usize;
10521                let w = w_all[row * n_used + j];
10522                if resident_expert[ex] {
10523                    let group = groups.entry(ex).or_insert_with(|| Group {
10524                        rows: Vec::new(),
10525                        slots: Vec::new(),
10526                        weights: Vec::new(),
10527                    });
10528                    group.rows.push(row as i32);
10529                    group.slots.push(j as i32);
10530                    group.weights.push(w);
10531                } else {
10532                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10533                    cpu_rows[row].push((ex, w));
10534                    cpu_by_expert.entry(ex).or_default().push((row, w));
10535                }
10536            }
10537        }
10538
10539        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10540        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10541        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10542        // order per row differs from the sequential single-call chunk — part of the
10543        // documented lockstep numeric class.
10544        let host_rows = e.dtoh(zbatch)?;
10545        let rows_ok = crate::cpu_experts::rows_supported();
10546        enum CpuPart {
10547            Single { row: usize },
10548            Rows { rows: Vec<usize> },
10549        }
10550        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10551        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10552        if rows_ok {
10553            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10554                .into_iter()
10555                .filter(|(_, rows)| rows.len() >= 2)
10556                .collect();
10557            shared.sort_by_key(|(ex, _)| *ex);
10558            for (ex, mut row_weights) in shared {
10559                row_weights.sort_by_key(|(row, _)| *row);
10560                let inputs: Vec<(&[f32], f32)> = row_weights
10561                    .iter()
10562                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10563                    .collect();
10564                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10565                    .map_err(std::io::Error::other)?;
10566                for &(row, _) in &row_weights {
10567                    rows_served.insert((row, ex));
10568                }
10569                tickets.push((
10570                    CpuPart::Rows {
10571                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10572                    },
10573                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10574                ));
10575            }
10576        }
10577        for (row, selected) in cpu_rows.iter().enumerate() {
10578            let leftover: Vec<(usize, f32)> = selected
10579                .iter()
10580                .copied()
10581                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10582                .collect();
10583            if leftover.is_empty() {
10584                continue;
10585            }
10586            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10587            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10588                .map_err(std::io::Error::other)?;
10589            tickets.push((
10590                CpuPart::Single { row },
10591                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
10592            ));
10593        }
10594
10595        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
10596        let mut wbuf = e.zeros(mrows * n_used)?;
10597        let mut order: Vec<usize> = groups.keys().copied().collect();
10598        order.sort_by(|&a, &b| {
10599            groups[&b]
10600                .rows
10601                .len()
10602                .cmp(&groups[&a].rows.len())
10603                .then(a.cmp(&b))
10604        });
10605        for &ex in &order {
10606            let group = &groups[&ex];
10607            let m_e = group.rows.len();
10608            let gl = m.gate_exps.expert_layout(ex);
10609            let ul = m.up_exps.expert_layout(ex);
10610            let dl = m.down_exps.expert_layout(ex);
10611            let row_idx_d = e.htod_i32(&group.rows)?;
10612            let slot_idx_d = e.htod_i32(&group.slots)?;
10613            let dmac = m.down_exps.macro_scale(ex);
10614            let weight_d = if dmac == 1.0 {
10615                e.htod(&group.weights)?
10616            } else {
10617                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
10618                e.htod(&scaled)?
10619            };
10620            let mut gathered = e.zeros(m_e * n_embd)?;
10621            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
10622            let gv = gathered.slice(0..m_e * n_embd);
10623            let gate = e.with_moe_cache(max_block, |c, eng| {
10624                let slot = c
10625                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
10626                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10627                eng.qmatvec_view(
10628                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10629                    0..gl.len,
10630                    &gv,
10631                    m_e,
10632                    m.gate_exps.in_f,
10633                    m.gate_exps.out_f,
10634                    gl.qtype,
10635                    gl.row_bytes,
10636                )
10637            })?;
10638            let up = e.with_moe_cache(max_block, |c, eng| {
10639                let slot = c
10640                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
10641                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10642                eng.qmatvec_view(
10643                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10644                    0..ul.len,
10645                    &gv,
10646                    m_e,
10647                    m.up_exps.in_f,
10648                    m.up_exps.out_f,
10649                    ul.qtype,
10650                    ul.row_bytes,
10651                )
10652            })?;
10653            let mut act = e.zeros(m_e * n_ff_exp)?;
10654            Self::ffn_act_lim(
10655                e,
10656                cfg,
10657                &gate,
10658                &up,
10659                m.gate_exps.macro_scale(ex),
10660                m.up_exps.macro_scale(ex),
10661                lim_exp,
10662                &mut act,
10663                m_e * n_ff_exp,
10664            )?;
10665            let actv = act.slice(0..m_e * n_ff_exp);
10666            let y = e.with_moe_cache(max_block, |c, eng| {
10667                let slot = c
10668                    .resident(BlockId::new(il, PROJ_DOWN, 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..dl.len,
10673                    &actv,
10674                    m_e,
10675                    m.down_exps.in_f,
10676                    m.down_exps.out_f,
10677                    dl.qtype,
10678                    dl.row_bytes,
10679                )
10680            })?;
10681            e.scatter_slot(
10682                &y,
10683                &row_idx_d,
10684                &slot_idx_d,
10685                &weight_d,
10686                &mut slot_buf,
10687                &mut wbuf,
10688                n_embd,
10689                n_used,
10690                m_e,
10691            )?;
10692        }
10693        let mut moe_out = e.zeros(mrows * n_embd)?;
10694        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
10695
10696        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
10697        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
10698        for (part, ticket) in tickets {
10699            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
10700            let mut add_row = |row: usize, chunk: &[f32]| {
10701                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
10702                for (accumulator, value) in sum.iter_mut().zip(chunk) {
10703                    *accumulator += value;
10704                }
10705            };
10706            match part {
10707                CpuPart::Single { row } => add_row(row, &cpu_output),
10708                CpuPart::Rows { rows } => {
10709                    for (slot, row) in rows.into_iter().enumerate() {
10710                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
10711                    }
10712                }
10713            }
10714        }
10715        for (row, sum) in row_sums.into_iter().enumerate() {
10716            let Some(sum) = sum else { continue };
10717            let cpu_output = e.htod(&sum)?;
10718            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
10719            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
10720        }
10721
10722        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10723            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10724        {
10725            let n_ff_sh = gate_shexp.out_features();
10726            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
10727            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
10728            let mut sa = e.zeros(mrows * n_ff_sh)?;
10729            Self::ffn_act_lim(
10730                e,
10731                cfg,
10732                &sg_gate,
10733                &sg_up,
10734                1.0,
10735                1.0,
10736                lim_shexp,
10737                &mut sa,
10738                mrows * n_ff_sh,
10739            )?;
10740            let sh = e.matmul(down_shexp, &sa, mrows)?;
10741            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
10742            // decode matches the single-sequence decode chain bit-for-bit.
10743            let g = match &m.gate_inp_shexp {
10744                Some(gate_inp_shexp) => {
10745                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
10746                }
10747                None => e.htod(&vec![1.0f32; mrows])?,
10748            };
10749            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
10750        }
10751
10752        Ok(moe_out)
10753    }
10754}
10755
10756// ============================ gemma4 (R8 verified wiring) ==================================
10757// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
10758// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
10759// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
10760// gemma variants after the correctness gate).
10761impl HybridModel {
10762    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
10763    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
10764    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
10765    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
10766    ///
10767    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
10768    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
10769    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
10770    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
10771    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
10772    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
10773    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
10774    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
10775    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
10776        let g = self
10777            .cfg
10778            .gemma4
10779            .as_ref()
10780            .expect("gemma4_rope_dims on a non-gemma4 config");
10781        if g.swa_pattern[il] {
10782            g.rope_dims_swa as usize
10783        } else {
10784            g.rope_dims_global as usize
10785        }
10786    }
10787
10788    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
10789        let g = self.cfg.gemma4.as_ref().unwrap();
10790        let swa = g.swa_pattern[il];
10791        let hd = if swa {
10792            g.key_length_swa
10793        } else {
10794            g.key_length_global
10795        } as usize;
10796        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
10797        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
10798        // rows exact (softmax over one element) while every later position drifted).
10799        (
10800            hd,
10801            g.head_count_kv[il] as usize,
10802            self.cfg.n_head as usize,
10803            if swa {
10804                g.rope_base_swa
10805            } else {
10806                g.rope_base_global
10807            },
10808            1.0,
10809            swa,
10810        )
10811    }
10812
10813    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
10814    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
10815    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
10816    pub(crate) fn gemma4_suppress(
10817        &self,
10818        e: &Engine,
10819        ld: &mut CudaSlice<f32>,
10820        t: usize,
10821    ) -> Result<(), Box<dyn std::error::Error>> {
10822        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
10823            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
10824            // stage as primary, and this tail runs only after the last stage). The assert turns
10825            // that argued invariant into a checked one: any topology violating primary==head
10826            // trips here in debug instead of silently peer-reading a device-0 buffer.
10827            #[cfg(debug_assertions)]
10828            crate::debug_assert_tensor_stream_device(
10829                ids,
10830                &e.stream(),
10831                "gemma4_suppress.suppress_d",
10832            );
10833            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
10834        }
10835        Ok(())
10836    }
10837
10838    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
10839    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
10840    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
10841    /// only (v0): attends within `tokens` via the f32 sdpa.
10842    #[allow(clippy::too_many_arguments)]
10843    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
10844    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
10845    /// switching program at `t > sliding_window`. The door is the measured cause of the
10846    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
10847    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
10848    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
10849    /// published prefix KV stops depending on the total prompt length. Off by default because
10850    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
10851    fn gemma_fa_one_program() -> bool {
10852        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10853        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
10854    }
10855
10856    fn gemma4_attn_prime(
10857        &self,
10858        e: &Engine,
10859        fa: &crate::hybrid::FullAttnLayer,
10860        il: usize,
10861        h: &CudaSlice<f32>,
10862        pos_d: &CudaSlice<i32>,
10863        t: usize,
10864        cache: Option<&mut Cache>,
10865        island: Option<&CudaSlice<i32>>,
10866    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10867        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
10868        let eps = self.cfg.rms_eps;
10869        let aux = self.gemma4_aux.as_ref().unwrap();
10870        let ones = aux.ones(e);
10871        #[cfg(debug_assertions)]
10872        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
10873
10874        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
10875        // (h stays borrowed across the triple, so the cache key can't go stale).
10876        e.mmq_act_begin();
10877        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
10878        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10879            let v = e.dtoh(&q0)?;
10880            let nan = v.iter().filter(|x| x.is_nan()).count();
10881            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10882            eprintln!(
10883                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
10884                v.len()
10885            );
10886        }
10887        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
10888        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
10889        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
10890        let v0 = if swa {
10891            e.matmul(&fa.wv, h, t)?
10892        } else {
10893            e.clone_dtod(&k0)?
10894        };
10895        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10896            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
10897                let v = e.dtoh(buf)?;
10898                let nan = v.iter().filter(|x| x.is_nan()).count();
10899                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10900                eprintln!(
10901                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
10902                    v.len()
10903                );
10904            }
10905        }
10906
10907        let mut q = e.uninit(t * nh * hd)?;
10908        let mut k = e.uninit(t * nkv * hd)?;
10909        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
10910        let mut v = e.uninit(t * nkv * hd)?;
10911        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
10912        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
10913        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
10914        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10915        // Island primes take the mask-capable naive kernel below; keep the operands f32
10916        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
10917        let emit = island.is_none()
10918            && t >= 16
10919            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
10920            && *EMIT.get_or_init(|| {
10921                std::env::var("MEMRA_FA_EMIT")
10922                    .map(|s| s != "0")
10923                    .unwrap_or(true)
10924            });
10925        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
10926        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10927        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10928        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
10929        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
10930        let v_f16 = emit
10931            && crate::fa_f16pv_on()
10932            && match hd {
10933                512 => true,
10934                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
10935                _ => false,
10936            };
10937        if emit {
10938            e.rms_norm_qkv_w4b(
10939                &q0,
10940                &k0,
10941                &v0,
10942                fa.q_norm.float_data(),
10943                fa.k_norm.float_data(),
10944                ones,
10945                &mut q,
10946                &mut k,
10947                &mut v,
10948                &mut vb,
10949                hd,
10950                nh * t,
10951                nkv * t,
10952                eps,
10953                v_f16,
10954            )?;
10955        } else {
10956            e.rms_norm_qkv(
10957                &q0,
10958                &k0,
10959                &v0,
10960                fa.q_norm.float_data(),
10961                fa.k_norm.float_data(),
10962                ones,
10963                &mut q,
10964                &mut k,
10965                &mut v,
10966                hd,
10967                nh * t,
10968                nkv * t,
10969                eps,
10970            )?;
10971        }
10972
10973        let ff = if swa {
10974            None
10975        } else {
10976            Some(
10977                aux.rope_freqs(e)
10978                    .expect("gemma4 global rope needs rope_freqs.weight"),
10979            )
10980        };
10981        #[cfg(debug_assertions)]
10982        if let Some(ff) = ff {
10983            crate::debug_assert_tensor_stream_device(
10984                ff,
10985                &e.stream(),
10986                "gemma4_attn_prime.rope_freqs",
10987            );
10988        }
10989        if emit {
10990            e.rope_neox2_bf16e(
10991                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
10992            )?;
10993        } else {
10994            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
10995        }
10996
10997        if let Some(cache) = cache {
10998            let kvl = cache.kv[il].as_mut().unwrap();
10999            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11000            e.append_kv_quantized_rows(
11001                &k,
11002                &v,
11003                &mut kvl.k,
11004                &mut kvl.v,
11005                kvl.len,
11006                t,
11007                kvl.kv_dim_k,
11008                kvl.kv_dim_v,
11009                kvl.k_tok_bytes,
11010                kvl.v_tok_bytes,
11011                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11012            )?;
11013            kvl.len += t;
11014        }
11015        let mut attn = e.zeros(t * nh * hd)?;
11016        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11017        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11018        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11019        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11020        if let Some(span) = island {
11021            // Masked-prefill arm: every layer routes through the island-aware naive
11022            // kernel (correctness-first, same posture as the vision tower v1). The
11023            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11024            // window, the real window beyond it.
11025            let w = if swa && t > win { win } else { 0 };
11026            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11027        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11028            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11029                if emit {
11030                    e.fa_prefill_w_pre(
11031                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11032                    )?;
11033                } else {
11034                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11035                }
11036            } else {
11037                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11038            }
11039        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11040            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11041        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11042            if emit {
11043                e.fa_prefill_hd512_pre(
11044                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11045                )?;
11046            } else {
11047                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11048            }
11049        } else {
11050            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11051        }
11052        Ok(e.matmul(&fa.wo, &attn, t)?)
11053    }
11054
11055    /// Back-compat wrapper (pure prefill, no cache).
11056    fn gemma4_attn(
11057        &self,
11058        e: &Engine,
11059        fa: &crate::hybrid::FullAttnLayer,
11060        il: usize,
11061        h: &CudaSlice<f32>,
11062        pos_d: &CudaSlice<i32>,
11063        t: usize,
11064    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11065        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11066    }
11067
11068    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11069    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11070    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11071    /// the q8z epilogue is quantize_q8_1 verbatim).
11072    fn gemma4_moe_q8(
11073        &self,
11074        e: &Engine,
11075        m: &crate::hybrid::MoeWeights,
11076        bits: &crate::hybrid::Gemma4MoeBits,
11077        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11078        router_in: &CudaSlice<f32>,
11079        t: usize,
11080    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11081        let cfg = &self.cfg;
11082        let moe = cfg.moe.as_ref().unwrap();
11083        let n_embd = cfg.n_embd as usize;
11084        let n_expert = moe.expert_count as usize;
11085        let n_used = moe.expert_used_count as usize;
11086        let n_ff_exp = moe.expert_ff_length as usize;
11087        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11088        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11089        // the pair's 12us is kernel time, not launch gaps.
11090        let logits = if crate::router_kernel_on() {
11091            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11092        } else {
11093            e.matmul(&m.gate_inp, router_in, t)?
11094        };
11095        let dev = m.dev_exps.as_ref().unwrap();
11096        let (sel_d, w_d) =
11097            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11098        let (zq, zd) = mq;
11099        if t == 1 {
11100            let selv = sel_d.slice(0..n_used);
11101            let wv = w_d.slice(0..n_used);
11102            let act = e.moe_gate_up_gelu8_dev_q8(
11103                &dev.ptr_row,
11104                &selv,
11105                zq,
11106                zd,
11107                n_embd,
11108                n_ff_exp,
11109                n_used,
11110                n_expert,
11111                m.gate_exps.qtype,
11112                m.up_exps.qtype,
11113                m.gate_exps.row_bytes,
11114                m.up_exps.row_bytes,
11115            )?;
11116            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11117            let mut moe_out = e.uninit(n_embd)?;
11118            e.moe_down8_fma_dev_q8(
11119                &dev.ptr_row,
11120                &selv,
11121                &wv,
11122                &aq2,
11123                &ad2,
11124                &mut moe_out.slice_mut(0..n_embd),
11125                n_ff_exp,
11126                n_embd,
11127                n_used,
11128                n_expert,
11129                m.down_exps.qtype,
11130                m.down_exps.row_bytes,
11131            )?;
11132            return Ok(moe_out);
11133        }
11134        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11135        let act = if csr {
11136            e.moe_gate_up_gelu8_dev_q8_csr(
11137                &dev.ptr_row,
11138                &sel_d,
11139                zq,
11140                zd,
11141                t * n_used,
11142                n_embd,
11143                n_ff_exp,
11144                n_used,
11145                n_expert,
11146                m.gate_exps.qtype,
11147                m.up_exps.qtype,
11148                m.gate_exps.row_bytes,
11149                m.up_exps.row_bytes,
11150            )?
11151        } else {
11152            e.moe_gate_up_gelu8_dev_q8_rows(
11153                &dev.ptr_row,
11154                &sel_d,
11155                zq,
11156                zd,
11157                t,
11158                n_embd,
11159                n_ff_exp,
11160                n_used,
11161                n_expert,
11162                m.gate_exps.qtype,
11163                m.up_exps.qtype,
11164                m.gate_exps.row_bytes,
11165                m.up_exps.row_bytes,
11166            )?
11167        };
11168        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11169        let mut moe_out = e.uninit(t * n_embd)?;
11170        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11171        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11172        e.moe_down8_fma_dev_q8_rows_g(
11173            &dev.ptr_row,
11174            &sel_d,
11175            &w_d,
11176            &aq2,
11177            &ad2,
11178            &mut moe_out,
11179            t,
11180            n_ff_exp,
11181            n_embd,
11182            n_used,
11183            n_expert,
11184            m.down_exps.qtype,
11185            m.down_exps.row_bytes,
11186        )?;
11187        Ok(moe_out)
11188    }
11189
11190    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11191    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11192    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11193    fn gemma4_moe(
11194        &self,
11195        e: &Engine,
11196        m: &crate::hybrid::MoeWeights,
11197        bits: &crate::hybrid::Gemma4MoeBits,
11198        moe_in: &CudaSlice<f32>,
11199        router_in: &CudaSlice<f32>,
11200        t: usize,
11201    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11202        let cfg = &self.cfg;
11203        let moe = cfg.moe.as_ref().unwrap();
11204        let n_embd = cfg.n_embd as usize;
11205        let n_expert = moe.expert_count as usize;
11206        let n_used = moe.expert_used_count as usize;
11207        let n_ff_exp = moe.expert_ff_length as usize;
11208
11209        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11210        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11211        // batched matmul only at real prefill.
11212        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11213            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11214        } else {
11215            e.matmul(&m.gate_inp, router_in, t)?
11216        };
11217
11218        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11219        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11220        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11221        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11222        if t < PRIME_MIN_T
11223            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11224            && expert_dp4a_supported(m.gate_exps.qtype)
11225            && expert_dp4a_supported(m.up_exps.qtype)
11226            && expert_dp4a_supported(m.down_exps.qtype)
11227            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11228        {
11229            let dev = m.dev_exps.as_ref().unwrap();
11230            let (sel_d, w_d) =
11231                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11232            if t == 1 {
11233                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11234                let selv = sel_d.slice(0..n_used);
11235                let wv = w_d.slice(0..n_used);
11236                let act = e.moe_gate_up_gelu8_dev_q8(
11237                    &dev.ptr_row,
11238                    &selv,
11239                    &zq,
11240                    &zd,
11241                    n_embd,
11242                    n_ff_exp,
11243                    n_used,
11244                    n_expert,
11245                    m.gate_exps.qtype,
11246                    m.up_exps.qtype,
11247                    m.gate_exps.row_bytes,
11248                    m.up_exps.row_bytes,
11249                )?;
11250                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11251                let mut moe_out = e.uninit(n_embd)?;
11252                e.moe_down8_fma_dev_q8(
11253                    &dev.ptr_row,
11254                    &selv,
11255                    &wv,
11256                    &aq2,
11257                    &ad2,
11258                    &mut moe_out.slice_mut(0..n_embd),
11259                    n_ff_exp,
11260                    n_embd,
11261                    n_used,
11262                    n_expert,
11263                    m.down_exps.qtype,
11264                    m.down_exps.row_bytes,
11265                )?;
11266                return Ok(moe_out);
11267            }
11268            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11269            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11270            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11271            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11272            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11273            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11274            let act = if csr {
11275                e.moe_gate_up_gelu8_dev_q8_csr(
11276                    &dev.ptr_row,
11277                    &sel_d,
11278                    &zq,
11279                    &zd,
11280                    t * n_used,
11281                    n_embd,
11282                    n_ff_exp,
11283                    n_used,
11284                    n_expert,
11285                    m.gate_exps.qtype,
11286                    m.up_exps.qtype,
11287                    m.gate_exps.row_bytes,
11288                    m.up_exps.row_bytes,
11289                )?
11290            } else {
11291                e.moe_gate_up_gelu8_dev_q8_rows(
11292                    &dev.ptr_row,
11293                    &sel_d,
11294                    &zq,
11295                    &zd,
11296                    t,
11297                    n_embd,
11298                    n_ff_exp,
11299                    n_used,
11300                    n_expert,
11301                    m.gate_exps.qtype,
11302                    m.up_exps.qtype,
11303                    m.gate_exps.row_bytes,
11304                    m.up_exps.row_bytes,
11305                )?
11306            };
11307            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11308            let mut moe_out = e.uninit(t * n_embd)?;
11309            e.moe_down8_fma_dev_q8_rows_g(
11310                &dev.ptr_row,
11311                &sel_d,
11312                &w_d,
11313                &aq2,
11314                &ad2,
11315                &mut moe_out,
11316                t,
11317                n_ff_exp,
11318                n_embd,
11319                n_used,
11320                n_expert,
11321                m.down_exps.qtype,
11322                m.down_exps.row_bytes,
11323            )?;
11324            return Ok(moe_out);
11325        }
11326
11327        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11328        for (i, &sx) in sel_all.iter().enumerate() {
11329            w_all[i] *= bits.per_expert_scale[sx as usize];
11330        }
11331
11332        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11333        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11334        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11335        if t >= PRIME_MIN_T
11336            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11337            && expert_dp4a_supported(m.gate_exps.qtype)
11338            && expert_dp4a_supported(m.up_exps.qtype)
11339            && expert_dp4a_supported(m.down_exps.qtype)
11340            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11341        {
11342            let dev = m.dev_exps.as_ref().unwrap();
11343            let n_pairs = t * n_used;
11344            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11345            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11346            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11347            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11348            let pt = e.htod_i32(&pair_tok)?;
11349            let pw = e.htod(&w_all)?;
11350            let toff = e.htod_i32(&tok_off)?;
11351            let tids = e.htod_i32(&tok_ids)?;
11352            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11353            for p in 0..n_pairs {
11354                by_ex[pair_ex[p] as usize].push(p as i32);
11355            }
11356            let mut ex_ids: Vec<i32> = Vec::new();
11357            let mut ex_off: Vec<i32> = vec![0];
11358            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11359            for (ex, list) in by_ex.iter().enumerate() {
11360                if list.is_empty() {
11361                    continue;
11362                }
11363                ex_ids.push(ex as i32);
11364                ex_pairs.extend_from_slice(list);
11365                ex_off.push(ex_pairs.len() as i32);
11366            }
11367            let n_active = ex_ids.len();
11368            let exi = e.htod_i32(&ex_ids)?;
11369            let exo = e.htod_i32(&ex_off)?;
11370            let exp_d = e.htod_i32(&ex_pairs)?;
11371            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11372            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11373            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11374            // ragged down k (704) needs no padding here — cublas takes any k.
11375            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11376            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11377            // Hopper default — see moe_f16g_gemma_on.
11378            if crate::moe_f16g_gemma_on()
11379                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11380                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11381                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11382            {
11383                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11384                let csr_tok_d = e.htod_i32(&csr_tok)?;
11385                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11386                let g_csr = e.moe_f16_grouped(
11387                    &dev.ptr_row,
11388                    0,
11389                    n_expert,
11390                    &exi,
11391                    &ex_off,
11392                    &exo,
11393                    &z_f16,
11394                    &z_s,
11395                    n_embd,
11396                    n_ff_exp,
11397                    n_active,
11398                    n_pairs,
11399                    m.gate_exps.qtype,
11400                    m.gate_exps.row_bytes,
11401                )?;
11402                let u_csr = e.moe_f16_grouped(
11403                    &dev.ptr_row,
11404                    1,
11405                    n_expert,
11406                    &exi,
11407                    &ex_off,
11408                    &exo,
11409                    &z_f16,
11410                    &z_s,
11411                    n_embd,
11412                    n_ff_exp,
11413                    n_active,
11414                    n_pairs,
11415                    m.up_exps.qtype,
11416                    m.up_exps.row_bytes,
11417                )?;
11418                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11419                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11420                let d_csr = e.moe_f16_grouped(
11421                    &dev.ptr_row,
11422                    2,
11423                    n_expert,
11424                    &exi,
11425                    &ex_off,
11426                    &exo,
11427                    &a_f16,
11428                    &a_s,
11429                    n_ff_exp,
11430                    n_embd,
11431                    n_active,
11432                    n_pairs,
11433                    m.down_exps.qtype,
11434                    m.down_exps.row_bytes,
11435                )?;
11436                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11437                let mut moe_out = e.uninit(t * n_embd)?;
11438                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11439                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11440                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11441                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11442                    eprintln!(
11443                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11444                        scan(&yd),
11445                        scan(&mo)
11446                    );
11447                }
11448                return Ok(moe_out);
11449            }
11450            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11451            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11452            let mma =
11453                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11454            let (gate, up) = if mma {
11455                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11456                (
11457                    e.mmq_iq_experts(
11458                        &dev.ptr_row,
11459                        0,
11460                        n_expert,
11461                        &exi,
11462                        &exo,
11463                        &exp_d,
11464                        &pt,
11465                        &z_scr,
11466                        n_embd,
11467                        n_ff_exp,
11468                        n_active,
11469                        n_pairs,
11470                        t,
11471                        m.gate_exps.qtype,
11472                        m.gate_exps.row_bytes,
11473                    )?,
11474                    e.mmq_iq_experts(
11475                        &dev.ptr_row,
11476                        1,
11477                        n_expert,
11478                        &exi,
11479                        &exo,
11480                        &exp_d,
11481                        &pt,
11482                        &z_scr,
11483                        n_embd,
11484                        n_ff_exp,
11485                        n_active,
11486                        n_pairs,
11487                        t,
11488                        m.up_exps.qtype,
11489                        m.up_exps.row_bytes,
11490                    )?,
11491                )
11492            } else {
11493                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11494                (
11495                    e.moe_pairs_matvec_q8_dec(
11496                        &dev.ptr_row,
11497                        0,
11498                        &exi,
11499                        &exo,
11500                        &exp_d,
11501                        &pt,
11502                        &zq,
11503                        &zd,
11504                        n_embd,
11505                        n_ff_exp,
11506                        n_expert,
11507                        n_active,
11508                        n_pairs,
11509                        m.gate_exps.qtype,
11510                        m.gate_exps.row_bytes,
11511                    )?,
11512                    e.moe_pairs_matvec_q8_dec(
11513                        &dev.ptr_row,
11514                        1,
11515                        &exi,
11516                        &exo,
11517                        &exp_d,
11518                        &pt,
11519                        &zq,
11520                        &zd,
11521                        n_embd,
11522                        n_ff_exp,
11523                        n_expert,
11524                        n_active,
11525                        n_pairs,
11526                        m.up_exps.qtype,
11527                        m.up_exps.row_bytes,
11528                    )?,
11529                )
11530            };
11531            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11532            let pself = e.htod_i32(&pair_self)?;
11533            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11534            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11535            // to the 256-val superblock (768) while the act quantizer's zero padding
11536            // makes every padded-k product exactly zero (weight overread bytes multiply
11537            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11538            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11539            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11540            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11541            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11542            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11543            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11544            let y_down = if mma {
11545                let in_pad = n_ff_exp.div_ceil(256) * 256;
11546                let a_scr = if crate::moe_fuse_actq_on() {
11547                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11548                } else {
11549                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11550                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11551                };
11552                e.mmq_iq_experts(
11553                    &dev.ptr_row,
11554                    2,
11555                    n_expert,
11556                    &exi,
11557                    &exo,
11558                    &exp_d,
11559                    &pself,
11560                    &a_scr,
11561                    in_pad,
11562                    n_embd,
11563                    n_active,
11564                    n_pairs,
11565                    n_pairs,
11566                    m.down_exps.qtype,
11567                    m.down_exps.row_bytes,
11568                )?
11569            } else {
11570                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11571                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11572                e.moe_pairs_matvec_q8_dec(
11573                    &dev.ptr_row,
11574                    2,
11575                    &exi,
11576                    &exo,
11577                    &exp_d,
11578                    &pself,
11579                    &aq2,
11580                    &ad2,
11581                    n_ff_exp,
11582                    n_embd,
11583                    n_expert,
11584                    n_active,
11585                    n_pairs,
11586                    m.down_exps.qtype,
11587                    m.down_exps.row_bytes,
11588                )?
11589            };
11590            let mut moe_out = e.uninit(t * n_embd)?;
11591            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11592            return Ok(moe_out);
11593        }
11594
11595        let g_len = m.gate_exps.expert_stride;
11596        let u_len = m.up_exps.expert_stride;
11597        let d_len = m.down_exps.expert_stride;
11598        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
11599        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
11600        // the spill fallback.
11601        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
11602        let (mut sg, mut su, mut sd) = if dev.is_some() {
11603            (None, None, None)
11604        } else {
11605            (
11606                Some(e.alloc_u8_uninit(g_len)?),
11607                Some(e.alloc_u8_uninit(u_len)?),
11608                Some(e.alloc_u8_uninit(d_len)?),
11609            )
11610        };
11611        let mut moe_out = e.zeros(t * n_embd)?;
11612        for tok in 0..t {
11613            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
11614            let w = &w_all[tok * n_used..(tok + 1) * n_used];
11615            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
11616            for (j, &ex) in sel.iter().enumerate() {
11617                let ex = ex as usize;
11618                let gate = match dev {
11619                    Some(d) => e.qmatvec_view(
11620                        &d.gate,
11621                        ex * g_len..(ex + 1) * g_len,
11622                        &zt,
11623                        1,
11624                        m.gate_exps.in_f,
11625                        m.gate_exps.out_f,
11626                        m.gate_exps.qtype,
11627                        m.gate_exps.row_bytes,
11628                    )?,
11629                    None => {
11630                        let sg = sg.as_mut().unwrap();
11631                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
11632                        e.qmatvec_view(
11633                            sg,
11634                            0..g_len,
11635                            &zt,
11636                            1,
11637                            m.gate_exps.in_f,
11638                            m.gate_exps.out_f,
11639                            m.gate_exps.qtype,
11640                            m.gate_exps.row_bytes,
11641                        )?
11642                    }
11643                };
11644                let up = match dev {
11645                    Some(d) => e.qmatvec_view(
11646                        &d.up,
11647                        ex * u_len..(ex + 1) * u_len,
11648                        &zt,
11649                        1,
11650                        m.up_exps.in_f,
11651                        m.up_exps.out_f,
11652                        m.up_exps.qtype,
11653                        m.up_exps.row_bytes,
11654                    )?,
11655                    None => {
11656                        let su = su.as_mut().unwrap();
11657                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
11658                        e.qmatvec_view(
11659                            su,
11660                            0..u_len,
11661                            &zt,
11662                            1,
11663                            m.up_exps.in_f,
11664                            m.up_exps.out_f,
11665                            m.up_exps.qtype,
11666                            m.up_exps.row_bytes,
11667                        )?
11668                    }
11669                };
11670                let mut act = e.uninit(n_ff_exp)?;
11671                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
11672                let actv = act.slice(0..n_ff_exp);
11673                let y = match dev {
11674                    Some(d) => e.qmatvec_view(
11675                        &d.down,
11676                        ex * d_len..(ex + 1) * d_len,
11677                        &actv,
11678                        1,
11679                        m.down_exps.in_f,
11680                        m.down_exps.out_f,
11681                        m.down_exps.qtype,
11682                        m.down_exps.row_bytes,
11683                    )?,
11684                    None => {
11685                        let sd = sd.as_mut().unwrap();
11686                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
11687                        e.qmatvec_view(
11688                            sd,
11689                            0..d_len,
11690                            &actv,
11691                            1,
11692                            m.down_exps.in_f,
11693                            m.down_exps.out_f,
11694                            m.down_exps.qtype,
11695                            m.down_exps.row_bytes,
11696                        )?
11697                    }
11698                };
11699                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
11700                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
11701            }
11702        }
11703        Ok(moe_out)
11704    }
11705
11706    /// One gemma4 trunk layer (R8): x -> x_next.
11707    fn gemma4_layer(
11708        &self,
11709        e: &Engine,
11710        il: usize,
11711        layer: &crate::hybrid::HybridLayer,
11712        x: &CudaSlice<f32>,
11713        pos_d: &CudaSlice<i32>,
11714        t: usize,
11715    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11716        let n_embd = self.cfg.n_embd as usize;
11717        let eps = self.cfg.rms_eps;
11718
11719        let mut h = e.zeros(t * n_embd)?;
11720        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
11721        let Mixer::Full(fa) = &layer.mixer else {
11722            panic!("gemma4 layer {il} not full-attn")
11723        };
11724        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
11725        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
11726        let mut cur = e.zeros(t * n_embd)?;
11727        e.rms_norm(
11728            &o,
11729            layer.post_attn_norm.float_data(),
11730            &mut cur,
11731            n_embd,
11732            t,
11733            eps,
11734        )?;
11735        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
11736    }
11737
11738    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
11739    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
11740    /// layer scale — shared verbatim by the prefill, decode and verify paths.
11741    fn gemma4_layer_tail_add(
11742        &self,
11743        e: &Engine,
11744        layer: &crate::hybrid::HybridLayer,
11745        cur: &CudaSlice<f32>,
11746        x: &CudaSlice<f32>,
11747        t: usize,
11748    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11749        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
11750    }
11751
11752    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
11753    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
11754    fn gemma4_layer_tail_add_n(
11755        &self,
11756        e: &Engine,
11757        layer: &crate::hybrid::HybridLayer,
11758        cur: &CudaSlice<f32>,
11759        x: &CudaSlice<f32>,
11760        t: usize,
11761        next_norm: Option<&CudaSlice<f32>>,
11762    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
11763        let n_embd = self.cfg.n_embd as usize;
11764        let bits = layer.gemma4.as_ref().unwrap();
11765        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
11766        let mut xn = e.uninit(t * n_embd)?;
11767        match next_norm {
11768            Some(w) => {
11769                let mut hn = e.uninit(t * n_embd)?;
11770                e.add_scale_rms_norm(
11771                    &sn,
11772                    &attn_out,
11773                    bits.layer_scale,
11774                    w,
11775                    &mut xn,
11776                    &mut hn,
11777                    n_embd,
11778                    t,
11779                    self.cfg.rms_eps,
11780                )?;
11781                Ok((xn, Some(hn)))
11782            }
11783            None => {
11784                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
11785                Ok((xn, None))
11786            }
11787        }
11788    }
11789
11790    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
11791    /// norm — returns (sn, attn_out) for the closing add+scale variants.
11792    fn gemma4_layer_tail_core(
11793        &self,
11794        e: &Engine,
11795        layer: &crate::hybrid::HybridLayer,
11796        cur: &CudaSlice<f32>,
11797        x: &CudaSlice<f32>,
11798        t: usize,
11799    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11800        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
11801    }
11802
11803    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
11804    /// means `cur` is the RAW attention output and the dense entry runs
11805    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
11806    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
11807    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
11808    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
11809    fn gemma4_layer_tail_core_pn(
11810        &self,
11811        e: &Engine,
11812        layer: &crate::hybrid::HybridLayer,
11813        cur: &CudaSlice<f32>,
11814        x: &CudaSlice<f32>,
11815        t: usize,
11816        pre_norm: Option<&CudaSlice<f32>>,
11817        defer_post_norm: bool,
11818    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11819        let n_embd = self.cfg.n_embd as usize;
11820        let eps = self.cfg.rms_eps;
11821        let bits = layer.gemma4.as_ref().unwrap();
11822
11823        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
11824        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
11825        let Some(mbits) = bits.moe_bits.as_ref() else {
11826            let crate::hybrid::Ffn::Dense {
11827                ffn_gate,
11828                ffn_up,
11829                ffn_down,
11830            } = &layer.ffn
11831            else {
11832                panic!("gemma4 dense layer without Dense ffn")
11833            };
11834            let mut attn_out = e.uninit(t * n_embd)?;
11835            let mut zsh = e.uninit(t * n_embd)?;
11836            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
11837            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
11838            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
11839            match pre_norm {
11840                Some(wa) if t == 1 => {
11841                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
11842                        cur,
11843                        wa,
11844                        x,
11845                        bits.ffn_norm.float_data(),
11846                        &mut attn_out,
11847                        &mut zsh,
11848                        n_embd,
11849                        t,
11850                        eps,
11851                    )?);
11852                }
11853                Some(wa) => e.rms_pre_add_rms_norm(
11854                    cur,
11855                    wa,
11856                    x,
11857                    bits.ffn_norm.float_data(),
11858                    &mut attn_out,
11859                    &mut zsh,
11860                    n_embd,
11861                    t,
11862                    eps,
11863                )?,
11864                None => e.add_rms_norm(
11865                    cur,
11866                    x,
11867                    bits.ffn_norm.float_data(),
11868                    &mut attn_out,
11869                    &mut zsh,
11870                    n_embd,
11871                    t,
11872                    eps,
11873                )?,
11874            }
11875            let n_ff = ffn_gate.out_features();
11876            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
11877            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
11878            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
11879            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
11880            // rescue segment C — the megakernel front is closed for the dense tail.
11881            let (gate, up) = if t == 1 {
11882                let (zq, zd) = match zpair {
11883                    Some(p) => p,
11884                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
11885                };
11886                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
11887                    Some(p) => p,
11888                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
11889                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
11890                        Some(p) => p,
11891                        None => (
11892                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
11893                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
11894                        ),
11895                    },
11896                }
11897            } else {
11898                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
11899                // launch for the verify's gate+up — the up segment's blocks fill SMs as
11900                // the gate segment drains (the launch-tail mechanism behind the b-tier
11901                // plateau; first positive after six falsified in-kernel variants).
11902                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11903                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
11904                let fused = if f2b {
11905                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
11906                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
11907                } else {
11908                    None
11909                };
11910                match fused {
11911                    Some(p) => p,
11912                    None => {
11913                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
11914                        e.mmq_act_begin();
11915                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
11916                    }
11917                }
11918            };
11919            let mut act = e.uninit(t * n_ff)?;
11920            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
11921            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
11922            let f0 = if e.uses_q8_1_fast(ffn_down) {
11923                let upv = e.view(&up, t * n_ff);
11924                let up_all = upv.slice(0..t * n_ff);
11925                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
11926                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
11927            } else {
11928                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11929                e.matmul(ffn_down, &act, t)?
11930            };
11931            if defer_post_norm {
11932                return Ok((f0, attn_out));
11933            }
11934            let mut sn = e.uninit(t * n_embd)?;
11935            e.rms_norm(
11936                &f0,
11937                bits.post_ffw_norm.float_data(),
11938                &mut sn,
11939                n_embd,
11940                t,
11941                eps,
11942            )?;
11943            return Ok((sn, attn_out));
11944        };
11945
11946        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
11947        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
11948        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
11949        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
11950        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
11951        let mut attn_out = e.uninit(t * n_embd)?;
11952        let mut router_in = e.uninit(t * n_embd)?;
11953        let fast_moe = match &layer.ffn {
11954            crate::hybrid::Ffn::Moe(m) => {
11955                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11956                    && expert_dp4a_supported(m.gate_exps.qtype)
11957                    && expert_dp4a_supported(m.up_exps.qtype)
11958                    && expert_dp4a_supported(m.down_exps.qtype)
11959                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11960            }
11961            _ => false,
11962        };
11963        let q8z = t < PRIME_MIN_T && fast_moe;
11964        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
11965            let (z0, m2) = e.add_rms_norm3_q8z(
11966                cur,
11967                x,
11968                bits.ffn_norm.float_data(),
11969                &mbits.router_scale_pre,
11970                mbits.pre_ffw_norm_2.float_data(),
11971                &mut attn_out,
11972                &mut router_in,
11973                n_embd,
11974                t,
11975                eps,
11976            )?;
11977            (None, Some(z0), Some(m2))
11978        } else {
11979            let mut zsh = e.uninit(t * n_embd)?;
11980            let mut moe_in = e.uninit(t * n_embd)?;
11981            e.add_rms_norm3(
11982                cur,
11983                x,
11984                bits.ffn_norm.float_data(),
11985                &mbits.router_scale_pre,
11986                mbits.pre_ffw_norm_2.float_data(),
11987                &mut attn_out,
11988                &mut zsh,
11989                &mut router_in,
11990                &mut moe_in,
11991                n_embd,
11992                t,
11993                eps,
11994            )?;
11995            (Some((zsh, moe_in)), None, None)
11996        };
11997        let attn_out2 = attn_out;
11998        #[allow(unused_variables)]
11999        let attn_out = &attn_out2;
12000        let n_ff = mbits.shared_gate.out_features();
12001        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12002            if t == 1 {
12003                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12004                    Some(p) => p,
12005                    None => match e.matmul_nvfp4_fused2(
12006                        &mbits.shared_gate,
12007                        &mbits.shared_up,
12008                        zq,
12009                        zd,
12010                        1,
12011                    )? {
12012                        Some(p) => p,
12013                        None => {
12014                            let h0 = e.zeros(0)?;
12015                            (
12016                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12017                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12018                            )
12019                        }
12020                    },
12021                }
12022            } else {
12023                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12024                let h0 = e.zeros(0)?;
12025                (
12026                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12027                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12028                )
12029            }
12030        } else {
12031            let (zsh, _) = zsh_f32.as_ref().unwrap();
12032            (
12033                e.matmul(&mbits.shared_gate, zsh, t)?,
12034                e.matmul(&mbits.shared_up, zsh, t)?,
12035            )
12036        };
12037        let mut act = e.uninit(t * n_ff)?;
12038        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12039        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12040        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12041            panic!("gemma4 layer not MoE")
12042        };
12043        let moe0 = match (&moe_q8, &zsh_f32) {
12044            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12045            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12046            _ => unreachable!(),
12047        };
12048        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12049        let mut mlp = e.uninit(t * n_embd)?;
12050        let mut moe = e.uninit(t * n_embd)?;
12051        e.rms_norm2x(
12052            &mlp0,
12053            &moe0,
12054            mbits.post_ffw_norm_1.float_data(),
12055            mbits.post_ffw_norm_2.float_data(),
12056            &mut mlp,
12057            &mut moe,
12058            n_embd,
12059            t,
12060            eps,
12061        )?;
12062
12063        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12064        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12065        let mut sum = e.uninit(t * n_embd)?;
12066        let mut sn = e.uninit(t * n_embd)?;
12067        e.add_rms_norm(
12068            &mlp,
12069            &moe,
12070            bits.post_ffw_norm.float_data(),
12071            &mut sum,
12072            &mut sn,
12073            n_embd,
12074            t,
12075            eps,
12076        )?;
12077        Ok((sn, attn_out2))
12078    }
12079
12080    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12081    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12082    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12083    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12084    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12085    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12086    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12087    /// decode == verify == graph parity holds by construction at either seam value.
12088    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12089    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12090        &self,
12091        e: &Engine,
12092        layer: &crate::hybrid::HybridLayer,
12093        o: &CudaSlice<f32>,
12094        x: &CudaSlice<f32>,
12095        t: usize,
12096        next_norm: Option<&CudaSlice<f32>>,
12097    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12098    {
12099        let n_embd = self.cfg.n_embd as usize;
12100        let eps = self.cfg.rms_eps;
12101        let bits = layer.gemma4.as_ref().unwrap();
12102        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12103            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12104                e,
12105                layer,
12106                o,
12107                x,
12108                t,
12109                Some(layer.post_attn_norm.float_data()),
12110                true,
12111            )?;
12112            let mut xn = e.uninit(t * n_embd)?;
12113            return match next_norm {
12114                Some(w) => {
12115                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12116                        &f0,
12117                        bits.post_ffw_norm.float_data(),
12118                        &attn_out,
12119                        bits.layer_scale,
12120                        w,
12121                        &mut xn,
12122                        n_embd,
12123                        t,
12124                        eps,
12125                    )?;
12126                    Ok((xn, Some(pair)))
12127                }
12128                None => {
12129                    let mut sn = e.uninit(t * n_embd)?;
12130                    e.rms_norm(
12131                        &f0,
12132                        bits.post_ffw_norm.float_data(),
12133                        &mut sn,
12134                        n_embd,
12135                        t,
12136                        eps,
12137                    )?;
12138                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12139                    Ok((xn, None))
12140                }
12141            };
12142        }
12143        let mut cur = e.uninit(t * n_embd)?;
12144        e.rms_norm(
12145            o,
12146            layer.post_attn_norm.float_data(),
12147            &mut cur,
12148            n_embd,
12149            t,
12150            eps,
12151        )?;
12152        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12153    }
12154
12155    pub(crate) fn gemma4_layer_tail_add_nq(
12156        &self,
12157        e: &Engine,
12158        layer: &crate::hybrid::HybridLayer,
12159        cur: &CudaSlice<f32>,
12160        x: &CudaSlice<f32>,
12161        t: usize,
12162        next_norm: Option<&CudaSlice<f32>>,
12163    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12164    {
12165        let n_embd = self.cfg.n_embd as usize;
12166        let bits = layer.gemma4.as_ref().unwrap();
12167        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12168        let mut xn = e.uninit(t * n_embd)?;
12169        match next_norm {
12170            Some(w) => {
12171                let pair = e.add_scale_rms_norm_q8_1(
12172                    &sn,
12173                    &attn_out,
12174                    bits.layer_scale,
12175                    w,
12176                    &mut xn,
12177                    n_embd,
12178                    t,
12179                    self.cfg.rms_eps,
12180                )?;
12181                Ok((xn, Some(pair)))
12182            }
12183            None => {
12184                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12185                Ok((xn, None))
12186            }
12187        }
12188    }
12189
12190    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12191    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12192    fn gemma4_forward(
12193        &self,
12194        e: &Engine,
12195        tokens: &[u32],
12196        last_only: bool,
12197    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12198        // E4B routes to its own forward regardless of the caller's entry point (forward /
12199        // forward_last / prime paths all funnel here for gemma4).
12200        if self.is_gemma4_e4b() {
12201            return self.gemma4_e4b_forward(e, tokens, last_only);
12202        }
12203        let n_embd = self.cfg.n_embd as usize;
12204        let t = tokens.len();
12205        let pos: Vec<i32> = (0..t as i32).collect();
12206        let pos_d = e.htod_i32(&pos)?;
12207
12208        let mut x = self.embed(e, tokens)?;
12209        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12210        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12211        // the bring-up bisect vs llama-eval-callback node stats.
12212        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12213        let stat =
12214            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12215                let h = e.dtoh(x)?;
12216                let bad = h.iter().filter(|v| !v.is_finite()).count();
12217                let mx = h
12218                    .iter()
12219                    .filter(|v| v.is_finite())
12220                    .fold(0.0f32, |m, v| m.max(v.abs()));
12221                eprintln!(
12222                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12223                    &h[..3]
12224                );
12225                Ok(())
12226            };
12227        if probe {
12228            stat(e, &x, "embed")?;
12229        }
12230        for (il, layer) in self.layers.iter().enumerate() {
12231            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12232            if probe {
12233                stat(e, &x, &format!("L{il}"))?;
12234            }
12235        }
12236        let mut hn = e.zeros(t * n_embd)?;
12237        e.rms_norm(
12238            &x,
12239            self.output_norm.float_data(),
12240            &mut hn,
12241            n_embd,
12242            t,
12243            self.cfg.rms_eps,
12244        )?;
12245        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12246        let n_vocab = self.output.out_features();
12247        let logits = if last_only {
12248            let hv = e.view(&hn, t * n_embd);
12249            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12250            let mut hlast = e.zeros(n_embd)?;
12251            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12252            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12253            e.softcap(&mut ld, cap, n_vocab)?;
12254            self.gemma4_suppress(e, &mut ld, 1)?;
12255            e.dtoh(&ld)?
12256        } else {
12257            let mut ld = e.matmul(&self.output, &hn, t)?;
12258            e.softcap(&mut ld, cap, t * n_vocab)?;
12259            self.gemma4_suppress(e, &mut ld, t)?;
12260            e.dtoh(&ld)?
12261        };
12262        Ok(logits)
12263    }
12264
12265    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12266    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12267    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12268    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12269    pub(crate) fn gemma4_prime(
12270        &self,
12271        e: &Engine,
12272        tokens: &[u32],
12273        cache: &mut Cache,
12274        overlay: Option<&crate::vision::EmbedOverlay>,
12275    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12276        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12277        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12278        // whole worker process on this line. The worker now primes gemma4 monolithically and
12279        // routes continuation suffixes tokenwise; this is the per-request backstop.
12280        if cache.pos != 0 {
12281            return Err(
12282                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12283                        — prime the full prompt in one call or decode tokenwise"
12284                    .into(),
12285            );
12286        }
12287        let n_embd = self.cfg.n_embd as usize;
12288        let eps = self.cfg.rms_eps;
12289        let t = tokens.len();
12290        let pos: Vec<i32> = (0..t as i32).collect();
12291        let pos_d = e.htod_i32(&pos)?;
12292        let mut x = self.embed(e, tokens)?;
12293        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12294        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12295        // sqrt(n_embd) text scale — the reference scales token batches only
12296        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12297        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12298        // bidirectional within itself, causal+SWA everywhere else, matching the
12299        // reference's llama_set_causal_attn(false) image batch exactly.
12300        let island: Option<CudaSlice<i32>> = match overlay {
12301            Some(ov) => {
12302                let mut span_id = vec![-1i32; t];
12303                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12304                    if pos + n_rows > t {
12305                        return Err(format!(
12306                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12307                            pos + n_rows
12308                        )
12309                        .into());
12310                    }
12311                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12312                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12313                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12314                        *s = i as i32;
12315                    }
12316                }
12317                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12318                // keep the plain causal mask. Exists only so the decisive probe can show
12319                // the island mask itself changes the answer; never on in serving.
12320                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12321                    None
12322                } else {
12323                    Some(e.htod_i32(&span_id)?)
12324                }
12325            }
12326            None => None,
12327        };
12328        for (il, layer) in self.layers.iter().enumerate() {
12329            let mut h = e.zeros(t * n_embd)?;
12330            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12331            let Mixer::Full(fa) = &layer.mixer else {
12332                panic!("gemma4 layer not full-attn")
12333            };
12334            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12335            if trace {
12336                let v = e.dtoh(&h)?;
12337                let nan = v.iter().filter(|x| x.is_nan()).count();
12338                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12339            }
12340            let o =
12341                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12342            if trace {
12343                let v = e.dtoh(&o)?;
12344                let nan = v.iter().filter(|x| x.is_nan()).count();
12345                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12346            }
12347            let mut cur = e.zeros(t * n_embd)?;
12348            e.rms_norm(
12349                &o,
12350                layer.post_attn_norm.float_data(),
12351                &mut cur,
12352                n_embd,
12353                t,
12354                eps,
12355            )?;
12356            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12357            self.dflash_tap(e, cache, il, &x, t)?;
12358            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12359            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12360                let h = e.dtoh(&x)?;
12361                let nan = h.iter().filter(|v| v.is_nan()).count();
12362                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12363                eprintln!(
12364                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12365                    h.len()
12366                );
12367                if nan > 0 {
12368                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12369                }
12370            }
12371        }
12372        cache.pos += t;
12373        let hiddens = e.clone_dtod(&x)?;
12374        let xv = e.view(&x, t * n_embd);
12375        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12376        let mut h_seed = e.zeros(n_embd)?;
12377        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12378        let mut hn = e.uninit(n_embd)?;
12379        e.rms_norm(
12380            &h_seed,
12381            self.output_norm.float_data(),
12382            &mut hn,
12383            n_embd,
12384            1,
12385            eps,
12386        )?;
12387        let mut ld = e.matmul(&self.output, &hn, 1)?;
12388        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12389        e.softcap(&mut ld, cap, self.output.out_features())?;
12390        self.gemma4_suppress(e, &mut ld, 1)?;
12391        let logits = e.dtoh(&ld)?;
12392        Ok((logits, h_seed, hiddens))
12393    }
12394
12395    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12396    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12397    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12398    /// fused norm emits q8 directly — the f32 h never materializes).
12399    fn gemma4_decode_attn(
12400        &self,
12401        e: &Engine,
12402        fa: &crate::hybrid::FullAttnLayer,
12403        il: usize,
12404        hq: &CudaSlice<i8>,
12405        hdq: &CudaSlice<f32>,
12406        pos_d: &CudaSlice<i32>,
12407        cache: &mut Cache,
12408    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12409        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12410        let eps = self.cfg.rms_eps;
12411        let aux = self.gemma4_aux.as_ref().unwrap();
12412        let ones = aux.ones(e);
12413        #[cfg(debug_assertions)]
12414        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12415        let (hq, hdq) = (hq, hdq);
12416        let h0 = e.zeros(0)?;
12417        let h = &h0;
12418        let (q0, k0, v0) = if swa {
12419            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12420                Some(t3) => t3,
12421                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12422                // match — fuse the uniform (q,k) pair and take v as its own single.
12423                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12424                    Some((q0, k0)) => {
12425                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12426                        (q0, k0, v0)
12427                    }
12428                    None => (
12429                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12430                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12431                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12432                    ),
12433                },
12434            }
12435        } else {
12436            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12437                Some(p) => p,
12438                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12439                    Some(p) => p,
12440                    None => (
12441                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12442                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12443                    ),
12444                },
12445            };
12446            let v0 = e.clone_dtod(&k0)?;
12447            (q0, k0, v0)
12448        };
12449        let mut q = e.uninit(nh * hd)?;
12450        let mut k = e.uninit(nkv * hd)?;
12451        let mut v = e.uninit(nkv * hd)?;
12452        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12453        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12454        let ff = if swa {
12455            None
12456        } else {
12457            Some(
12458                aux.rope_freqs(e)
12459                    .expect("gemma4 global rope needs rope_freqs.weight"),
12460            )
12461        };
12462        #[cfg(debug_assertions)]
12463        if let Some(ff) = ff {
12464            crate::debug_assert_tensor_stream_device(
12465                ff,
12466                &e.stream(),
12467                "gemma4_decode_attn.rope_freqs",
12468            );
12469        }
12470        let kvl = cache.kv[il].as_mut().unwrap();
12471        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12472        if crate::Engine::qkv_append_on() {
12473            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12474            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12475            // twin of the dc fold — bit-identical bodies, one launch per layer.
12476            e.rms_norm_qkv_rope_append(
12477                &q0,
12478                &k0,
12479                &v0,
12480                fa.q_norm.float_data(),
12481                fa.k_norm.float_data(),
12482                ones,
12483                &mut q,
12484                &mut k,
12485                &mut v,
12486                hd,
12487                self.gemma4_rope_dims(il),
12488                nh,
12489                nkv,
12490                pos_d,
12491                nh,
12492                nkv,
12493                base,
12494                1.0,
12495                ff,
12496                eps,
12497                &mut kvl.k,
12498                &mut kvl.v,
12499                kvl.len,
12500                kvl.k_tok_bytes,
12501                kvl.v_tok_bytes,
12502                kv_fp8,
12503            )?;
12504        } else {
12505            e.rms_norm_qkv_rope(
12506                &q0,
12507                &k0,
12508                &v0,
12509                fa.q_norm.float_data(),
12510                fa.k_norm.float_data(),
12511                ones,
12512                &mut q,
12513                &mut k,
12514                &mut v,
12515                hd,
12516                self.gemma4_rope_dims(il),
12517                nh,
12518                nkv,
12519                pos_d,
12520                nh,
12521                nkv,
12522                base,
12523                1.0,
12524                ff,
12525                eps,
12526            )?;
12527            e.append_kv_quantized(
12528                &k,
12529                &v,
12530                &mut kvl.k,
12531                &mut kvl.v,
12532                kvl.len,
12533                kvl.kv_dim_k,
12534                kvl.kv_dim_v,
12535                kvl.k_tok_bytes,
12536                kvl.v_tok_bytes,
12537                kv_fp8,
12538            )?;
12539        }
12540        kvl.len += 1;
12541        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12542        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12543        // positional). Globals attend the full history.
12544        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12545        let mut attn = e.uninit(nh * hd)?;
12546        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12547        if !swa
12548            && hd == 512
12549            && kvl.len >= crate::fa512_min_tkv()
12550            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12551        {
12552            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12553            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12554            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12555            let base = kvl.len as i32;
12556            e.i32_set_k(&mut kvl.len_d, base)?;
12557            e.fa_decode_rows(
12558                &q,
12559                &kp,
12560                &vp,
12561                &mut attn,
12562                hd,
12563                nh,
12564                nkv,
12565                kvl.len - 1,
12566                1,
12567                scale,
12568                kvl.k_tok_bytes,
12569                kvl.v_tok_bytes,
12570                Some((&kvl.len_d, -1)),
12571                false,
12572                false,
12573                None,
12574            )?;
12575            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12576        }
12577        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12578        if swa
12579            && kvl.len > win
12580            && hd == 256
12581            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12582        {
12583            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12584            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12585            let base = kvl.len as i32;
12586            e.i32_set_k(&mut kvl.len_d, base)?;
12587            e.fa_decode_rows_w(
12588                &q,
12589                &kp,
12590                &vp,
12591                &mut attn,
12592                hd,
12593                nh,
12594                nkv,
12595                &kvl.len_d,
12596                -1,
12597                1,
12598                scale,
12599                win,
12600                kvl.k_tok_bytes,
12601                kvl.v_tok_bytes,
12602                None,
12603            )?;
12604            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12605        }
12606        let (off_tok, t_kv) = if swa && kvl.len > win {
12607            (kvl.len - win, win)
12608        } else {
12609            (0, kvl.len)
12610        };
12611        let k_view = e.view_u8_range(
12612            &kvl.k,
12613            off_tok * kvl.k_tok_bytes,
12614            (off_tok + t_kv) * kvl.k_tok_bytes,
12615        );
12616        let v_view = e.view_u8_range(
12617            &kvl.v,
12618            off_tok * kvl.v_tok_bytes,
12619            (off_tok + t_kv) * kvl.v_tok_bytes,
12620        );
12621        e.fa_decode_kvmod(
12622            &q,
12623            &k_view,
12624            &v_view,
12625            &mut attn,
12626            hd,
12627            nh,
12628            nkv,
12629            t_kv,
12630            scale,
12631            kvl.k_tok_bytes,
12632            kvl.v_tok_bytes,
12633            swa && crate::Engine::wkv_on(),
12634        )?;
12635        Ok(e.matmul(&fa.wo, &attn, 1)?)
12636    }
12637
12638    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
12639    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
12640    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
12641    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
12642    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
12643    /// in-graph; the driver gates).
12644    #[allow(clippy::too_many_arguments)]
12645    pub fn gemma4_decode_step_dc(
12646        &self,
12647        e: &Engine,
12648        token_d: &CudaSlice<u32>,
12649        pos_d: &mut CudaSlice<i32>,
12650        embd_gpu: &CudaSlice<u8>,
12651        embd_qt: i32,
12652        embd_rb: usize,
12653        cache: &mut Cache,
12654        n_vocab: usize,
12655        cap_bucket_max: Option<(usize, usize)>,
12656    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
12657        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
12658        self.gemma4_decode_step_dc_into(
12659            e,
12660            token_d,
12661            pos_d,
12662            embd_gpu,
12663            embd_qt,
12664            embd_rb,
12665            cache,
12666            n_vocab,
12667            cap_bucket_max,
12668            &mut tok_out,
12669        )?;
12670        Ok(tok_out)
12671    }
12672
12673    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
12674    /// every replay; pass `token_d` itself for the self-feeding graph loop).
12675    #[allow(clippy::too_many_arguments)]
12676    pub fn gemma4_decode_step_dc_into(
12677        &self,
12678        e: &Engine,
12679        token_d: &CudaSlice<u32>,
12680        pos_d: &mut CudaSlice<i32>,
12681        embd_gpu: &CudaSlice<u8>,
12682        embd_qt: i32,
12683        embd_rb: usize,
12684        cache: &mut Cache,
12685        n_vocab: usize,
12686        cap_bucket_max: Option<(usize, usize)>,
12687        tok_out: &mut CudaSlice<u32>,
12688    ) -> Result<(), Box<dyn std::error::Error>> {
12689        let n_embd = self.cfg.n_embd as usize;
12690        let eps = self.cfg.rms_eps;
12691        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
12692        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
12693        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12694        let n_layers = self.layers.len();
12695        for (il, layer) in self.layers.iter().enumerate() {
12696            let (hq, hdq) = match h_carry.take() {
12697                Some(p) => p,
12698                None => {
12699                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
12700                }
12701            };
12702            let Mixer::Full(fa) = &layer.mixer else {
12703                panic!("gemma4 layer {il} not full-attn")
12704            };
12705            let o =
12706                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
12707            let next_norm = if il + 1 < n_layers {
12708                Some(self.layers[il + 1].attn_norm.float_data())
12709            } else {
12710                None
12711            };
12712            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
12713            x = xn;
12714            h_carry = hn;
12715        }
12716        let mut hn = e.uninit(n_embd)?;
12717        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
12718        let mut logits = e.matmul(&self.output, &hn, 1)?;
12719        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
12720        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
12721        e.inc_seqlen(pos_d)?;
12722        if cap_bucket_max.is_none() {
12723            cache.pos += 1;
12724        }
12725        Ok(())
12726    }
12727
12728    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
12729    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
12730    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
12731    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
12732
12733    /// Build the slot set (call OUTSIDE any capture).
12734    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
12735        let n_embd = self.cfg.n_embd as usize;
12736        let n_vocab = self.output.out_features();
12737        let n_layers = self.layers.len();
12738        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
12739        for il in 0..n_layers {
12740            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
12741            qmax = qmax.max(nh * hd);
12742            kvmax = kvmax.max(nkv * hd);
12743            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
12744                ffmax = ffmax.max(ffn_gate.out_features());
12745            }
12746        }
12747        Ok(G4DcSlots {
12748            x: e.uninit(n_embd)?,
12749            xn: e.uninit(n_embd)?,
12750            cur: e.uninit(n_embd)?,
12751            hq: e.alloc_i8_uninit(n_embd)?,
12752            hd_: e.uninit(n_embd / 32)?,
12753            q0: e.uninit(qmax)?,
12754            k0: e.uninit(kvmax)?,
12755            v0: e.uninit(kvmax)?,
12756            q: e.uninit(qmax)?,
12757            k: e.uninit(kvmax)?,
12758            v: e.uninit(kvmax)?,
12759            attn: e.uninit(qmax)?,
12760            o: e.uninit(n_embd)?,
12761            attn_out: e.uninit(n_embd)?,
12762            zsh: e.uninit(n_embd)?,
12763            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
12764            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
12765            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
12766            zd: e.uninit(n_embd.max(qmax) / 32)?,
12767            gate: e.uninit(ffmax)?,
12768            up: e.uninit(ffmax)?,
12769            act: e.uninit(ffmax)?,
12770            actq: e.alloc_i8_uninit(ffmax)?,
12771            actd: e.uninit(ffmax / 32)?,
12772            f0: e.uninit(n_embd)?,
12773            sn: e.uninit(n_embd)?,
12774            hn: e.uninit(n_embd)?,
12775            logits: e.uninit(n_vocab)?,
12776        })
12777    }
12778
12779    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
12780    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
12781    fn g4_matvec_m1_into(
12782        &self,
12783        e: &Engine,
12784        w: &crate::model::GpuTensor,
12785        aq: &CudaSlice<i8>,
12786        ad: &CudaSlice<f32>,
12787        y: &mut CudaSlice<f32>,
12788    ) -> Result<(), Box<dyn std::error::Error>> {
12789        use crate::model::GpuTensor;
12790        let (bytes, qtype, row_bytes, scale, rp) = match w {
12791            GpuTensor::Quant {
12792                bytes,
12793                qtype,
12794                row_bytes,
12795                scale,
12796                rp,
12797                ..
12798            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12799            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
12800        };
12801        let (mbytes, mrp) = match w {
12802            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12803            _ => (bytes, rp),
12804        };
12805        e.qmatvec_mmvq_into(
12806            mbytes,
12807            aq,
12808            ad,
12809            1,
12810            w.in_features(),
12811            w.out_features(),
12812            qtype,
12813            row_bytes,
12814            scale,
12815            mrp,
12816            y,
12817        )
12818    }
12819
12820    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
12821    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
12822    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
12823    #[allow(clippy::too_many_arguments)]
12824    pub fn gemma4_decode_step_dc_slotted(
12825        &self,
12826        e: &Engine,
12827        token_d: &CudaSlice<u32>,
12828        pos_d: &mut CudaSlice<i32>,
12829        embd_gpu: &CudaSlice<u8>,
12830        embd_qt: i32,
12831        embd_rb: usize,
12832        cache: &mut Cache,
12833        n_vocab: usize,
12834        cap_bucket_max: Option<(usize, usize)>,
12835        sl: &mut G4DcSlots,
12836        tok_out: &mut CudaSlice<u32>,
12837        ring: Option<(&mut CudaSlice<u32>, usize)>,
12838    ) -> Result<(), Box<dyn std::error::Error>> {
12839        let n_embd = self.cfg.n_embd as usize;
12840        let eps = self.cfg.rms_eps;
12841        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
12842        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
12843        let n_layers = self.layers.len();
12844        let mut has_carry = false;
12845        for il in 0..n_layers {
12846            if !has_carry {
12847                e.rms_norm_q8_1_into(
12848                    &sl.x,
12849                    self.layers[il].attn_norm.float_data(),
12850                    n_embd,
12851                    1,
12852                    eps,
12853                    &mut sl.hq,
12854                    &mut sl.hd_,
12855                )?;
12856            }
12857            has_carry = true;
12858            let layer = &self.layers[il];
12859            let Mixer::Full(fa) = &layer.mixer else {
12860                panic!("gemma4 layer {il} not full-attn")
12861            };
12862            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
12863            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
12864            // the standalone norm only survives on the unfused seam arm.
12865            if !Engine::g4_pnfold_on() {
12866                e.rms_norm(
12867                    &sl.o,
12868                    layer.post_attn_norm.float_data(),
12869                    &mut sl.cur,
12870                    n_embd,
12871                    1,
12872                    eps,
12873                )?;
12874            }
12875            let next_norm = if il + 1 < n_layers {
12876                Some(self.layers[il + 1].attn_norm.float_data())
12877            } else {
12878                None
12879            };
12880            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
12881            std::mem::swap(&mut sl.x, &mut sl.xn);
12882        }
12883        e.rms_norm(
12884            &sl.x,
12885            self.output_norm.float_data(),
12886            &mut sl.hn,
12887            n_embd,
12888            1,
12889            eps,
12890        )?;
12891        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
12892        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
12893        {
12894            let (zq, zd) = (&sl.zq, &sl.zd);
12895            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
12896            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
12897            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
12898        }
12899        self.gemma4_suppress(e, &mut sl.logits, 1)?;
12900        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
12901        if let Some((ring, base)) = ring {
12902            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
12903            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
12904            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
12905            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
12906        }
12907        e.inc_seqlen(pos_d)?;
12908        if cap_bucket_max.is_none() {
12909            cache.pos += 1;
12910        }
12911        Ok(())
12912    }
12913
12914    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
12915    #[allow(clippy::too_many_arguments)]
12916    fn gemma4_decode_attn_dc_slotted(
12917        &self,
12918        e: &Engine,
12919        fa: &crate::hybrid::FullAttnLayer,
12920        il: usize,
12921        pos_d: &CudaSlice<i32>,
12922        cache: &mut Cache,
12923        cap_bucket_max: Option<(usize, usize)>,
12924        sl: &mut G4DcSlots,
12925    ) -> Result<(), Box<dyn std::error::Error>> {
12926        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12927        let eps = self.cfg.rms_eps;
12928        let aux = self.gemma4_aux.as_ref().unwrap();
12929        let ones = aux.ones(e);
12930        #[cfg(debug_assertions)]
12931        crate::debug_assert_tensor_stream_device(
12932            ones,
12933            &e.stream(),
12934            "gemma4_decode_attn_dc_slotted.ones",
12935        );
12936        {
12937            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
12938            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
12939            if swa {
12940                if !e.matmul_q4_fused3_into(
12941                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
12942                )? {
12943                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
12944                    // (q,k) pair, v through the generic m1 slot matvec — the same two
12945                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
12946                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12947                    {
12948                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
12949                    } else {
12950                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
12951                    }
12952                }
12953            } else {
12954                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12955                    && !e
12956                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12957                {
12958                    return Err("slotted step: fused2 unavailable".into());
12959                }
12960                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
12961                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
12962            }
12963        }
12964        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
12965        // kernel-for-kernel (graph stream-identity gate).
12966        let ff = if swa {
12967            None
12968        } else {
12969            Some(
12970                aux.rope_freqs(e)
12971                    .expect("gemma4 global rope needs rope_freqs.weight"),
12972            )
12973        };
12974        #[cfg(debug_assertions)]
12975        if let Some(ff) = ff {
12976            crate::debug_assert_tensor_stream_device(
12977                ff,
12978                &e.stream(),
12979                "gemma4_decode_attn_dc_slotted.rope_freqs",
12980            );
12981        }
12982        let kvl = cache.kv[il].as_mut().unwrap();
12983        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12984        if crate::Engine::qkv_append_on() {
12985            // append fold (2026-07-23): mirrors dc_into.
12986            e.rms_norm_qkv_rope_append_dc(
12987                &sl.q0,
12988                &sl.k0,
12989                &sl.v0,
12990                fa.q_norm.float_data(),
12991                fa.k_norm.float_data(),
12992                ones,
12993                &mut sl.q,
12994                &mut sl.k,
12995                &mut sl.v,
12996                hd,
12997                self.gemma4_rope_dims(il),
12998                nh,
12999                nkv,
13000                pos_d,
13001                nh,
13002                nkv,
13003                base,
13004                1.0,
13005                ff,
13006                eps,
13007                &mut kvl.k,
13008                &mut kvl.v,
13009                &kvl.len_d,
13010                kvl.k_tok_bytes,
13011                kvl.v_tok_bytes,
13012                kv_fp8,
13013            )?;
13014        } else {
13015            e.rms_norm_qkv_rope(
13016                &sl.q0,
13017                &sl.k0,
13018                &sl.v0,
13019                fa.q_norm.float_data(),
13020                fa.k_norm.float_data(),
13021                ones,
13022                &mut sl.q,
13023                &mut sl.k,
13024                &mut sl.v,
13025                hd,
13026                self.gemma4_rope_dims(il),
13027                nh,
13028                nkv,
13029                pos_d,
13030                nh,
13031                nkv,
13032                base,
13033                1.0,
13034                ff,
13035                eps,
13036            )?;
13037            e.append_kv_quantized_dc(
13038                &sl.k,
13039                &sl.v,
13040                &mut kvl.k,
13041                &mut kvl.v,
13042                &kvl.len_d,
13043                kvl.kv_dim_k,
13044                kvl.kv_dim_v,
13045                kvl.k_tok_bytes,
13046                kvl.v_tok_bytes,
13047                kv_fp8,
13048            )?;
13049        }
13050        e.inc_seqlen(&mut kvl.len_d)?;
13051        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13052        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13053        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13054        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13055        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13056        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13057        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13058        // the dc_into arm branch-for-branch (stream gate).
13059        let mut fa_q8 = false;
13060        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13061            e.fa_decode_rows(
13062                &sl.q,
13063                &k_view,
13064                &v_view,
13065                &mut sl.attn,
13066                hd,
13067                nh,
13068                nkv,
13069                b_glob - 1,
13070                1,
13071                scale,
13072                kvl.k_tok_bytes,
13073                kvl.v_tok_bytes,
13074                Some((&kvl.len_d, -1)),
13075                false,
13076                false,
13077                Some((&mut sl.zq, &mut sl.zd)),
13078            )?;
13079            fa_q8 = true;
13080        } else if swa && b_swa > win && hd == 256 && rows_on {
13081            e.fa_decode_rows_w(
13082                &sl.q,
13083                &k_view,
13084                &v_view,
13085                &mut sl.attn,
13086                hd,
13087                nh,
13088                nkv,
13089                &kvl.len_d,
13090                -1,
13091                1,
13092                scale,
13093                win,
13094                kvl.k_tok_bytes,
13095                kvl.v_tok_bytes,
13096                Some((&mut sl.zq, &mut sl.zd)),
13097            )?;
13098            fa_q8 = true;
13099        } else {
13100            let b = if swa { b_swa } else { b_glob };
13101            e.fa_decode_dc(
13102                &sl.q,
13103                &k_view,
13104                &v_view,
13105                &mut sl.attn,
13106                hd,
13107                nh,
13108                nkv,
13109                &kvl.len_d,
13110                b,
13111                scale,
13112                kvl.k_tok_bytes,
13113                kvl.v_tok_bytes,
13114                swa && crate::Engine::wkv_on(),
13115            )?;
13116        }
13117        if !fa_q8 {
13118            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13119            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13120        }
13121        {
13122            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13123            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13124            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13125        }
13126        Ok(())
13127    }
13128
13129    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13130    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13131    fn gemma4_layer_tail_slotted(
13132        &self,
13133        e: &Engine,
13134        layer: &crate::hybrid::HybridLayer,
13135        next_norm: Option<&CudaSlice<f32>>,
13136        sl: &mut G4DcSlots,
13137    ) -> Result<(), Box<dyn std::error::Error>> {
13138        let n_embd = self.cfg.n_embd as usize;
13139        let eps = self.cfg.rms_eps;
13140        let bits = layer.gemma4.as_ref().unwrap();
13141        let crate::hybrid::Ffn::Dense {
13142            ffn_gate,
13143            ffn_up,
13144            ffn_down,
13145        } = &layer.ffn
13146        else {
13147            return Err("slotted tail: dense ffn only".into());
13148        };
13149        let pnfold = Engine::g4_pnfold_on();
13150        if pnfold {
13151            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13152            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13153            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13154            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13155            e.rms_pre_add_rms_norm_q8z_into(
13156                or,
13157                layer.post_attn_norm.float_data(),
13158                xr,
13159                bits.ffn_norm.float_data(),
13160                &mut sl.attn_out,
13161                &mut sl.zsh,
13162                n_embd,
13163                1,
13164                eps,
13165                &mut sl.zq,
13166                &mut sl.zd,
13167            )?;
13168        } else {
13169            e.add_rms_norm(
13170                &sl.cur,
13171                &sl.x,
13172                bits.ffn_norm.float_data(),
13173                &mut sl.attn_out,
13174                &mut sl.zsh,
13175                n_embd,
13176                1,
13177                eps,
13178            )?;
13179        }
13180        let n_ff = ffn_gate.out_features();
13181        if !pnfold {
13182            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13183            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13184        }
13185        {
13186            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13187            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13188            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13189                && !e.matmul_nvfp4_fused2_into(
13190                    ffn_gate,
13191                    ffn_up,
13192                    zq,
13193                    zd,
13194                    &mut sl.gate,
13195                    &mut sl.up,
13196                )?
13197            {
13198                return Err("slotted tail: ffn fused2 unavailable".into());
13199            }
13200        }
13201        debug_assert!(e.uses_q8_1_fast(ffn_down));
13202        {
13203            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13204            let upv = e.view(upr, n_ff);
13205            let up_all = upv.slice(0..n_ff);
13206            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13207            e.gelu_tanh_mul_q8_1_into(
13208                gr,
13209                &up_all,
13210                &mut sl.act,
13211                n_ff,
13212                1,
13213                &mut sl.actq,
13214                &mut sl.actd,
13215            )?;
13216        }
13217        {
13218            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13219            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13220            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13221        }
13222        if pnfold {
13223            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13224            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13225            if let Some(w) = next_norm {
13226                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13227                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13228                e.rms_pre_add_scale_rms_norm_q8_1_into(
13229                    f0r,
13230                    bits.post_ffw_norm.float_data(),
13231                    aor,
13232                    bits.layer_scale,
13233                    w,
13234                    &mut sl.xn,
13235                    n_embd,
13236                    1,
13237                    eps,
13238                    &mut sl.hq,
13239                    &mut sl.hd_,
13240                )?;
13241                return Ok(());
13242            }
13243        }
13244        e.rms_norm(
13245            &sl.f0,
13246            bits.post_ffw_norm.float_data(),
13247            &mut sl.sn,
13248            n_embd,
13249            1,
13250            eps,
13251        )?;
13252        match next_norm {
13253            Some(w) => {
13254                e.add_scale_rms_norm_q8_1_into(
13255                    &sl.sn,
13256                    &sl.attn_out,
13257                    bits.layer_scale,
13258                    w,
13259                    &mut sl.xn,
13260                    n_embd,
13261                    1,
13262                    eps,
13263                    &mut sl.hq,
13264                    &mut sl.hd_,
13265                )?;
13266            }
13267            None => {
13268                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13269            }
13270        }
13271        Ok(())
13272    }
13273
13274    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13275    #[allow(clippy::too_many_arguments)]
13276    fn gemma4_decode_attn_dc(
13277        &self,
13278        e: &Engine,
13279        fa: &crate::hybrid::FullAttnLayer,
13280        il: usize,
13281        hq: &CudaSlice<i8>,
13282        hdq: &CudaSlice<f32>,
13283        pos_d: &CudaSlice<i32>,
13284        cache: &mut Cache,
13285        cap_bucket_max: Option<(usize, usize)>,
13286    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13287        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13288        let eps = self.cfg.rms_eps;
13289        let aux = self.gemma4_aux.as_ref().unwrap();
13290        let ones = aux.ones(e);
13291        #[cfg(debug_assertions)]
13292        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13293        let (q0, k0, v0) = if swa {
13294            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13295                Some(t3) => t3,
13296                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13297                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13298                    Some((q0, k0)) => {
13299                        let h0 = e.zeros(0)?;
13300                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13301                        (q0, k0, v0)
13302                    }
13303                    None => {
13304                        let h0 = e.zeros(0)?;
13305                        (
13306                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13307                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13308                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13309                        )
13310                    }
13311                },
13312            }
13313        } else {
13314            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13315                Some(p) => p,
13316                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13317                    Some(p) => p,
13318                    None => {
13319                        let h0 = e.zeros(0)?;
13320                        (
13321                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13322                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13323                        )
13324                    }
13325                },
13326            };
13327            let v0 = e.clone_dtod(&k0)?;
13328            (q0, k0, v0)
13329        };
13330        let mut q = e.uninit(nh * hd)?;
13331        let mut k = e.uninit(nkv * hd)?;
13332        let mut v = e.uninit(nkv * hd)?;
13333        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13334        let ff = if swa {
13335            None
13336        } else {
13337            Some(
13338                aux.rope_freqs(e)
13339                    .expect("gemma4 global rope needs rope_freqs.weight"),
13340            )
13341        };
13342        #[cfg(debug_assertions)]
13343        if let Some(ff) = ff {
13344            crate::debug_assert_tensor_stream_device(
13345                ff,
13346                &e.stream(),
13347                "gemma4_decode_attn_dc.rope_freqs",
13348            );
13349        }
13350        let kvl = cache.kv[il].as_mut().unwrap();
13351        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13352        if crate::Engine::qkv_append_on() {
13353            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13354            e.rms_norm_qkv_rope_append_dc(
13355                &q0,
13356                &k0,
13357                &v0,
13358                fa.q_norm.float_data(),
13359                fa.k_norm.float_data(),
13360                ones,
13361                &mut q,
13362                &mut k,
13363                &mut v,
13364                hd,
13365                self.gemma4_rope_dims(il),
13366                nh,
13367                nkv,
13368                pos_d,
13369                nh,
13370                nkv,
13371                base,
13372                1.0,
13373                ff,
13374                eps,
13375                &mut kvl.k,
13376                &mut kvl.v,
13377                &kvl.len_d,
13378                kvl.k_tok_bytes,
13379                kvl.v_tok_bytes,
13380                kv_fp8,
13381            )?;
13382        } else {
13383            e.rms_norm_qkv_rope(
13384                &q0,
13385                &k0,
13386                &v0,
13387                fa.q_norm.float_data(),
13388                fa.k_norm.float_data(),
13389                ones,
13390                &mut q,
13391                &mut k,
13392                &mut v,
13393                hd,
13394                self.gemma4_rope_dims(il),
13395                nh,
13396                nkv,
13397                pos_d,
13398                nh,
13399                nkv,
13400                base,
13401                1.0,
13402                ff,
13403                eps,
13404            )?;
13405            e.append_kv_quantized_dc(
13406                &k,
13407                &v,
13408                &mut kvl.k,
13409                &mut kvl.v,
13410                &kvl.len_d,
13411                kvl.kv_dim_k,
13412                kvl.kv_dim_v,
13413                kvl.k_tok_bytes,
13414                kvl.v_tok_bytes,
13415                kv_fp8,
13416            )?;
13417        }
13418        e.inc_seqlen(&mut kvl.len_d)?;
13419        let mut attn = e.uninit(nh * hd)?;
13420        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13421        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13422        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13423        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13424        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13425        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13426        // (gemma4_e4b_attn, +0.65% valid window).
13427        match cap_bucket_max {
13428            None => {
13429                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13430                // decode (SWA layers attend the last `sliding_window` keys); the device
13431                // counters carry only the append slot + the graph seam.
13432                kvl.len += 1;
13433                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13434                if !swa
13435                    && hd == 512
13436                    && kvl.len >= crate::fa512_min_tkv()
13437                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13438                {
13439                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13440                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13441                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13442                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13443                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13444                    e.fa_decode_rows(
13445                        &q,
13446                        &kp,
13447                        &vp,
13448                        &mut attn,
13449                        hd,
13450                        nh,
13451                        nkv,
13452                        kvl.len - 1,
13453                        1,
13454                        scale,
13455                        kvl.k_tok_bytes,
13456                        kvl.v_tok_bytes,
13457                        Some((&kvl.len_d, -1)),
13458                        false,
13459                        false,
13460                        Some((&mut aq8, &mut ad8)),
13461                    )?;
13462                    fa_q8 = Some((aq8, ad8));
13463                } else if swa
13464                    && kvl.len > win
13465                    && hd == 256
13466                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13467                {
13468                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
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_w(
13473                        &q,
13474                        &kp,
13475                        &vp,
13476                        &mut attn,
13477                        hd,
13478                        nh,
13479                        nkv,
13480                        &kvl.len_d,
13481                        -1,
13482                        1,
13483                        scale,
13484                        win,
13485                        kvl.k_tok_bytes,
13486                        kvl.v_tok_bytes,
13487                        Some((&mut aq8, &mut ad8)),
13488                    )?;
13489                    fa_q8 = Some((aq8, ad8));
13490                } else {
13491                    let (off_tok, t_kv) = if swa && kvl.len > win {
13492                        (kvl.len - win, win)
13493                    } else {
13494                        (0, kvl.len)
13495                    };
13496                    let k_view = e.view_u8_range(
13497                        &kvl.k,
13498                        off_tok * kvl.k_tok_bytes,
13499                        (off_tok + t_kv) * kvl.k_tok_bytes,
13500                    );
13501                    let v_view = e.view_u8_range(
13502                        &kvl.v,
13503                        off_tok * kvl.v_tok_bytes,
13504                        (off_tok + t_kv) * kvl.v_tok_bytes,
13505                    );
13506                    e.fa_decode_kvmod(
13507                        &q,
13508                        &k_view,
13509                        &v_view,
13510                        &mut attn,
13511                        hd,
13512                        nh,
13513                        nkv,
13514                        t_kv,
13515                        scale,
13516                        kvl.k_tok_bytes,
13517                        kvl.v_tok_bytes,
13518                        swa && crate::Engine::wkv_on(),
13519                    )?;
13520                }
13521            }
13522            Some((b_swa, b_glob)) => {
13523                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13524                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13525                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13526                // the RUNG max for the rows family (kernels derive per-replay splits from
13527                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13528                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13529                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13530                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13531                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13532                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13533                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13534                    e.fa_decode_rows(
13535                        &q,
13536                        &k_view,
13537                        &v_view,
13538                        &mut attn,
13539                        hd,
13540                        nh,
13541                        nkv,
13542                        b_glob - 1,
13543                        1,
13544                        scale,
13545                        kvl.k_tok_bytes,
13546                        kvl.v_tok_bytes,
13547                        Some((&kvl.len_d, -1)),
13548                        false,
13549                        false,
13550                        Some((&mut aq8, &mut ad8)),
13551                    )?;
13552                    fa_q8 = Some((aq8, ad8));
13553                } else if swa && b_swa > win && hd == 256 && rows_on {
13554                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13555                    e.fa_decode_rows_w(
13556                        &q,
13557                        &k_view,
13558                        &v_view,
13559                        &mut attn,
13560                        hd,
13561                        nh,
13562                        nkv,
13563                        &kvl.len_d,
13564                        -1,
13565                        1,
13566                        scale,
13567                        win,
13568                        kvl.k_tok_bytes,
13569                        kvl.v_tok_bytes,
13570                        Some((&mut aq8, &mut ad8)),
13571                    )?;
13572                    fa_q8 = Some((aq8, ad8));
13573                } else {
13574                    let b = if swa { b_swa } else { b_glob };
13575                    e.fa_decode_dc(
13576                        &q,
13577                        &k_view,
13578                        &v_view,
13579                        &mut attn,
13580                        hd,
13581                        nh,
13582                        nkv,
13583                        &kvl.len_d,
13584                        b,
13585                        scale,
13586                        kvl.k_tok_bytes,
13587                        kvl.v_tok_bytes,
13588                        swa && crate::Engine::wkv_on(),
13589                    )?;
13590                }
13591            }
13592        }
13593        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
13594        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
13595        if let Some((aq8, ad8)) = fa_q8 {
13596            let mut y = e.uninit(fa.wo.out_features())?;
13597            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
13598            return Ok(y);
13599        }
13600        Ok(e.matmul(&fa.wo, &attn, 1)?)
13601    }
13602
13603    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
13604    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
13605    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
13606    /// views in-graph); caller gates and falls back to the dc-eager loop.
13607    pub fn gemma4_generate_graph(
13608        &self,
13609        e: &Engine,
13610        prompt_pos: usize,
13611        first_token: u32,
13612        cache: &mut Cache,
13613        max_new: usize,
13614        eos: &[u32],
13615        mut on_token: impl FnMut(u32) -> bool,
13616    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
13617        if self.is_gemma4_e4b() {
13618            return Err(
13619                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
13620                    .into(),
13621            );
13622        }
13623        use crate::decode::StopReason;
13624        let n_vocab = self.output.out_features();
13625        let n_embd = self.cfg.n_embd as usize;
13626        let embd_gpu = self
13627            .embd_gpu
13628            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13629        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13630        for kvl in cache.kv.iter_mut().flatten() {
13631            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
13632        }
13633        let mut token_d = e.stream().clone_htod(&[first_token])?;
13634        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
13635        let g4 = self.cfg.gemma4.as_ref().unwrap();
13636        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
13637        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
13638        let nkv_s = g4
13639            .head_count_kv
13640            .iter()
13641            .zip(g4.swa_pattern.iter())
13642            .find(|p| *p.1)
13643            .map(|p| *p.0 as usize)
13644            .unwrap_or(8);
13645        let nkv_g = g4
13646            .head_count_kv
13647            .iter()
13648            .zip(g4.swa_pattern.iter())
13649            .find(|p| !*p.1)
13650            .map(|p| *p.0 as usize)
13651            .unwrap_or(2);
13652        let mut graphs: std::collections::HashMap<
13653            ((bool, usize), (bool, usize), bool, bool),
13654            (
13655                cudarc::driver::CudaGraph,
13656                Vec<Box<dyn std::any::Any + Send>>,
13657            ),
13658        > = Default::default();
13659        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
13660        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
13661        let mut slots = self.g4_dc_slots(e)?;
13662        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
13663        // baked at the door entry (the modulo keeps every capture valid indefinitely).
13664        const RING: usize = 64;
13665        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
13666        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
13667        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
13668        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
13669        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
13670        const DRAIN: usize = 1;
13671        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
13672        let ring_base = prompt_pos;
13673        let mut out = Vec::with_capacity(max_new);
13674        let mut reason = StopReason::MaxNew;
13675        let mut next = first_token;
13676        let mut captures = 0usize;
13677        for _ in 0..max_new {
13678            out.push(next);
13679            if eos.contains(&next) {
13680                reason = StopReason::Eos;
13681                break;
13682            }
13683            if !on_token(next) {
13684                reason = StopReason::Callback;
13685                break;
13686            }
13687            let t_kv = cache.pos + 1;
13688            // Bucket key per ARM (graph arc step 3):
13689            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
13690            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
13691            //    the component collapses to a single marker).
13692            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
13693            //    at/above it — the kernel derives splits from len_d per replay, so buckets
13694            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
13695            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13696            let f512 = crate::fa512_min_tkv();
13697            let key_s = if t_kv > win {
13698                (true, usize::MAX)
13699            } else {
13700                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
13701            };
13702            let (key_g, rung_end) = if t_kv >= f512 {
13703                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
13704                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
13705                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
13706                ((true, end), end)
13707            } else {
13708                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
13709            };
13710            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
13711            if !graphs.contains_key(&key) {
13712                let bucket_max = (t_kv, rung_end);
13713                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
13714                let snap = cache.snapshot(e)?;
13715                let pos_save = e.dtoh_i32_one(&pos_d)?;
13716                let len_save: Vec<Option<i32>> = cache
13717                    .kv
13718                    .iter()
13719                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
13720                    .collect();
13721                let tok_save = e.dtoh_u32_one(&token_d)?;
13722                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
13723                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
13724                // regression class, and this door's measured -8.8%. The keeper pins warmup
13725                // transients so the captured graph holds kernel nodes only.
13726                let graph = {
13727                    let tok_ref = &mut token_d;
13728                    let pos_ref = &mut pos_d;
13729                    let cache_ref = &mut *cache;
13730                    let slots_ref = &mut slots;
13731                    let ring_ref = &mut ring;
13732                    e.capture_graph_retained_flags(
13733                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
13734                        |e| {
13735                        // self-feeding: the argmax writes token_d itself.
13736                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
13737                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
13738                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
13739                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
13740                                                           cache_ref, n_vocab, Some(bucket_max),
13741                                                           sl, tok_ref, Some((rg, ring_base)))
13742                    })?
13743                };
13744                cache.rollback(e, &snap, 0)?;
13745                e.set_i32_one(&mut pos_d, pos_save)?;
13746                for (il, ls) in len_save.iter().enumerate() {
13747                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
13748                        e.set_i32_one(&mut kvl.len_d, *v)?;
13749                    }
13750                }
13751                e.set_u32_one(&mut token_d, tok_save)?;
13752                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
13753                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
13754                        eprintln!("[graph-census] {c:?}");
13755                    }
13756                }
13757                graphs.insert(key, graph);
13758                captures += 1;
13759            }
13760            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
13761            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
13762            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
13763            // the budget; capture warmups already emitted their tokens through the ring.
13764            let mut chunk = 1usize;
13765            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
13766                .ok()
13767                .and_then(|v| v.parse().ok())
13768                .unwrap_or(DRAIN);
13769            while chunk < drain_cap && out.len() + chunk < max_new {
13770                let t_next = cache.pos + 1 + chunk;
13771                let key_s2 = if t_next > win {
13772                    (true, usize::MAX)
13773                } else {
13774                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
13775                };
13776                let key_g2 = if t_next >= f512 {
13777                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
13778                } else {
13779                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
13780                };
13781                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
13782                    break;
13783                }
13784                chunk += 1;
13785            }
13786            let g = &graphs.get(&key).unwrap().0;
13787            for _ in 0..chunk {
13788                g.launch()?;
13789            }
13790            e.stream().synchronize()?;
13791            let ringh = e.dtoh_u32(&ring)?;
13792            for j in 0..chunk {
13793                let pos_j = cache.pos + j;
13794                let tok_j = ringh[(pos_j - ring_base) % RING];
13795                cache.pos += 0; // advanced below in one shot
13796                if j + 1 == chunk {
13797                    next = tok_j;
13798                } else {
13799                    out.push(tok_j);
13800                    if eos.contains(&tok_j) || !on_token(tok_j) {
13801                        reason = if eos.contains(&tok_j) {
13802                            StopReason::Eos
13803                        } else {
13804                            StopReason::Callback
13805                        };
13806                        // roll device/host state back to the stop point.
13807                        let keep = cache.pos + j + 1;
13808                        e.set_i32_one(&mut pos_d, keep as i32)?;
13809                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13810                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
13811                            kvl.len = keep;
13812                        }
13813                        cache.pos = keep;
13814                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13815                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13816                        }
13817                        return Ok((out, reason));
13818                    }
13819                }
13820            }
13821            cache.pos += chunk;
13822            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13823                kvl.len += chunk;
13824            }
13825        }
13826        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13827            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13828        }
13829        Ok((out, reason))
13830    }
13831
13832    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
13833    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
13834    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
13835    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
13836    /// logits (host) + advances cache.pos by t.
13837    pub(crate) fn gemma4_decode_step_t(
13838        &self,
13839        e: &Engine,
13840        tokens: &[u32],
13841        pos0: usize,
13842        cache: &mut Cache,
13843    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13844        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
13845    }
13846
13847    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
13848    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
13849    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
13850    pub(crate) fn gemma4_decode_step_t_am(
13851        &self,
13852        e: &Engine,
13853        tokens: &[u32],
13854        pos0: usize,
13855        cache: &mut Cache,
13856    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13857        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13858        let t = tokens.len();
13859        let n_vocab = self.output.out_features();
13860        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
13861        for i in 0..t {
13862            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
13863        }
13864        Ok((e.dtoh_u32(&toks)?, hn))
13865    }
13866
13867    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
13868    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
13869    pub(crate) fn gemma4_decode_step_t_am_dev(
13870        &self,
13871        e: &Engine,
13872        tok_d: &CudaSlice<u32>,
13873        t: usize,
13874        pos0: usize,
13875        cache: &mut Cache,
13876    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13877        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
13878        let n_vocab = self.output.out_features();
13879        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13880        for i in 0..t {
13881            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13882        }
13883        Ok((vam, hn))
13884    }
13885
13886    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
13887    /// llama's h_nextn convention).
13888    pub(crate) fn gemma4_decode_step_t_h(
13889        &self,
13890        e: &Engine,
13891        tokens: &[u32],
13892        pos0: usize,
13893        cache: &mut Cache,
13894    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13895        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13896        let t = tokens.len();
13897        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
13898        e.softcap(&mut ld, cap, t * self.output.out_features())?;
13899        Ok((e.dtoh(&ld)?, hn))
13900    }
13901
13902    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
13903    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
13904    pub(crate) fn verify_stream_scratch(
13905        &self,
13906        e: &Engine,
13907        cap: usize,
13908    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
13909        Ok(VerifyStreamScratch {
13910            pos_d: e.htod_i32(&vec![0i32; cap])?,
13911            row_ctrs: (0..cap)
13912                .map(|_| e.htod_i32(&[0]))
13913                .collect::<Result<_, _>>()?,
13914        })
13915    }
13916
13917    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
13918    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
13919    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
13920    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
13921    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
13922    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
13923    /// sync, exactly the turnaround the burst exists to remove.
13924    pub(crate) fn gemma4_verify_t_am_stream(
13925        &self,
13926        e: &Engine,
13927        tok_d: &CudaSlice<u32>,
13928        t: usize,
13929        ctr: &CudaSlice<i32>,
13930        hint: usize,
13931        cache: &mut Cache,
13932        scr: &mut VerifyStreamScratch,
13933    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13934        let n_embd = self.cfg.n_embd as usize;
13935        let eps = self.cfg.rms_eps;
13936        assert!(t <= scr.row_ctrs.len() && t <= 64);
13937        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
13938        for i in 0..t {
13939            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
13940        }
13941        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
13942        let embd_gpu = self
13943            .embd_gpu
13944            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13945        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13946        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
13947        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
13948        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13949        let n_layers = self.layers.len();
13950        for (il, layer) in self.layers.iter().enumerate() {
13951            let (hq, hdq) = match h_carry.take() {
13952                Some(p) => p,
13953                None => {
13954                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
13955                }
13956            };
13957            let Mixer::Full(fa) = &layer.mixer else {
13958                panic!("gemma4 layer {il} not full-attn")
13959            };
13960            let o = self
13961                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
13962            let next_norm = if il + 1 < n_layers {
13963                Some(self.layers[il + 1].attn_norm.float_data())
13964            } else {
13965                None
13966            };
13967            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
13968            x = xn;
13969            h_carry = hn;
13970            self.dflash_tap(e, cache, il, &x, t)?;
13971        }
13972        let mut hn = e.uninit(t * n_embd)?;
13973        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
13974        let ld = e.matmul(&self.output, &hn, t)?;
13975        let n_vocab = self.output.out_features();
13976        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13977        for i in 0..t {
13978            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13979        }
13980        Ok((vam, hn))
13981    }
13982
13983    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
13984    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
13985    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
13986    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
13987    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
13988    /// kernel later if it shows in the profile).
13989    pub(crate) fn dflash_tap(
13990        &self,
13991        e: &Engine,
13992        cache: &mut Cache,
13993        il: usize,
13994        x: &CudaSlice<f32>,
13995        t: usize,
13996    ) -> Result<(), Box<dyn std::error::Error>> {
13997        let Some(taps) = cache.dflash_taps.as_mut() else {
13998            return Ok(());
13999        };
14000        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14001            return Ok(());
14002        };
14003        let h = taps.hidden;
14004        let n_taps = taps.layer_ids.len();
14005        let base = taps.base;
14006        debug_assert!(
14007            base + t <= taps.t,
14008            "tap window {base}+{t} exceeds sink {}",
14009            taps.t
14010        );
14011        let xv = e.view(x, t * h);
14012        for r in 0..t {
14013            let row = xv.slice(r * h..(r + 1) * h);
14014            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14015        }
14016        Ok(())
14017    }
14018
14019    fn gemma4_verify_trunk(
14020        &self,
14021        e: &Engine,
14022        tokens: &[u32],
14023        pos0: usize,
14024        cache: &mut Cache,
14025        tok_dev: Option<&CudaSlice<u32>>,
14026    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14027        let n_embd = self.cfg.n_embd as usize;
14028        let eps = self.cfg.rms_eps;
14029        let t = tokens.len();
14030        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14031        let pos_d = e.htod_i32(&pos)?;
14032        let mut x = match tok_dev {
14033            Some(td) => {
14034                let embd_gpu = self
14035                    .embd_gpu
14036                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14037                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14038                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14039            }
14040            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14041        };
14042        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14043        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14044        let n_layers = self.layers.len();
14045        for (il, layer) in self.layers.iter().enumerate() {
14046            let (hq, hdq) = match h_carry.take() {
14047                Some(p) => p,
14048                None => {
14049                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14050                }
14051            };
14052            let Mixer::Full(fa) = &layer.mixer else {
14053                panic!("gemma4 layer {il} not full-attn")
14054            };
14055            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14056            let next_norm = if il + 1 < n_layers {
14057                Some(self.layers[il + 1].attn_norm.float_data())
14058            } else {
14059                None
14060            };
14061            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14062            x = xn;
14063            h_carry = hn;
14064            self.dflash_tap(e, cache, il, &x, t)?;
14065        }
14066        let mut hn = e.uninit(t * n_embd)?;
14067        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14068        let mut ld = e.matmul(&self.output, &hn, t)?;
14069        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14070        cache.pos += t;
14071        Ok((ld, hn))
14072    }
14073
14074    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14075    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14076    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14077    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14078    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14079    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14080    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14081    #[allow(clippy::too_many_arguments)]
14082    fn gemma4_verify_attn_stream(
14083        &self,
14084        e: &Engine,
14085        fa: &crate::hybrid::FullAttnLayer,
14086        il: usize,
14087        hq: &CudaSlice<i8>,
14088        hdq: &CudaSlice<f32>,
14089        pos_d: &CudaSlice<i32>,
14090        t: usize,
14091        cache: &mut Cache,
14092        hint: usize,
14093        row_ctrs: &[CudaSlice<i32>],
14094    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14095        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14096        let eps = self.cfg.rms_eps;
14097        let aux = self.gemma4_aux.as_ref().unwrap();
14098        let ones = aux.ones(e);
14099        #[cfg(debug_assertions)]
14100        crate::debug_assert_tensor_stream_device(
14101            ones,
14102            &e.stream(),
14103            "gemma4_verify_attn_stream.ones",
14104        );
14105        let h0 = e.zeros(0)?;
14106        let h = &h0;
14107        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14108        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14109        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14110        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14111        let fused_qkv = if f2b {
14112            if swa {
14113                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14114                    .map(|(a, b, c)| (a, b, Some(c)))
14115            } else {
14116                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14117                    .map(|(a, b)| (a, b, None))
14118            }
14119        } else {
14120            None
14121        };
14122        let (q0, k0, v0) = match fused_qkv {
14123            Some((a, b, cv)) => {
14124                let v = match cv {
14125                    Some(c) => c,
14126                    None => e.clone_dtod(&b)?,
14127                };
14128                (a, b, v)
14129            }
14130            None => {
14131                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14132                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14133                let v0 = if swa {
14134                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14135                } else {
14136                    e.clone_dtod(&k0)?
14137                };
14138                (q0, k0, v0)
14139            }
14140        };
14141        let mut q = e.uninit(t * nh * hd)?;
14142        let mut k = e.uninit(t * nkv * hd)?;
14143        let mut v = e.uninit(t * nkv * hd)?;
14144        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14145        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14146        let ff = if swa {
14147            None
14148        } else {
14149            Some(
14150                aux.rope_freqs(e)
14151                    .expect("gemma4 global rope needs rope_freqs.weight"),
14152            )
14153        };
14154        #[cfg(debug_assertions)]
14155        if let Some(ff) = ff {
14156            crate::debug_assert_tensor_stream_device(
14157                ff,
14158                &e.stream(),
14159                "gemma4_verify_attn_stream.rope_freqs",
14160            );
14161        }
14162        e.rms_norm_qkv_rope(
14163            &q0,
14164            &k0,
14165            &v0,
14166            fa.q_norm.float_data(),
14167            fa.k_norm.float_data(),
14168            ones,
14169            &mut q,
14170            &mut k,
14171            &mut v,
14172            hd,
14173            self.gemma4_rope_dims(il),
14174            nh * t,
14175            nkv * t,
14176            pos_d,
14177            nh,
14178            nkv,
14179            base,
14180            1.0,
14181            ff,
14182            eps,
14183        )?;
14184        let kvl = cache.kv[il].as_mut().unwrap();
14185        // append at the DEVICE slot; the counter advances by t on-device.
14186        e.append_kv_quantized_rows_dc(
14187            &k,
14188            &v,
14189            &mut kvl.k,
14190            &mut kvl.v,
14191            &kvl.len_d,
14192            t,
14193            kvl.kv_dim_k,
14194            kvl.kv_dim_v,
14195            kvl.k_tok_bytes,
14196            kvl.v_tok_bytes,
14197            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14198        )?;
14199        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14200        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14201        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14202        let mut attn = e.uninit(t * nh * hd)?;
14203        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14204        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14205        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14206        // and a stable window regime — the same rung/regime keys as the draft graph).
14207        if swa && hint + 1 >= win {
14208            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14209            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14210            e.fa_decode_rows_w(
14211                &q,
14212                &k_view,
14213                &v_view,
14214                &mut attn,
14215                hd,
14216                nh,
14217                nkv,
14218                &kvl.len_d,
14219                0,
14220                t,
14221                scale,
14222                win,
14223                kvl.k_tok_bytes,
14224                kvl.v_tok_bytes,
14225                None,
14226            )?;
14227        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14228            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14229            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14230            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14231            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14232            // for every row.
14233            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14234            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14235            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14236            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14237            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14238            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14239            // any bucket >= the live length is exact.
14240            let bucket = (hint + t + 2)
14241                .next_power_of_two()
14242                .min(crate::fa512_min_tkv().saturating_sub(1));
14243            let qv = e.view(&q, t * nh * hd);
14244            for i in 0..t {
14245                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14246                let mut q_one = e.uninit(nh * hd)?;
14247                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14248                let mut a_one = e.uninit(nh * hd)?;
14249                e.fa_decode_dc(
14250                    &q_one,
14251                    &k_view,
14252                    &v_view,
14253                    &mut a_one,
14254                    hd,
14255                    nh,
14256                    nkv,
14257                    &row_ctrs[i],
14258                    bucket,
14259                    scale,
14260                    kvl.k_tok_bytes,
14261                    kvl.v_tok_bytes,
14262                    false,
14263                )?;
14264                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14265            }
14266        } else if hd == 512 {
14267            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14268            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14269            e.fa_decode_rows(
14270                &q,
14271                &k_view,
14272                &v_view,
14273                &mut attn,
14274                hd,
14275                nh,
14276                nkv,
14277                hint,
14278                t,
14279                scale,
14280                kvl.k_tok_bytes,
14281                kvl.v_tok_bytes,
14282                Some((&kvl.len_d, 0)),
14283                false,
14284                false,
14285                None,
14286            )?;
14287        } else {
14288            // hd256 under-window: v4 device-len rows twin.
14289            e.fa_decode_rows_dc(
14290                &q,
14291                &k_view,
14292                &v_view,
14293                &mut attn,
14294                hd,
14295                nh,
14296                nkv,
14297                &kvl.len_d,
14298                hint + t,
14299                t,
14300                scale,
14301                kvl.k_tok_bytes,
14302                kvl.v_tok_bytes,
14303                0,
14304                swa && crate::Engine::wkv_on(),
14305            )?;
14306        }
14307        Ok(e.matmul(&fa.wo, &attn, t)?)
14308    }
14309
14310    fn gemma4_verify_attn(
14311        &self,
14312        e: &Engine,
14313        fa: &crate::hybrid::FullAttnLayer,
14314        il: usize,
14315        hq: &CudaSlice<i8>,
14316        hdq: &CudaSlice<f32>,
14317        pos_d: &CudaSlice<i32>,
14318        t: usize,
14319        cache: &mut Cache,
14320    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14321        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14322        let eps = self.cfg.rms_eps;
14323        let aux = self.gemma4_aux.as_ref().unwrap();
14324        let ones = aux.ones(e);
14325        #[cfg(debug_assertions)]
14326        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14327        let n_embd = self.cfg.n_embd as usize;
14328        let _ = n_embd;
14329
14330        let h0 = e.zeros(0)?;
14331        let h = &h0;
14332        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14333        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14334        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14335        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14336        let fused_qkv = if f2b {
14337            if swa {
14338                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14339                    .map(|(a, b, c)| (a, b, Some(c)))
14340            } else {
14341                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14342                    .map(|(a, b)| (a, b, None))
14343            }
14344        } else {
14345            None
14346        };
14347        let (q0, k0, v0) = match fused_qkv {
14348            Some((a, b, cv)) => {
14349                let v = match cv {
14350                    Some(c) => c,
14351                    None => e.clone_dtod(&b)?,
14352                };
14353                (a, b, v)
14354            }
14355            None => {
14356                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14357                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14358                let v0 = if swa {
14359                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14360                } else {
14361                    e.clone_dtod(&k0)?
14362                };
14363                (q0, k0, v0)
14364            }
14365        };
14366        let mut q = e.uninit(t * nh * hd)?;
14367        let mut k = e.uninit(t * nkv * hd)?;
14368        let mut v = e.uninit(t * nkv * hd)?;
14369        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14370        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14371        let ff = if swa {
14372            None
14373        } else {
14374            Some(
14375                aux.rope_freqs(e)
14376                    .expect("gemma4 global rope needs rope_freqs.weight"),
14377            )
14378        };
14379        #[cfg(debug_assertions)]
14380        if let Some(ff) = ff {
14381            crate::debug_assert_tensor_stream_device(
14382                ff,
14383                &e.stream(),
14384                "gemma4_verify_attn.rope_freqs",
14385            );
14386        }
14387        e.rms_norm_qkv_rope(
14388            &q0,
14389            &k0,
14390            &v0,
14391            fa.q_norm.float_data(),
14392            fa.k_norm.float_data(),
14393            ones,
14394            &mut q,
14395            &mut k,
14396            &mut v,
14397            hd,
14398            self.gemma4_rope_dims(il),
14399            nh * t,
14400            nkv * t,
14401            pos_d,
14402            nh,
14403            nkv,
14404            base,
14405            1.0,
14406            ff,
14407            eps,
14408        )?;
14409        let kvl = cache.kv[il].as_mut().unwrap();
14410        let base_len = kvl.len;
14411        e.append_kv_quantized_rows(
14412            &k,
14413            &v,
14414            &mut kvl.k,
14415            &mut kvl.v,
14416            base_len,
14417            t,
14418            kvl.kv_dim_k,
14419            kvl.kv_dim_v,
14420            kvl.k_tok_bytes,
14421            kvl.v_tok_bytes,
14422            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14423        )?;
14424        kvl.len += t;
14425        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14426        let mut attn = e.uninit(t * nh * hd)?;
14427        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14428        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14429        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14430            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14431            // decode rides the SAME symbol at t=1 (parity law).
14432            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14433        if rows_ok && (!swa || base_len + t <= win) {
14434            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14435            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14436            if hd == 512 {
14437                // device-len twin: sync the counter to the verify base (async arg-store).
14438                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14439                e.fa_decode_rows(
14440                    &q,
14441                    &k_view,
14442                    &v_view,
14443                    &mut attn,
14444                    hd,
14445                    nh,
14446                    nkv,
14447                    base_len,
14448                    t,
14449                    scale,
14450                    kvl.k_tok_bytes,
14451                    kvl.v_tok_bytes,
14452                    Some((&kvl.len_d, 0)),
14453                    false,
14454                    swa && crate::Engine::wkv_on(),
14455                    None,
14456                )?;
14457            } else {
14458                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14459                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14460                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14461                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14462                e.fa_decode_rows_dc(
14463                    &q,
14464                    &k_view,
14465                    &v_view,
14466                    &mut attn,
14467                    hd,
14468                    nh,
14469                    nkv,
14470                    &kvl.len_d,
14471                    base_len + t,
14472                    t,
14473                    scale,
14474                    kvl.k_tok_bytes,
14475                    kvl.v_tok_bytes,
14476                    0,
14477                    swa && crate::Engine::wkv_on(),
14478                )?;
14479            }
14480            return Ok(e.matmul(&fa.wo, &attn, t)?);
14481        }
14482        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14483        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14484        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14485        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14486        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14487        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14488        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14489        if hd == 256
14490            && swa
14491            && base_len + 1 >= win
14492            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14493        {
14494            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14495            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14496            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14497            e.fa_decode_rows_w(
14498                &q,
14499                &k_view,
14500                &v_view,
14501                &mut attn,
14502                hd,
14503                nh,
14504                nkv,
14505                &kvl.len_d,
14506                0,
14507                t,
14508                scale,
14509                win,
14510                kvl.k_tok_bytes,
14511                kvl.v_tok_bytes,
14512                None,
14513            )?;
14514            return Ok(e.matmul(&fa.wo, &attn, t)?);
14515        }
14516        for i in 0..t {
14517            let avail = base_len + i + 1;
14518            let (off_tok, t_kv) = if swa && avail > win {
14519                (avail - win, win)
14520            } else {
14521                (0, avail)
14522            };
14523            let k_view = e.view_u8_range(
14524                &kvl.k,
14525                off_tok * kvl.k_tok_bytes,
14526                (off_tok + t_kv) * kvl.k_tok_bytes,
14527            );
14528            let v_view = e.view_u8_range(
14529                &kvl.v,
14530                off_tok * kvl.v_tok_bytes,
14531                (off_tok + t_kv) * kvl.v_tok_bytes,
14532            );
14533            let qi = e.view(&q, t * nh * hd);
14534            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14535            let mut q_one = e.uninit(nh * hd)?;
14536            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14537            let mut a_one = e.uninit(nh * hd)?;
14538            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14539            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14540            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14541            if swa
14542                && avail > win
14543                && hd == 256
14544                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14545            {
14546                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14547                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14548                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14549                e.fa_decode_rows_w(
14550                    &q_one,
14551                    &kp,
14552                    &vp,
14553                    &mut a_one,
14554                    hd,
14555                    nh,
14556                    nkv,
14557                    &kvl.len_d,
14558                    0,
14559                    1,
14560                    scale,
14561                    win,
14562                    kvl.k_tok_bytes,
14563                    kvl.v_tok_bytes,
14564                    None,
14565                )?;
14566            } else if !swa
14567                && hd == 512
14568                && avail >= crate::fa512_min_tkv()
14569                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14570            {
14571                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14572                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14573                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14574                e.fa_decode_rows(
14575                    &q_one,
14576                    &kp,
14577                    &vp,
14578                    &mut a_one,
14579                    hd,
14580                    nh,
14581                    nkv,
14582                    avail - 1,
14583                    1,
14584                    scale,
14585                    kvl.k_tok_bytes,
14586                    kvl.v_tok_bytes,
14587                    Some((&kvl.len_d, 0)),
14588                    false,
14589                    false,
14590                    None,
14591                )?;
14592            } else {
14593                e.fa_decode_kvmod(
14594                    &q_one,
14595                    &k_view,
14596                    &v_view,
14597                    &mut a_one,
14598                    hd,
14599                    nh,
14600                    nkv,
14601                    t_kv,
14602                    scale,
14603                    kvl.k_tok_bytes,
14604                    kvl.v_tok_bytes,
14605                    swa && crate::Engine::wkv_on(),
14606                )?;
14607            }
14608            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14609        }
14610        Ok(e.matmul(&fa.wo, &attn, t)?)
14611    }
14612
14613    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
14614    /// h_seed = pre-output_norm hidden). Advances cache.pos.
14615    pub(crate) fn gemma4_decode_step_h(
14616        &self,
14617        e: &Engine,
14618        token: u32,
14619        cache: &mut Cache,
14620    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14621        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
14622        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
14623        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
14624        // unsplit rather than guessing a fence.
14625        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
14626            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
14627        }
14628        if crate::pp::pp_cuts(self.layers.len()).is_some() {
14629            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
14630        }
14631        let n_embd = self.cfg.n_embd as usize;
14632        let eps = self.cfg.rms_eps;
14633        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14634        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14635        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14636        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
14637        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
14638        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14639        let n_layers = self.layers.len();
14640        for (il, layer) in self.layers.iter().enumerate() {
14641            let (hq, hdq) = match h_carry.take() {
14642                Some(p) => p,
14643                None => {
14644                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
14645                }
14646            };
14647            let Mixer::Full(fa) = &layer.mixer else {
14648                panic!("gemma4 layer {il} not full-attn")
14649            };
14650            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
14651            let next_norm = if il + 1 < n_layers {
14652                Some(self.layers[il + 1].attn_norm.float_data())
14653            } else {
14654                None
14655            };
14656            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14657            x = xn;
14658            h_carry = hn;
14659        }
14660        let mut hn = e.uninit(n_embd)?;
14661        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14662        let h_seed = e.clone_dtod(&x)?;
14663        let mut ld = e.matmul(&self.output, &hn, 1)?;
14664        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14665        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
14666        self.gemma4_suppress(e, &mut ld, 1)?;
14667        let logits = e.dtoh(&ld)?;
14668        cache.pos += 1;
14669        Ok((logits, h_seed))
14670    }
14671
14672    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
14673    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
14674    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
14675    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
14676    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
14677    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
14678    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
14679    fn gemma4_decode_layers(
14680        &self,
14681        e: &Engine,
14682        mut x: CudaSlice<f32>,
14683        lo: usize,
14684        hi: usize,
14685        pos_d: &CudaSlice<i32>,
14686        cache: &mut Cache,
14687    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14688        let n_embd = self.cfg.n_embd as usize;
14689        let eps = self.cfg.rms_eps;
14690        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14691        for il in lo..hi {
14692            let layer = &self.layers[il];
14693            let (hq, hdq) = match h_carry.take() {
14694                Some(p) => p,
14695                // range head: il == lo — norm against THIS layer's attn_norm.
14696                None => {
14697                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
14698                }
14699            };
14700            let Mixer::Full(fa) = &layer.mixer else {
14701                panic!("gemma4 layer {il} not full-attn")
14702            };
14703            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
14704            let next_norm = if il + 1 < hi {
14705                Some(self.layers[il + 1].attn_norm.float_data())
14706            } else {
14707                None
14708            };
14709            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14710            x = xn;
14711            h_carry = hn;
14712        }
14713        Ok(x)
14714    }
14715
14716    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
14717    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
14718    /// boundary handoff — same choreography as the generic arm (decode.rs), same
14719    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
14720    /// stage 1 = layers [split, n) + output_norm + softcapped head.
14721    /// Each stage uploads its own copy of the step's position scalar on its own stream.
14722    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
14723    fn gemma4_decode_step_h_pp2(
14724        &self,
14725        e: &Engine,
14726        token: u32,
14727        cache: &mut Cache,
14728        split: usize,
14729    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14730        if crate::pp::pp2_streams_off() {
14731            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
14732        }
14733        let rt = crate::pp::Pp2Rt::get(e)?;
14734        let e0 = rt.engine(0, e);
14735        let e1 = rt.engine(1, e);
14736        let n_embd = self.cfg.n_embd as usize;
14737        let eps = self.cfg.rms_eps;
14738        let pos = cache.pos as i32;
14739
14740        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
14741        let slot = {
14742            let _st0 = rt.enter(0);
14743            let pos_d = e0.htod_i32(&[pos])?;
14744            #[cfg(debug_assertions)]
14745            crate::debug_assert_tensor_stream_device(
14746                &pos_d,
14747                &e0.stream(),
14748                "gemma4_decode_step_h_pp2.stage0.pos_d",
14749            );
14750            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
14751            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14752            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
14753            rt.tx(0, &x, n_embd)?
14754        };
14755
14756        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
14757        let _st1 = rt.enter(1);
14758        let pos_d = e1.htod_i32(&[pos])?;
14759        #[cfg(debug_assertions)]
14760        crate::debug_assert_tensor_stream_device(
14761            &pos_d,
14762            &e1.stream(),
14763            "gemma4_decode_step_h_pp2.stage1.pos_d",
14764        );
14765        let x = rt.rx(0, slot, n_embd)?;
14766        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
14767
14768        let mut hn = e1.uninit(n_embd)?;
14769        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14770        let h_seed = e1.clone_dtod(&x)?;
14771        let mut ld = e1.matmul(&self.output, &hn, 1)?;
14772        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14773        e1.softcap(&mut ld, cap, self.output.out_features())?;
14774        self.gemma4_suppress(e1, &mut ld, 1)?;
14775        let logits = e1.dtoh(&ld)?;
14776        cache.pos += 1;
14777        Ok((logits, h_seed))
14778    }
14779
14780    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
14781    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
14782    fn gemma4_decode_step_h_pp2_samestream(
14783        &self,
14784        e: &Engine,
14785        token: u32,
14786        cache: &mut Cache,
14787        split: usize,
14788    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14789        let n_embd = self.cfg.n_embd as usize;
14790        let eps = self.cfg.rms_eps;
14791        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14792
14793        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
14794        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14795        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14796        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
14797
14798        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
14799        let boundary_tx = e.clone_dtod(&x)?;
14800        let boundary_rx = e.clone_dtod(&boundary_tx)?;
14801
14802        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
14803        let x =
14804            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
14805
14806        let mut hn = e.uninit(n_embd)?;
14807        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14808        let h_seed = e.clone_dtod(&x)?;
14809        let mut ld = e.matmul(&self.output, &hn, 1)?;
14810        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14811        e.softcap(&mut ld, cap, self.output.out_features())?;
14812        self.gemma4_suppress(e, &mut ld, 1)?;
14813        let logits = e.dtoh(&ld)?;
14814        cache.pos += 1;
14815        Ok((logits, h_seed))
14816    }
14817}
14818
14819// ============================ step35 (Step-3.7-Flash) ==================================
14820// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
14821// FAMILY and not a few branches inside the generic `full_attn*` chain:
14822//
14823//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
14824//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
14825//      shapes and the FA head counts would be wrong on 33 of 45 layers.
14826//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
14827//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
14828//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
14829//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
14830//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
14831//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
14832//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
14833//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
14834//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
14835//
14836// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
14837impl HybridModel {
14838    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
14839    /// synthesize a drafter or trunk layer from a neighboring class.
14840    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
14841        let geometry = self
14842            .cfg
14843            .layer_geometry(il as u32)
14844            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
14845        debug_assert_eq!(
14846            geometry.attention_gate,
14847            memra_gguf::config::AttentionGateKind::SeparateHead
14848        );
14849        geometry
14850    }
14851
14852    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
14853    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
14854    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
14855    ///
14856    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
14857    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
14858    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
14859    /// `cache`:
14860    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
14861    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
14862    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
14863    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
14864    ///     contract, lane/chunkinv-flip).
14865    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
14866    ///     q/k/v, no cache side effect.
14867    ///
14868    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
14869    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
14870    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
14871    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
14872    /// still contains must be masked per query. memra's window convention
14873    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
14874    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
14875    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
14876    ///
14877    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
14878    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
14879    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
14880    ///
14881    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
14882    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
14883    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
14884    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
14885    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
14886    /// hidden rows, and the generated text — a function of the chunk size:
14887    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
14888    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
14889    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
14890    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
14891    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
14892    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
14893    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
14894    ///   one-token change in a documented machine-config knob changed the answer.
14895    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
14896    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
14897    /// the same rows moves the logits by ~1.8.
14898    ///
14899    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
14900    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
14901    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
14902    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
14903    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
14904    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
14905    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
14906    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
14907    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
14908    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
14909    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
14910    /// those with t_kv <= win = 512.
14911    #[allow(clippy::too_many_arguments)]
14912    fn step35_attn_pre_wo(
14913        &self,
14914        e: &Engine,
14915        fa: &FullAttnLayer,
14916        mut g3: Vec<CudaSlice<f32>>,
14917        hg: Option<&CudaSlice<f32>>,
14918        gt_pre: Option<&CudaSlice<f32>>,
14919        pos_d: &CudaSlice<i32>,
14920        t: usize,
14921        cache: Option<&mut Cache>,
14922        il: usize,
14923        seq_end: usize,
14924    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14925        let geometry = self.step35_geom(il);
14926        let hd = geometry.head_dim_k as usize;
14927        let nkv = geometry.n_head_kv as usize;
14928        let nh = geometry.n_head as usize;
14929        let rbase = geometry.rope_base;
14930        let scale = geometry.attention_scale();
14931        let swa = geometry.window.is_some();
14932        let eps = self.cfg.rms_eps;
14933        let win = geometry.window.unwrap_or(0) as usize;
14934        let n_rot = geometry.n_rot as usize;
14935
14936        let v = g3.pop().unwrap();
14937        let k0 = g3.pop().unwrap();
14938        let q0 = g3.pop().unwrap();
14939
14940        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
14941        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
14942        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
14943        let mut q = e.uninit(t * nh * hd)?;
14944        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
14945        let mut k = e.uninit(t * nkv * hd)?;
14946        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
14947        let ff = if geometry.rope_factors {
14948            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
14949        } else {
14950            None
14951        };
14952        #[cfg(debug_assertions)]
14953        if let Some(ff) = ff {
14954            crate::debug_assert_tensor_stream_device(
14955                ff,
14956                &e.stream(),
14957                "step35_attn_pre_wo.rope_freqs",
14958            );
14959        }
14960        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
14961
14962        let mut attn = e.uninit(t * nh * hd)?;
14963        match cache {
14964            Some(cache) => {
14965                let base_len = cache.kv[il].as_ref().unwrap().len;
14966                // Read per layer call, never in a measured default.
14967                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
14968                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
14969                let off = if swa {
14970                    let raw = base_len.saturating_sub(win - 1);
14971                    if legacy_tkv || legacy_calllocal {
14972                        raw
14973                    } else {
14974                        raw & !31usize
14975                    }
14976                } else {
14977                    0
14978                };
14979                {
14980                    let kvl = cache.kv[il].as_mut().unwrap();
14981                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
14982                    let write_row = e.prepare_kv_append(kvl, off, t)?;
14983                    e.append_kv_quantized_rows(
14984                        &k,
14985                        &v,
14986                        &mut kvl.k,
14987                        &mut kvl.v,
14988                        write_row,
14989                        t,
14990                        kvl.kv_dim_k,
14991                        kvl.kv_dim_v,
14992                        kvl.k_tok_bytes,
14993                        kvl.v_tok_bytes,
14994                        crate::Engine::kv_fp8_on(),
14995                    )?;
14996                    kvl.len += t;
14997                    let new_len = kvl.len as i32;
14998                    e.set_i32_one(&mut kvl.len_d, new_len)?;
14999                }
15000                let kvl = cache.kv[il].as_ref().unwrap();
15001                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15002                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15003                // unaligned view offset here. Both halves are load-bearing for the canaries:
15004                // on the FA default the predicate arms agree bitwise wherever they can differ
15005                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15006                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15007                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15008                // on the current FA path: its tile grid starts at the chunk/call boundary.
15009                // SWA: trim the view to the oldest key any query in this chunk can reach —
15010                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15011                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15012                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15013                // the VIEW START — so an unaligned off regroups the same absolute keys into
15014                // different tiles at different chunk sizes = different (m,l) rounding =
15015                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15016                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15017                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15018                // size; the <=31 extra leading keys are older than EVERY query's window
15019                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15020                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15021                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15022                // the floor arm's bits do not move either (gated: G2f, battery 2).
15023                let t_kv = base_len + t - off;
15024                let physical = kvl.physical_rows(off, off + t_kv)?;
15025                let k_view = e.view_u8_range(
15026                    &kvl.k,
15027                    physical.start * kvl.k_tok_bytes,
15028                    physical.end * kvl.k_tok_bytes,
15029                );
15030                let v_view = e.view_u8_range(
15031                    &kvl.v,
15032                    physical.start * kvl.v_tok_bytes,
15033                    physical.end * kvl.v_tok_bytes,
15034                );
15035                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15036                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15037                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15038                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15039                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15040                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15041                // construction, so the invariance assertion MUST break under it (the seam whose
15042                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15043                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15044                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15045                // cached (probes flip it in-process). Never on in a measured default run.
15046                let swa_naive = if legacy_tkv {
15047                    t_kv > win
15048                } else {
15049                    seq_end > win
15050                };
15051                if swa && swa_naive {
15052                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15053                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15054                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15055                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15056                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15057                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15058                    // identically to the unwindowed one modulo the mask, which is the point.
15059                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15060                    // selected on `seq_end` like every arm here, so the class is uniform for
15061                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15062                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15063                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15064                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15065                        e.sdpa_naive_w_quantized_view(
15066                            &q,
15067                            &k_view,
15068                            &v_view,
15069                            &mut attn,
15070                            hd,
15071                            nh,
15072                            nkv,
15073                            t,
15074                            t_kv,
15075                            scale,
15076                            true,
15077                            win,
15078                            kvl.k_tok_bytes,
15079                            kvl.v_tok_bytes,
15080                        )?;
15081                    } else {
15082                        e.fa_prefill_view_ws_w_hd128(
15083                            &q,
15084                            &k_view,
15085                            &v_view,
15086                            &mut attn,
15087                            hd,
15088                            nh,
15089                            nkv,
15090                            t,
15091                            t_kv,
15092                            scale,
15093                            true,
15094                            win,
15095                            kvl.k_tok_bytes,
15096                            kvl.v_tok_bytes,
15097                        )?;
15098                    }
15099                } else if std::env::var("MEMRA_NOFA").is_ok() {
15100                    e.sdpa_naive_quantized_view(
15101                        &q,
15102                        &k_view,
15103                        &v_view,
15104                        &mut attn,
15105                        hd,
15106                        nh,
15107                        nkv,
15108                        t,
15109                        t_kv,
15110                        scale,
15111                        true,
15112                        kvl.k_tok_bytes,
15113                        kvl.v_tok_bytes,
15114                    )?;
15115                } else {
15116                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15117                    // reach past the window, so the window mask is a no-op under causal and every
15118                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15119                    // request either way, which is what makes the chunk size arithmetic-free.
15120                    e.fa_prefill_view_ws(
15121                        &q,
15122                        &k_view,
15123                        &v_view,
15124                        &mut attn,
15125                        hd,
15126                        nh,
15127                        nkv,
15128                        t,
15129                        t_kv,
15130                        scale,
15131                        true,
15132                        kvl.k_tok_bytes,
15133                        kvl.v_tok_bytes,
15134                        crate::Engine::kv_fp8_on(),
15135                    )?;
15136                }
15137            }
15138            None => {
15139                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15140                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15141                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15142                // seq_end here too or it re-opens the same door.
15143                debug_assert_eq!(
15144                    seq_end, t,
15145                    "step35 cacheless prefill is monolithic (seq_end == t)"
15146                );
15147                if swa && seq_end > win {
15148                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15149                } else if std::env::var("MEMRA_NOFA").is_ok() {
15150                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15151                } else {
15152                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15153                }
15154            }
15155        }
15156
15157        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15158        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15159        let gw = fa
15160            .attn_gate
15161            .as_ref()
15162            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15163        let gt_owned = if gt_pre.is_none() {
15164            Some(e.matmul(
15165                gw,
15166                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15167                t,
15168            )?)
15169        } else {
15170            None
15171        };
15172        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15173        let mut ag = e.uninit(t * nh * hd)?;
15174        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15175        Ok(ag)
15176    }
15177
15178    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15179    /// `forward_last`, t2probe). Post-`wo`.
15180    pub(crate) fn step35_attn(
15181        &self,
15182        e: &Engine,
15183        fa: &FullAttnLayer,
15184        h: &CudaSlice<f32>,
15185        pos_d: &CudaSlice<i32>,
15186        t: usize,
15187        il: usize,
15188    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15189        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15190            Some(g3) => g3,
15191            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15192        };
15193        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15194        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15195        self.step35_o(e, fa, &ag, t)
15196    }
15197
15198    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15199    /// resident quantized cache, attend through the cache view). Post-`wo`.
15200    ///
15201    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15202    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15203    /// own extent.
15204    #[allow(clippy::too_many_arguments)]
15205    pub(crate) fn step35_attn_prime(
15206        &self,
15207        e: &Engine,
15208        fa: &FullAttnLayer,
15209        h: &CudaSlice<f32>,
15210        hx: Option<&CudaSlice<u8>>,
15211        pos_d: &CudaSlice<i32>,
15212        t: usize,
15213        cache: &mut Cache,
15214        il: usize,
15215        seq_end: usize,
15216    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15217        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15218            if hx.is_some() {
15219                return Err(
15220                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15221                     pre-quantized prime path"
15222                        .into(),
15223                );
15224            }
15225            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15226        }
15227        let g3 = if fa.step_tp_qkv.is_some() {
15228            if hx.is_some() {
15229                return Err(
15230                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15231                     pre-quantized prime path"
15232                        .into(),
15233                );
15234            }
15235            self.step35_tp_qkv(e, fa, h, t)?
15236                .expect("Step Q/K/V TP disappeared after the presence check")
15237        } else {
15238            match hx {
15239                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15240                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15241            }
15242        };
15243        let ag =
15244            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15245        self.step35_o(e, fa, &ag, t)
15246    }
15247
15248    fn ensure_step_tp_kv_cache(
15249        &self,
15250        e: &Engine,
15251        fa: &FullAttnLayer,
15252        il: usize,
15253        cache: &mut Cache,
15254    ) -> Result<bool, Box<dyn std::error::Error>> {
15255        let tp = fa
15256            .step_tp_qkv
15257            .as_ref()
15258            .ok_or("Step TP cache hydration lost its resident projections")?;
15259        let geometry = self.step35_geom(il);
15260        let window = geometry.window.map(|window| window as usize);
15261        let ranks = tp.runtime.devices().len();
15262        let head_dim = geometry.head_dim_k as usize;
15263        let kv_heads = geometry.n_head_kv as usize;
15264        let max_ctx = cache.max_ctx;
15265
15266        if cache.tp_kv[il].is_some() {
15267            return Ok(false);
15268        }
15269        let local = cache.kv[il]
15270            .as_ref()
15271            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15272        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15273            return Err(format!(
15274                "Step TP layer {il} local KV geometry k={} v={} != {}",
15275                local.kv_dim_k,
15276                local.kv_dim_v,
15277                kv_heads * head_dim
15278            )
15279            .into());
15280        }
15281        let resident_start = window
15282            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15283            .unwrap_or(0);
15284        let resident_rows = local.len - resident_start;
15285        let physical = local.physical_rows(resident_start, local.len)?;
15286        let k_rows = if resident_rows == 0 {
15287            Vec::new()
15288        } else {
15289            e.dtoh_u8_view(&e.view_u8_range(
15290                &local.k,
15291                physical.start * local.k_tok_bytes,
15292                physical.end * local.k_tok_bytes,
15293            ))?
15294        };
15295        let v_rows = if resident_rows == 0 {
15296            Vec::new()
15297        } else {
15298            e.dtoh_u8_view(&e.view_u8_range(
15299                &local.v,
15300                physical.start * local.v_tok_bytes,
15301                physical.end * local.v_tok_bytes,
15302            ))?
15303        };
15304        let mut distributed = match window {
15305            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15306                kv_heads * head_dim,
15307                kv_heads * head_dim,
15308                max_ctx,
15309                window,
15310            )?,
15311            None => tp.runtime.allocate_tp_kv_cache(
15312                kv_heads * head_dim,
15313                kv_heads * head_dim,
15314                max_ctx,
15315            )?,
15316        };
15317        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15318            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15319        {
15320            return Err(format!(
15321                "Step TP layer {il} distributed/local KV token bytes disagree: \
15322                 k={}x{ranks}/{} v={}x{ranks}/{}",
15323                distributed.k_tok_bytes(),
15324                local.k_tok_bytes,
15325                distributed.v_tok_bytes(),
15326                local.v_tok_bytes,
15327            )
15328            .into());
15329        }
15330        tp.runtime.hydrate_tp_kv_cache_from(
15331            &mut distributed,
15332            local.len,
15333            resident_start,
15334            &k_rows,
15335            &v_rows,
15336        )?;
15337        cache.tp_kv[il] = Some(distributed);
15338        Ok(true)
15339    }
15340
15341    #[allow(clippy::too_many_arguments)]
15342    fn step35_tp_prefill_attn_resident(
15343        &self,
15344        e: &Engine,
15345        fa: &FullAttnLayer,
15346        il: usize,
15347        h: &CudaSlice<f32>,
15348        pos_d: &CudaSlice<i32>,
15349        tokens: usize,
15350        cache: &mut Cache,
15351        seq_end: usize,
15352    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15353        let tp = fa
15354            .step_tp_qkv
15355            .as_ref()
15356            .ok_or("Step TP prefill lost its resident projections")?;
15357        let attention = tp
15358            .attention
15359            .as_ref()
15360            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15361        let ranks = tp.runtime.devices().len();
15362        if !step_tp_prefill_shape(
15363            true,
15364            tokens,
15365            ranks,
15366            tp.runtime.native_p2p(),
15367            true,
15368            crate::Engine::kv_fp8_on(),
15369        ) {
15370            return Err(format!(
15371                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP4 native P2P, \
15372                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15373                 native_p2p={} fp8_kv={}",
15374                tp.runtime.native_p2p(),
15375                crate::Engine::kv_fp8_on(),
15376            )
15377            .into());
15378        }
15379        for seam in [
15380            "MEMRA_STEP35_SWA_TKV",
15381            "MEMRA_PRIME_CALLLOCAL",
15382            "MEMRA_PRIME_F32CHUNK0",
15383        ] {
15384            if std::env::var(seam).as_deref() == Ok("1") {
15385                return Err(format!(
15386                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15387                )
15388                .into());
15389            }
15390        }
15391
15392        let geometry = self.step35_geom(il);
15393        let window = geometry.window.map(|window| window as usize);
15394        let head_dim = geometry.head_dim_k as usize;
15395        let heads = geometry.n_head as usize;
15396        let kv_heads = geometry.n_head_kv as usize;
15397        if heads % ranks != 0 || kv_heads % ranks != 0 {
15398            return Err(format!(
15399                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15400            )
15401            .into());
15402        }
15403        let local_heads = heads / ranks;
15404        let local_kv_heads = kv_heads / ranks;
15405        let local_kv_dim = local_kv_heads * head_dim;
15406        let hidden = self.cfg.n_embd as usize;
15407        let expected_input = tokens
15408            .checked_mul(hidden)
15409            .ok_or("Step TP prefill input size overflow")?;
15410        if h.len() < expected_input {
15411            return Err(format!(
15412                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15413                h.len()
15414            )
15415            .into());
15416        }
15417        let positions = e.dtoh_i32(pos_d)?;
15418        if positions.len() != tokens {
15419            return Err(format!(
15420                "rank-local Step prefill positions {} != tokens {tokens}",
15421                positions.len()
15422            )
15423            .into());
15424        }
15425
15426        let mut active_input = e.uninit(expected_input)?;
15427        e.copy_view_into(
15428            &mut active_input,
15429            0,
15430            &h.slice(0..expected_input),
15431            expected_input,
15432        )?;
15433        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15434        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15435        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15436        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15437        // layer-count-amplified arm of the boot flake.
15438        e.stream().synchronize()?;
15439        tp.runtime
15440            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15441        let q_raw = tp
15442            .runtime
15443            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15444        let k_raw = tp
15445            .runtime
15446            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15447        let v_raw = tp
15448            .runtime
15449            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15450        let mut q = Vec::with_capacity(ranks);
15451        let mut k = Vec::with_capacity(ranks);
15452        for rank in 0..ranks {
15453            let engine = tp
15454                .runtime
15455                .rank_engine(rank)
15456                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15457            let _main = engine.gpu.enter_main()?;
15458            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15459            engine.rms_norm(
15460                &q_raw[rank],
15461                &attention.q_norm[rank],
15462                &mut q_rank,
15463                head_dim,
15464                tokens * local_heads,
15465                self.cfg.rms_eps,
15466            )?;
15467            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15468            engine.rms_norm(
15469                &k_raw[rank],
15470                &attention.k_norm[rank],
15471                &mut k_rank,
15472                head_dim,
15473                tokens * local_kv_heads,
15474                self.cfg.rms_eps,
15475            )?;
15476            let position = engine.htod_i32(&positions)?;
15477            let rope_freqs = if geometry.rope_factors {
15478                self.step35_aux
15479                    .as_ref()
15480                    .and_then(|aux| aux.rope_freqs(engine))
15481            } else {
15482                None
15483            };
15484            engine.rope_neox2(
15485                &mut q_rank,
15486                &mut k_rank,
15487                &position,
15488                head_dim,
15489                geometry.n_rot as usize,
15490                local_heads,
15491                local_kv_heads,
15492                tokens,
15493                geometry.rope_base,
15494                1.0,
15495                rope_freqs,
15496            )?;
15497            q.push(q_rank);
15498            k.push(k_rank);
15499        }
15500
15501        let gate_weight = fa
15502            .attn_gate
15503            .as_ref()
15504            .ok_or("step35 layer is missing attn_gate.weight")?;
15505        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15506        if gate.len() != tokens * heads {
15507            return Err(format!(
15508                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15509                gate.len()
15510            )
15511            .into());
15512        }
15513
15514        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15515        let base_len = cache.kv[il]
15516            .as_ref()
15517            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15518            .len;
15519        let distributed = cache.tp_kv[il]
15520            .as_ref()
15521            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15522        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15523            return Err(format!(
15524                "Step TP layer {il} cache lengths diverged before prefill: \
15525                 local={base_len} distributed={}/{}",
15526                distributed.committed_len(),
15527                distributed.staged_len()
15528            )
15529            .into());
15530        }
15531        let target_len = base_len
15532            .checked_add(tokens)
15533            .ok_or("Step TP prefill cache length overflow")?;
15534        if target_len > cache.max_ctx {
15535            return Err(format!(
15536                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15537                cache.max_ctx
15538            )
15539            .into());
15540        }
15541        if seq_end < target_len {
15542            return Err(format!(
15543                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15544            )
15545            .into());
15546        }
15547
15548        let transaction = cache.tp_kv[il]
15549            .as_mut()
15550            .expect("distributed cache checked above")
15551            .begin_transaction()?;
15552        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15553            cache.tp_kv[il]
15554                .as_mut()
15555                .expect("distributed cache checked above"),
15556            transaction,
15557            &k,
15558            &v_raw,
15559            tokens,
15560        ) {
15561            let _ = tp.runtime.rollback_tp_kv_transaction(
15562                cache.tp_kv[il]
15563                    .as_mut()
15564                    .expect("distributed cache checked above"),
15565                transaction,
15566            );
15567            return Err(error);
15568        }
15569
15570        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15571            let distributed = cache.tp_kv[il]
15572                .as_ref()
15573                .expect("distributed cache checked above");
15574            let staged_len = distributed.staged_len();
15575            let view_start = window
15576                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15577                .unwrap_or(0);
15578            let physical = distributed.physical_range(view_start, staged_len)?;
15579            let t_kv = staged_len - view_start;
15580            let swa_naive = window.is_some_and(|window| seq_end > window);
15581            let mut gated = Vec::with_capacity(ranks);
15582            for rank in 0..ranks {
15583                let engine = tp
15584                    .runtime
15585                    .rank_engine(rank)
15586                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15587                let _main = engine.gpu.enter_main()?;
15588                let rank_cache = distributed
15589                    .rank(rank)
15590                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15591                let k_view = engine.view_u8_range(
15592                    rank_cache.k(),
15593                    physical.start * distributed.k_tok_bytes(),
15594                    physical.end * distributed.k_tok_bytes(),
15595                );
15596                let v_view = engine.view_u8_range(
15597                    rank_cache.v(),
15598                    physical.start * distributed.v_tok_bytes(),
15599                    physical.end * distributed.v_tok_bytes(),
15600                );
15601                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
15602                if swa_naive {
15603                    let window = window.expect("SWA predicate requires a window");
15604                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15605                        engine.sdpa_naive_w_quantized_view(
15606                            &q[rank],
15607                            &k_view,
15608                            &v_view,
15609                            &mut attention_out,
15610                            head_dim,
15611                            local_heads,
15612                            local_kv_heads,
15613                            tokens,
15614                            t_kv,
15615                            geometry.attention_scale(),
15616                            true,
15617                            window,
15618                            distributed.k_tok_bytes(),
15619                            distributed.v_tok_bytes(),
15620                        )?;
15621                    } else {
15622                        engine.fa_prefill_view_ws_w_hd128(
15623                            &q[rank],
15624                            &k_view,
15625                            &v_view,
15626                            &mut attention_out,
15627                            head_dim,
15628                            local_heads,
15629                            local_kv_heads,
15630                            tokens,
15631                            t_kv,
15632                            geometry.attention_scale(),
15633                            true,
15634                            window,
15635                            distributed.k_tok_bytes(),
15636                            distributed.v_tok_bytes(),
15637                        )?;
15638                    }
15639                } else if std::env::var("MEMRA_NOFA").is_ok() {
15640                    engine.sdpa_naive_quantized_view(
15641                        &q[rank],
15642                        &k_view,
15643                        &v_view,
15644                        &mut attention_out,
15645                        head_dim,
15646                        local_heads,
15647                        local_kv_heads,
15648                        tokens,
15649                        t_kv,
15650                        geometry.attention_scale(),
15651                        true,
15652                        distributed.k_tok_bytes(),
15653                        distributed.v_tok_bytes(),
15654                    )?;
15655                } else {
15656                    engine.fa_prefill_view_ws(
15657                        &q[rank],
15658                        &k_view,
15659                        &v_view,
15660                        &mut attention_out,
15661                        head_dim,
15662                        local_heads,
15663                        local_kv_heads,
15664                        tokens,
15665                        t_kv,
15666                        geometry.attention_scale(),
15667                        true,
15668                        distributed.k_tok_bytes(),
15669                        distributed.v_tok_bytes(),
15670                        false,
15671                    )?;
15672                }
15673
15674                let gate_start = rank * local_heads;
15675                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
15676                for token in 0..tokens {
15677                    let start = token * heads + gate_start;
15678                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
15679                }
15680                let gate_rank = engine.htod(&gate_rank)?;
15681                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
15682                engine.attn_head_gate(
15683                    &attention_out,
15684                    &gate_rank,
15685                    &mut gated_rank,
15686                    None,
15687                    head_dim,
15688                    local_heads,
15689                    tokens,
15690                )?;
15691                gated.push(gated_rank);
15692            }
15693            for rank in 1..ranks {
15694                let engine = tp
15695                    .runtime
15696                    .rank_engine(rank)
15697                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15698                let _main = engine.gpu.enter_main()?;
15699                engine.stream().synchronize()?;
15700            }
15701
15702            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
15703                let output = tp
15704                    .runtime
15705                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
15706                let k_shadow =
15707                    tp.runtime
15708                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
15709                let v_shadow =
15710                    tp.runtime
15711                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
15712                let root = tp
15713                    .runtime
15714                    .rank_engine(0)
15715                    .ok_or("Step TP prefill lost its root engine")?;
15716                let _main = root.gpu.enter_main()?;
15717                root.stream().synchronize()?;
15718                (output, k_shadow, v_shadow)
15719            } else {
15720                let attention = tp.runtime.gather_native_column_shards(
15721                    &gated,
15722                    tokens,
15723                    local_heads * head_dim,
15724                )?;
15725                let output = tp
15726                    .runtime
15727                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
15728                let k_shadow = tp
15729                    .runtime
15730                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
15731                let v_shadow =
15732                    tp.runtime
15733                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
15734                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
15735            };
15736            let local = cache.kv[il]
15737                .as_mut()
15738                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
15739            if local.len != base_len {
15740                return Err(format!(
15741                    "Step TP layer {il} local cache changed during prefill: \
15742                     len={} base={base_len}",
15743                    local.len
15744                )
15745                .into());
15746            }
15747            let retain_from = window
15748                .map(|window| {
15749                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
15750                    let rollback_retain =
15751                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
15752                    staged_retain.min(rollback_retain)
15753                })
15754                .unwrap_or(0);
15755            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
15756            e.append_kv_quantized_rows(
15757                &k_shadow,
15758                &v_shadow,
15759                &mut local.k,
15760                &mut local.v,
15761                write_row,
15762                tokens,
15763                local.kv_dim_k,
15764                local.kv_dim_v,
15765                local.k_tok_bytes,
15766                local.v_tok_bytes,
15767                false,
15768            )?;
15769            local.len = staged_len;
15770            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
15771            Ok(output)
15772        })();
15773
15774        let output = match staged {
15775            Ok(output) => output,
15776            Err(error) => {
15777                let _ = tp.runtime.rollback_tp_kv_transaction(
15778                    cache.tp_kv[il]
15779                        .as_mut()
15780                        .expect("distributed cache checked above"),
15781                    transaction,
15782                );
15783                if let Some(local) = cache.kv[il].as_mut() {
15784                    local.len = base_len;
15785                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
15786                }
15787                return Err(error);
15788            }
15789        };
15790        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
15791            cache.tp_kv[il]
15792                .as_mut()
15793                .expect("distributed cache checked above"),
15794            transaction,
15795            tokens,
15796        ) {
15797            let _ = tp.runtime.rollback_tp_kv_transaction(
15798                cache.tp_kv[il]
15799                    .as_mut()
15800                    .expect("distributed cache checked above"),
15801                transaction,
15802            );
15803            let local = cache.kv[il].as_mut().expect("local cache checked above");
15804            local.len = base_len;
15805            e.set_i32_one(&mut local.len_d, base_len as i32)?;
15806            return Err(error);
15807        }
15808
15809        let committed = cache.tp_kv[il]
15810            .as_ref()
15811            .expect("distributed cache checked above")
15812            .committed_len();
15813        let local_len = cache.kv[il]
15814            .as_ref()
15815            .expect("local cache checked above")
15816            .len;
15817        if committed != local_len {
15818            return Err(format!(
15819                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
15820            )
15821            .into());
15822        }
15823        eprintln!(
15824            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
15825             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
15826             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
15827             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
15828             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
15829             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
15830             output={} performance_claim=false",
15831            tp.layer,
15832            tp.devices,
15833            hydrated,
15834            if window.is_some() {
15835                "rank-local-swa-ring"
15836            } else {
15837                "rank-local-global"
15838            },
15839            tp.runtime.transport_label(),
15840            tp.runtime.bulk_p2p(),
15841            if tp.runtime.bulk_p2p() {
15842                "root-device"
15843            } else {
15844                "root-readback"
15845            },
15846        );
15847        Ok(output)
15848    }
15849
15850    fn step35_tp_decode_attn_resident(
15851        &self,
15852        e: &Engine,
15853        fa: &FullAttnLayer,
15854        il: usize,
15855        h: &CudaSlice<f32>,
15856        pos_d: &CudaSlice<i32>,
15857        cache: &mut Cache,
15858    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15859        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
15860        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
15861        // nvfp4-dev-routes counter.
15862        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15863        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15864        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15865        let started = timing.then(std::time::Instant::now);
15866        let result = if crate::tp::step_tp_decode_v2_enabled()? {
15867            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
15868        } else {
15869            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
15870        };
15871        if let Some(started) = started {
15872            use std::sync::atomic::Ordering;
15873            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
15874                + started.elapsed().as_nanos() as u64;
15875            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
15876            if calls % 430 == 0 {
15877                eprintln!(
15878                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
15879                    ns as f64 / 1.0e6,
15880                    ns as f64 / calls as f64 / 1.0e3,
15881                );
15882            }
15883        }
15884        result
15885    }
15886
15887    #[allow(clippy::too_many_arguments)]
15888    fn step35_tp_decode_attn_resident_inner(
15889        &self,
15890        e: &Engine,
15891        fa: &FullAttnLayer,
15892        il: usize,
15893        h: &CudaSlice<f32>,
15894        pos_d: &CudaSlice<i32>,
15895        cache: &mut Cache,
15896    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15897        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
15898        // drains every stream so queued async work is billed to the phase that queued it — the
15899        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
15900        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
15901        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15902        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15903        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15904        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15905        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15906        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15907        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15908        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15909        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15910        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15911        fn lap(
15912            runtime: &crate::tp::TpE4m3HostBounce,
15913            e: &Engine,
15914            timer: &std::sync::atomic::AtomicU64,
15915            started: &mut Option<std::time::Instant>,
15916        ) -> Result<(), Box<dyn std::error::Error>> {
15917            let Some(start) = started.as_mut() else {
15918                return Ok(());
15919            };
15920            for rank in 0..runtime.devices().len() {
15921                if let Some(engine) = runtime.rank_engine(rank) {
15922                    let _main = engine.gpu.enter_main()?;
15923                    engine.stream().synchronize()?;
15924                }
15925            }
15926            e.stream().synchronize()?;
15927            timer.fetch_add(
15928                start.elapsed().as_nanos() as u64,
15929                std::sync::atomic::Ordering::Relaxed,
15930            );
15931            *start = std::time::Instant::now();
15932            Ok(())
15933        }
15934        let tp = fa
15935            .step_tp_qkv
15936            .as_ref()
15937            .ok_or("Step TP decode lost its resident projections")?;
15938        let attention = tp
15939            .attention
15940            .as_ref()
15941            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
15942        if !tp.runtime.native_p2p() {
15943            return Err("rank-local Step attention requires native P2P".into());
15944        }
15945        if crate::Engine::kv_fp8_on() {
15946            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
15947        }
15948
15949        let geometry = self.step35_geom(il);
15950        let window = geometry.window.map(|window| window as usize);
15951        let ranks = tp.runtime.devices().len();
15952        let head_dim = geometry.head_dim_k as usize;
15953        let heads = geometry.n_head as usize;
15954        let kv_heads = geometry.n_head_kv as usize;
15955        if heads % ranks != 0 || kv_heads % ranks != 0 {
15956            return Err(format!(
15957                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15958            )
15959            .into());
15960        }
15961        let local_heads = heads / ranks;
15962        let local_kv_heads = kv_heads / ranks;
15963        let local_kv_dim = local_kv_heads * head_dim;
15964        let max_ctx = cache.max_ctx;
15965
15966        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15967
15968        let base_len = cache.kv[il]
15969            .as_ref()
15970            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15971            .len;
15972        let distributed = cache.tp_kv[il]
15973            .as_ref()
15974            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15975        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15976            return Err(format!(
15977                "Step TP layer {il} cache lengths diverged before decode: \
15978                 local={base_len} distributed={}/{}",
15979                distributed.committed_len(),
15980                distributed.staged_len()
15981            )
15982            .into());
15983        }
15984
15985        let mut lap_start = timing.then(std::time::Instant::now);
15986        let positions = e.dtoh_i32(pos_d)?;
15987        if positions.len() != 1 {
15988            return Err(format!(
15989                "rank-local Step decode requires one position, got {}",
15990                positions.len()
15991            )
15992            .into());
15993        }
15994        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
15995        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
15996            attention.decode_input.as_ref()
15997        {
15998            let mut decode_input = decode_input
15999                .lock()
16000                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16001            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16002            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16003            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16004            e.stream().synchronize()?;
16005            tp.runtime
16006                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16007            let q_raw = tp
16008                .runtime
16009                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16010            let k_raw = tp
16011                .runtime
16012                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16013            let v_raw = tp
16014                .runtime
16015                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16016            (q_raw, k_raw, v_raw, "root-device-replicated")
16017        } else {
16018            let activation = e.dtoh(h)?;
16019            let q_raw =
16020                tp.runtime
16021                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16022            let k_raw =
16023                tp.runtime
16024                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16025            let v_raw =
16026                tp.runtime
16027                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16028            (q_raw, k_raw, v_raw, "host-replicated")
16029        };
16030        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16031        let mut q = Vec::with_capacity(ranks);
16032        let mut k = Vec::with_capacity(ranks);
16033        for rank in 0..ranks {
16034            let engine = tp
16035                .runtime
16036                .rank_engine(rank)
16037                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16038            let _main = engine.gpu.enter_main()?;
16039            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16040            engine.rms_norm(
16041                &q_raw[rank],
16042                &attention.q_norm[rank],
16043                &mut q_rank,
16044                head_dim,
16045                local_heads,
16046                self.cfg.rms_eps,
16047            )?;
16048            let mut k_rank = engine.uninit(local_kv_dim)?;
16049            engine.rms_norm(
16050                &k_raw[rank],
16051                &attention.k_norm[rank],
16052                &mut k_rank,
16053                head_dim,
16054                local_kv_heads,
16055                self.cfg.rms_eps,
16056            )?;
16057            let position = engine.htod_i32(&positions)?;
16058            let rope_freqs = if geometry.rope_factors {
16059                self.step35_aux
16060                    .as_ref()
16061                    .and_then(|aux| aux.rope_freqs(engine))
16062            } else {
16063                None
16064            };
16065            engine.rope_neox2(
16066                &mut q_rank,
16067                &mut k_rank,
16068                &position,
16069                head_dim,
16070                geometry.n_rot as usize,
16071                local_heads,
16072                local_kv_heads,
16073                1,
16074                geometry.rope_base,
16075                1.0,
16076                rope_freqs,
16077            )?;
16078            q.push(q_rank);
16079            k.push(k_rank);
16080        }
16081        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16082
16083        let gate_weight = fa
16084            .attn_gate
16085            .as_ref()
16086            .ok_or("step35 layer is missing attn_gate.weight")?;
16087        let gate = e.matmul(gate_weight, h, 1)?;
16088        let gate = e.dtoh(&gate)?;
16089        if gate.len() != heads {
16090            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16091        }
16092        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16093
16094        let transaction = cache.tp_kv[il]
16095            .as_mut()
16096            .expect("distributed cache checked above")
16097            .begin_transaction()?;
16098        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16099            cache.tp_kv[il]
16100                .as_mut()
16101                .expect("distributed cache checked above"),
16102            transaction,
16103            &k,
16104            &v_raw,
16105            1,
16106        ) {
16107            let _ = tp.runtime.rollback_tp_kv_transaction(
16108                cache.tp_kv[il]
16109                    .as_mut()
16110                    .expect("distributed cache checked above"),
16111                transaction,
16112            );
16113            return Err(error);
16114        }
16115        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16116
16117        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16118            let distributed = cache.tp_kv[il]
16119                .as_ref()
16120                .expect("distributed cache checked above");
16121            let staged_len = distributed.staged_len();
16122            let view_start = window
16123                .map(|window| staged_len.saturating_sub(window))
16124                .unwrap_or(0);
16125            let physical = distributed.physical_range(view_start, staged_len)?;
16126            let t_kv = staged_len - view_start;
16127            let mut gated = Vec::with_capacity(ranks);
16128            for rank in 0..ranks {
16129                let engine = tp
16130                    .runtime
16131                    .rank_engine(rank)
16132                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16133                let _main = engine.gpu.enter_main()?;
16134                let rank_cache = distributed
16135                    .rank(rank)
16136                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16137                let k_view = engine.view_u8_range(
16138                    rank_cache.k(),
16139                    physical.start * distributed.k_tok_bytes(),
16140                    physical.end * distributed.k_tok_bytes(),
16141                );
16142                let v_view = engine.view_u8_range(
16143                    rank_cache.v(),
16144                    physical.start * distributed.v_tok_bytes(),
16145                    physical.end * distributed.v_tok_bytes(),
16146                );
16147                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16148                engine.fa_decode_kvmod(
16149                    &q[rank],
16150                    &k_view,
16151                    &v_view,
16152                    &mut attention_out,
16153                    head_dim,
16154                    local_heads,
16155                    local_kv_heads,
16156                    t_kv,
16157                    geometry.attention_scale(),
16158                    distributed.k_tok_bytes(),
16159                    distributed.v_tok_bytes(),
16160                    false,
16161                )?;
16162                let gate_start = rank * local_heads;
16163                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16164                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16165                engine.attn_head_gate(
16166                    &attention_out,
16167                    &gate_rank,
16168                    &mut gated_rank,
16169                    None,
16170                    head_dim,
16171                    local_heads,
16172                    1,
16173                )?;
16174                gated.push(gated_rank);
16175            }
16176            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16177
16178            let gathered =
16179                tp.runtime
16180                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16181            let output = tp
16182                .runtime
16183                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16184            let output = e.htod(&output)?;
16185            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16186
16187            let k_shadow = tp
16188                .runtime
16189                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16190            let v_shadow = tp
16191                .runtime
16192                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16193            let k_shadow = e.htod(&k_shadow)?;
16194            let v_shadow = e.htod(&v_shadow)?;
16195            let local = cache.kv[il]
16196                .as_mut()
16197                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16198            if local.len != base_len || base_len + 1 > max_ctx {
16199                return Err(format!(
16200                    "Step TP layer {il} local cache changed during decode: \
16201                     len={} base={base_len} max={max_ctx}",
16202                    local.len
16203                )
16204                .into());
16205            }
16206            let retain_from = window
16207                .map(|window| {
16208                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16209                    let rollback_retain =
16210                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16211                    staged_retain.min(rollback_retain)
16212                })
16213                .unwrap_or(0);
16214            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16215            e.append_kv_quantized(
16216                &k_shadow,
16217                &v_shadow,
16218                &mut local.k,
16219                &mut local.v,
16220                write_row,
16221                local.kv_dim_k,
16222                local.kv_dim_v,
16223                local.k_tok_bytes,
16224                local.v_tok_bytes,
16225                false,
16226            )?;
16227            local.len = base_len + 1;
16228            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16229            Ok(output)
16230        })();
16231
16232        let output = match staged {
16233            Ok(output) => output,
16234            Err(error) => {
16235                let _ = tp.runtime.rollback_tp_kv_transaction(
16236                    cache.tp_kv[il]
16237                        .as_mut()
16238                        .expect("distributed cache checked above"),
16239                    transaction,
16240                );
16241                if let Some(local) = cache.kv[il].as_mut() {
16242                    local.len = base_len;
16243                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16244                }
16245                return Err(error);
16246            }
16247        };
16248        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16249            cache.tp_kv[il]
16250                .as_mut()
16251                .expect("distributed cache checked above"),
16252            transaction,
16253            1,
16254        ) {
16255            let _ = tp.runtime.rollback_tp_kv_transaction(
16256                cache.tp_kv[il]
16257                    .as_mut()
16258                    .expect("distributed cache checked above"),
16259                transaction,
16260            );
16261            let local = cache.kv[il].as_mut().expect("local cache checked above");
16262            local.len = base_len;
16263            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16264            return Err(error);
16265        }
16266
16267        let committed = cache.tp_kv[il]
16268            .as_ref()
16269            .expect("distributed cache checked above")
16270            .committed_len();
16271        let local_len = cache.kv[il]
16272            .as_ref()
16273            .expect("local cache checked above")
16274            .len;
16275        if committed != local_len {
16276            return Err(format!(
16277                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16278            )
16279            .into());
16280        }
16281        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16282        if timing {
16283            use std::sync::atomic::Ordering;
16284            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16285            if calls % 430 == 0 {
16286                let avg = |t: &std::sync::atomic::AtomicU64| {
16287                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16288                };
16289                eprintln!(
16290                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16291                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16292                    avg(&T_POS),
16293                    avg(&T_QKV),
16294                    avg(&T_NORMROPE),
16295                    avg(&T_GATE),
16296                    avg(&T_APPEND),
16297                    avg(&T_ATTN),
16298                    avg(&T_OPROJ),
16299                    avg(&T_SHADOW),
16300                );
16301            }
16302        }
16303        eprintln!(
16304            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16305             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16306             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16307             attention_scope={} input_path={} kv_physical_rows={} \
16308             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16309             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16310             bulk_p2p={} output=root-readback performance_claim=false",
16311            tp.layer,
16312            tp.devices,
16313            hydrated,
16314            if window.is_some() {
16315                "rank-local-swa-ring"
16316            } else {
16317                "rank-local-global"
16318            },
16319            input_path,
16320            cache.tp_kv[il]
16321                .as_ref()
16322                .expect("distributed cache checked above")
16323                .physical_capacity(),
16324            tp.runtime.transport_label(),
16325            tp.runtime.bulk_p2p(),
16326        );
16327        Ok(output)
16328    }
16329
16330    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16331    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16332    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16333    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16334    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16335    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16336    #[allow(clippy::too_many_arguments)]
16337    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16338    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16339    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16340    /// the resident fused TP2 class (caller falls back to the per-row walk).
16341    pub(crate) fn step35_verify_qkv_precompute(
16342        &self,
16343        e: &Engine,
16344        il: usize,
16345        h_t: &CudaSlice<f32>,
16346        t: usize,
16347    ) -> Result<bool, Box<dyn std::error::Error>> {
16348        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16349            return Ok(false);
16350        };
16351        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16352            return Ok(false);
16353        };
16354        let Some(attention) = tp.attention.as_ref() else {
16355            return Ok(false);
16356        };
16357        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16358            return Ok(false);
16359        }
16360        let geometry = self.step35_geom(il);
16361        let heads = geometry.n_head as usize;
16362        let ws_index = tp
16363            .runtime
16364            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16365        let gate_shards = attention
16366            .gate_shards_bf16
16367            .as_deref()
16368            .map(crate::tp::StepTpGateShards::Bf16);
16369        tp.runtime.decode_v2_input_qkv_tcol(
16370            ws_index,
16371            e,
16372            h_t,
16373            t,
16374            &tp.q,
16375            &tp.k,
16376            &tp.v,
16377            gate_shards,
16378        )?;
16379        Ok(true)
16380    }
16381
16382    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16383    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16384    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16385    /// flag confirmed the defer engaged for every column.
16386    pub(crate) fn step35_verify_oproj_tcol(
16387        &self,
16388        e: &Engine,
16389        il: usize,
16390        t: usize,
16391    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16392        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16393            return Err("tcol o_proj join expects full attention".into());
16394        };
16395        let tp = fa
16396            .step_tp_qkv
16397            .as_ref()
16398            .ok_or("tcol o_proj join lost its resident projections")?;
16399        let heads = self.step35_geom(il).n_head as usize;
16400        let ws_index = tp
16401            .runtime
16402            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16403        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16404    }
16405
16406    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16407    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16408    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16409    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16410    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16411    /// walk runs the ordinary per-column program.
16412    pub(crate) fn step35_spec_fa2_precheck(
16413        &self,
16414        cache: &Cache,
16415        il: usize,
16416        pos0: usize,
16417    ) -> Result<bool, Box<dyn std::error::Error>> {
16418        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16419        // a silently-vacuous door is indistinguishable from a slow one without this.
16420        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16421            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16422            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16423            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16424                let mut seen = SEEN.lock().unwrap();
16425                if !seen.iter().any(|c| *c == clause) {
16426                    // leak: bounded by the clause-id set
16427                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16428                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16429                }
16430            }
16431            false
16432        }
16433        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16434        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16435        if let Some(only) =
16436            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16437        {
16438            if *only != il {
16439                return Ok(false);
16440            }
16441        }
16442        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16443            return Ok(nope("mixer", il, pos0));
16444        };
16445        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16446            return Ok(nope("step_tp", il, pos0));
16447        };
16448        let Some(attention) = tp.attention.as_ref() else {
16449            return Ok(nope("attention", il, pos0));
16450        };
16451        if !tp.runtime.native_p2p()
16452            || crate::Engine::kv_fp8_on()
16453            || !crate::tp::step_tp_dcw_enabled()?
16454            || !crate::tp::step_tp_qkv_fused_enabled()?
16455            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16456        {
16457            return Ok(nope("runtime-doors", il, pos0));
16458        }
16459        let geometry = self.step35_geom(il);
16460        let head_dim = geometry.head_dim_k as usize;
16461        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16462            return Ok(nope("fa-class", il, pos0));
16463        }
16464        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16465            return Ok(nope("tp-kv", il, pos0));
16466        };
16467        if distributed.staged_len() != pos0 {
16468            return Ok(nope("staged-len", il, pos0));
16469        }
16470        // Both appends must land without a ring rebase (rebase columns take the
16471        // host-row path, which cannot stash).
16472        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16473        if would_rebase {
16474            return Ok(nope("rebase", il, pos0));
16475        }
16476        let window = geometry.window.map(|w| w as usize);
16477        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16478        // shift by one key, so one shared tile grid cannot reproduce both rows'
16479        // per-column FP grouping) — and drifted verify logits change accept decisions,
16480        // breaking the spec==target contract. Engage only when BOTH rows' views start
16481        // at 0 (global, or SWA still inside its window): bitwise per row under the
16482        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16483        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16484        if let Some(w) = window {
16485            if pos0 + 2 > w {
16486                return Ok(nope("swa-capped", il, pos0));
16487            }
16488        }
16489        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16490        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16491        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16492        let (t0, t1) = (pos0 + 1, pos0 + 2);
16493        if t0 < 96 {
16494            return Ok(nope("dcw-floor", il, pos0));
16495        }
16496        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16497            return Ok(nope("vec-floor", il, pos0));
16498        }
16499        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16500        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16501        // the two rows' own launches — the joined kernel derives one grid from T1 and
16502        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16503        // the combine's merge rounding. Boundary rounds fall back per column.
16504        let ranks = tp.runtime.devices().len();
16505        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16506        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16507        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16508        if sp0 != sp1 {
16509            return Ok(nope("partition-sp", il, pos0));
16510        }
16511        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16512        if ns0 != ns1 {
16513            return Ok(nope("partition-ns", il, pos0));
16514        }
16515        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16516            return Ok(nope("partition-per", il, pos0));
16517        }
16518        Ok(true)
16519    }
16520
16521    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
16522    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
16523    /// slab on `e`.
16524    pub(crate) fn step35_verify_spec_fa2_join(
16525        &self,
16526        e: &Engine,
16527        il: usize,
16528        cache: &Cache,
16529        pos0: usize,
16530    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16531        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16532            return Err("spec fa2 join expects full attention".into());
16533        };
16534        let tp = fa
16535            .step_tp_qkv
16536            .as_ref()
16537            .ok_or("spec fa2 join lost its resident projections")?;
16538        let geometry = self.step35_geom(il);
16539        let heads = geometry.n_head as usize;
16540        let head_dim = geometry.head_dim_k as usize;
16541        let window = geometry.window.map(|w| w as usize);
16542        // POST-append view of the second row (kernel T1 = len - lstart with len =
16543        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
16544        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
16545        let distributed = cache.tp_kv[il]
16546            .as_ref()
16547            .ok_or("spec fa2 join lost its distributed KV cache")?;
16548        let ws_index = tp
16549            .runtime
16550            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16551        tp.runtime.decode_v2_spec_fa2_join(
16552            ws_index,
16553            e,
16554            &tp.o,
16555            distributed,
16556            head_dim,
16557            window.unwrap_or(0),
16558            bucket,
16559            geometry.attention_scale(),
16560        )
16561    }
16562
16563    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
16564    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
16565    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
16566    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
16567    /// (caller falls back to the per-column walk).
16568    pub(crate) fn step35_verify_moe_tn(
16569        &self,
16570        e: &Engine,
16571        il: usize,
16572        z_t: &CudaSlice<f32>,
16573        t: usize,
16574    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16575        let layer = &self.layers[il];
16576        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
16577            return Ok(None);
16578        };
16579        let Some(tp) = m.step_tp.as_ref() else {
16580            return Ok(None);
16581        };
16582        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
16583            return Ok(None);
16584        };
16585        if !crate::tp::step_nvfp4_dev_routes_enabled()?
16586            || !crate::tp::step_tp_dev_router_enabled()?
16587            || !crate::tp::nvfp4_bank_v2_on()
16588            || bank.ep2
16589        {
16590            return Ok(None);
16591        }
16592        let cfg = &self.cfg;
16593        let Some(moe) = cfg.moe.as_ref() else {
16594            return Ok(None);
16595        };
16596        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
16597            return Ok(None);
16598        };
16599        let n_embd = cfg.n_embd as usize;
16600        let n_expert = moe.expert_count as usize;
16601        let n_used = moe.expert_used_count as usize;
16602        if t < 2 || t > 8 || z_t.len() < t * n_embd {
16603            return Err("verify moe t-row geometry".into());
16604        }
16605        let trace = std::env::var("MEMRA_TN_TRACE").as_deref() == Ok("1");
16606        if trace {
16607            eprintln!("[tn-trace] il={il} t={t} logits");
16608        }
16609        let logits = Self::moe_router_logits(e, m, z_t, t, cfg)?;
16610        // Persistent selection rows (host-op diet, same shape law as the t=1 SELW),
16611        // sized for the widest walk (t <= 8).
16612        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
16613            std::sync::Mutex::new(None);
16614        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
16615        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
16616            *selw = Some((
16617                e.ctx().ordinal(),
16618                e.htod_i32(&vec![0i32; 8 * n_used])?,
16619                e.htod(&vec![0.0f32; 8 * n_used])?,
16620            ));
16621        }
16622        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
16623        if trace {
16624            eprintln!("[tn-trace] il={il} topk logits_len={}", logits.len());
16625        }
16626        e.moe_router_sigmoid_topk_into(
16627            &logits,
16628            t,
16629            n_expert,
16630            n_used,
16631            m.active_count(),
16632            &m.exp_probs_b_dev,
16633            &m.active_experts_dev,
16634            sf,
16635            route_norm,
16636            sel_d,
16637            w_d,
16638        )?;
16639        if trace {
16640            eprintln!("[tn-trace] il={il} driver");
16641        }
16642        let mut out_t = tp
16643            .runtime
16644            .run_tensor_parallel_routes_nvfp4_device_routed_tn(
16645                bank,
16646                e,
16647                z_t,
16648                sel_d,
16649                w_d,
16650                t,
16651                n_used,
16652                tp.activation_limit,
16653            )?;
16654        if trace {
16655            eprintln!("[tn-trace] il={il} shexp out_t={}", out_t.len());
16656        }
16657        // Shared expert: the exact t=1 program per column, added into that column's row.
16658        let mut z_row = e.uninit(n_embd)?;
16659        let mut out_row = e.uninit(n_embd)?;
16660        for c in 0..t {
16661            e.dtod_copy_view(&z_t.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
16662            e.dtod_copy_view(&out_t.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
16663            Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
16664            e.dtod_copy_into(&out_row, &mut out_t, c * n_embd)?;
16665        }
16666        Ok(Some(out_t))
16667    }
16668
16669    fn step35_tp_decode_attn_resident_v2(
16670        &self,
16671        e: &Engine,
16672        fa: &FullAttnLayer,
16673        il: usize,
16674        h: &CudaSlice<f32>,
16675        pos_d: &CudaSlice<i32>,
16676        cache: &mut Cache,
16677    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16678        let tp = fa
16679            .step_tp_qkv
16680            .as_ref()
16681            .ok_or("Step TP decode lost its resident projections")?;
16682        let attention = tp
16683            .attention
16684            .as_ref()
16685            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
16686        if !tp.runtime.native_p2p() {
16687            return Err("rank-local Step attention requires native P2P".into());
16688        }
16689        if crate::Engine::kv_fp8_on() {
16690            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
16691        }
16692
16693        let geometry = self.step35_geom(il);
16694        let window = geometry.window.map(|window| window as usize);
16695        let ranks = tp.runtime.devices().len();
16696        let head_dim = geometry.head_dim_k as usize;
16697        let heads = geometry.n_head as usize;
16698        let kv_heads = geometry.n_head_kv as usize;
16699        if heads % ranks != 0 || kv_heads % ranks != 0 {
16700            return Err(format!(
16701                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
16702            )
16703            .into());
16704        }
16705        let local_heads = heads / ranks;
16706        let local_kv_heads = kv_heads / ranks;
16707        let max_ctx = cache.max_ctx;
16708
16709        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
16710
16711        let base_len = cache.kv[il]
16712            .as_ref()
16713            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
16714            .len;
16715        {
16716            let distributed = cache.tp_kv[il]
16717                .as_ref()
16718                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16719            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16720                return Err(format!(
16721                    "Step TP layer {il} cache lengths diverged before decode: \
16722                     local={base_len} distributed={}/{}",
16723                    distributed.committed_len(),
16724                    distributed.staged_len()
16725                )
16726                .into());
16727            }
16728        }
16729        if pos_d.len() != 1 {
16730            return Err(format!(
16731                "rank-local Step decode requires one position, got {}",
16732                pos_d.len()
16733            )
16734            .into());
16735        }
16736
16737        let decode_input = attention
16738            .decode_input
16739            .as_ref()
16740            .ok_or("Step TP decode v2 requires the replicated decode input")?;
16741        let mut decode_input = decode_input
16742            .lock()
16743            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16744
16745        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
16746        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
16747        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
16748        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
16749        let use_gate_shards = (attention.gate_shards.is_some()
16750            || attention.gate_shards_bf16.is_some())
16751            && crate::tp::step_tp_qkv_fused_enabled()?;
16752        let gate_raw = if use_gate_shards {
16753            None
16754        } else {
16755            let gate_weight = fa
16756                .attn_gate
16757                .as_ref()
16758                .ok_or("step35 layer is missing attn_gate.weight")?;
16759            let gate_raw = e.matmul(gate_weight, h, 1)?;
16760            if gate_raw.len() != heads {
16761                return Err(format!(
16762                    "Step TP layer {il} gate output {} != {heads}",
16763                    gate_raw.len()
16764                )
16765                .into());
16766            }
16767            Some(gate_raw)
16768        };
16769
16770        let ws_index = tp
16771            .runtime
16772            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16773        let mut ws_guard = tp
16774            .runtime
16775            .decode_v2_workspace()
16776            .lock()
16777            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
16778        let ws = ws_guard
16779            .get_mut(ws_index)
16780            .ok_or("Step TP decode v2 workspace missing after ensure")?;
16781
16782        let mut rope_freqs = Vec::with_capacity(ranks);
16783        for rank in 0..ranks {
16784            let engine = tp
16785                .runtime
16786                .rank_engine(rank)
16787                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16788            rope_freqs.push(if geometry.rope_factors {
16789                self.step35_aux
16790                    .as_ref()
16791                    .and_then(|aux| aux.rope_freqs(engine))
16792            } else {
16793                None
16794            });
16795        }
16796        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
16797        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
16798        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
16799        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
16800        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
16801        // the fused rope+append+inc launch on dcw tokens.)
16802        let staged_next = base_len + 1;
16803        let t_kv_eff = window
16804            .map(|window| staged_next.min(window))
16805            .unwrap_or(staged_next);
16806        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
16807            let (write_row, would_rebase) = cache.tp_kv[il]
16808                .as_ref()
16809                .expect("distributed cache checked above")
16810                .peek_append_ring(1)?;
16811            if !would_rebase {
16812                // Arm the base mirrors on first use: base = logical staged - physical row.
16813                let base = (base_len - write_row) as i32;
16814                let distributed = cache.tp_kv[il]
16815                    .as_mut()
16816                    .expect("distributed cache checked above");
16817                for rank in 0..ranks {
16818                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
16819                        format!("Step TP layer {il} has no engine for rank {rank}")
16820                    })?;
16821                    let _main = engine.gpu.enter_main()?;
16822                    let rank_cache = distributed
16823                        .rank_mut(rank)
16824                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16825                    if rank_cache.base_d().is_none() {
16826                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
16827                    }
16828                }
16829            }
16830            !would_rebase
16831        };
16832        let fuse_rope = dcw
16833            && crate::tp::fuse_rope_append_on()
16834            && head_dim == 128
16835            && cache.tp_kv[il]
16836                .as_ref()
16837                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
16838                .unwrap_or(false);
16839
16840        let tcol_col = crate::tp::take_verify_tcol();
16841        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
16842        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
16843        // state must advance per column) but skips the fa+gate launch; post-rope q and
16844        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
16845        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
16846        // normally and the walk consumes the real output — stash flag stays unset).
16847        let fa2_col = crate::tp::take_spec_fa2_defer();
16848        tp.runtime.decode_v2_input_qkv(
16849            ws,
16850            e,
16851            h,
16852            pos_d,
16853            gate_raw.as_ref(),
16854            if !use_gate_shards {
16855                None
16856            } else if let Some(shards) = attention.gate_shards.as_deref() {
16857                Some(crate::tp::StepTpGateShards::F32(shards))
16858            } else {
16859                attention
16860                    .gate_shards_bf16
16861                    .as_deref()
16862                    .map(crate::tp::StepTpGateShards::Bf16)
16863            },
16864            &mut decode_input,
16865            &tp.q,
16866            &tp.k,
16867            &tp.v,
16868            &attention.q_norm,
16869            &attention.k_norm,
16870            head_dim,
16871            geometry.n_rot as usize,
16872            geometry.rope_base,
16873            &rope_freqs,
16874            self.cfg.rms_eps,
16875            fuse_rope,
16876            tcol_col,
16877        )?;
16878
16879        let transaction = cache.tp_kv[il]
16880            .as_mut()
16881            .expect("distributed cache checked above")
16882            .begin_transaction()?;
16883        let append_result = tp.runtime.append_tp_kv_transaction_inner(
16884            cache.tp_kv[il]
16885                .as_mut()
16886                .expect("distributed cache checked above"),
16887            transaction,
16888            &ws.k,
16889            &ws.v_raw,
16890            1,
16891            dcw,
16892        );
16893        if let Err(error) = append_result {
16894            let _ = tp.runtime.rollback_tp_kv_transaction(
16895                cache.tp_kv[il]
16896                    .as_mut()
16897                    .expect("distributed cache checked above"),
16898                transaction,
16899            );
16900            return Err(error);
16901        }
16902
16903        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16904            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
16905            // reborrows the cache mutably per rank.
16906            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
16907                let distributed = cache.tp_kv[il]
16908                    .as_ref()
16909                    .expect("distributed cache checked above");
16910                let staged_len = distributed.staged_len();
16911                let view_start = window
16912                    .map(|window| staged_len.saturating_sub(window))
16913                    .unwrap_or(0);
16914                (
16915                    staged_len,
16916                    distributed.physical_range(view_start, staged_len)?,
16917                    distributed.k_tok_bytes(),
16918                    distributed.v_tok_bytes(),
16919                    distributed.physical_capacity(),
16920                )
16921            };
16922            let view_start = window
16923                .map(|window| staged_len.saturating_sub(window))
16924                .unwrap_or(0);
16925            let t_kv = staged_len - view_start;
16926            for rank in 0..ranks {
16927                let engine = tp
16928                    .runtime
16929                    .rank_engine(rank)
16930                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16931                let _main = engine.gpu.enter_main()?;
16932                if dcw {
16933                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
16934                    // stream visit. distributed is borrowed shared here; the planes need mut —
16935                    // reborrow through the cache Option (the closure holds cache mutably).
16936                    {
16937                        let distributed_mut = cache.tp_kv[il]
16938                            .as_mut()
16939                            .expect("distributed cache checked above");
16940                        let (kv_dim_k, kv_dim_v) =
16941                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
16942                        let (k_tok_bytes, v_tok_bytes) =
16943                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
16944                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
16945                            format!("Step TP layer {il} has no KV cache rank {rank}")
16946                        })?;
16947                        let (k_plane, v_plane, len_d, base_d) =
16948                            rank_cache.planes_and_counters_mut();
16949                        if fuse_rope {
16950                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
16951                            // + last-block len inc in ONE launch. Bit-identical bodies.
16952                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
16953                            let crate::tp::StepTpDecodeV2Ws {
16954                                q_raw,
16955                                k_raw,
16956                                v_raw,
16957                                q,
16958                                k,
16959                                pos,
16960                                pos_stage,
16961                                fuse_ctr,
16962                                ..
16963                            } = &mut *ws;
16964                            // Same-device rank: the staged-copy elision leaves pos[rank]
16965                            // stale — read the e-context pos stage directly (mirrors the
16966                            // rope elision in input_qkv_rank).
16967                            let pos_ref: &CudaSlice<i32> = if same_dev {
16968                                pos_stage
16969                                    .as_ref()
16970                                    .ok_or("step TP decode v2 pos stage not armed")?
16971                            } else {
16972                                &pos[rank]
16973                            };
16974                            engine.qk_norm_rope_append_inc_dcw(
16975                                &q_raw[rank],
16976                                &k_raw[rank],
16977                                &v_raw[rank],
16978                                &attention.q_norm[rank],
16979                                &attention.k_norm[rank],
16980                                &mut q[rank],
16981                                &mut k[rank],
16982                                pos_ref,
16983                                k_plane,
16984                                v_plane,
16985                                len_d,
16986                                base_d,
16987                                &mut fuse_ctr[rank],
16988                                kv_dim_k,
16989                                kv_dim_v,
16990                                k_tok_bytes,
16991                                v_tok_bytes,
16992                                head_dim,
16993                                geometry.n_rot as usize,
16994                                local_heads,
16995                                local_kv_heads,
16996                                self.cfg.rms_eps,
16997                                geometry.rope_base,
16998                                1.0,
16999                                rope_freqs[rank],
17000                            )?;
17001                        } else {
17002                            engine.append_kv_quantized_dcw(
17003                                &ws.k[rank],
17004                                &ws.v_raw[rank],
17005                                k_plane,
17006                                v_plane,
17007                                len_d,
17008                                base_d,
17009                                kv_dim_k,
17010                                kv_dim_v,
17011                                k_tok_bytes,
17012                                v_tok_bytes,
17013                            )?;
17014                        }
17015                        if !fuse_rope {
17016                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17017                                format!("Step TP layer {il} has no KV cache rank {rank}")
17018                            })?;
17019                            engine.inc_i32(rank_cache.len_d_mut())?;
17020                        }
17021                    }
17022                    let distributed = cache.tp_kv[il]
17023                        .as_ref()
17024                        .expect("distributed cache checked above");
17025                    let rank_cache = distributed
17026                        .rank(rank)
17027                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17028                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
17029                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
17030                    if fa2_col.is_some() {
17031                        // SPEC_FA2 defer: append landed above; the fa for this column
17032                        // runs in the T=2 joined launch after the pair's second append.
17033                        continue;
17034                    }
17035                    {
17036                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
17037                        // the gated output directly (bit-identical; one launch saved).
17038                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
17039                        engine.fa_decode_dcw(
17040                            &q[rank],
17041                            &k_ring,
17042                            &v_ring,
17043                            &mut gated[rank],
17044                            head_dim,
17045                            local_heads,
17046                            local_kv_heads,
17047                            rank_cache.len_d(),
17048                            rank_cache.base_d(),
17049                            window.unwrap_or(0),
17050                            t_kv,
17051                            geometry.attention_scale(),
17052                            k_tok_bytes_c,
17053                            v_tok_bytes_c,
17054                            Some(&gate[rank]),
17055                        )?;
17056                    }
17057                    continue;
17058                }
17059                let distributed = cache.tp_kv[il]
17060                    .as_ref()
17061                    .expect("distributed cache checked above");
17062                let rank_cache = distributed
17063                    .rank(rank)
17064                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17065                let k_view = engine.view_u8_range(
17066                    rank_cache.k(),
17067                    physical.start * k_tok_bytes_c,
17068                    physical.end * k_tok_bytes_c,
17069                );
17070                let v_view = engine.view_u8_range(
17071                    rank_cache.v(),
17072                    physical.start * v_tok_bytes_c,
17073                    physical.end * v_tok_bytes_c,
17074                );
17075                engine.fa_decode_kvmod(
17076                    &ws.q[rank],
17077                    &k_view,
17078                    &v_view,
17079                    &mut ws.attn_out[rank],
17080                    head_dim,
17081                    local_heads,
17082                    local_kv_heads,
17083                    t_kv,
17084                    geometry.attention_scale(),
17085                    k_tok_bytes_c,
17086                    v_tok_bytes_c,
17087                    false,
17088                )?;
17089                engine.attn_head_gate(
17090                    &ws.attn_out[rank],
17091                    &ws.gate[rank],
17092                    &mut ws.gated[rank],
17093                    None,
17094                    head_dim,
17095                    local_heads,
17096                    1,
17097                )?;
17098            }
17099
17100            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
17101            // column's `gated` rows and skip the per-column finish choreography entirely
17102            // (the batched b4_tcol + join runs after every column). The returned buffer
17103            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
17104            // stashed flag, never this buffer. Ineligible configs fall back to the
17105            // normal finish and the driver consumes the real `mixed` per column.
17106            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
17107                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
17108                // finish all run in the joined pass. Returned buffer is UNWRITTEN
17109                // (oproj-defer precedent — the walk reads the stash flag, never this).
17110                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
17111                crate::tp::set_spec_fa2_stashed();
17112                e.uninit(ws.o_out)?
17113            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
17114                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
17115                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
17116                    crate::tp::set_tcol_oproj_stashed();
17117                    e.uninit(ws.o_out)?
17118                } else {
17119                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17120                }
17121            } else {
17122                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17123            };
17124
17125            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
17126            // decode_v2_finish ordered behind the root event. Same math and cache state
17127            // transitions as v1.
17128            let local = cache.kv[il]
17129                .as_mut()
17130                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
17131            if local.len != base_len || base_len + 1 > max_ctx {
17132                return Err(format!(
17133                    "Step TP layer {il} local cache changed during decode: \
17134                     len={} base={base_len} max={max_ctx}",
17135                    local.len
17136                )
17137                .into());
17138            }
17139            if crate::tp::no_local_shadow_on() {
17140                // Lengths advance, contents stay stale (graph-door precedent: decode reads
17141                // only the distributed TP caches; local contents feed spec/MTP scratch).
17142                local.len = base_len + 1;
17143                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
17144                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
17145                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
17146                if !crate::tp::len_mirror_lazy_on() {
17147                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
17148                }
17149            } else {
17150                let retain_from = window
17151                    .map(|window| {
17152                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
17153                        let rollback_retain =
17154                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
17155                        staged_retain.min(rollback_retain)
17156                    })
17157                    .unwrap_or(0);
17158                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
17159                e.append_kv_quantized(
17160                    &ws.k_shadow,
17161                    &ws.v_shadow,
17162                    &mut local.k,
17163                    &mut local.v,
17164                    write_row,
17165                    local.kv_dim_k,
17166                    local.kv_dim_v,
17167                    local.k_tok_bytes,
17168                    local.v_tok_bytes,
17169                    false,
17170                )?;
17171                local.len = base_len + 1;
17172                e.set_i32_one(&mut local.len_d, local.len as i32)?;
17173            }
17174            Ok(output)
17175        })();
17176
17177        let output = match staged {
17178            Ok(output) => output,
17179            Err(error) => {
17180                let _ = tp.runtime.rollback_tp_kv_transaction(
17181                    cache.tp_kv[il]
17182                        .as_mut()
17183                        .expect("distributed cache checked above"),
17184                    transaction,
17185                );
17186                if let Some(local) = cache.kv[il].as_mut() {
17187                    local.len = base_len;
17188                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
17189                }
17190                return Err(error);
17191            }
17192        };
17193        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
17194        // the rank counters (same value as the absolute re-set on full accept), so commit
17195        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
17196        // keeps the absolute set (its appends do NOT inc).
17197        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
17198        if lazy_commit {
17199            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
17200                cache.tp_kv[il]
17201                    .as_mut()
17202                    .expect("distributed cache checked above"),
17203                transaction,
17204                1,
17205            ) {
17206                let _ = tp.runtime.rollback_tp_kv_transaction(
17207                    cache.tp_kv[il]
17208                        .as_mut()
17209                        .expect("distributed cache checked above"),
17210                    transaction,
17211                );
17212                let local = cache.kv[il].as_mut().expect("local cache checked above");
17213                local.len = base_len;
17214                e.set_i32_one(&mut local.len_d, base_len as i32)?;
17215                return Err(error);
17216            }
17217        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
17218            cache.tp_kv[il]
17219                .as_mut()
17220                .expect("distributed cache checked above"),
17221            transaction,
17222            1,
17223        ) {
17224            let _ = tp.runtime.rollback_tp_kv_transaction(
17225                cache.tp_kv[il]
17226                    .as_mut()
17227                    .expect("distributed cache checked above"),
17228                transaction,
17229            );
17230            let local = cache.kv[il].as_mut().expect("local cache checked above");
17231            local.len = base_len;
17232            e.set_i32_one(&mut local.len_d, base_len as i32)?;
17233            return Err(error);
17234        }
17235
17236        let committed = cache.tp_kv[il]
17237            .as_ref()
17238            .expect("distributed cache checked above")
17239            .committed_len();
17240        let local_len = cache.kv[il]
17241            .as_ref()
17242            .expect("local cache checked above")
17243            .len;
17244        if committed != local_len {
17245            return Err(format!(
17246                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
17247            )
17248            .into());
17249        }
17250        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
17251        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
17252            eprintln!(
17253                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
17254                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
17255                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
17256                 attention_tensor_parallel=true attention_scope={} \
17257                 input_path=root-device-replicated gate_tensor_parallel=false \
17258                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
17259                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
17260                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
17261                 performance_claim=false (logged once; every decode layer runs this driver)",
17262                tp.layer,
17263                tp.devices,
17264                if window.is_some() {
17265                    "rank-local-swa-ring"
17266                } else {
17267                    "rank-local-global"
17268                },
17269                tp.runtime.transport_label(),
17270                tp.runtime.bulk_p2p(),
17271            );
17272        }
17273        Ok(output)
17274    }
17275
17276    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
17277    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
17278    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
17279    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
17280    /// requiring `attn_gate`).
17281    ///
17282    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
17283    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
17284    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
17285    #[allow(clippy::too_many_arguments)]
17286    pub(crate) fn step35_decode_attn(
17287        &self,
17288        e: &Engine,
17289        fa: &FullAttnLayer,
17290        il: usize,
17291        h: &CudaSlice<f32>,
17292        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
17293        pos_d: &CudaSlice<i32>,
17294        cache: &mut Cache,
17295    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17296        if fa
17297            .step_tp_qkv
17298            .as_ref()
17299            .is_some_and(|tp| tp.attention.is_some())
17300        {
17301            if pre_q.is_some() {
17302                return Err(
17303                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
17304                     pre-quantized decode path"
17305                        .into(),
17306                );
17307            }
17308            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
17309        }
17310
17311        let geometry = self.step35_geom(il);
17312        let hd = geometry.head_dim_k as usize;
17313        let nkv = geometry.n_head_kv as usize;
17314        let nh = geometry.n_head as usize;
17315        let rbase = geometry.rope_base;
17316        let scale = geometry.attention_scale();
17317        let swa = geometry.window.is_some();
17318        let eps = self.cfg.rms_eps;
17319        let win = geometry.window.unwrap_or(0) as usize;
17320        let n_rot = geometry.n_rot as usize;
17321        let n_embd = self.cfg.n_embd as usize;
17322        let gw = fa
17323            .attn_gate
17324            .as_ref()
17325            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
17326
17327        let tp_qkv = if fa.step_tp_qkv.is_some() {
17328            if pre_q.is_some() {
17329                return Err(
17330                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
17331                     pre-quantized decode path"
17332                        .into(),
17333                );
17334            }
17335            self.step35_tp_qkv(e, fa, h, 1)?
17336        } else {
17337            None
17338        };
17339
17340        let (q0, k0, v0, gt) = match tp_qkv {
17341            Some(mut g3) => {
17342                let v = g3.pop().unwrap();
17343                let k = g3.pop().unwrap();
17344                let q = g3.pop().unwrap();
17345                let gt = e.matmul(gw, h, 1)?;
17346                (q, k, v, gt)
17347            }
17348            None => match pre_q {
17349                Some((hq, hdq)) => {
17350                    debug_assert!(
17351                        e.uses_q8_1_fast(gw),
17352                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
17353                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
17354                    );
17355                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
17356                        Some(t3) => t3,
17357                        None => (
17358                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
17359                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
17360                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
17361                        ),
17362                    };
17363                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
17364                    (a, b, c, gt)
17365                }
17366                None => {
17367                    if e.uses_q8_1_fast(&fa.wq)
17368                        && e.uses_q8_1_fast(&fa.wk)
17369                        && e.uses_q8_1_fast(&fa.wv)
17370                        && e.uses_q8_1_fast(gw)
17371                    {
17372                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
17373                        let (a, b, c) =
17374                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
17375                                Some(t3) => t3,
17376                                None => (
17377                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
17378                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
17379                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
17380                                ),
17381                            };
17382                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
17383                        (a, b, c, gt)
17384                    } else {
17385                        (
17386                            e.matmul(&fa.wq, h, 1)?,
17387                            e.matmul(&fa.wk, h, 1)?,
17388                            e.matmul(&fa.wv, h, 1)?,
17389                            e.matmul(gw, h, 1)?,
17390                        )
17391                    }
17392                }
17393            },
17394        };
17395
17396        let mut q = e.uninit(nh * hd)?;
17397        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
17398        let mut k = e.uninit(nkv * hd)?;
17399        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
17400        let ff = if swa {
17401            None
17402        } else {
17403            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
17404        };
17405        #[cfg(debug_assertions)]
17406        if let Some(ff) = ff {
17407            crate::debug_assert_tensor_stream_device(
17408                ff,
17409                &e.stream(),
17410                "step35_decode_attn.rope_freqs",
17411            );
17412        }
17413        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
17414
17415        if std::env::var("MEMRA_NOFA").is_ok() {
17416            return Err(
17417                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
17418                        cache; unset MEMRA_NOFA to use fa_decode"
17419                    .into(),
17420            );
17421        }
17422        let kvl = cache.kv[il].as_mut().unwrap();
17423        let next_len = kvl.len + 1;
17424        let (off, t_kv) = if swa && next_len > win {
17425            (next_len - win, win)
17426        } else {
17427            (0, next_len)
17428        };
17429        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
17430        e.append_kv_quantized(
17431            &k,
17432            &v0,
17433            &mut kvl.k,
17434            &mut kvl.v,
17435            write_row,
17436            kvl.kv_dim_k,
17437            kvl.kv_dim_v,
17438            kvl.k_tok_bytes,
17439            kvl.v_tok_bytes,
17440            crate::Engine::kv_fp8_on(),
17441        )?;
17442        kvl.len = next_len;
17443        let physical = kvl.physical_rows(off, off + t_kv)?;
17444        let k_view = e.view_u8_range(
17445            &kvl.k,
17446            physical.start * kvl.k_tok_bytes,
17447            physical.end * kvl.k_tok_bytes,
17448        );
17449        let v_view = e.view_u8_range(
17450            &kvl.v,
17451            physical.start * kvl.v_tok_bytes,
17452            physical.end * kvl.v_tok_bytes,
17453        );
17454        let mut attn = e.uninit(nh * hd)?;
17455        e.fa_decode_kvmod(
17456            &q,
17457            &k_view,
17458            &v_view,
17459            &mut attn,
17460            hd,
17461            nh,
17462            nkv,
17463            t_kv,
17464            scale,
17465            kvl.k_tok_bytes,
17466            kvl.v_tok_bytes,
17467            crate::Engine::kv_fp8_on(),
17468        )?;
17469
17470        let mut ag = e.uninit(nh * hd)?;
17471        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
17472        self.step35_o(e, fa, &ag, 1)
17473    }
17474}
17475
17476// ===================================================================================== //
17477//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
17478//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
17479//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
17480//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
17481//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
17482//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
17483// ===================================================================================== //
17484impl HybridModel {
17485    pub fn is_gemma4_e4b(&self) -> bool {
17486        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
17487    }
17488
17489    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
17490    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
17491    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
17492    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
17493        let g = self.cfg.gemma4.as_ref().unwrap();
17494        let swa = g.swa_pattern[il];
17495        let hd = if swa {
17496            g.key_length_swa
17497        } else {
17498            g.key_length_global
17499        } as usize;
17500        let Mixer::Full(fa) = &self.layers[il].mixer else {
17501            panic!("e4b layer {il} not full-attn")
17502        };
17503        let nh = fa.wq.out_features() / hd;
17504        let nkv = fa.wk.out_features() / hd;
17505        (
17506            hd,
17507            nkv,
17508            nh,
17509            if swa {
17510                g.rope_base_swa
17511            } else {
17512                g.rope_base_global
17513            },
17514            1.0,
17515            swa,
17516        )
17517    }
17518
17519    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
17520    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
17521        self.layers[il]
17522            .gemma4
17523            .as_ref()
17524            .and_then(|b| b.e4b.as_ref())
17525            .and_then(|e4| e4.kv_share.map(|t| t as usize))
17526    }
17527
17528    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
17529    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
17530    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
17531    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
17532    fn gemma4_e4b_inp_pl(
17533        &self,
17534        e: &Engine,
17535        tokens: &[u32],
17536        x_scaled: &CudaSlice<f32>,
17537        t: usize,
17538    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17539        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
17540        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
17541    }
17542
17543    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
17544    fn gemma4_e4b_inp_pl_dev(
17545        &self,
17546        e: &Engine,
17547        tok_d: &CudaSlice<u32>,
17548        x_scaled: &CudaSlice<f32>,
17549        t: usize,
17550    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17551        let aux = self.gemma4_aux.as_ref().unwrap();
17552        let m = aux.e4b.as_ref().unwrap();
17553        let n_embd = self.cfg.n_embd as usize;
17554        let n_layer = self.layers.len();
17555        let width = m.n_epl * n_layer;
17556        let tbl = m.tok_tbl_gpu.get_or_init(|| {
17557            e.upload_u8(&m.tok_embd_bytes)
17558                .expect("e4b per-layer token table upload")
17559        });
17560        let mut a =
17561            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
17562        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
17563        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
17564        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
17565        let mut pn = e.uninit(t * width)?;
17566        e.rms_norm(
17567            &p,
17568            m.proj_norm.float_data(),
17569            &mut pn,
17570            m.n_epl,
17571            t * n_layer,
17572            self.cfg.rms_eps,
17573        )?;
17574        let mut out = e.uninit(t * width)?;
17575        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
17576        Ok(out)
17577    }
17578
17579    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
17580    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
17581    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
17582    /// already holds this forward's rows — the target runs earlier in the stack).
17583    #[allow(clippy::too_many_arguments)]
17584    fn gemma4_e4b_attn(
17585        &self,
17586        e: &Engine,
17587        il: usize,
17588        hq: &CudaSlice<i8>,
17589        hdq: &CudaSlice<f32>,
17590        pos_d: &CudaSlice<i32>,
17591        t: usize,
17592        cache: &mut Cache,
17593        dc_bucket: Option<usize>,
17594    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17595        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
17596        let eps = self.cfg.rms_eps;
17597        let aux = self.gemma4_aux.as_ref().unwrap();
17598        let ones = aux.ones(e);
17599        #[cfg(debug_assertions)]
17600        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
17601        let Mixer::Full(fa) = &self.layers[il].mixer else {
17602            unreachable!()
17603        };
17604        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
17605        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
17606        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
17607        let h0 = e.zeros(0)?;
17608        let h = &h0;
17609
17610        let ff = if swa {
17611            None
17612        } else {
17613            Some(
17614                aux.rope_freqs(e)
17615                    .expect("e4b global rope needs rope_freqs.weight"),
17616            )
17617        };
17618        #[cfg(debug_assertions)]
17619        if let Some(ff) = ff {
17620            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
17621        }
17622        let share = self.gemma4_e4b_kv_target(il);
17623        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
17624        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
17625        let mut q;
17626        if let Some(_tgt) = share {
17627            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
17628            q = e.uninit(t * nh * hd)?;
17629            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
17630            // empty; q0 stands in for the unused k/v pointers).
17631            let mut kdummy = e.uninit(1)?;
17632            let mut vdummy = e.uninit(1)?;
17633            e.rms_norm_qkv_rope(
17634                &q0,
17635                &q0,
17636                &q0,
17637                fa.q_norm.float_data(),
17638                fa.q_norm.float_data(),
17639                ones,
17640                &mut q,
17641                &mut kdummy,
17642                &mut vdummy,
17643                hd,
17644                self.gemma4_rope_dims(il),
17645                nh * t,
17646                0,
17647                pos_d,
17648                nh,
17649                1,
17650                base,
17651                1.0,
17652                ff,
17653                eps,
17654            )?;
17655        } else {
17656            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
17657            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
17658            // q|k|v rows — the cat norm+rope twin consumes it directly.
17659            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
17660            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
17661            q = e.uninit(t * nh * hd)?;
17662            let mut k = e.uninit(t * nkv * hd)?;
17663            let mut v = e.uninit(t * nkv * hd)?;
17664            if t == 1 && cat.is_some() {
17665                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
17666                e.rms_norm_qkv_rope_cat(
17667                    &qkv0,
17668                    fa.q_norm.float_data(),
17669                    fa.k_norm.float_data(),
17670                    ones,
17671                    &mut q,
17672                    &mut k,
17673                    &mut v,
17674                    hd,
17675                    self.gemma4_rope_dims(il),
17676                    nh,
17677                    nkv,
17678                    pos_d,
17679                    nh,
17680                    nkv,
17681                    base,
17682                    1.0,
17683                    ff,
17684                    eps,
17685                )?;
17686            } else {
17687                let (q0, k0, v0) = match if t == 1 {
17688                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
17689                } else {
17690                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
17691                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
17692                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17693                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
17694                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
17695                    } else {
17696                        None
17697                    }
17698                } {
17699                    Some(triple) => triple,
17700                    None => (
17701                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
17702                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
17703                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
17704                    ), // E4B: real v (K != V)
17705                };
17706                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
17707                // the normed rows; V ones-rms, never roped).
17708                e.rms_norm_qkv_rope(
17709                    &q0,
17710                    &k0,
17711                    &v0,
17712                    fa.q_norm.float_data(),
17713                    fa.k_norm.float_data(),
17714                    ones,
17715                    &mut q,
17716                    &mut k,
17717                    &mut v,
17718                    hd,
17719                    self.gemma4_rope_dims(il),
17720                    nh * t,
17721                    nkv * t,
17722                    pos_d,
17723                    nh,
17724                    nkv,
17725                    base,
17726                    1.0,
17727                    ff,
17728                    eps,
17729                )?;
17730            }
17731            let kvl = cache.kv[il].as_mut().unwrap();
17732            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
17733            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
17734            // degenerate tok-0 stream, 2026-07-12).
17735            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17736            if dc_bucket.is_some() {
17737                // DC arm (graph serving): append at the len_d slot, advance the counter
17738                // in-stream — replay-correct, no host len in the launch args. Host mirrors
17739                // are NOT touched here (the replay loop owns them; a bump at capture-record
17740                // time would double-count the capture iteration).
17741                debug_assert!(t == 1);
17742                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
17743                e.append_kv_quantized_row_dc_inc(
17744                    &k,
17745                    &v,
17746                    &mut kvl.k,
17747                    &mut kvl.v,
17748                    &mut kvl.len_d,
17749                    kvl.kv_dim_k,
17750                    kvl.kv_dim_v,
17751                    kvl.k_tok_bytes,
17752                    kvl.v_tok_bytes,
17753                    cls,
17754                )?;
17755            } else {
17756                e.append_kv_quantized_rows(
17757                    &k,
17758                    &v,
17759                    &mut kvl.k,
17760                    &mut kvl.v,
17761                    kvl.len,
17762                    t,
17763                    kvl.kv_dim_k,
17764                    kvl.kv_dim_v,
17765                    kvl.k_tok_bytes,
17766                    kvl.v_tok_bytes,
17767                    cls,
17768                )?;
17769                kvl.len += t;
17770            }
17771            kv_f32 = Some((k, v));
17772        }
17773        // attention: per-row causal fa over the (own or target) quantized cache. The cache
17774        // already contains this forward's rows in both arms; row i attends [.., base+i].
17775        let kvl_idx = share.unwrap_or(il);
17776        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
17777        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
17778        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
17779        let mut attn = e.uninit(t * nh * hd)?;
17780        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
17781        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
17782        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
17783        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
17784        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
17785        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
17786        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
17787        //     rows (the T=K verify kernel; the target appended this forward's rows already).
17788        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
17789        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
17790        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
17791        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
17792            if let Some((kf, vf)) = &kv_f32 {
17793                if hd == 256 && t <= win {
17794                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17795                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17796                }
17797                if hd == 256 && swa && t > win {
17798                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
17799                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17800                }
17801                if hd == 512 && !swa {
17802                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17803                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17804                }
17805            } else if share.is_some() {
17806                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17807                let k_view = e.view_u8(&kvl.k, kvl.k.len());
17808                let v_view = e.view_u8(&kvl.v, kvl.v.len());
17809                if hd == 256 && (!swa || t <= win) {
17810                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
17811                    e.fa_prefill_view(
17812                        &q,
17813                        &k_view,
17814                        &v_view,
17815                        &mut attn,
17816                        hd,
17817                        nh,
17818                        nkv,
17819                        t,
17820                        t,
17821                        scale,
17822                        true,
17823                        kvl.k_tok_bytes,
17824                        kvl.v_tok_bytes,
17825                        g,
17826                    )?;
17827                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17828                }
17829                // remaining shared classes (swa above the window; hd512 globals): dequant
17830                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
17831                let kv_dim = nkv * hd;
17832                let mut kf = e.uninit(t * kv_dim)?;
17833                let mut vf = e.uninit(t * kv_dim)?;
17834                e.fa_dequant_kv_view_f32(
17835                    &k_view,
17836                    &v_view,
17837                    &mut kf,
17838                    &mut vf,
17839                    kv_dim,
17840                    kv_dim,
17841                    t,
17842                    kvl.k_tok_bytes,
17843                    kvl.v_tok_bytes,
17844                    g,
17845                )?;
17846                if hd == 512 {
17847                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17848                } else {
17849                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
17850                }
17851                return Ok(e.matmul(&fa.wo, &attn, t)?);
17852            }
17853        }
17854        if let Some(bucket) = dc_bucket {
17855            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
17856            // fa_decode_dc over the live counter. len_d already advanced past this token
17857            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
17858            // counter (advanced when the target ran earlier in the stack).
17859            assert!(t == 1);
17860            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
17861            // and under the window every live t_kv sits below it — cap the capture bucket
17862            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
17863            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
17864            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
17865            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
17866                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
17867            } else {
17868                bucket
17869            };
17870            let k_view = e.view_u8(&kvl.k, kvl.k.len());
17871            let v_view = e.view_u8(&kvl.v, kvl.v.len());
17872            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17873            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
17874            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
17875            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
17876            // captured into the dc graph like any other launch. Extending the cascade to
17877            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
17878            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
17879            // MEMRA_WPF=0 rollback seam.
17880            if crate::Engine::wpf_level() >= 1 {
17881                e.prefetch_weight_l2(&fa.wo)?;
17882            }
17883            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
17884            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
17885            if e.uses_q8_1_fast(&fa.wo) {
17886                let mut oq = e.alloc_i8_uninit(nh * hd)?;
17887                let mut od = e.zeros(nh * hd / 32)?;
17888                e.fa_decode_dc_q8(
17889                    &q,
17890                    &k_view,
17891                    &v_view,
17892                    &mut attn,
17893                    hd,
17894                    nh,
17895                    nkv,
17896                    &kvl.len_d,
17897                    bucket,
17898                    scale,
17899                    kvl.k_tok_bytes,
17900                    kvl.v_tok_bytes,
17901                    g,
17902                    Some((&mut oq, &mut od)),
17903                )?;
17904                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
17905            }
17906            e.fa_decode_dc(
17907                &q,
17908                &k_view,
17909                &v_view,
17910                &mut attn,
17911                hd,
17912                nh,
17913                nkv,
17914                &kvl.len_d,
17915                bucket,
17916                scale,
17917                kvl.k_tok_bytes,
17918                kvl.v_tok_bytes,
17919                g,
17920            )?;
17921            return Ok(e.matmul(&fa.wo, &attn, t)?);
17922        }
17923        for i in 0..t {
17924            let avail = base_len + i + 1;
17925            let (off_tok, t_kv) = if swa && avail > win {
17926                (avail - win, win)
17927            } else {
17928                (0, avail)
17929            };
17930            let k_view = e.view_u8_range(
17931                &kvl.k,
17932                off_tok * kvl.k_tok_bytes,
17933                (off_tok + t_kv) * kvl.k_tok_bytes,
17934            );
17935            let v_view = e.view_u8_range(
17936                &kvl.v,
17937                off_tok * kvl.v_tok_bytes,
17938                (off_tok + t_kv) * kvl.v_tok_bytes,
17939            );
17940            let qv = e.view(&q, t * nh * hd);
17941            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
17942            let mut q_one = e.uninit(nh * hd)?;
17943            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
17944            let mut a_one = e.uninit(nh * hd)?;
17945            // read class MUST match the append class (globals are e4m3 under gkv): the
17946            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
17947            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
17948            e.fa_decode_kvmod(
17949                &q_one,
17950                &k_view,
17951                &v_view,
17952                &mut a_one,
17953                hd,
17954                nh,
17955                nkv,
17956                t_kv,
17957                scale,
17958                kvl.k_tok_bytes,
17959                kvl.v_tok_bytes,
17960                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
17961            )?;
17962            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
17963        }
17964        Ok(e.matmul(&fa.wo, &attn, t)?)
17965    }
17966
17967    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
17968    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
17969    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
17970    /// layer; does NOT advance cache.pos (caller owns pos).
17971    fn gemma4_e4b_trunk(
17972        &self,
17973        e: &Engine,
17974        tokens: &[u32],
17975        pos0: usize,
17976        cache: &mut Cache,
17977        head_last: bool,
17978    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17979        let n_embd = self.cfg.n_embd as usize;
17980        let t = tokens.len();
17981        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
17982        let pos_d = e.htod_i32(&pos)?;
17983        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
17984        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
17985        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
17986        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
17987    }
17988
17989    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
17990    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
17991    /// eager chain by construction: SAME functions, not twins).
17992    fn gemma4_e4b_trunk_core(
17993        &self,
17994        e: &Engine,
17995        x_in: CudaSlice<f32>,
17996        inp_pl: CudaSlice<f32>,
17997        pos_d: &CudaSlice<i32>,
17998        t: usize,
17999        cache: &mut Cache,
18000        dc_bucket: Option<usize>,
18001        cap_logits: bool,
18002        head_last: bool,
18003    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18004        let n_embd = self.cfg.n_embd as usize;
18005        let eps = self.cfg.rms_eps;
18006        let n_layer = self.layers.len();
18007        let mut x = x_in;
18008        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
18009        let n_epl = aux_e4b.n_epl;
18010
18011        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
18012        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
18013        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
18014        // head rides matmul_pre too. First layer's pair comes from a standalone fused
18015        // norm+quant.
18016        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
18017        for il in 0..n_layer {
18018            let layer = &self.layers[il];
18019            let (hq, hdq) = match h_carry.take() {
18020                Some(p) => p,
18021                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
18022            };
18023            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
18024            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
18025            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
18026            let bits = layer.gemma4.as_ref().unwrap();
18027            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
18028            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
18029            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
18030            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
18031            // the fused single-phase reduction is NOT FP-order-identical to the unfused
18032            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
18033            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
18034            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
18035            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
18036            // gate dropped, decode AND verify ride the same fused chain — parity by
18037            // construction, VERIFY-GATE 0.000e0.
18038            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
18039            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
18040                e,
18041                layer,
18042                &o,
18043                &x,
18044                t,
18045                Some(layer.post_attn_norm.float_data()),
18046                fuse_exit,
18047            )?;
18048            let mut resid = e.uninit(t * n_embd)?;
18049            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
18050            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
18051            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
18052            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
18053            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
18054            let g = if fuse_exit {
18055                // sn here = RAW f0 (post_ffw deferred).
18056                let (rq, rd) = e.rms_pre_add_q8_1(
18057                    &sn,
18058                    bits.post_ffw_norm.float_data(),
18059                    &attn_out,
18060                    &mut resid,
18061                    n_embd,
18062                    t,
18063                    self.cfg.rms_eps,
18064                )?;
18065                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
18066            } else {
18067                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
18068                e.matmul(&e4b.inp_gate, &resid, t)?
18069            };
18070            let mut act = e.uninit(t * n_epl)?;
18071            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
18072                let ipv = e.view(&inp_pl, n_epl * n_layer);
18073                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
18074                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
18075                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
18076            } else {
18077                let mut inp_this = e.uninit(t * n_epl)?;
18078                e.copy_rows_strided(
18079                    &inp_pl,
18080                    &mut inp_this,
18081                    n_epl,
18082                    t,
18083                    n_epl * n_layer,
18084                    il * n_epl,
18085                )?;
18086                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
18087                e.matmul(&e4b.proj, &act, t)?
18088            };
18089            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
18090            // ONE launch (glue-fusion lane; last layer emits through output_norm).
18091            let next_norm = if il + 1 < n_layer {
18092                self.layers[il + 1].attn_norm.float_data()
18093            } else {
18094                self.output_norm.float_data()
18095            };
18096            let mut xn = e.uninit(t * n_embd)?;
18097            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
18098                &y,
18099                e4b.post_norm.float_data(),
18100                &resid,
18101                bits.layer_scale,
18102                next_norm,
18103                &mut xn,
18104                n_embd,
18105                t,
18106                eps,
18107            )?;
18108            h_carry = Some(pair);
18109            x = xn;
18110        }
18111        // the head consumes the last layer's fused (output_norm) emit. head_last callers
18112        // (prime, last_only forward) need only the final row's logits — the all-T head is
18113        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
18114        let (oq, odq) = h_carry.take().unwrap();
18115        let h0 = e.zeros(0)?;
18116        let hm = if head_last { 1 } else { t };
18117        let (hq, hd) = if head_last && t > 1 {
18118            let mut q1 = e.uninit_i8(n_embd)?;
18119            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
18120            let nb = n_embd / 32;
18121            let mut d1 = e.uninit(nb)?;
18122            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
18123            (q1, d1)
18124        } else {
18125            (oq, odq)
18126        };
18127        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
18128        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
18129        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
18130        // Logit-returning callers (host logits / spec prime) keep the capped emit.
18131        if cap_logits {
18132            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
18133            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
18134        }
18135        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
18136        Ok((ld, x))
18137    }
18138
18139    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
18140    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
18141    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
18142    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
18143    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
18144    /// covers exactly the layers that appended).
18145    pub fn gemma4_e4b_decode_step_t_am_dev(
18146        &self,
18147        e: &Engine,
18148        tok_d: &CudaSlice<u32>,
18149        t: usize,
18150        pos0: usize,
18151        cache: &mut Cache,
18152    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18153        let n_embd = self.cfg.n_embd as usize;
18154        let eps = self.cfg.rms_eps;
18155        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18156        let pos_d = e.htod_i32(&pos)?;
18157        let embd_gpu = self
18158            .embd_gpu
18159            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
18160        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
18161        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
18162        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18163        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
18164        let (ld, xp) =
18165            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
18166        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
18167        // emit is already capped, matching the eager chain bit-for-bit).
18168        let n_vocab = self.output.out_features();
18169        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
18170        for i in 0..t {
18171            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
18172        }
18173        let mut hn = e.uninit(t * n_embd)?;
18174        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18175        cache.pos += t;
18176        Ok((vam, hn))
18177    }
18178
18179    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
18180    /// prime path — mirror of `gemma4_decode_step_t_h`).
18181    pub(crate) fn gemma4_e4b_decode_step_t_h(
18182        &self,
18183        e: &Engine,
18184        tokens: &[u32],
18185        pos0: usize,
18186        cache: &mut Cache,
18187    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18188        let n_embd = self.cfg.n_embd as usize;
18189        let eps = self.cfg.rms_eps;
18190        let t = tokens.len();
18191        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
18192        let mut hn = e.uninit(t * n_embd)?;
18193        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18194        cache.pos += t;
18195        Ok((e.dtoh(&ld)?, hn))
18196    }
18197
18198    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
18199    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
18200    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
18201    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
18202    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
18203    pub fn gemma4_e4b_decode_step_dcg(
18204        &self,
18205        e: &Engine,
18206        token_d: &mut CudaSlice<u32>,
18207        pos_d: &mut CudaSlice<i32>,
18208        embd_gpu: &CudaSlice<u8>,
18209        embd_qt: i32,
18210        embd_rb: usize,
18211        cache: &mut Cache,
18212        n_vocab: usize,
18213        bucket: usize,
18214    ) -> Result<(), Box<dyn std::error::Error>> {
18215        let n_embd = self.cfg.n_embd as usize;
18216        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18217        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18218        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18219        let (ld, _x) =
18220            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
18221        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
18222        e.inc_seqlen(pos_d)?;
18223        Ok(())
18224    }
18225
18226    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
18227    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
18228    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
18229    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
18230    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
18231    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
18232    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
18233    #[allow(clippy::too_many_arguments)]
18234    pub fn gemma4_e4b_decode_step_dc(
18235        &self,
18236        e: &Engine,
18237        token_d: &CudaSlice<u32>,
18238        pos_d: &mut CudaSlice<i32>,
18239        embd_gpu: &CudaSlice<u8>,
18240        embd_qt: i32,
18241        embd_rb: usize,
18242        cache: &mut Cache,
18243        n_vocab: usize,
18244    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
18245        let n_embd = self.cfg.n_embd as usize;
18246        let eps = self.cfg.rms_eps;
18247        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18248        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18249        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18250        let (ld, _x) =
18251            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
18252        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
18253        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
18254        e.inc_seqlen(pos_d)?;
18255        cache.pos += 1;
18256        let _ = eps;
18257        Ok(tok_out)
18258    }
18259
18260    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
18261    /// pre-output_norm hidden). Advances cache.pos.
18262    pub(crate) fn gemma4_e4b_decode_step_h(
18263        &self,
18264        e: &Engine,
18265        token: u32,
18266        cache: &mut Cache,
18267    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18268        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
18269        let logits = e.dtoh(&ld)?;
18270        cache.pos += 1;
18271        Ok((logits, x))
18272    }
18273
18274    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
18275    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
18276    /// fast; the prefill fa arms come later.
18277    pub(crate) fn gemma4_e4b_prime(
18278        &self,
18279        e: &Engine,
18280        tokens: &[u32],
18281        cache: &mut Cache,
18282    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18283        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
18284        // process-kill as gemma4_prime — refuse per-request.
18285        if cache.pos != 0 {
18286            return Err(
18287                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
18288                        call or decode tokenwise"
18289                    .into(),
18290            );
18291        }
18292        let n_embd = self.cfg.n_embd as usize;
18293        let t = tokens.len();
18294        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
18295        cache.pos += t;
18296        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
18297        let xv = e.view(&x, t * n_embd);
18298        let row = xv.slice((t - 1) * n_embd..t * n_embd);
18299        let mut h_seed = e.uninit(n_embd)?;
18300        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
18301        Ok((last, h_seed, x))
18302    }
18303
18304    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
18305    pub(crate) fn gemma4_e4b_forward(
18306        &self,
18307        e: &Engine,
18308        tokens: &[u32],
18309        last_only: bool,
18310    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
18311        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
18312        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
18313        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
18314    }
18315}
18316
18317#[cfg(test)]
18318mod prime_chunk_schedule_tests {
18319    use super::{
18320        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
18321        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
18322        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
18323        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
18324    };
18325
18326    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
18327        ranges.iter().map(|(start, end)| end - start).collect()
18328    }
18329
18330    fn auto_chunk(t: usize) -> usize {
18331        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
18332    }
18333
18334    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
18335    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
18336    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
18337    /// must land every boundary on it without changing coverage.
18338    #[test]
18339    fn auto_prime_ranges_align_to_the_gdn_grid() {
18340        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
18341        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
18342            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
18343            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
18344            for w in ranges.windows(2) {
18345                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
18346            }
18347            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
18348        };
18349
18350        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
18351        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
18352        let t = 9510usize;
18353        let fill = auto_chunk(t);
18354        let fixed = fixed_prime_chunk_ranges(t, fill);
18355        assert!(
18356            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
18357            "broken arm vanished: fixed auto boundaries all landed on-grid"
18358        );
18359        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
18360        assert!(
18361            dynamic[..dynamic.len() - 1]
18362                .iter()
18363                .any(|&(_, e)| e % c != 0),
18364            "broken arm vanished: dynamic auto boundaries all landed on-grid"
18365        );
18366
18367        for ranges in [&fixed, &dynamic] {
18368            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
18369            assert_covers(&aligned, t);
18370            for &(_, e) in &aligned[..aligned.len() - 1] {
18371                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
18372            }
18373            // boundaries only move DOWN, at most c-1 tokens.
18374            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
18375                assert!(a <= b && b - a < c);
18376            }
18377        }
18378
18379        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
18380        // empty range; the schedule survives degenerate short fills.
18381        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
18382        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
18383        assert_covers(&aligned, 200);
18384        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
18385
18386        // No-ops: single range, c=0 (grid off), already-aligned schedules.
18387        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
18388        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
18389        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
18390        assert_eq!(
18391            align_prime_ranges_to_gdn(&on_grid, 300, c),
18392            on_grid.as_slice()
18393        );
18394    }
18395
18396    #[test]
18397    fn active_matrix_prefix_scopes_reused_prime_slabs() {
18398        assert_eq!(
18399            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
18400            29 * 4096
18401        );
18402        assert_eq!(
18403            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
18404            29 * 4096
18405        );
18406        assert_eq!(
18407            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
18408            24 * 4096
18409        );
18410        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
18411        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
18412    }
18413
18414    #[test]
18415    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
18416        assert!(validate_step_prime_batch_modes(false, false).is_ok());
18417
18418        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
18419        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
18420
18421        for grouped in [false, true] {
18422            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
18423            assert!(err.contains("did not clear the live-server performance gate"));
18424            assert!(err.contains("per-session grouped prefill"));
18425        }
18426    }
18427
18428    #[test]
18429    fn step_grouped_path_is_eager_single_token_only() {
18430        assert!(step_grouped_decode_shape(false, 1));
18431        assert!(!step_grouped_decode_shape(true, 1));
18432        assert!(!step_grouped_decode_shape(false, 2));
18433        assert!(!step_grouped_decode_shape(true, 2));
18434    }
18435
18436    #[test]
18437    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
18438        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
18439        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
18440        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
18441        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
18442        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
18443        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
18444
18445        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
18446        assert!(step_grouped_prefill_shape(
18447            true,
18448            true,
18449            crate::cache::PRIME_CHUNK_MAX_TOKENS,
18450        ));
18451        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
18452        assert!(!step_grouped_prefill_shape(
18453            true,
18454            true,
18455            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
18456        ));
18457        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
18458        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
18459    }
18460
18461    #[test]
18462    fn step_tp_prefill_door_is_strict_and_default_off() {
18463        assert!(!parse_step_tp_prefill(None).unwrap());
18464        assert!(!parse_step_tp_prefill(Some("")).unwrap());
18465        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
18466        assert!(parse_step_tp_prefill(Some("1")).unwrap());
18467        assert!(parse_step_tp_prefill(Some("true")).is_err());
18468        assert!(parse_step_tp_prefill(Some("2")).is_err());
18469    }
18470
18471    #[test]
18472    fn step_tp_prefill_requires_the_qualified_tp4_shape() {
18473        assert!(step_tp_prefill_shape(
18474            true,
18475            PRIME_MIN_T,
18476            4,
18477            true,
18478            true,
18479            false,
18480        ));
18481        assert!(!step_tp_prefill_shape(
18482            false,
18483            PRIME_MIN_T,
18484            4,
18485            true,
18486            true,
18487            false,
18488        ));
18489        assert!(!step_tp_prefill_shape(
18490            true,
18491            PRIME_MIN_T - 1,
18492            4,
18493            true,
18494            true,
18495            false,
18496        ));
18497        assert!(!step_tp_prefill_shape(
18498            true,
18499            PRIME_MIN_T,
18500            2,
18501            true,
18502            true,
18503            false,
18504        ));
18505        assert!(!step_tp_prefill_shape(
18506            true,
18507            PRIME_MIN_T,
18508            4,
18509            false,
18510            true,
18511            false,
18512        ));
18513        assert!(!step_tp_prefill_shape(
18514            true,
18515            PRIME_MIN_T,
18516            4,
18517            true,
18518            false,
18519            false,
18520        ));
18521        assert!(!step_tp_prefill_shape(
18522            true,
18523            PRIME_MIN_T,
18524            4,
18525            true,
18526            true,
18527            true,
18528        ));
18529    }
18530
18531    #[test]
18532    fn fixed_schedule_retains_measured_geometry() {
18533        assert_eq!(
18534            sizes(&fixed_prime_chunk_ranges(461, 128)),
18535            vec![128, 128, 128, 77]
18536        );
18537        assert_eq!(
18538            sizes(&fixed_prime_chunk_ranges(1833, 230)),
18539            vec![230, 230, 230, 230, 230, 230, 230, 223]
18540        );
18541        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
18542        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
18543        assert_eq!(capped, vec![4096, 4088, 16]);
18544        assert!(capped.iter().all(|&rows| rows <= 4096));
18545        assert_eq!(
18546            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
18547            vec![4100],
18548            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
18549        );
18550    }
18551
18552    #[test]
18553    fn dynamic_schedule_matches_registered_shapes() {
18554        let cases = [
18555            (461, vec![64, 141, 132, 124]),
18556            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
18557            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
18558        ];
18559        for (t, expected) in cases {
18560            let chunk = auto_chunk(t);
18561            let fixed = fixed_prime_chunk_ranges(t, chunk);
18562            assert_eq!(
18563                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
18564                expected
18565            );
18566        }
18567    }
18568
18569    #[test]
18570    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
18571        for t in 256..=8192 {
18572            let chunk = auto_chunk(t);
18573            let fixed = fixed_prime_chunk_ranges(t, chunk);
18574            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
18575            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
18576            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
18577            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
18578            for pair in dynamic.windows(2) {
18579                assert_eq!(pair[0].1, pair[1].0, "T={t}");
18580            }
18581            assert!(
18582                dynamic
18583                    .iter()
18584                    .all(|(start, end)| end - start >= PRIME_MIN_T),
18585                "T={t} sizes={:?}",
18586                sizes(&dynamic)
18587            );
18588            if dynamic.len() >= 3 {
18589                let chunk_sizes = sizes(&dynamic);
18590                assert!(
18591                    chunk_sizes[0] < chunk_sizes[1],
18592                    "T={t} sizes={chunk_sizes:?}"
18593                );
18594                assert!(
18595                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
18596                    "T={t} sizes={chunk_sizes:?}"
18597                );
18598            }
18599        }
18600    }
18601}
18602
18603#[cfg(test)]
18604mod page_prefetch_tests {
18605    use super::{
18606        grouped_worker_prefetch_position, page_prefetch_positions,
18607        page_prefetch_window_from_values, worker_prefetch_positions,
18608    };
18609
18610    #[test]
18611    fn page_prefetch_window_keeps_existing_opt_in_default() {
18612        assert_eq!(page_prefetch_window_from_values(false, None), 0);
18613        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
18614        assert_eq!(page_prefetch_window_from_values(true, None), 1);
18615        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
18616        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
18617        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
18618    }
18619
18620    #[test]
18621    fn rolling_page_prefetch_advises_each_future_expert_once() {
18622        let advised: Vec<_> = (0..7)
18623            .flat_map(|position| page_prefetch_positions(position, 7, 3))
18624            .collect();
18625        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
18626
18627        let one_ahead: Vec<_> = (0..4)
18628            .flat_map(|position| page_prefetch_positions(position, 4, 1))
18629            .collect();
18630        assert_eq!(one_ahead, vec![1, 2, 3]);
18631        assert!(page_prefetch_positions(0, 4, 0).is_empty());
18632    }
18633
18634    #[test]
18635    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
18636        assert_eq!(grouped_worker_prefetch_position(0, None), None);
18637        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
18638            .chain(
18639                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
18640            )
18641            .collect();
18642        assert_eq!(positions, vec![0, 1, 2, 3]);
18643        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
18644    }
18645
18646    #[test]
18647    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
18648        let queued: Vec<_> = (0..8)
18649            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
18650            .collect();
18651        assert_eq!(queued, (0..8).collect::<Vec<_>>());
18652
18653        let one_at_a_time: Vec<_> = (0..4)
18654            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
18655            .collect();
18656        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
18657        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
18658    }
18659}
18660
18661pub struct G4DcSlots {
18662    x: CudaSlice<f32>,
18663    xn: CudaSlice<f32>,
18664    cur: CudaSlice<f32>,
18665    hq: CudaSlice<i8>,
18666    hd_: CudaSlice<f32>,
18667    q0: CudaSlice<f32>,
18668    k0: CudaSlice<f32>,
18669    v0: CudaSlice<f32>,
18670    q: CudaSlice<f32>,
18671    k: CudaSlice<f32>,
18672    v: CudaSlice<f32>,
18673    attn: CudaSlice<f32>,
18674    o: CudaSlice<f32>,
18675    attn_out: CudaSlice<f32>,
18676    zsh: CudaSlice<f32>,
18677    zq: CudaSlice<i8>,
18678    zd: CudaSlice<f32>,
18679    gate: CudaSlice<f32>,
18680    up: CudaSlice<f32>,
18681    act: CudaSlice<f32>,
18682    actq: CudaSlice<i8>,
18683    actd: CudaSlice<f32>,
18684    f0: CudaSlice<f32>,
18685    sn: CudaSlice<f32>,
18686    hn: CudaSlice<f32>,
18687    logits: CudaSlice<f32>,
18688}
18689
18690/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
18691/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
18692/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
18693/// fixed logits stage the head writes.
18694pub struct Step35TokenGraphState {
18695    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
18696    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
18697    pub token_d: cudarc::driver::CudaSlice<u32>,
18698    pub pos_d: cudarc::driver::CudaSlice<i32>,
18699    pub logits_stage: cudarc::driver::CudaSlice<f32>,
18700    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
18701    /// launch, so an alloc made inside one captured child is not referable from another):
18702    /// the running residual, the post-attention pair, the shared-expert row, and the
18703    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
18704    pub x: cudarc::driver::CudaSlice<f32>,
18705    pub x1: cudarc::driver::CudaSlice<f32>,
18706    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
18707    pub sh_stage: cudarc::driver::CudaSlice<f32>,
18708    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
18709    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
18710    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
18711    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
18712    pub router_logits: cudarc::driver::CudaSlice<f32>,
18713    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
18714    pub shexp_up: cudarc::driver::CudaSlice<f32>,
18715    pub shexp_act: cudarc::driver::CudaSlice<f32>,
18716    pub gate_sig: cudarc::driver::CudaSlice<f32>,
18717    pub dense_z: cudarc::driver::CudaSlice<f32>,
18718    pub dense_gate: cudarc::driver::CudaSlice<f32>,
18719    pub dense_up: cudarc::driver::CudaSlice<f32>,
18720    pub dense_act: cudarc::driver::CudaSlice<f32>,
18721    pub hn: cudarc::driver::CudaSlice<f32>,
18722    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
18723    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
18724    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
18725    pub probe_x: cudarc::driver::CudaSlice<f32>,
18726    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
18727    /// the in-graph tail argmax chain; host reads the ring once per chunk.
18728    pub token_hist: cudarc::driver::CudaSlice<u32>,
18729    pub hist_idx: cudarc::driver::CudaSlice<i32>,
18730}
18731
18732impl HybridModel {
18733    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
18734    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
18735    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
18736    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
18737    /// needs a rebuild this token).
18738    ///
18739    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
18740    /// but not their contents under this door (the TP rank caches are fully maintained
18741    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
18742    /// must not run with the door on until the local-dcw twin lands.
18743    pub(crate) fn step35_token_graph_step(
18744        &self,
18745        e: &Engine,
18746        token: u32,
18747        cache: &mut Cache,
18748    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
18749        if !self.uses_sliding_gated_moe_program()
18750            || !crate::tp::step_tp_graph_enabled()?
18751            || !crate::tp::step_tp_dcw_enabled()?
18752            || !crate::tp::step_tp_qkv_fused_enabled()?
18753            || !crate::tp::step_tp_dev_router_enabled()?
18754            || !crate::tp::step_nvfp4_dev_routes_enabled()?
18755        {
18756            return Ok(None);
18757        }
18758        let n_embd = self.cfg.n_embd as usize;
18759        let n_vocab = self.cfg.n_vocab as usize;
18760        let eps = self.cfg.rms_eps;
18761        let n_layers = self.layers.len();
18762        let pos = cache.pos;
18763        let staged_next = pos + 1;
18764        if staged_next < 96 {
18765            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
18766        }
18767
18768        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
18769        // eager fallback for the whole token; the host path also updates base_d there).
18770        for il in 0..n_layers {
18771            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
18772                return Ok(None); // caches not hydrated yet — eager warms them
18773            };
18774            if tp_kv.peek_append_ring(1)?.1 {
18775                return Ok(None);
18776            }
18777        }
18778
18779        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
18780        // their window and share one bucket forever after ctx > window).
18781        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
18782        if !fa_vec {
18783            return Ok(None);
18784        }
18785        let sp = crate::fa_split_keys(staged_next, 8);
18786        let bucket_max = (n_splits * sp).max(staged_next);
18787
18788        let mut state_guard = self
18789            .step35_token_graph
18790            .lock()
18791            .map_err(|_| "step35 token graph lock is poisoned")?;
18792        if state_guard.is_none() {
18793            let _main = e.gpu.enter_main()?;
18794            let n_expert = self
18795                .cfg
18796                .moe
18797                .as_ref()
18798                .map(|m| m.expert_count as usize)
18799                .unwrap_or(0);
18800            let n_ff_sh = self
18801                .layers
18802                .iter()
18803                .find_map(|l| match &l.ffn {
18804                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
18805                    _ => None,
18806                })
18807                .unwrap_or(0);
18808            let n_ff_dense = self
18809                .layers
18810                .iter()
18811                .find_map(|l| match &l.ffn {
18812                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
18813                    _ => None,
18814                })
18815                .unwrap_or(0);
18816            *state_guard = Some(Step35TokenGraphState {
18817                graphs: Vec::new(),
18818                token_d: e.stream().clone_htod(&[0u32])?,
18819                pos_d: e.htod_i32(&[pos as i32])?,
18820                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
18821                x: e.htod(&vec![0.0f32; n_embd])?,
18822                x1: e.htod(&vec![0.0f32; n_embd])?,
18823                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
18824                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
18825                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
18826                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
18827                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
18828                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18829                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18830                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18831                gate_sig: e.htod(&vec![1.0f32; 1])?,
18832                dense_z: e.htod(&vec![0.0f32; n_embd])?,
18833                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18834                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18835                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18836                hn: e.htod(&vec![0.0f32; n_embd])?,
18837                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
18838                probe_x: e.htod(&vec![0.0f32; n_embd])?,
18839                token_hist: e.stream().clone_htod(&[0u32; 16])?,
18840                hist_idx: e.htod_i32(&[0])?,
18841            });
18842        }
18843        let state = state_guard.as_mut().expect("state armed above");
18844        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
18845        // first use, and an alloc inside a captured section is a mem node (child graphs
18846        // reject those — the tail argmax chain needs them already resident).
18847        {
18848            let _main = e.gpu.enter_main()?;
18849            let Step35TokenGraphState {
18850                logits_stage,
18851                token_d,
18852                ..
18853            } = &mut *state;
18854            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
18855        }
18856
18857        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
18858        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
18859        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
18860        // ceiling at build so the baked pointers never move.
18861        if state.graphs.is_empty() {
18862            // Build the parent at this bucket. Capture executes nothing; correctness is
18863            // pinned at replay by the token-identity gate.
18864            self.step35_token_graph_build(e, cache, state, bucket_max)?;
18865        }
18866        {
18867            let (b, g) = state.graphs.first_mut().expect("graph built above");
18868            if *b != bucket_max {
18869                g.retarget_bucket(bucket_max)?;
18870                *b = bucket_max;
18871            }
18872        }
18873        let graph = state
18874            .graphs
18875            .first()
18876            .map(|(_, g)| g)
18877            .expect("graph built above");
18878
18879        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
18880        let t_fence = tg_timing.then(std::time::Instant::now);
18881        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
18882        // queued on the rank streams, and graph children carry no ordering edge to those
18883        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
18884        // sync is a no-op between consecutive replays.
18885        {
18886            let fa0 = match &self.layers[0].mixer {
18887                Mixer::Full(fa) => fa,
18888                _ => return Err("step35 token graph expects full-attention layers".into()),
18889            };
18890            let tp0 = fa0
18891                .step_tp_qkv
18892                .as_ref()
18893                .ok_or("step35 token graph lost its TP state")?;
18894            for rank in 0..tp0.runtime.devices().len() {
18895                let engine = tp0
18896                    .runtime
18897                    .rank_engine(rank)
18898                    .ok_or("step35 token graph lost a rank engine")?;
18899                let _main = engine.gpu.enter_main()?;
18900                engine.stream().synchronize()?;
18901            }
18902        }
18903
18904        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
18905        {
18906            let _main = e.gpu.enter_main()?;
18907            e.set_u32_one(&mut state.token_d, token)?;
18908            e.set_i32_one(&mut state.pos_d, pos as i32)?;
18909        }
18910        let t_launch = tg_timing.then(std::time::Instant::now);
18911        graph.launch(e)?;
18912        let t_book = tg_timing.then(std::time::Instant::now);
18913        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
18914        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
18915        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
18916        // replay error the counters are already advanced — acceptable: the decode aborts.
18917        for il in 0..n_layers {
18918            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
18919            let transaction = tp_kv.begin_transaction()?;
18920            let fa = match &self.layers[il].mixer {
18921                Mixer::Full(fa) => fa,
18922                _ => return Err("step35 token graph expects full-attention layers".into()),
18923            };
18924            let tp = fa
18925                .step_tp_qkv
18926                .as_ref()
18927                .ok_or("step35 token graph lost its TP state")?;
18928            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
18929            // incs own the counters). Shards unused.
18930            let empty: [CudaSlice<f32>; 0] = [];
18931            tp.runtime.append_tp_kv_transaction_inner(
18932                tp_kv,
18933                transaction,
18934                &empty,
18935                &empty,
18936                1,
18937                true,
18938            )?;
18939            tp.runtime
18940                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
18941            // Local shadow: lengths advance (v1 keeps contents stale under the door).
18942            if let Some(local) = cache.kv[il].as_mut() {
18943                local.len = pos + 1;
18944                let _main = e.gpu.enter_main()?;
18945                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
18946            }
18947        }
18948        cache.pos = pos + 1;
18949        let t_sync = tg_timing.then(std::time::Instant::now);
18950        let (logits, h_seed) = {
18951            let _main = e.gpu.enter_main()?;
18952            e.stream().synchronize()?;
18953            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
18954        };
18955        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
18956            use std::sync::atomic::{AtomicU64, Ordering};
18957            static NS: [AtomicU64; 5] = [
18958                AtomicU64::new(0),
18959                AtomicU64::new(0),
18960                AtomicU64::new(0),
18961                AtomicU64::new(0),
18962                AtomicU64::new(0),
18963            ];
18964            static CALLS: AtomicU64 = AtomicU64::new(0);
18965            let now = std::time::Instant::now();
18966            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
18967            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
18968            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
18969            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
18970            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
18971            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
18972            if calls % 100 == 0 {
18973                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
18974                eprintln!(
18975                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
18976                     syncdtoh_us={:.0} total_us={:.0}",
18977                    avg(0),
18978                    avg(1),
18979                    avg(2),
18980                    avg(3),
18981                    avg(4)
18982                );
18983            }
18984        }
18985        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
18986        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
18987            use std::io::Write;
18988            let (pm, px) = {
18989                let _main = e.gpu.enter_main()?;
18990                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
18991            };
18992            for (path, data) in [
18993                ("/root/tg-probe-mixed.bin", &pm),
18994                ("/root/tg-probe-x.bin", &px),
18995            ] {
18996                let mut fo = std::fs::OpenOptions::new()
18997                    .create(true)
18998                    .append(true)
18999                    .open(path)?;
19000                for v in data {
19001                    fo.write_all(&v.to_le_bytes())?;
19002                }
19003            }
19004        }
19005        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
19006        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
19007        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
19008            let hh = {
19009                let _main = e.gpu.enter_main()?;
19010                e.dtoh(&state.hn)?
19011            };
19012            use std::io::Write;
19013            let mut fo = std::fs::OpenOptions::new()
19014                .create(true)
19015                .append(true)
19016                .open(path)?;
19017            for v in &hh {
19018                fo.write_all(&v.to_le_bytes())?;
19019            }
19020        }
19021        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
19022        // per rank per token; diagnostics only.
19023        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
19024            for il in [0usize, 1, 44] {
19025                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
19026                let host_len = tp_kv.staged_len();
19027                let fa = match &self.layers[il].mixer {
19028                    Mixer::Full(fa) => fa,
19029                    _ => continue,
19030                };
19031                let tp = fa
19032                    .step_tp_qkv
19033                    .as_ref()
19034                    .ok_or("step35 token graph lost its TP state")?;
19035                for rank in 0..tp.runtime.devices().len() {
19036                    let engine = tp
19037                        .runtime
19038                        .rank_engine(rank)
19039                        .ok_or("step35 token graph lost a rank engine")?;
19040                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
19041                    let _main = engine.gpu.enter_main()?;
19042                    engine.stream().synchronize()?;
19043                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
19044                    let base_d = match rank_cache.base_d() {
19045                        Some(b) => engine.dtoh_i32_one(b)?,
19046                        None => -1,
19047                    };
19048                    eprintln!(
19049                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
19050                         len_d={len_d} base_d={base_d}"
19051                    );
19052                }
19053            }
19054        }
19055        Ok(Some((logits, h_seed)))
19056    }
19057
19058    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
19059    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
19060    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
19061    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
19062    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
19063    pub(crate) fn head_split_matvec(
19064        &self,
19065        e: &Engine,
19066        hn: &CudaSlice<f32>,
19067    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
19068        if self.head_split_fill_device(e, hn)?.is_none() {
19069            return Ok(None);
19070        }
19071        let guard = HEAD_SPLIT_WS
19072            .lock()
19073            .map_err(|_| "head split lock is poisoned")?;
19074        let ws = guard.as_ref().expect("filled above");
19075        let _main = e.gpu.enter_main()?;
19076        Ok(Some(e.dtoh(&ws.logits_e)?))
19077    }
19078
19079    /// Compute body of the split head: arms the replica + staging on first use, then fills
19080    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
19081    /// push) and orders e's stream behind it. None = ineligible.
19082    fn head_split_fill_device(
19083        &self,
19084        e: &Engine,
19085        hn: &CudaSlice<f32>,
19086    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
19087        use cudarc::driver::DevicePtr;
19088        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
19089            return Ok(None);
19090        };
19091        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
19092            Mixer::Full(fa) => fa
19093                .step_tp_qkv
19094                .as_ref()
19095                .and_then(|tp| tp.runtime.rank_engine(1)),
19096            _ => None,
19097        }) else {
19098            return Ok(None);
19099        };
19100        let n_embd = self.cfg.n_embd as usize;
19101        let n_vocab = self.cfg.n_vocab as usize;
19102        let half = n_vocab / 2;
19103        let mut guard = HEAD_SPLIT_WS
19104            .lock()
19105            .map_err(|_| "head split lock is poisoned")?;
19106        let pin = {
19107            let _main = e.gpu.enter_main()?;
19108            let stream = e.stream();
19109            let (ptr, _g) = head.device_ptr(&stream);
19110            ptr as u64
19111        };
19112        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
19113            // One-time: upload rank1's row half + persistent staging.
19114            let hi_rows = n_vocab - half;
19115            let (w1, hn1, y1, ev_done) = {
19116                let _r1 = rank1.gpu.enter_main()?;
19117                (
19118                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
19119                    rank1.htod(&vec![0.0f32; n_embd])?,
19120                    rank1.htod(&vec![0.0f32; hi_rows])?,
19121                    rank1.ctx().new_event(None)?,
19122                )
19123            };
19124            {
19125                use cudarc::driver::sys;
19126                let src = pin + (half * n_embd * 2) as u64;
19127                let dst = {
19128                    let _r1 = rank1.gpu.enter_main()?;
19129                    let rstream = rank1.stream();
19130                    let (d, _g) = w1.device_ptr(&rstream);
19131                    d as u64
19132                };
19133                let _r1 = rank1.gpu.enter_main()?;
19134                let r = unsafe {
19135                    sys::cuMemcpyAsync(
19136                        dst as sys::CUdeviceptr,
19137                        src as sys::CUdeviceptr,
19138                        hi_rows * n_embd * 2,
19139                        rank1.stream().cu_stream() as sys::CUstream,
19140                    )
19141                };
19142                if r != sys::CUresult::CUDA_SUCCESS {
19143                    return Err(format!("head split replica upload: {r:?}").into());
19144                }
19145                rank1.stream().synchronize()?;
19146            }
19147            let (logits_e, ev_hn) = {
19148                let _main = e.gpu.enter_main()?;
19149                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
19150            };
19151            let (raw_hn1, raw_y1) = {
19152                let _r1 = rank1.gpu.enter_main()?;
19153                let rstream = rank1.stream();
19154                let (a, _g0) = hn1.device_ptr(&rstream);
19155                let (b, _g1) = y1.device_ptr(&rstream);
19156                (a as u64, b as u64)
19157            };
19158            let raw_logits_hi = {
19159                let _main = e.gpu.enter_main()?;
19160                let stream = e.stream();
19161                let (l, _g) = logits_e.device_ptr(&stream);
19162                l as u64 + (half * 4) as u64
19163            };
19164            *guard = Some(HeadSplit {
19165                pin,
19166                w1,
19167                hn1,
19168                y1,
19169                logits_e,
19170                ev_hn,
19171                ev_done,
19172                raw_hn1,
19173                raw_y1,
19174                raw_logits_hi,
19175            });
19176        }
19177        let ws = guard.as_mut().expect("armed above");
19178        let hi_rows = n_vocab - half;
19179        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
19180        let raw_hn = {
19181            let _main = e.gpu.enter_main()?;
19182            let stream = e.stream();
19183            let (h, _g) = hn.device_ptr(&stream);
19184            ws.ev_hn.record(&stream)?;
19185            h as u64
19186        };
19187        {
19188            let _r1 = rank1.gpu.enter_main()?;
19189            rank1.stream().wait(&ws.ev_hn)?;
19190            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
19191            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
19192            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
19193            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
19194            ws.ev_done.record(&rank1.stream())?;
19195        }
19196        {
19197            let _main = e.gpu.enter_main()?;
19198            let head_lo = head.slice(0..half * n_embd * 2);
19199            let HeadSplit { logits_e, .. } = &mut *ws;
19200            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
19201            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
19202            e.stream().wait(&ws.ev_done)?;
19203            Ok(Some(()))
19204        }
19205    }
19206
19207    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
19208    /// row exactly like the host variant (identical halves, identical concat) and runs the
19209    /// device argmax into `token_d` — NO host readback. Returns false when the split is
19210    /// ineligible (caller falls back to the plain matmul head).
19211    pub(crate) fn head_split_argmax_device(
19212        &self,
19213        e: &Engine,
19214        hn: &CudaSlice<f32>,
19215        token_d: &mut CudaSlice<u32>,
19216    ) -> Result<bool, Box<dyn std::error::Error>> {
19217        if self.head_split_fill_device(e, hn)?.is_none() {
19218            return Ok(false);
19219        }
19220        let n_vocab = self.cfg.n_vocab as usize;
19221        let guard = HEAD_SPLIT_WS
19222            .lock()
19223            .map_err(|_| "head split lock is poisoned")?;
19224        let ws = guard.as_ref().expect("filled above");
19225        let _main = e.gpu.enter_main()?;
19226        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
19227        Ok(true)
19228    }
19229
19230    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
19231    /// token's row).
19232    pub(crate) fn head_split_logits_dtoh(
19233        &self,
19234        e: &Engine,
19235    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19236        let guard = HEAD_SPLIT_WS
19237            .lock()
19238            .map_err(|_| "head split lock is poisoned")?;
19239        let ws = guard.as_ref().ok_or("head split logits not armed")?;
19240        let _main = e.gpu.enter_main()?;
19241        Ok(e.dtoh(&ws.logits_e)?)
19242    }
19243
19244    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
19245    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
19246    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
19247    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
19248    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
19249    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
19250    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
19251    /// own loop re-derive hist[k-1] from the returned row.
19252    pub fn step35_token_graph_chunk(
19253        &self,
19254        e: &Engine,
19255        token: u32,
19256        k_target: usize,
19257        cache: &mut Cache,
19258    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
19259        if !self.uses_sliding_gated_moe_program()
19260            || !crate::tp::step_tp_graph_enabled()?
19261            || !crate::tp::step_tp_dcw_enabled()?
19262            || !crate::tp::step_tp_qkv_fused_enabled()?
19263            || !crate::tp::step_tp_dev_router_enabled()?
19264            || !crate::tp::step_nvfp4_dev_routes_enabled()?
19265        {
19266            return Ok(None);
19267        }
19268        let n_layers = self.layers.len();
19269        let pos = cache.pos;
19270        let staged_next = pos + 1;
19271        if staged_next < 96 {
19272            return Ok(None);
19273        }
19274        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
19275        // exec's n_splits ladder must match eager per depth).
19276        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
19277        if !fa_vec {
19278            return Ok(None);
19279        }
19280        let sp = crate::fa_split_keys(staged_next, 8);
19281        let bucket_max = (n_splits * sp).max(staged_next);
19282        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
19283        let mut k = k_target.min(to_boundary).min(16);
19284        if k < 2 {
19285            return Ok(None);
19286        }
19287        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
19288        for il in 0..n_layers {
19289            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
19290                return Ok(None);
19291            };
19292            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
19293                k -= 1;
19294            }
19295            if k < 2 {
19296                return Ok(None);
19297            }
19298        }
19299
19300        let mut state_guard = self
19301            .step35_token_graph
19302            .lock()
19303            .map_err(|_| "step35 token graph lock is poisoned")?;
19304        let Some(state) = state_guard.as_mut() else {
19305            return Ok(None); // per-token path arms the state + stages first
19306        };
19307        if state.graphs.is_empty() {
19308            return Ok(None);
19309        }
19310        {
19311            let (b, g) = state.graphs.first_mut().expect("checked above");
19312            if *b != bucket_max {
19313                g.retarget_bucket(bucket_max)?;
19314                *b = bucket_max;
19315            }
19316        }
19317        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
19318
19319        // Rank-stream fence (eager stragglers; see the per-token path).
19320        {
19321            let fa0 = match &self.layers[0].mixer {
19322                Mixer::Full(fa) => fa,
19323                _ => return Err("step35 token graph expects full-attention layers".into()),
19324            };
19325            let tp0 = fa0
19326                .step_tp_qkv
19327                .as_ref()
19328                .ok_or("step35 token graph lost its TP state")?;
19329            for rank in 0..tp0.runtime.devices().len() {
19330                let engine = tp0
19331                    .runtime
19332                    .rank_engine(rank)
19333                    .ok_or("step35 token graph lost a rank engine")?;
19334                let _main = engine.gpu.enter_main()?;
19335                engine.stream().synchronize()?;
19336            }
19337        }
19338
19339        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
19340        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
19341        {
19342            let _main = e.gpu.enter_main()?;
19343            e.set_u32_one(&mut state.token_d, token)?;
19344            e.set_i32_one(&mut state.pos_d, pos as i32)?;
19345            e.set_i32_one(&mut state.hist_idx, 0)?;
19346        }
19347        for _ in 0..k {
19348            graph.launch(e)?;
19349        }
19350        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
19351        for il in 0..n_layers {
19352            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
19353            let transaction = tp_kv.begin_transaction()?;
19354            let fa = match &self.layers[il].mixer {
19355                Mixer::Full(fa) => fa,
19356                _ => return Err("step35 token graph expects full-attention layers".into()),
19357            };
19358            let tp = fa
19359                .step_tp_qkv
19360                .as_ref()
19361                .ok_or("step35 token graph lost its TP state")?;
19362            let empty: [CudaSlice<f32>; 0] = [];
19363            tp.runtime.append_tp_kv_transaction_inner(
19364                tp_kv,
19365                transaction,
19366                &empty,
19367                &empty,
19368                k,
19369                true,
19370            )?;
19371            tp.runtime
19372                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
19373            if let Some(local) = cache.kv[il].as_mut() {
19374                local.len = pos + k;
19375                let _main = e.gpu.enter_main()?;
19376                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
19377            }
19378        }
19379        cache.pos = pos + k;
19380        let (hist, logits) = {
19381            let _main = e.gpu.enter_main()?;
19382            e.stream().synchronize()?;
19383            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
19384        };
19385        Ok(Some((hist[..k].to_vec(), logits)))
19386    }
19387}
19388
19389impl HybridModel {
19390    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
19391    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
19392    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
19393    /// of each phase fork in parallel and merge into the following root section.
19394    #[allow(clippy::too_many_arguments)]
19395    fn step35_token_graph_build(
19396        &self,
19397        e: &Engine,
19398        cache: &mut Cache,
19399        state: &mut Step35TokenGraphState,
19400        bucket_max: usize,
19401    ) -> Result<(), Box<dyn std::error::Error>> {
19402        use cudarc::driver::DevicePtr;
19403        let n_embd = self.cfg.n_embd as usize;
19404        let eps = self.cfg.rms_eps;
19405        let n_layers = self.layers.len();
19406        let started = std::time::Instant::now();
19407        if !crate::router_kernel_on() {
19408            return Err(
19409                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
19410            );
19411        }
19412        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
19413            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
19414        }
19415
19416        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
19417        let embd_gpu = self
19418            .embd_gpu_try(e)
19419            .ok_or("step35 token graph could not upload the device embed table")?;
19420        let embd_qtype = match self.embd.ggml_type {
19421            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
19422            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
19423            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
19424        };
19425        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
19426
19427        // Fixed-stage pointers the sections reference.
19428        let (p_mixed, p_kshadow, p_vshadow) = {
19429            let _main = e.gpu.enter_main()?;
19430            let stream = e.stream();
19431            let (a, _g) = state.mixed_stage.device_ptr(&stream);
19432            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
19433            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
19434            (a as u64, b as u64, c as u64)
19435        };
19436
19437        crate::tp::token_graph_build_begin()?;
19438        let mut group_id: u32 = 0;
19439        for il in 0..n_layers {
19440            let layer = &self.layers[il];
19441            let fa = match &layer.mixer {
19442                Mixer::Full(fa) => fa,
19443                _ => return Err("step35 token graph expects full-attention layers".into()),
19444            };
19445            let tp = fa
19446                .step_tp_qkv
19447                .as_ref()
19448                .ok_or("step35 token graph lost its TP state")?;
19449            let attention = tp
19450                .attention
19451                .as_ref()
19452                .ok_or("step35 token graph lost its attention aux")?;
19453            let geometry = self.step35_geom(il);
19454            let window = geometry.window.map(|w| w as usize);
19455            let head_dim = geometry.head_dim_k as usize;
19456            let heads = geometry.n_head as usize;
19457            let kv_heads = geometry.n_head_kv as usize;
19458            let ranks = tp.runtime.devices().len();
19459            let local_heads = heads / ranks;
19460            let local_kv_heads = kv_heads / ranks;
19461            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
19462            let use_gate_shards =
19463                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
19464            if !use_gate_shards {
19465                return Err("step35 token graph requires the fused gate shards".into());
19466            }
19467
19468            let ws_index = tp
19469                .runtime
19470                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
19471            let ws_mutex = tp.runtime.decode_v2_workspace();
19472            let mut ws_guard = ws_mutex
19473                .lock()
19474                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
19475            let ws = ws_guard
19476                .get_mut(ws_index)
19477                .ok_or("step TP decode v2 workspace missing after ensure")?;
19478            tp.runtime
19479                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
19480            let mut rope_freqs = Vec::with_capacity(ranks);
19481            for rank in 0..ranks {
19482                let engine = tp
19483                    .runtime
19484                    .rank_engine(rank)
19485                    .ok_or("step35 token graph lost a rank engine")?;
19486                rope_freqs.push(if geometry.rope_factors {
19487                    self.step35_aux
19488                        .as_ref()
19489                        .and_then(|aux| aux.rope_freqs(engine))
19490                } else {
19491                    None
19492                });
19493            }
19494            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
19495                Some(crate::tp::StepTpGateShards::F32(shards))
19496            } else {
19497                attention
19498                    .gate_shards_bf16
19499                    .as_deref()
19500                    .map(crate::tp::StepTpGateShards::Bf16)
19501            };
19502
19503            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
19504            let decode_input = attention
19505                .decode_input
19506                .as_ref()
19507                .ok_or("step35 token graph requires the replicated decode input")?;
19508            let mut decode_input = decode_input
19509                .lock()
19510                .map_err(|_| "replicated decode input lock is poisoned")?;
19511            // Stage arming happens through the eager stage flow once; require it here.
19512            if ws.h_stage.is_none() {
19513                return Err(
19514                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
19515                );
19516            }
19517            {
19518                let state_x = &mut state.x;
19519                let token_d = &state.token_d;
19520                let pos_d = &state.pos_d;
19521                crate::tp::graph_section(e, None, || {
19522                    let _main = e.gpu.enter_main()?;
19523                    if il == 0 {
19524                        e.embed_gather_device_into(
19525                            embd_gpu,
19526                            token_d,
19527                            state_x,
19528                            n_embd,
19529                            embd_qtype,
19530                            embd_row_bytes,
19531                        )?;
19532                    }
19533                    {
19534                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
19535                        e.rms_norm(
19536                            state_x,
19537                            layer.attn_norm.float_data(),
19538                            h_stage,
19539                            n_embd,
19540                            1,
19541                            eps,
19542                        )?;
19543                    }
19544                    {
19545                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
19546                        let mut dst = pos_stage.slice_mut(0..1);
19547                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
19548                    }
19549                    Ok(())
19550                })?;
19551            }
19552
19553            // ---- R0/R1 (parallel): projections + dcw attention interior ----
19554            group_id += 1;
19555            for rank in 0..ranks {
19556                let engine = tp
19557                    .runtime
19558                    .rank_engine(rank)
19559                    .ok_or("step35 token graph lost a rank engine")?;
19560                {
19561                    // fa partial pool must reach the RUN CEILING before capture — an
19562                    // in-capture grow is a mem node (child graphs reject those), and the
19563                    // retarget path (increment C) widens the baked memsets up to the ceiling
19564                    // without moving the pool pointers. Two ensures cover both sp rungs.
19565                    let ceiling = window
19566                        .map(|w| cache.max_ctx.min(w))
19567                        .unwrap_or(cache.max_ctx);
19568                    let _main = engine.gpu.enter_main()?;
19569                    engine.fa_dcw_pool_ensure(
19570                        head_dim,
19571                        local_heads,
19572                        local_kv_heads,
19573                        ceiling.min(2048),
19574                    )?;
19575                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
19576                    engine.fa_dcw_pool_ensure(
19577                        head_dim,
19578                        local_heads,
19579                        local_kv_heads,
19580                        layer_bucket,
19581                    )?;
19582                }
19583                let runtime = &tp.runtime;
19584                let q_norm = &attention.q_norm;
19585                let k_norm = &attention.k_norm;
19586                let gate_ref = gate_shards_arg.as_ref();
19587                crate::tp::graph_section(engine, Some(group_id), || {
19588                    runtime.decode_v2_input_qkv_rank(
19589                        ws,
19590                        &state.pos_d,
19591                        &mut decode_input,
19592                        &tp.q,
19593                        &tp.k,
19594                        &tp.v,
19595                        q_norm,
19596                        k_norm,
19597                        head_dim,
19598                        geometry.n_rot as usize,
19599                        geometry.rope_base,
19600                        &rope_freqs,
19601                        eps,
19602                        gate_ref,
19603                        true,
19604                        false,
19605                        rank,
19606                        None,
19607                    )?;
19608                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
19609                    // replayed values track the live counters).
19610                    let distributed = cache.tp_kv[il]
19611                        .as_mut()
19612                        .ok_or("step35 token graph lost a TP cache")?;
19613                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
19614                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
19615                    let capacity = distributed.physical_capacity();
19616                    {
19617                        let rank_cache = distributed
19618                            .rank_mut(rank)
19619                            .ok_or("step35 token graph lost a rank cache")?;
19620                        let (k_plane, v_plane, len_d, base_d) =
19621                            rank_cache.planes_and_counters_mut();
19622                        engine.append_kv_quantized_dcw(
19623                            &ws.k[rank],
19624                            &ws.v_raw[rank],
19625                            k_plane,
19626                            v_plane,
19627                            len_d,
19628                            base_d,
19629                            kv_dim_k,
19630                            kv_dim_v,
19631                            ktb,
19632                            vtb,
19633                        )?;
19634                    }
19635                    {
19636                        let rank_cache = distributed
19637                            .rank_mut(rank)
19638                            .ok_or("step35 token graph lost a rank cache")?;
19639                        engine.inc_i32(rank_cache.len_d_mut())?;
19640                    }
19641                    let rank_cache = distributed
19642                        .rank(rank)
19643                        .ok_or("step35 token graph lost a rank cache")?;
19644                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
19645                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
19646                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
19647                    // retarget addresses combine's nsp at arg slot 6, and the fused
19648                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
19649                    // only the eager arm takes FUSION #2d.
19650                    engine.fa_decode_dcw(
19651                        &ws.q[rank],
19652                        &k_ring,
19653                        &v_ring,
19654                        &mut ws.attn_out[rank],
19655                        head_dim,
19656                        local_heads,
19657                        local_kv_heads,
19658                        rank_cache.len_d(),
19659                        rank_cache.base_d(),
19660                        window.unwrap_or(0),
19661                        layer_bucket,
19662                        geometry.attention_scale(),
19663                        ktb,
19664                        vtb,
19665                        None,
19666                    )?;
19667                    engine.attn_head_gate(
19668                        &ws.attn_out[rank],
19669                        &ws.gate[rank],
19670                        &mut ws.gated[rank],
19671                        None,
19672                        head_dim,
19673                        local_heads,
19674                        1,
19675                    )?;
19676                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
19677                    Ok(())
19678                })?;
19679            }
19680
19681            // ---- ROOT: combine + shadows + e-mirrors ----
19682            {
19683                let root = tp
19684                    .runtime
19685                    .rank_engine(0)
19686                    .ok_or("step35 token graph lost the root engine")?;
19687                let runtime = &tp.runtime;
19688                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
19689            }
19690            drop(ws_guard);
19691            drop(decode_input);
19692
19693            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
19694                .ok()
19695                .and_then(|v| v.parse().ok());
19696            if probe_layer == Some(il) {
19697                let Step35TokenGraphState {
19698                    mixed_stage,
19699                    probe_mixed,
19700                    ..
19701                } = &mut *state;
19702                crate::tp::graph_section(e, None, || {
19703                    let _main = e.gpu.enter_main()?;
19704                    let mut dst = probe_mixed.slice_mut(0..n_embd);
19705                    e.stream()
19706                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
19707                    Ok(())
19708                })?;
19709            }
19710
19711            // ---- FFN half ----
19712            match &layer.ffn {
19713                crate::hybrid::Ffn::Dense {
19714                    ffn_gate,
19715                    ffn_up,
19716                    ffn_down,
19717                } => {
19718                    let n_ff = ffn_gate.out_features();
19719                    let lim = self.cfg.clamp_shexp_at(il as u32);
19720                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
19721                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
19722                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
19723                    if lim.is_some() {
19724                        return Err("step35 token graph dense FFN with clamp unsupported".into());
19725                    }
19726                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
19727                        (
19728                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
19729                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
19730                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
19731                        ) => (wg, wu, wd),
19732                        _ => {
19733                            return Err(
19734                                "step35 token graph dense FFN requires bf16-resident weights"
19735                                    .into(),
19736                            );
19737                        }
19738                    };
19739                    crate::tp::graph_section(e, None, || {
19740                        let _main = e.gpu.enter_main()?;
19741                        let Step35TokenGraphState {
19742                            x,
19743                            x1,
19744                            mixed_stage,
19745                            dense_z,
19746                            dense_gate,
19747                            dense_up,
19748                            dense_act,
19749                            sh_stage,
19750                            ..
19751                        } = &mut *state;
19752                        e.add_rms_norm(
19753                            x,
19754                            mixed_stage,
19755                            layer.post_attn_norm.float_data(),
19756                            x1,
19757                            dense_z,
19758                            n_embd,
19759                            1,
19760                            eps,
19761                        )?;
19762                        // TWO SINGLE matvecs, not the dual: eager dense rides two
19763                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
19764                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
19765                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
19766                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
19767                        Self::ffn_act_lim(
19768                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
19769                        )?;
19770                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
19771                        e.add(x1, sh_stage, x, n_embd)?;
19772                        Ok(())
19773                    })?;
19774                }
19775                crate::hybrid::Ffn::Moe(m) => {
19776                    let moe = self
19777                        .cfg
19778                        .moe
19779                        .as_ref()
19780                        .ok_or("step35 token graph needs moe cfg")?;
19781                    let n_expert = moe.expert_count as usize;
19782                    let n_used = moe.expert_used_count as usize;
19783                    let sigmoid = self
19784                        .cfg
19785                        .sigmoid_router()
19786                        .ok_or("step35 token graph needs the sigmoid router")?;
19787                    let step_tp = m
19788                        .step_tp
19789                        .as_ref()
19790                        .ok_or("step35 token graph needs TP experts")?;
19791                    let bank = match &step_tp.experts {
19792                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
19793                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
19794                    };
19795                    let routes_ws_mutex = bank.device_workspace_handle();
19796                    let mut routes_guard = routes_ws_mutex
19797                        .lock()
19798                        .map_err(|_| "routes workspace lock is poisoned")?;
19799                    let routes_ws = routes_guard
19800                        .as_mut()
19801                        .ok_or("step35 token graph requires the routes workspace warmed")?;
19802                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
19803                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
19804                    let p_z = {
19805                        let root = step_tp
19806                            .runtime
19807                            .rank_engine(0)
19808                            .ok_or("routes root engine missing")?;
19809                        let _main = root.gpu.enter_main()?;
19810                        let stream = root.stream();
19811                        let in_stage = routes_ws
19812                            .in_stage_handle()
19813                            .ok_or("routes in stage not armed")?;
19814                        let (a, _g) = in_stage.device_ptr(&stream);
19815                        a as u64
19816                    };
19817                    let local_out = bank.expert_width / ranks;
19818
19819                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
19820                    crate::tp::graph_section(e, None, || {
19821                        let _main = e.gpu.enter_main()?;
19822                        {
19823                            let in_stage = routes_ws
19824                                .in_stage_mut()
19825                                .ok_or("routes in stage not armed")?;
19826                            let Step35TokenGraphState {
19827                                x, x1, mixed_stage, ..
19828                            } = &mut *state;
19829                            e.add_rms_norm(
19830                                x,
19831                                mixed_stage,
19832                                layer.post_attn_norm.float_data(),
19833                                x1,
19834                                in_stage,
19835                                n_embd,
19836                                1,
19837                                eps,
19838                            )?;
19839                        }
19840                        {
19841                            let z_ref = routes_ws
19842                                .in_stage_handle()
19843                                .ok_or("routes in stage not armed")?;
19844                            e.router_gemv_into(
19845                                m.gate_inp.float_data(),
19846                                z_ref,
19847                                &mut state.router_logits,
19848                                n_embd,
19849                                n_expert,
19850                                1,
19851                            )?;
19852                        }
19853                        let (sel_e, w_e) = routes_ws
19854                            .dev_route_e_mut()
19855                            .ok_or("routes staging not armed")?;
19856                        e.moe_router_sigmoid_topk_into(
19857                            &state.router_logits,
19858                            1,
19859                            n_expert,
19860                            n_used,
19861                            m.active_count(),
19862                            &m.exp_probs_b_dev,
19863                            &m.active_experts_dev,
19864                            sigmoid.0,
19865                            sigmoid.1,
19866                            sel_e,
19867                            w_e,
19868                        )?;
19869                        Ok(())
19870                    })?;
19871
19872                    // ---- R0r/R1r (parallel): routes sweeps ----
19873                    group_id += 1;
19874                    for rank in 0..ranks {
19875                        let engine = step_tp
19876                            .runtime
19877                            .rank_engine(rank)
19878                            .ok_or("routes rank engine missing")?;
19879                        let runtime = &step_tp.runtime;
19880                        crate::tp::graph_section(engine, Some(group_id), || {
19881                            runtime.routes_rank_section(
19882                                bank,
19883                                routes_ws,
19884                                p_z,
19885                                local_out,
19886                                n_used,
19887                                step_tp.activation_limit,
19888                                rank,
19889                            )
19890                        })?;
19891                    }
19892
19893                    // ---- ROOTr: combine into the out stage ----
19894                    {
19895                        let root = step_tp
19896                            .runtime
19897                            .rank_engine(0)
19898                            .ok_or("routes root engine missing")?;
19899                        let runtime = &step_tp.runtime;
19900                        crate::tp::graph_section(root, None, || {
19901                            runtime.routes_root_section(bank, routes_ws)
19902                        })?;
19903                    }
19904
19905                    // ---- E3: shexp + add_shared onto the out stage + residual ----
19906                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
19907                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
19908                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
19909                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
19910                        (
19911                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
19912                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
19913                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
19914                        ) => (wg, wu, wd),
19915                        _ => {
19916                            return Err(
19917                                "step35 token graph shexp requires bf16-resident weights".into()
19918                            );
19919                        }
19920                    };
19921                    let n_ff_sh = m
19922                        .gate_shexp
19923                        .as_ref()
19924                        .expect("matched Some above")
19925                        .out_features();
19926                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
19927                    // init, reproducing eager's ones vector without a launch.
19928                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
19929                    crate::tp::graph_section(e, None, || {
19930                        let _main = e.gpu.enter_main()?;
19931                        let (z_ref, out_stage) = routes_ws
19932                            .in_and_out_stages_mut()
19933                            .ok_or("routes stages not armed")?;
19934                        let Step35TokenGraphState {
19935                            x,
19936                            x1,
19937                            sh_stage,
19938                            shexp_gate,
19939                            shexp_up,
19940                            shexp_act,
19941                            gate_sig,
19942                            ..
19943                        } = &mut *state;
19944                        e.matvec_bf16_dual_into(
19945                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
19946                        )?;
19947                        Self::ffn_act_lim(
19948                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
19949                            n_ff_sh,
19950                        )?;
19951                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
19952                        if let Some(gate_w) = gate_inp_shexp {
19953                            e.sigmoid_dot_rows_into(
19954                                z_ref,
19955                                gate_w.float_data(),
19956                                gate_sig,
19957                                n_embd,
19958                                1,
19959                            )?;
19960                        }
19961                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
19962                        e.add(x1, out_stage, x, n_embd)?;
19963                        Ok(())
19964                    })?;
19965                }
19966            }
19967            if probe_layer == Some(il) {
19968                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
19969                crate::tp::graph_section(e, None, || {
19970                    let _main = e.gpu.enter_main()?;
19971                    let mut dst = probe_x.slice_mut(0..n_embd);
19972                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
19973                    Ok(())
19974                })?;
19975            }
19976        }
19977
19978        // ---- Tail: output norm + head into the logits stage ----
19979        let head = match &self.output {
19980            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
19981            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
19982        };
19983        crate::tp::graph_section(e, None, || {
19984            let _main = e.gpu.enter_main()?;
19985            let Step35TokenGraphState {
19986                x,
19987                hn,
19988                logits_stage,
19989                token_d,
19990                pos_d,
19991                token_hist,
19992                hist_idx,
19993                ..
19994            } = &mut *state;
19995            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
19996            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
19997            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
19998            // argmax_gate-validated), the id lands in the history ring, and pos advances on
19999            // device — consecutive launches chain with NO host sync. Single-token mode
20000            // overwrites token_d/pos_d from the host before each launch, so these nodes are
20001            // harmless there.
20002            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
20003            e.u32_hist_append(token_d, token_hist, hist_idx)?;
20004            e.inc_i32(pos_d)?;
20005            Ok(())
20006        })?;
20007
20008        let graph = crate::tp::token_graph_build_finish()?;
20009        state.graphs.push((bucket_max, graph));
20010        eprintln!(
20011            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
20012             build_ms={:.0} performance_claim=false",
20013            started.elapsed().as_secs_f64() * 1e3
20014        );
20015        Ok(())
20016    }
20017}