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.
664pub fn prime_chunk_ranges(t: usize, n_layers: usize) -> Vec<(usize, usize)> {
665    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
666    let chunk = prime_chunk_tokens(t, n_layers);
667    let fixed = fixed_prime_chunk_ranges(t, chunk);
668    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
669        Ok(value) => value == "dynamic",
670        Err(_) => true,
671    };
672    if explicit_chunk || !dynamic || !prime_pp2_auto_geometry(n_layers) {
673        fixed
674    } else {
675        dynamic_prime_chunk_ranges(t, chunk, &fixed)
676    }
677}
678
679struct HeadSplit {
680    pin: u64,
681    w1: CudaSlice<u8>,
682    hn1: CudaSlice<f32>,
683    y1: CudaSlice<f32>,
684    logits_e: CudaSlice<f32>,
685    ev_hn: cudarc::driver::CudaEvent,
686    ev_done: cudarc::driver::CudaEvent,
687    raw_hn1: u64,
688    raw_y1: u64,
689    raw_logits_hi: u64,
690}
691/// HEAD-SPLIT workspace (host + device twins share it).
692static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
693
694/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
695/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
696/// input bits — rank1's local selection is bit-equal to the root's.
697#[allow(clippy::type_complexity)]
698static DEV1_ROUTER_REPS: std::sync::Mutex<
699    Option<(
700        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
701        Option<CudaSlice<f32>>,
702    )>,
703> = std::sync::Mutex::new(None);
704
705/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
706/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
707/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
708#[allow(clippy::type_complexity)]
709static SHEXP_D1_REPS: std::sync::Mutex<
710    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
711> = std::sync::Mutex::new(None);
712#[allow(clippy::type_complexity)]
713static SHEXP_D1_WS: std::sync::Mutex<
714    Option<(
715        (usize, usize),
716        CudaSlice<f32>,
717        CudaSlice<f32>,
718        CudaSlice<f32>,
719        cudarc::driver::CudaEvent,
720        cudarc::driver::CudaEvent,
721    )>,
722> = std::sync::Mutex::new(None);
723
724/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
725static SHEXP_OV_WS: std::sync::Mutex<
726    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
727> = std::sync::Mutex::new(None);
728
729impl HybridModel {
730    fn step35_tp_qkv(
731        &self,
732        e: &Engine,
733        fa: &FullAttnLayer,
734        h: &CudaSlice<f32>,
735        t: usize,
736    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
737        let Some(tp) = fa.step_tp_qkv.as_ref() else {
738            return Ok(None);
739        };
740        let values = active_matrix_values(
741            h.len(),
742            t,
743            self.cfg.n_embd as usize,
744            "Step TP QKV activation",
745        )?;
746        let host = e.dtoh_view(&h.slice(0..values))?;
747        let q = if tp.runtime.native_p2p() {
748            tp.runtime
749                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
750        } else {
751            tp.runtime
752                .bf16_column_parallel_resident(&tp.q, &host, t)?
753                .gathered
754        };
755        let k = if tp.runtime.native_p2p() {
756            tp.runtime
757                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
758        } else {
759            tp.runtime
760                .bf16_column_parallel_resident(&tp.k, &host, t)?
761                .gathered
762        };
763        let v = if tp.runtime.native_p2p() {
764            tp.runtime
765                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
766        } else {
767            tp.runtime
768                .bf16_column_parallel_resident(&tp.v, &host, t)?
769                .gathered
770        };
771        eprintln!(
772            "[step-tp-qkv] execute layer={} devices={:?} tokens={t} projections=qkv \
773             qkv_tensor_parallel=true attention_local=true kv_local=true output_local=true \
774             transport={} native_p2p={} bulk_p2p={} activation=host-canonical \
775             output=root-readback \
776             performance_claim=false",
777            tp.layer,
778            tp.devices,
779            tp.runtime.transport_label(),
780            tp.runtime.native_p2p(),
781            tp.runtime.bulk_p2p(),
782        );
783        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
784    }
785
786    fn step35_tp_o(
787        &self,
788        e: &Engine,
789        fa: &FullAttnLayer,
790        activation: &CudaSlice<f32>,
791        tokens: usize,
792    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
793        let Some(tp) = fa.step_tp_qkv.as_ref() else {
794            return Ok(None);
795        };
796        let host = e.dtoh(activation)?;
797        let output = if tp.runtime.native_p2p() {
798            tp.runtime
799                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
800        } else {
801            tp.runtime
802                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
803        };
804        eprintln!(
805            "[step-tp-o] execute layer={} devices={:?} tokens={tokens} projection=o \
806             o_tensor_parallel=true attention_local=true kv_local=true transport={} \
807             native_p2p={} bulk_p2p={} activation=host-canonical \
808             reduction=global-tp8-block-order \
809             output=root-readback performance_claim=false",
810            tp.layer,
811            tp.devices,
812            tp.runtime.transport_label(),
813            tp.runtime.native_p2p(),
814            tp.runtime.bulk_p2p(),
815        );
816        Ok(Some(e.htod(&output)?))
817    }
818
819    fn step35_o(
820        &self,
821        e: &Engine,
822        fa: &FullAttnLayer,
823        activation: &CudaSlice<f32>,
824        tokens: usize,
825    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
826        match self.step35_tp_o(e, fa, activation, tokens)? {
827            Some(output) => Ok(output),
828            None => e.matmul(&fa.wo, activation, tokens),
829        }
830    }
831
832    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
833    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
834    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
835    /// (it forces a dtoh + host hash per layer).
836    fn prime_trace_path() -> Option<&'static str> {
837        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
838        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
839            .as_deref()
840    }
841
842    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
843    /// each prime_layers stage and accumulates wall time per stage class, printed after
844    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
845    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
846    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
847    fn prime_anatomy_on() -> bool {
848        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
849        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
850    }
851
852    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
853        static S: [std::sync::atomic::AtomicU64; 5] = [
854            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
855            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
856            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
857            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
858            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
859        ];
860        &S
861    }
862
863    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
864    pub fn forward(
865        &self,
866        e: &Engine,
867        tokens: &[u32],
868    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
869        if self.is_gemma4_e4b() {
870            return self.gemma4_e4b_forward(e, tokens, false);
871        }
872        if self.uses_gemma_program() {
873            return self.gemma4_forward(e, tokens, false);
874        }
875        let cfg = &self.cfg;
876        let n_embd = cfg.n_embd as usize;
877        let t = tokens.len();
878        let eps = cfg.rms_eps;
879        let pos: Vec<i32> = (0..t as i32).collect();
880        let pos_d = e.htod_i32(&pos)?;
881
882        let mut x = self.embed(e, tokens)?; // [T, n_embd]
883
884        for (il, layer) in self.layers.iter().enumerate() {
885            // attn_norm
886            let mut h = e.uninit(t * n_embd)?;
887            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
888
889            let mixed = match &layer.mixer {
890                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
891                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
892                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
893            };
894
895            // residual 1
896            let mut x1 = e.uninit(t * n_embd)?;
897            e.add(&x, &mixed, &mut x1, t * n_embd)?;
898
899            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
900            let mut z = e.uninit(t * n_embd)?;
901            e.rms_norm(
902                &x1,
903                layer.post_attn_norm.float_data(),
904                &mut z,
905                n_embd,
906                t,
907                eps,
908            )?;
909            let ffn_out = match &layer.ffn {
910                crate::hybrid::Ffn::Dense {
911                    ffn_gate,
912                    ffn_up,
913                    ffn_down,
914                } => {
915                    let n_ff = ffn_gate.out_features();
916                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
917                    let up = g2.pop().unwrap();
918                    let gate = g2.pop().unwrap();
919                    let mut act = e.uninit(t * n_ff)?;
920                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
921                    // both the dense MLP and the shared expert, and its limit is
922                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
923                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
924                    Self::ffn_act_lim(
925                        e,
926                        &self.cfg,
927                        &gate,
928                        &up,
929                        1.0,
930                        1.0,
931                        self.cfg.clamp_shexp_at(il as u32),
932                        &mut act,
933                        t * n_ff,
934                    )?;
935                    e.matmul(ffn_down, &act, t)?
936                }
937                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
938            };
939            let mut x2 = e.uninit(t * n_embd)?;
940            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
941            x = x2;
942        }
943
944        let mut hn = e.uninit(t * n_embd)?;
945        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
946        let logits = e.matmul(&self.output, &hn, t)?;
947        Ok(e.dtoh(&logits)?)
948    }
949
950    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
951    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
952    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
953    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
954    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
955    pub fn forward_last(
956        &self,
957        e: &Engine,
958        tokens: &[u32],
959    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
960        if self.uses_gemma_program() {
961            return self.gemma4_forward(e, tokens, true);
962        }
963        let cfg = &self.cfg;
964        let n_embd = cfg.n_embd as usize;
965        let t = tokens.len();
966        let eps = cfg.rms_eps;
967        let pos: Vec<i32> = (0..t as i32).collect();
968        let pos_d = e.htod_i32(&pos)?;
969
970        let mut x = self.embed(e, tokens)?; // [T, n_embd]
971        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
972        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
973        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
974        let anat = Self::prime_anatomy_on();
975        let mut anat_last = if anat {
976            e.stream().synchronize()?;
977            Some(std::time::Instant::now())
978        } else {
979            None
980        };
981        macro_rules! anat_mark {
982            ($slot:expr) => {
983                if let Some(ts) = anat_last.as_mut() {
984                    e.stream().synchronize()?;
985                    Self::prime_anatomy_slots()[$slot].fetch_add(
986                        ts.elapsed().as_nanos() as u64,
987                        std::sync::atomic::Ordering::Relaxed,
988                    );
989                    *ts = std::time::Instant::now();
990                }
991            };
992        }
993        for (il, layer) in self.layers.iter().enumerate() {
994            let mut h = e.uninit(t * n_embd)?;
995            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
996            if probe {
997                e.stream().synchronize()?;
998                eprintln!("[probe] L{il} norm ok");
999            }
1000            anat_mark!(4);
1001            let mixed = match &layer.mixer {
1002                Mixer::Full(fa) => {
1003                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1004                    anat_mark!(0);
1005                    y
1006                }
1007                Mixer::Linear(la) => {
1008                    let y = self.linear_attn(e, la, &h, t)?;
1009                    anat_mark!(1);
1010                    y
1011                }
1012                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1013            };
1014            if probe {
1015                e.stream().synchronize()?;
1016                eprintln!("[probe] L{il} mixer ok");
1017            }
1018            let mut x1 = e.uninit(t * n_embd)?;
1019            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1020            let mut z = e.uninit(t * n_embd)?;
1021            e.rms_norm(
1022                &x1,
1023                layer.post_attn_norm.float_data(),
1024                &mut z,
1025                n_embd,
1026                t,
1027                eps,
1028            )?;
1029            anat_mark!(4);
1030            let ffn_out = match &layer.ffn {
1031                crate::hybrid::Ffn::Dense {
1032                    ffn_gate,
1033                    ffn_up,
1034                    ffn_down,
1035                } => {
1036                    let n_ff = ffn_gate.out_features();
1037                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1038                    let up = g2.pop().unwrap();
1039                    let gate = g2.pop().unwrap();
1040                    let mut act = e.uninit(t * n_ff)?;
1041                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1042                    Self::ffn_act_lim(
1043                        e,
1044                        &self.cfg,
1045                        &gate,
1046                        &up,
1047                        1.0,
1048                        1.0,
1049                        self.cfg.clamp_shexp_at(il as u32),
1050                        &mut act,
1051                        t * n_ff,
1052                    )?;
1053                    let y = e.matmul(ffn_down, &act, t)?;
1054                    anat_mark!(3);
1055                    y
1056                }
1057                crate::hybrid::Ffn::Moe(m) => {
1058                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1059                    anat_mark!(2);
1060                    y
1061                }
1062            };
1063            if probe {
1064                e.stream().synchronize()?;
1065                eprintln!("[probe] L{il} ffn ok");
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        if anat {
1072            let s = Self::prime_anatomy_slots();
1073            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1074            eprintln!(
1075                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1076                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1077                ms(0),
1078                ms(1),
1079                ms(2),
1080                ms(3),
1081                ms(4)
1082            );
1083        }
1084        // norm over all T, then slice the LAST row and run lm_head on that single row.
1085        let mut hn = e.uninit(t * n_embd)?;
1086        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1087        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1088        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1089        let mut hlast = e.uninit(n_embd)?;
1090        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1091        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1092        Ok(e.dtoh(&logits)?)
1093    }
1094
1095    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1096    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1097    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1098    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1099    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1100    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1101    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1102    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1103    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1104    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1105    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1106    ///   (c) `cache.pos`/KV len/len_d advance by T.
1107    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1108    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1109    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1110    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1111    ///
1112    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1113    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1114    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1115    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1116    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1117    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1118    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1119    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1120    /// differently under load — research/tick-seg-20260807, receipt in
1121    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1122    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1123    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1124    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1125    /// caller that SPLITS one request across calls passes the remainder.
1126    pub fn prime_cache(
1127        &self,
1128        e: &Engine,
1129        tokens: &[u32],
1130        cache: &mut Cache,
1131        queued_after: usize,
1132    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1133        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1134    }
1135
1136    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1137    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1138    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1139    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1140    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1141    pub fn prime_cache_overlaid(
1142        &self,
1143        e: &Engine,
1144        tokens: &[u32],
1145        cache: &mut Cache,
1146        queued_after: usize,
1147        overlay: Option<&crate::vision::EmbedOverlay>,
1148    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1149        let n_embd = self.cfg.n_embd as usize;
1150        let t = tokens.len();
1151        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1152        // session cache — every chunk (including the first) takes the continuation arm
1153        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1154        assert!(
1155            t >= PRIME_MIN_T,
1156            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1157        );
1158        assert!(
1159            cache.pos + t <= cache.max_ctx,
1160            "prime_cache: prompt exceeds cache max_ctx"
1161        );
1162
1163        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1164        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1165        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1166        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1167        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1168        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1169        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1170        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1171        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1172        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1173        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1174        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1175            if self.is_gemma4_e4b() {
1176                if overlay.is_some() {
1177                    return Err(
1178                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1179                    );
1180                }
1181                return self.gemma4_e4b_prime(e, tokens, cache);
1182            }
1183            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1184            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1185            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1186            // spans become bidirectional attention islands (lane/gemma-vision).
1187            return self.gemma4_prime(e, tokens, cache, overlay);
1188        }
1189        let ranges = prime_chunk_ranges(t, self.layers.len());
1190        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1191        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1192        // the prefill's ARITHMETIC, so two rigs with different values produced different
1193        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1194        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1195        // (VERDICT.md) — and it is NOT what docs originally said:
1196        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1197        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1198        //     output head), so growing a chunk cannot move an existing row's value.
1199        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1200        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1201        //     not describe our leak.
1202        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1203        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1204        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1205        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1206        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1207        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1208        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1209        // the source — every row is in one numeric class, so the chunk size no longer steers
1210        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1211        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1212        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1213        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1214        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1215        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1216        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1217        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1218        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1219        // across calls, the request still ends at the same absolute position, whatever the tick
1220        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1221        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1222        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1223        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1224        // on in a measured default run.
1225        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1226        let seq_end = if legacy_calllocal {
1227            cache.pos + t
1228        } else {
1229            cache.pos + t + queued_after
1230        };
1231        if ranges.len() == 1 {
1232            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1233        }
1234        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1235        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1236        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1237        // this lane owns the balanced two-stage schedule only.
1238        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1239            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1240                if overlay.is_some() {
1241                    return Err(
1242                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1243                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1244                            .into(),
1245                    );
1246                }
1247                if crate::pp::pp_multi_stream_same_device() {
1248                    return Err(
1249                        "prime chunk pipeline refused with 2 stage streams on one device — \
1250                         that concurrent-stream placement remains quarantined by the deferred \
1251                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1252                         the serial split."
1253                            .into(),
1254                    );
1255                }
1256                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1257            }
1258        }
1259        let mut hiddens = e.uninit(t * n_embd)?;
1260        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1261        for &(start, end) in &ranges {
1262            // chunked prime writes tap rows at the chunk's absolute offset
1263            if let Some(taps) = cache.dflash_taps.as_mut() {
1264                taps.base = start;
1265            }
1266            let (l, hs, x) =
1267                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1268            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1269            last = Some((l, hs));
1270        }
1271        let (logits, h_seed) = last.unwrap();
1272        Ok((logits, h_seed, hiddens))
1273    }
1274
1275    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1276    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1277    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1278    /// norm, lm head, and caller hidden-stack copy as the serial split.
1279    fn prime_cache_pp2_pipelined(
1280        &self,
1281        e: &Engine,
1282        tokens: &[u32],
1283        cache: &mut Cache,
1284        seq_end: usize,
1285        ranges: &[(usize, usize)],
1286        fence: &[usize],
1287    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1288        debug_assert_eq!(fence.len(), 3);
1289        debug_assert!(ranges.len() >= 2);
1290        let rt = crate::pp::PpNRt::get(e)?;
1291        assert_eq!(
1292            rt.n_stages(),
1293            2,
1294            "prime pipeline requires exactly two PP stages"
1295        );
1296        let n_embd = self.cfg.n_embd as usize;
1297        let t = tokens.len();
1298        let initial_base = cache.pos;
1299        let caller_stream = e.stream();
1300
1301        // #87 reverse publication before any new stage allocation, then prewarm both
1302        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1303        // after stage 1(N) is queued would synchronize that stream and erase the first
1304        // overlap on a two-chunk prompt.
1305        rt.fence_stages_behind(&caller_stream)?;
1306        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1307        rt.prepare_overlap_slots(0, max_payload)?;
1308
1309        let mut hiddens = e.uninit(t * n_embd)?;
1310        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1311        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1312        let (cache0, cache1) = stage_caches.parts();
1313        let (first_start, first_end) = ranges[0];
1314        let mut slot = self.prime_pp2_stage0_enqueue(
1315            e,
1316            rt,
1317            &tokens[first_start..first_end],
1318            cache0,
1319            seq_end,
1320            fence,
1321            initial_base + first_start,
1322            true,
1323        )?;
1324        cache0.pos = initial_base + first_end;
1325
1326        for (i, &(start, end)) in ranges.iter().enumerate() {
1327            let base = initial_base + start;
1328            debug_assert_eq!(
1329                cache1.pos, base,
1330                "stage 1 must drain chunks in original position order"
1331            );
1332            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1333                let next_base = initial_base + next_start;
1334                debug_assert_eq!(
1335                    cache0.pos, next_base,
1336                    "stage 0 must issue chunks in original position order"
1337                );
1338                let cache0_stage = &mut *cache0;
1339                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1340                // on one host thread therefore serialize even if the calls are ordered as
1341                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1342                // stage 1 consumes slot N while stage 0 produces slot N+1.
1343                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1344                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1345                        let next = self
1346                            .prime_pp2_stage0_enqueue(
1347                                e,
1348                                rt,
1349                                &tokens[next_start..next_end],
1350                                cache0_stage,
1351                                seq_end,
1352                                fence,
1353                                next_base,
1354                                true,
1355                            )
1356                            .map_err(|err| err.to_string())?;
1357                        cache0_stage.pos = initial_base + next_end;
1358                        Ok(next)
1359                    });
1360                    let x = self.prime_pp2_stage1_enqueue(
1361                        e,
1362                        rt,
1363                        slot,
1364                        end - start,
1365                        cache1,
1366                        seq_end,
1367                        fence,
1368                        base,
1369                        true,
1370                    )?;
1371                    let out = {
1372                        rt.bind_stage(1)?;
1373                        let _st1 = rt.enter(1);
1374                        let e1 = rt.engine(1, e);
1375                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1376                    };
1377                    let next = stage0
1378                        .join()
1379                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1380                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1381                    Ok((out, Some(next)))
1382                })?
1383            } else {
1384                let x = self.prime_pp2_stage1_enqueue(
1385                    e,
1386                    rt,
1387                    slot,
1388                    end - start,
1389                    cache1,
1390                    seq_end,
1391                    fence,
1392                    base,
1393                    true,
1394                )?;
1395                let out = {
1396                    rt.bind_stage(1)?;
1397                    let _st1 = rt.enter(1);
1398                    let e1 = rt.engine(1, e);
1399                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1400                };
1401                (out, None)
1402            };
1403
1404            rt.publish_to(1, &caller_stream)?;
1405            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1406            last = Some((out.0, out.1));
1407            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1408
1409            if let Some(next) = next_slot {
1410                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1411                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1412                // Stage 0(N+1) is already queued before this wait is appended, so its
1413                // overlap with stage 1(N) is preserved.
1414                rt.fence_stages_behind(&caller_stream)?;
1415                slot = next;
1416            }
1417        }
1418
1419        debug_assert_eq!(cache0.pos, initial_base + t);
1420        debug_assert_eq!(cache1.pos, initial_base + t);
1421        let (logits, h_seed) = last.unwrap();
1422        Ok((logits, h_seed, hiddens))
1423    }
1424
1425    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1426    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1427    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1428    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1429    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1430    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1431    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1432        if Engine::gdn_db_on()
1433            && Engine::gdn_chunked_enabled()
1434            && t >= 16
1435            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1436            && num_k * 2 == num_v
1437        {
1438            num_k
1439        } else {
1440            num_v
1441        }
1442    }
1443
1444    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1445    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1446    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1447    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1448    fn f16out_on(e: &Engine, t: usize) -> bool {
1449        crate::f16_ffi::pp_f16_enabled()
1450            && t >= 16
1451            && !e.verify_exact_on()
1452            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1453    }
1454
1455    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1456    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1457    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1458    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1459    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1460    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1461    /// see one entry, byte-identical behavior.
1462    pub fn prime_slabs_get(
1463        &self,
1464        e: &Engine,
1465        t: usize,
1466        n_embd: usize,
1467        n_ff_max: usize,
1468    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1469        let mut slabs = self.prime_slabs.lock().unwrap();
1470        let dev = e.ctx().ordinal();
1471        let need_new = match slabs.get(&dev) {
1472            None => true,
1473            Some(sl) => sl.lock().unwrap().t_cap < t,
1474        };
1475        if need_new {
1476            slabs.insert(
1477                dev,
1478                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1479                    t_cap: t,
1480                    h: e.uninit(t * n_embd)?,
1481                    x1: e.uninit(t * n_embd)?,
1482                    z: e.uninit(t * n_embd)?,
1483                    act: e.uninit(t * n_ff_max)?,
1484                    xa: e.uninit(t * n_embd)?,
1485                    xb: e.uninit(t * n_embd)?,
1486                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1487                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1488                    gate: e.uninit(t * n_ff_max)?,
1489                    up: e.uninit(t * n_ff_max)?,
1490                    ffn_out: e.uninit(t * n_embd)?,
1491                    seg_glue: Vec::new(),
1492                    mixed: e.uninit(t * n_embd)?,
1493                    seg_mid: Vec::new(),
1494                    seg_t: 0,
1495                })),
1496            );
1497        }
1498        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1499    }
1500
1501    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1502    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1503    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1504    fn prime_chunk(
1505        &self,
1506        e: &Engine,
1507        tokens: &[u32],
1508        cache: &mut Cache,
1509        seq_end: usize,
1510        chunk_off: usize,
1511        overlay: Option<&crate::vision::EmbedOverlay>,
1512    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1513        if crate::pp::pp_host_bounce_active()
1514            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1515        {
1516            return Err(
1517                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1518                 has no active prime stage split and would peer-read remote weights; keep \
1519                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1520                    .into(),
1521            );
1522        }
1523        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1524        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1525        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1526        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1527        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1528        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1529        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1530        // loader is off and there is nothing remote to split for.
1531        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1532            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1533                if overlay.is_some() {
1534                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1535                         run single-device or MEMRA_PRIME_PP=0"
1536                        .into());
1537                }
1538                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1539            }
1540        }
1541        if crate::pp::pp_host_bounce_active() {
1542            return Err(
1543                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1544                 refusing an unsplit remote-weight walk"
1545                    .into(),
1546            );
1547        }
1548        let t = tokens.len();
1549        let base = cache.pos;
1550        debug_assert!(
1551            seq_end >= base + t,
1552            "prime_chunk: seq_end must cover this chunk"
1553        );
1554        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1555        let pos_d = e.htod_i32(&pos)?;
1556
1557        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1558        if let Some(ov) = overlay {
1559            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1560            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1561            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1562            let n_embd = self.cfg.n_embd as usize;
1563            for &(pos, row_off, n_rows) in &ov.spans {
1564                let lo = pos.max(chunk_off);
1565                let hi = (pos + n_rows).min(chunk_off + t);
1566                if lo < hi {
1567                    let src_row = row_off + (lo - pos);
1568                    let view = ov
1569                        .rows
1570                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1571                    e.copy_view_into(
1572                        &mut x_embed,
1573                        (lo - chunk_off) * n_embd,
1574                        &view,
1575                        (hi - lo) * n_embd,
1576                    )?;
1577                }
1578            }
1579        }
1580        let x = self.prime_layers(
1581            e,
1582            x_embed,
1583            0,
1584            self.layers.len(),
1585            &pos_d,
1586            t,
1587            base,
1588            cache,
1589            seq_end,
1590        )?;
1591        self.prime_chunk_epilogue(e, x, t, cache)
1592    }
1593
1594    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1595    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1596    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1597    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1598    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1599    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1600    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1601    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1602    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1603    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1604    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1605    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1606    ///     each stage walks through its own resident transients;
1607    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1608    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1609    #[allow(clippy::too_many_arguments)]
1610    fn prime_layers(
1611        &self,
1612        e: &Engine,
1613        x_in: CudaSlice<f32>,
1614        lo: usize,
1615        hi: usize,
1616        pos_d: &CudaSlice<i32>,
1617        t: usize,
1618        base: usize,
1619        cache: &mut Cache,
1620        seq_end: usize,
1621    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1622        let cfg = &self.cfg;
1623        let n_embd = cfg.n_embd as usize;
1624        let eps = cfg.rms_eps;
1625        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1626        // standalone convert launches). Only when the f16 lane serves and T reaches the
1627        // GEMM tier; bit-identical either way.
1628        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1629        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1630        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1631        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1632        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1633        // capacity tail must stay behind checked views. The hidden-stack return clones the
1634        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1635        let n_ff_max = self
1636            .layers
1637            .iter()
1638            .map(|l| match &l.ffn {
1639                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1640                _ => n_embd,
1641            })
1642            .max()
1643            .unwrap_or(n_embd)
1644            .max(n_embd);
1645        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1646        let slab = if use_slabs {
1647            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1648        } else {
1649            None
1650        };
1651        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1652        let mut x_own; // fallback storage when slabs are off
1653        type SlabRefs<'a> = (
1654            &'a mut CudaSlice<f32>,
1655            &'a mut CudaSlice<f32>,
1656            &'a mut CudaSlice<f32>,
1657            &'a mut CudaSlice<f32>,
1658            &'a mut CudaSlice<u8>,
1659            &'a mut CudaSlice<u8>,
1660            &'a mut CudaSlice<f32>,
1661            &'a mut CudaSlice<f32>,
1662            &'a mut CudaSlice<f32>,
1663        );
1664        let (mut x_cur, mut x_nxt, sl): (
1665            &mut CudaSlice<f32>,
1666            &mut CudaSlice<f32>,
1667            Option<SlabRefs>,
1668        );
1669        let mut seg: Option<(
1670            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1671            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1672            &mut CudaSlice<f32>,
1673            &mut usize,
1674        )> = None;
1675        let mut x_own2;
1676        match slab_guard.as_mut() {
1677            Some(g) => {
1678                let slabs = &mut **g;
1679                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1680                let PrimeSlabs {
1681                    xa,
1682                    xb,
1683                    h,
1684                    x1,
1685                    z,
1686                    act,
1687                    h16,
1688                    z16,
1689                    gate,
1690                    up,
1691                    ffn_out,
1692                    seg_glue,
1693                    mixed,
1694                    seg_mid,
1695                    seg_t,
1696                    ..
1697                } = slabs;
1698                x_cur = xa;
1699                x_nxt = xb;
1700                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1701                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1702            }
1703            None => {
1704                x_own = x_in;
1705                x_own2 = e.uninit(t * n_embd)?;
1706                x_cur = &mut x_own;
1707                x_nxt = &mut x_own2;
1708                sl = None;
1709            }
1710        }
1711        let mut alloc_h;
1712        let mut alloc_x1;
1713        let mut alloc_z;
1714        let mut alloc_act;
1715        let mut alloc_h16;
1716        let mut alloc_z16;
1717        let mut alloc_gate;
1718        let mut alloc_up;
1719        let mut alloc_fo;
1720        let (h, x1, z, act): (
1721            &mut CudaSlice<f32>,
1722            &mut CudaSlice<f32>,
1723            &mut CudaSlice<f32>,
1724            &mut CudaSlice<f32>,
1725        );
1726        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
1727        let (sl_gate, sl_up, sl_fo): (
1728            &mut CudaSlice<f32>,
1729            &mut CudaSlice<f32>,
1730            &mut CudaSlice<f32>,
1731        );
1732        match sl {
1733            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
1734                h = a;
1735                x1 = b;
1736                z = c;
1737                act = d;
1738                h16 = e16;
1739                z16 = f16b;
1740                sl_gate = g;
1741                sl_up = u;
1742                sl_fo = fo;
1743            }
1744            None => {
1745                alloc_h = e.uninit(t * n_embd)?;
1746                alloc_x1 = e.uninit(t * n_embd)?;
1747                alloc_z = e.uninit(t * n_embd)?;
1748                alloc_act = e.uninit(t * n_ff_max)?;
1749                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1750                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1751                alloc_gate = e.uninit(t * n_ff_max)?;
1752                alloc_up = e.uninit(t * n_ff_max)?;
1753                alloc_fo = e.uninit(t * n_embd)?;
1754                h = &mut alloc_h;
1755                x1 = &mut alloc_x1;
1756                z = &mut alloc_z;
1757                act = &mut alloc_act;
1758                h16 = &mut alloc_h16;
1759                z16 = &mut alloc_z16;
1760                sl_gate = &mut alloc_gate;
1761                sl_up = &mut alloc_up;
1762                sl_fo = &mut alloc_fo;
1763            }
1764        }
1765        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
1766        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
1767        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
1768        // first prime at this t (capture does not execute -> launch right after).
1769        let n_layers = self.layers.len();
1770        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
1771        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
1772        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
1773        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
1774        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
1775        // machinery stays (byte-identical) as their foundation.
1776        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
1777        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
1778        // step35 rides its own mixer through the normal per-layer arm below.
1779        let use_seg = f16fuse
1780            && seg.is_some()
1781            && !self.uses_sliding_gated_moe_program()
1782            && lo == 0
1783            && hi == n_layers
1784            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
1785        if let Some((sg, sm, _, st)) = seg.as_mut() {
1786            if **st != t {
1787                sg.clear();
1788                sg.extend((0..n_layers).map(|_| None));
1789                sm.clear();
1790                sm.extend((0..n_layers).map(|_| None));
1791                **st = t;
1792            }
1793        }
1794        {
1795            let layer_lo = &self.layers[lo];
1796            if f16fuse {
1797                e.rms_norm_f16out(
1798                    x_cur,
1799                    layer_lo.attn_norm.float_data(),
1800                    h,
1801                    h16,
1802                    n_embd,
1803                    t,
1804                    eps,
1805                )?;
1806            } else {
1807                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
1808            }
1809        }
1810        let anat = Self::prime_anatomy_on();
1811        let mut anat_last = if anat {
1812            e.stream().synchronize()?;
1813            Some(std::time::Instant::now())
1814        } else {
1815            None
1816        };
1817        // Closes the region that just ENDED into `slot`, restarting the clock.
1818        macro_rules! anat_mark {
1819            ($slot:expr) => {
1820                if let Some(ts) = anat_last.as_mut() {
1821                    e.stream().synchronize()?;
1822                    Self::prime_anatomy_slots()[$slot].fetch_add(
1823                        ts.elapsed().as_nanos() as u64,
1824                        std::sync::atomic::Ordering::Relaxed,
1825                    );
1826                    *ts = std::time::Instant::now();
1827                }
1828            };
1829        }
1830        for il in lo..hi {
1831            let layer = &self.layers[il];
1832            let hx16 = if f16fuse { Some(&*h16) } else { None };
1833            if use_seg {
1834                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
1835                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
1836                let (pre, pre16, w_out) = match &layer.mixer {
1837                    Mixer::Full(fa) => {
1838                        let g3 = match hx16 {
1839                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
1840                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
1841                        };
1842                        let (pre, pre16) =
1843                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
1844                        (pre, pre16, &fa.wo)
1845                    }
1846                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1847                    Mixer::Linear(la) => {
1848                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
1849                        let g4 = match hx16 {
1850                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
1851                            None => e.matmul_group(&ws, h, t)?,
1852                        };
1853                        let (pre, pre16) =
1854                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
1855                        (pre, pre16, &la.ssm_out)
1856                    }
1857                };
1858                {
1859                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
1860                    let pre_n = pre.len() / t;
1861                    let xh_pre = match pre16 {
1862                        Some(x) => x,
1863                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
1864                    };
1865                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
1866                        let y = e.matmul(w_out, &pre, t)?;
1867                        e.copy_into(mslab, 0, &y, t * n_embd)?;
1868                    }
1869                    if sm[il].is_none() {
1870                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
1871                        let w_post = layer.post_attn_norm.float_data();
1872                        e.stream().synchronize()?;
1873                        e.stream()
1874                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
1875                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
1876                            e.add(x_cur, mslab, x1, t * n_embd)?;
1877                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
1878                            Ok(())
1879                        })();
1880                        let g = e.stream().end_capture(
1881                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
1882                        r?;
1883                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
1884                    }
1885                    sm[il].as_ref().unwrap().launch()?;
1886                }
1887            } else {
1888                let mixed = match &layer.mixer {
1889                    Mixer::Full(fa) => {
1890                        let y =
1891                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
1892                        anat_mark!(0);
1893                        y
1894                    }
1895                    Mixer::Linear(la) => {
1896                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
1897                        anat_mark!(1);
1898                        y
1899                    }
1900                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1901                };
1902                if f16fuse {
1903                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
1904                    // bit-identical) — the standalone add pass disappears.
1905                    e.add_rms_norm_f16out(
1906                        x_cur,
1907                        &mixed,
1908                        layer.post_attn_norm.float_data(),
1909                        x1,
1910                        z,
1911                        z16,
1912                        n_embd,
1913                        t,
1914                        eps,
1915                    )?;
1916                } else {
1917                    e.add(x_cur, &mixed, x1, t * n_embd)?;
1918                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
1919                }
1920                anat_mark!(4);
1921            }
1922            let zx16 = if f16fuse { Some(&*z16) } else { None };
1923            match &layer.ffn {
1924                crate::hybrid::Ffn::Dense {
1925                    ffn_gate,
1926                    ffn_up,
1927                    ffn_down,
1928                } => {
1929                    let n_ff = ffn_gate.out_features();
1930                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
1931                    // the allocating group + copy when a mirror is missing.
1932                    let mut into_ok = false;
1933                    if let Some(xh) = zx16 {
1934                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
1935                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
1936                    }
1937                    if !into_ok {
1938                        let mut g2 = match zx16 {
1939                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
1940                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
1941                        };
1942                        let up_y = g2.pop().unwrap();
1943                        let gate_y = g2.pop().unwrap();
1944                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
1945                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
1946                    }
1947                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
1948                    // operand in-epilogue; non-silu activations keep the standalone convert.
1949                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
1950                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
1951                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
1952                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
1953                    {
1954                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
1955                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
1956                        Some(a16)
1957                    } else {
1958                        Self::ffn_act_lim(
1959                            e,
1960                            &self.cfg,
1961                            sl_gate,
1962                            sl_up,
1963                            1.0,
1964                            1.0,
1965                            d_lim,
1966                            act,
1967                            t * n_ff,
1968                        )?;
1969                        None
1970                    };
1971                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
1972                    let xh_act = match act16 {
1973                        Some(x) => x,
1974                        None => e.f16_act(act, t * n_ff, n_ff)?,
1975                    };
1976                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
1977                        let y = e.matmul(ffn_down, &*act, t)?;
1978                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
1979                    }
1980                }
1981                crate::hybrid::Ffn::Moe(m) => {
1982                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
1983                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
1984                    anat_mark!(2);
1985                }
1986            }
1987            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
1988                anat_mark!(3);
1989            }
1990            if use_seg && il + 1 < hi {
1991                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
1992                let w_next = self.layers[il + 1].attn_norm.float_data();
1993                let (sg, _, _, _) = seg.as_mut().unwrap();
1994                if sg[il].is_none() {
1995                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
1996                    e.stream().synchronize()?;
1997                    e.stream()
1998                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
1999                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2000                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2001                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2002                        Ok(())
2003                    })();
2004                    let g = e.stream().end_capture(
2005                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2006                    );
2007                    r?;
2008                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2009                }
2010                sg[il].as_ref().unwrap().launch()?;
2011            } else {
2012                if il + 1 < hi {
2013                    let w_next = self.layers[il + 1].attn_norm.float_data();
2014                    if f16fuse {
2015                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2016                    } else {
2017                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2018                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2019                    }
2020                } else {
2021                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2022                }
2023            }
2024            anat_mark!(4);
2025            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2026            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2027            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2028            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2029            // unset (the default) costs one OnceLock read per layer.
2030            if let Some(path) = Self::prime_trace_path() {
2031                let row = (base + t - 1) as usize;
2032                let host = e.dtoh(x_nxt)?;
2033                let last = &host[(t - 1) * n_embd..t * n_embd];
2034                use std::io::Write as _;
2035                let mut f = std::fs::OpenOptions::new()
2036                    .create(true)
2037                    .append(true)
2038                    .open(path)?;
2039                let mut h64: u64 = 0xcbf29ce484222325;
2040                for v in last {
2041                    h64 ^= v.to_bits() as u64;
2042                    h64 = h64.wrapping_mul(0x100000001b3);
2043                }
2044                writeln!(
2045                    f,
2046                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2047                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2048                    last[0], last[1], last[2]
2049                )?;
2050            }
2051            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2052            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2053            self.dflash_tap(e, cache, il, x_nxt, t)?;
2054            std::mem::swap(&mut x_cur, &mut x_nxt);
2055        }
2056        if anat {
2057            let s = Self::prime_anatomy_slots();
2058            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2059            eprintln!(
2060                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2061                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2062                ms(0),
2063                ms(1),
2064                ms(2),
2065                ms(3),
2066                ms(4)
2067            );
2068        }
2069        // hidden-stack return: clone the final x out of the slab
2070        let mut x = e.uninit(t * n_embd)?;
2071        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2072        drop(slab_guard);
2073        Ok(x)
2074    }
2075
2076    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2077    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2078    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2079    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2080    fn prime_chunk_epilogue(
2081        &self,
2082        e: &Engine,
2083        x: CudaSlice<f32>,
2084        t: usize,
2085        cache: &mut Cache,
2086    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2087        let n_embd = self.cfg.n_embd as usize;
2088        let eps = self.cfg.rms_eps;
2089        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2090        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2091        // the post-norm copy happens after hn exists).
2092        let mut h_seed = e.uninit(n_embd)?;
2093        if !crate::spec::spec_hpost() {
2094            e.copy_view_into(
2095                &mut h_seed,
2096                0,
2097                &x.slice((t - 1) * n_embd..t * n_embd),
2098                n_embd,
2099            )?;
2100        }
2101        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2102        let mut hn = e.uninit(t * n_embd)?;
2103        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2104        if crate::spec::spec_hpost() {
2105            e.copy_view_into(
2106                &mut h_seed,
2107                0,
2108                &hn.slice((t - 1) * n_embd..t * n_embd),
2109                n_embd,
2110            )?;
2111        }
2112        let last = e.view(&hn, t * n_embd);
2113        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2114        let mut hlast = e.uninit(n_embd)?;
2115        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2116        let logits = e.matmul(&self.output, &hlast, 1)?;
2117        cache.pos += t;
2118        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2119        // post-norm stack hn (MEMRA_SPEC_HPOST).
2120        Ok((
2121            e.dtoh(&logits)?,
2122            h_seed,
2123            if crate::spec::spec_hpost() { hn } else { x },
2124        ))
2125    }
2126
2127    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2128    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2129    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2130    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2131    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2132    /// prefill kernels. Structure mirrors the verify split exactly:
2133    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2134    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2135    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2136    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2137    ///                  there via the sharded loader) → `publish_to`
2138    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2139    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2140    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2141    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2142    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2143    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2144    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2145    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2146    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2147    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2148    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2149    /// and its liveness counter is bumped here — the gate goes green with this function.
2150    fn prime_chunk_ppn(
2151        &self,
2152        e: &Engine,
2153        tokens: &[u32],
2154        cache: &mut Cache,
2155        seq_end: usize,
2156        fence: &[usize],
2157    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2158        let rt = crate::pp::PpNRt::get(e)?;
2159        let n_st = fence.len() - 1;
2160        assert_eq!(
2161            rt.n_stages(),
2162            n_st,
2163            "PpNRt stage count {} != fence stages {n_st}",
2164            rt.n_stages()
2165        );
2166        let n_embd = self.cfg.n_embd as usize;
2167        let t = tokens.len();
2168        let base = cache.pos;
2169        debug_assert!(
2170            seq_end >= base + t,
2171            "prime_chunk_ppn: seq_end must cover this chunk"
2172        );
2173        let payload = t * n_embd;
2174        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2175        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2176        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2177        let caller_stream = e.stream();
2178        rt.fence_stages_behind(&caller_stream)?;
2179
2180        if n_st == 2 {
2181            let slot =
2182                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2183            let x =
2184                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2185            let out = {
2186                rt.bind_stage(1)?;
2187                let _st1 = rt.enter(1);
2188                let e1 = rt.engine(1, e);
2189                self.prime_chunk_epilogue(e1, x, t, cache)?
2190            };
2191            rt.publish_to(1, &caller_stream)?;
2192            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2193            return Ok(out);
2194        }
2195
2196        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2197
2198        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2199        let mut slot = {
2200            let _st0 = rt.enter(0);
2201            let e0 = rt.engine(0, e);
2202            let pos_d = e0.htod_i32(&pos)?;
2203            let x = self.embed(e0, tokens)?;
2204            let x =
2205                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2206            rt.tx(0, &x, payload)?
2207            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2208        };
2209
2210        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2211        for s in 1..n_st - 1 {
2212            let _st = rt.enter(s);
2213            let es = rt.engine(s, e);
2214            let pos_d = es.htod_i32(&pos)?;
2215            let x = rt.rx(s - 1, slot, payload)?;
2216            let x = self.prime_layers(
2217                es,
2218                x,
2219                fence[s],
2220                fence[s + 1],
2221                &pos_d,
2222                t,
2223                base,
2224                cache,
2225                seq_end,
2226            )?;
2227            slot = rt.tx(s, &x, payload)?;
2228        }
2229
2230        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2231        let _stl = rt.enter(n_st - 1);
2232        let el = rt.engine(n_st - 1, e);
2233        let pos_d = el.htod_i32(&pos)?;
2234        let x = rt.rx(n_st - 2, slot, payload)?;
2235        let x = self.prime_layers(
2236            el,
2237            x,
2238            fence[n_st - 1],
2239            fence[n_st],
2240            &pos_d,
2241            t,
2242            base,
2243            cache,
2244            seq_end,
2245        )?;
2246        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2247        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2248        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2249        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2250        // stage stream host-side, but the law is stated in events, not in a dtoh side
2251        // effect a later deferred form would remove.
2252        rt.publish_to(n_st - 1, &caller_stream)?;
2253        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2254        Ok(out)
2255    }
2256
2257    fn prime_pp2_stage0_enqueue(
2258        &self,
2259        e: &Engine,
2260        rt: &crate::pp::PpNRt,
2261        tokens: &[u32],
2262        cache: &mut Cache,
2263        seq_end: usize,
2264        fence: &[usize],
2265        base: usize,
2266        pipelined: bool,
2267    ) -> Result<usize, Box<dyn std::error::Error>> {
2268        let t = tokens.len();
2269        let n_embd = self.cfg.n_embd as usize;
2270        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2271        rt.bind_stage(0)?;
2272        let _st0 = rt.enter(0);
2273        let e0 = rt.engine(0, e);
2274        let pos_d = e0.htod_i32(&pos)?;
2275        let x = self.embed(e0, tokens)?;
2276        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2277        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2278        if pipelined {
2279            rt.tx_pipelined(0, &x, t * n_embd)
2280        } else {
2281            rt.tx(0, &x, t * n_embd)
2282        }
2283    }
2284
2285    fn prime_pp2_stage1_enqueue(
2286        &self,
2287        e: &Engine,
2288        rt: &crate::pp::PpNRt,
2289        slot: usize,
2290        t: usize,
2291        cache: &mut Cache,
2292        seq_end: usize,
2293        fence: &[usize],
2294        base: usize,
2295        pipelined: bool,
2296    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2297        let n_embd = self.cfg.n_embd as usize;
2298        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2299        rt.bind_stage(1)?;
2300        let _st1 = rt.enter(1);
2301        let e1 = rt.engine(1, e);
2302        let pos_d = e1.htod_i32(&pos)?;
2303        let x = rt.rx(0, slot, t * n_embd)?;
2304        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2305        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2306    }
2307
2308    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2309    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2310    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2311    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2312    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2313    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2314    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2315    /// bookkeeping still runs on the host per call — the real replay path moves the write
2316    /// slot to the len_d device counter (increment 3).
2317    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2318    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2319    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2320    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2321    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2322    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2323    pub fn prime_chunk_captured(
2324        &self,
2325        e: &Engine,
2326        x_in: &CudaSlice<f32>,
2327        pos_d: &CudaSlice<i32>,
2328        t: usize,
2329        cache: &mut Cache,
2330        len_d: &CudaSlice<i32>,
2331        logits_out: &mut CudaSlice<f32>,
2332        h_seed_out: &mut CudaSlice<f32>,
2333    ) -> Result<(), Box<dyn std::error::Error>> {
2334        let cfg = &self.cfg;
2335        let n_embd = cfg.n_embd as usize;
2336        let eps = cfg.rms_eps;
2337        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2338        let mut x = e.uninit(t * n_embd)?;
2339        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2340        for (il, layer) in self.layers.iter().enumerate() {
2341            let mut h = e.uninit(t * n_embd)?;
2342            let mut hx16: Option<CudaSlice<u8>> = None;
2343            if f16fuse {
2344                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2345                e.rms_norm_f16out(
2346                    &x,
2347                    layer.attn_norm.float_data(),
2348                    &mut h,
2349                    &mut b16,
2350                    n_embd,
2351                    t,
2352                    eps,
2353                )?;
2354                hx16 = Some(b16);
2355            } else {
2356                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2357            }
2358            let mixed = match &layer.mixer {
2359                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2360                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2361                // come from the caller (see step35_attn_pre_wo's doc note).
2362                Mixer::Full(fa) => {
2363                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2364                }
2365                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2366                Mixer::Linear(la) => {
2367                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2368                    let g4 = match hx16.as_ref() {
2369                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2370                        None => e.matmul_group(&ws, &h, t)?,
2371                    };
2372                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2373                }
2374            };
2375            let mut x1 = e.uninit(t * n_embd)?;
2376            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2377            let mut z = e.uninit(t * n_embd)?;
2378            let mut zx16: Option<CudaSlice<u8>> = None;
2379            if f16fuse {
2380                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2381                e.rms_norm_f16out(
2382                    &x1,
2383                    layer.post_attn_norm.float_data(),
2384                    &mut z,
2385                    &mut b16,
2386                    n_embd,
2387                    t,
2388                    eps,
2389                )?;
2390                zx16 = Some(b16);
2391            } else {
2392                e.rms_norm(
2393                    &x1,
2394                    layer.post_attn_norm.float_data(),
2395                    &mut z,
2396                    n_embd,
2397                    t,
2398                    eps,
2399                )?;
2400            }
2401            let ffn_out = match &layer.ffn {
2402                crate::hybrid::Ffn::Dense {
2403                    ffn_gate,
2404                    ffn_up,
2405                    ffn_down,
2406                } => {
2407                    let n_ff = ffn_gate.out_features();
2408                    let mut g2 = match &zx16 {
2409                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2410                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2411                    };
2412                    let up = g2.pop().unwrap();
2413                    let gate = g2.pop().unwrap();
2414                    let mut act = e.uninit(t * n_ff)?;
2415                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2416                    Self::ffn_act_lim(
2417                        e,
2418                        &self.cfg,
2419                        &gate,
2420                        &up,
2421                        1.0,
2422                        1.0,
2423                        self.cfg.clamp_shexp_at(il as u32),
2424                        &mut act,
2425                        t * n_ff,
2426                    )?;
2427                    e.matmul(ffn_down, &act, t)?
2428                }
2429                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2430            };
2431            let mut x2 = e.uninit(t * n_embd)?;
2432            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2433            x = x2;
2434        }
2435        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2436        if !crate::spec::spec_hpost() {
2437            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2438        }
2439        let mut hn = e.uninit(t * n_embd)?;
2440        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2441        if crate::spec::spec_hpost() {
2442            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2443        }
2444        let mut hlast = e.uninit(n_embd)?;
2445        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2446        let logits = e.matmul(&self.output, &hlast, 1)?;
2447        let nv = logits.len();
2448        e.copy_into(logits_out, 0, &logits, nv)?;
2449        Ok(())
2450    }
2451
2452    fn step35_prime_batch_on() -> bool {
2453        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2454    }
2455
2456    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2457    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2458    #[allow(clippy::too_many_arguments)]
2459    fn step35_prime_batch_layers(
2460        &self,
2461        e: &Engine,
2462        mut x: CudaSlice<f32>,
2463        lo: usize,
2464        hi: usize,
2465        ts: &[usize],
2466        offs: &[usize],
2467        pos_ds: &[CudaSlice<i32>],
2468        caches: &mut [&mut Cache],
2469    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2470        let cfg = &self.cfg;
2471        let n_embd = cfg.n_embd as usize;
2472        let eps = cfg.rms_eps;
2473        let b = ts.len();
2474        let total: usize = ts.iter().sum();
2475        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2476
2477        let split = |e: &Engine,
2478                     y: &CudaSlice<f32>,
2479                     dim: usize|
2480         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2481            let mut out = Vec::with_capacity(b);
2482            for s in 0..b {
2483                let mut ys = e.uninit(ts[s] * dim)?;
2484                e.copy_view_into(
2485                    &mut ys,
2486                    0,
2487                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2488                    ts[s] * dim,
2489                )?;
2490                out.push(ys);
2491            }
2492            Ok(out)
2493        };
2494
2495        for il in lo..hi {
2496            let layer = &self.layers[il];
2497            let Mixer::Full(fa) = &layer.mixer else {
2498                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2499            };
2500
2501            let mut h = e.uninit(total * n_embd)?;
2502            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2503            if f16fuse {
2504                e.rms_norm_f16out(
2505                    &x,
2506                    layer.attn_norm.float_data(),
2507                    &mut h,
2508                    &mut hx16,
2509                    n_embd,
2510                    total,
2511                    eps,
2512                )?;
2513            } else {
2514                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2515            }
2516
2517            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2518            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2519            // application stay verbatim.
2520            let gate_w = fa
2521                .attn_gate
2522                .as_ref()
2523                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2524            let mut g4 = if f16fuse {
2525                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2526            } else {
2527                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2528            };
2529            let gate = g4.pop().unwrap();
2530            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2531                (0..b).map(|_| Vec::with_capacity(3)).collect();
2532            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2533                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2534                    parts[s].push(ys);
2535                }
2536            }
2537            let gates = split(e, &gate, gate_w.out_features())?;
2538            let geometry = self.step35_geom(il);
2539            let hd = geometry.head_dim_k as usize;
2540            let nh = geometry.n_head as usize;
2541            let mut ag_cat = e.uninit(total * nh * hd)?;
2542            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2543                let ag = self.step35_attn_pre_wo(
2544                    e,
2545                    fa,
2546                    g3s,
2547                    None,
2548                    Some(&gate),
2549                    &pos_ds[s],
2550                    ts[s],
2551                    Some(&mut *caches[s]),
2552                    il,
2553                    ts[s],
2554                )?;
2555                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2556            }
2557            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2558
2559            let mut x1 = e.uninit(total * n_embd)?;
2560            let mut z = e.uninit(total * n_embd)?;
2561            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2562            if f16fuse {
2563                e.add_rms_norm_f16out(
2564                    &x,
2565                    &mixed,
2566                    layer.post_attn_norm.float_data(),
2567                    &mut x1,
2568                    &mut z,
2569                    &mut zx16,
2570                    n_embd,
2571                    total,
2572                    eps,
2573                )?;
2574            } else {
2575                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2576                e.rms_norm(
2577                    &x1,
2578                    layer.post_attn_norm.float_data(),
2579                    &mut z,
2580                    n_embd,
2581                    total,
2582                    eps,
2583                )?;
2584            }
2585
2586            let ffn_out = match &layer.ffn {
2587                crate::hybrid::Ffn::Dense {
2588                    ffn_gate,
2589                    ffn_up,
2590                    ffn_down,
2591                } => {
2592                    let n_ff = ffn_gate.out_features();
2593                    let mut g2 = if f16fuse {
2594                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2595                    } else {
2596                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2597                    };
2598                    let up = g2.pop().unwrap();
2599                    let gate = g2.pop().unwrap();
2600                    let mut act = e.uninit(total * n_ff)?;
2601                    let d_lim = cfg.clamp_shexp_at(il as u32);
2602                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2603                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2604                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2605                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2606                            Some(y) => y,
2607                            None => e.matmul(ffn_down, &act, total)?,
2608                        }
2609                    } else {
2610                        Self::ffn_act_lim(
2611                            e,
2612                            cfg,
2613                            &gate,
2614                            &up,
2615                            1.0,
2616                            1.0,
2617                            d_lim,
2618                            &mut act,
2619                            total * n_ff,
2620                        )?;
2621                        e.matmul(ffn_down, &act, total)?
2622                    }
2623                }
2624                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2625            };
2626            let mut x2 = e.uninit(total * n_embd)?;
2627            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2628            x = x2;
2629        }
2630        Ok(x)
2631    }
2632
2633    fn step35_prime_batch_epilogue(
2634        &self,
2635        e: &Engine,
2636        x: CudaSlice<f32>,
2637        ts: &[usize],
2638        offs: &[usize],
2639        caches: &mut [&mut Cache],
2640    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2641        let n_embd = self.cfg.n_embd as usize;
2642        let total: usize = ts.iter().sum();
2643        let mut hn = e.uninit(total * n_embd)?;
2644        e.rms_norm(
2645            &x,
2646            self.output_norm.float_data(),
2647            &mut hn,
2648            n_embd,
2649            total,
2650            self.cfg.rms_eps,
2651        )?;
2652
2653        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2654        let mut out = Vec::with_capacity(ts.len());
2655        for s in 0..ts.len() {
2656            let mut hidden = e.uninit(ts[s] * n_embd)?;
2657            e.copy_view_into(
2658                &mut hidden,
2659                0,
2660                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
2661                ts[s] * n_embd,
2662            )?;
2663            let last0 = (offs[s] + ts[s] - 1) * n_embd;
2664            let mut h_seed = e.uninit(n_embd)?;
2665            e.copy_view_into(
2666                &mut h_seed,
2667                0,
2668                &hidden_src.slice(last0..last0 + n_embd),
2669                n_embd,
2670            )?;
2671            // Exactness-first: the serial reference runs the output head at m=1.
2672            let mut hlast = e.uninit(n_embd)?;
2673            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
2674            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
2675            caches[s].pos += ts[s];
2676            out.push((logits, h_seed, hidden));
2677        }
2678        Ok(out)
2679    }
2680
2681    fn step35_prime_cache_batch(
2682        &self,
2683        e: &Engine,
2684        prompts: &[&[u32]],
2685        caches: &mut [&mut Cache],
2686    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2687        validate_step_prime_batch_modes(
2688            step_tp_prefill_enabled()?,
2689            step_ep_grouped_prefill_enabled()?,
2690        )?;
2691        if crate::pp::pp_host_bounce_active()
2692            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
2693        {
2694            return Err(
2695                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
2696                 stage split; refusing an unsplit remote-weight walk"
2697                    .into(),
2698            );
2699        }
2700        if !Self::step35_prime_batch_on() {
2701            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
2702        }
2703        if caches.iter().any(|c| c.pos != 0) {
2704            return Err(
2705                "step35 batched prime currently supports complete fresh prompts only; \
2706                 continuation/tick chunks require per-request queued_after"
2707                    .into(),
2708            );
2709        }
2710
2711        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2712        for &t in &ts {
2713            assert!(
2714                t >= PRIME_MIN_T,
2715                "step35 batched prime needs T >= {PRIME_MIN_T}"
2716            );
2717        }
2718        for (s, c) in caches.iter().enumerate() {
2719            assert!(
2720                ts[s] <= c.max_ctx,
2721                "step35 batched prime exceeds cache max_ctx"
2722            );
2723        }
2724        let offs: Vec<usize> = ts
2725            .iter()
2726            .scan(0usize, |a, &t| {
2727                let o = *a;
2728                *a += t;
2729                Some(o)
2730            })
2731            .collect();
2732        let total: usize = ts.iter().sum();
2733        let payload = total * self.cfg.n_embd as usize;
2734        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2735        let positions: Vec<Vec<i32>> = ts.iter().map(|&t| (0..t as i32).collect()).collect();
2736        let upload_positions =
2737            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
2738                positions
2739                    .iter()
2740                    .map(|p| e.htod_i32(p))
2741                    .collect::<Result<_, _>>()
2742            };
2743
2744        static ONCE: std::sync::Once = std::sync::Once::new();
2745        ONCE.call_once(|| {
2746            eprintln!(
2747                "[step35-prime-batch] first concat prime: B={} tokens={total}",
2748                prompts.len()
2749            );
2750        });
2751
2752        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
2753            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
2754                let rt = crate::pp::PpNRt::get(e)?;
2755                let n_st = fence.len() - 1;
2756                assert_eq!(
2757                    rt.n_stages(),
2758                    n_st,
2759                    "step35 prime batch stage count mismatch"
2760                );
2761                let caller_stream = e.stream();
2762                rt.fence_stages_behind(&caller_stream)?;
2763
2764                let mut slot = {
2765                    let _st0 = rt.enter(0);
2766                    let e0 = rt.engine(0, e);
2767                    let pos_ds = upload_positions(e0)?;
2768                    let x = self.embed(e0, &cat_tokens)?;
2769                    let x = self.step35_prime_batch_layers(
2770                        e0, x, fence[0], fence[1], &ts, &offs, &pos_ds, caches,
2771                    )?;
2772                    rt.tx(0, &x, payload)?
2773                };
2774                for s in 1..n_st - 1 {
2775                    let _st = rt.enter(s);
2776                    let es = rt.engine(s, e);
2777                    let pos_ds = upload_positions(es)?;
2778                    let x = rt.rx(s - 1, slot, payload)?;
2779                    let x = self.step35_prime_batch_layers(
2780                        es,
2781                        x,
2782                        fence[s],
2783                        fence[s + 1],
2784                        &ts,
2785                        &offs,
2786                        &pos_ds,
2787                        caches,
2788                    )?;
2789                    slot = rt.tx(s, &x, payload)?;
2790                }
2791
2792                let _stl = rt.enter(n_st - 1);
2793                let el = rt.engine(n_st - 1, e);
2794                let pos_ds = upload_positions(el)?;
2795                let x = rt.rx(n_st - 2, slot, payload)?;
2796                let x = self.step35_prime_batch_layers(
2797                    el,
2798                    x,
2799                    fence[n_st - 1],
2800                    fence[n_st],
2801                    &ts,
2802                    &offs,
2803                    &pos_ds,
2804                    caches,
2805                )?;
2806                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
2807                rt.publish_to(n_st - 1, &caller_stream)?;
2808                crate::pp::STEP35_PRIME_BATCH_SPLITS
2809                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2810                out
2811            } else {
2812                let pos_ds = upload_positions(e)?;
2813                let x = self.embed(e, &cat_tokens)?;
2814                let x = self.step35_prime_batch_layers(
2815                    e,
2816                    x,
2817                    0,
2818                    self.layers.len(),
2819                    &ts,
2820                    &offs,
2821                    &pos_ds,
2822                    caches,
2823                )?;
2824                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2825            }
2826        } else {
2827            let pos_ds = upload_positions(e)?;
2828            let x = self.embed(e, &cat_tokens)?;
2829            let x = self.step35_prime_batch_layers(
2830                e,
2831                x,
2832                0,
2833                self.layers.len(),
2834                &ts,
2835                &offs,
2836                &pos_ds,
2837                caches,
2838            )?;
2839            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2840        };
2841        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2842        Ok(out)
2843    }
2844
2845    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
2846    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
2847    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
2848    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
2849    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
2850    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
2851    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
2852    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
2853    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
2854    /// over the quantized past; Linear: the stateful pad_view twin — the same state
2855    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
2856    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
2857    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
2858    /// back to single-chunk serving).
2859    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
2860    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
2861    pub fn prime_cache_batch(
2862        &self,
2863        e: &Engine,
2864        prompts: &[&[u32]],
2865        caches: &mut [&mut Cache],
2866    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2867        if crate::pp::pp_cuts(self.layers.len()).is_some()
2868            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
2869        {
2870            return Err("pipeline rewrite is not qualified for batched prime".into());
2871        }
2872        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
2873            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
2874                return Err("neither batched-prime nor eager rewrite is qualified".into());
2875            }
2876            if prompts.len() != caches.len() {
2877                return Err("prime fallback prompt/cache shape mismatch".into());
2878            }
2879            static ONCE: std::sync::Once = std::sync::Once::new();
2880            ONCE.call_once(|| {
2881                eprintln!(
2882                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
2883                );
2884            });
2885            return prompts
2886                .iter()
2887                .copied()
2888                .zip(caches.iter_mut())
2889                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
2890                .collect();
2891        }
2892        let cfg = &self.cfg;
2893        let n_embd = cfg.n_embd as usize;
2894        let eps = cfg.rms_eps;
2895        let b = prompts.len();
2896        assert!(b >= 1 && b == caches.len());
2897        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
2898        let carried = pos0s.iter().any(|&p| p > 0);
2899        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
2900        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
2901        // generic concat attn core below (uniform geometry, no per-layer swa window, no
2902        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
2903        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
2904        if self.uses_gemma_program() {
2905            return Err(
2906                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
2907                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
2908                    .into(),
2909            );
2910        }
2911        // Step35 has a dedicated concat walk: the generic core below cannot express its
2912        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
2913        if self.uses_sliding_gated_moe_program() {
2914            return self.step35_prime_cache_batch(e, prompts, caches);
2915        }
2916        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2917        for &t in &ts {
2918            assert!(
2919                t >= PRIME_MIN_T,
2920                "prime_cache_batch needs T >= {PRIME_MIN_T}"
2921            );
2922        }
2923        for (s, c) in caches.iter().enumerate() {
2924            assert!(
2925                c.pos + ts[s] <= c.max_ctx,
2926                "prime_cache_batch: prompt exceeds cache max_ctx"
2927            );
2928        }
2929        let total: usize = ts.iter().sum();
2930        let offs: Vec<usize> = ts
2931            .iter()
2932            .scan(0usize, |a, &t| {
2933                let o = *a;
2934                *a += t;
2935                Some(o)
2936            })
2937            .collect();
2938        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
2939        let pos_ds: Vec<CudaSlice<i32>> = ts
2940            .iter()
2941            .zip(&pos0s)
2942            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
2943            .collect::<Result<_, _>>()?;
2944        // split a concat [total, dim] buffer into per-seq copies
2945        let split = |e: &Engine,
2946                     y: &CudaSlice<f32>,
2947                     dim: usize|
2948         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2949            let mut out = Vec::with_capacity(b);
2950            for s in 0..b {
2951                let mut ys = e.uninit(ts[s] * dim)?;
2952                e.copy_view_into(
2953                    &mut ys,
2954                    0,
2955                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2956                    ts[s] * dim,
2957                )?;
2958                out.push(ys);
2959            }
2960            Ok(out)
2961        };
2962
2963        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2964        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
2965        for (il, layer) in self.layers.iter().enumerate() {
2966            let mut h = e.uninit(total * n_embd)?;
2967            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2968            e.rms_norm_f16out(
2969                &x,
2970                layer.attn_norm.float_data(),
2971                &mut h,
2972                &mut hx16,
2973                n_embd,
2974                total,
2975                eps,
2976            )?;
2977            // mixer: projection GROUP on the concat (m = total), stateful core per seq
2978            let mut mixed = e.uninit(total * n_embd)?;
2979            match &layer.mixer {
2980                Mixer::Full(fa) => {
2981                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
2982                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
2983                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
2984                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
2985                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
2986                    // back to the per-seq dispatch.
2987                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
2988                    let (n_head, n_head_kv, head_dim) = (
2989                        geometry.n_head as usize,
2990                        geometry.n_head_kv as usize,
2991                        geometry.head_dim_k as usize,
2992                    );
2993                    let fa_scale = geometry.attention_scale();
2994                    let use_favl = !carried
2995                        && (2..=8).contains(&b)
2996                        && (head_dim == 256 || head_dim == 128)
2997                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
2998                        && std::env::var("MEMRA_NOFA").is_err()
2999                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3000                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3001                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3002                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3003                    if use_favl {
3004                        let (qf_w, kf_w, vf_w) = (
3005                            fa.wq.out_features(),
3006                            fa.wk.out_features(),
3007                            fa.wv.out_features(),
3008                        );
3009                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3010                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3011                        // cannot check its own extents; `qf_w` is the wq out-features that set
3012                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3013                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3014                        struct APre {
3015                            q: CudaSlice<f32>,
3016                            gate: Option<CudaSlice<f32>>,
3017                            qn: CudaSlice<f32>,
3018                            kn: CudaSlice<f32>,
3019                        }
3020                        let mut aps = Vec::with_capacity(b);
3021                        for &t in ts.iter().take(b) {
3022                            aps.push(APre {
3023                                q: e.uninit(t * n_head * head_dim)?,
3024                                gate: Some(e.uninit(t * n_head * head_dim)?),
3025                                qn: e.uninit(t * n_head * head_dim)?,
3026                                kn: e.uninit(t * n_head_kv * head_dim)?,
3027                            });
3028                        }
3029                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3030                            let kvl = caches[0].kv[il].as_ref().unwrap();
3031                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3032                        };
3033                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3034                            .map(|s| {
3035                                let (o, t) = (offs[s], ts[s]);
3036                                let kvl = caches[s].kv[il].as_ref().unwrap();
3037                                assert!(
3038                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3039                                    "prime_cache_batch attn vl: fresh + capacity"
3040                                );
3041                                crate::AttnPreVl {
3042                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3043                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3044                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3045                                    q: e.addr_f32(&aps[s].q),
3046                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3047                                    qn: e.addr_f32(&aps[s].qn),
3048                                    kn: e.addr_f32(&aps[s].kn),
3049                                    kc: e.addr_u8(&kvl.k),
3050                                    vc: e.addr_u8(&kvl.v),
3051                                    t: t as i32,
3052                                    pad: 0,
3053                                }
3054                            })
3055                            .collect();
3056                        e.attn_pre_vl8(
3057                            &pargs,
3058                            fa.q_norm.float_data(),
3059                            fa.k_norm.float_data(),
3060                            head_dim,
3061                            geometry.n_rot as usize,
3062                            n_head,
3063                            n_head_kv,
3064                            self.cfg.rms_eps,
3065                            geometry.rope_base,
3066                            1.0,
3067                            kv_dim_k,
3068                            kv_dim_v,
3069                            ktb,
3070                            vtb,
3071                        )?;
3072                        for s in 0..b {
3073                            let kvl = caches[s].kv[il].as_mut().unwrap();
3074                            kvl.len += ts[s];
3075                            let new_len = kvl.len as i32;
3076                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3077                        }
3078                        let mut attns = Vec::with_capacity(b);
3079                        let mut mirrors = Vec::with_capacity(b);
3080                        for &t in ts.iter().take(b) {
3081                            attns.push(e.uninit(t * n_head * head_dim)?);
3082                            let n = t * n_head_kv * head_dim;
3083                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3084                        }
3085                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3086                        // promoted single-seq config is on; else the mma favl.
3087                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3088                            Ok("0") => false,
3089                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3090                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3091                            // portable build.
3092                            Ok("1") => {
3093                                crate::refuse_portable_force(
3094                                    "MEMRA_FA3=1",
3095                                    "the sm_90a fa3/bf16 kernels",
3096                                );
3097                                true
3098                            }
3099                            _ => cfg!(memra_hopper_mma),
3100                        };
3101                        if fa3_on {
3102                            let mut q16s = Vec::with_capacity(b);
3103                            let mut v16s = Vec::with_capacity(b);
3104                            for s in 0..b {
3105                                let t = ts[s];
3106                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3107                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3108                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3109                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3110                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3111                                e.f32_to_bf16_v(
3112                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3113                                    &mut v16,
3114                                    t * n_head_kv * head_dim,
3115                                )?;
3116                                q16s.push(q16);
3117                                v16s.push((k16, v16));
3118                            }
3119                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3120                            let mut kp = qp;
3121                            let mut vp = qp;
3122                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3123                            let mut tsv = [0i32; 8];
3124                            for s in 0..b {
3125                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3126                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3127                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3128                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3129                                tsv[s] = ts[s] as i32;
3130                            }
3131                            let rc = unsafe {
3132                                crate::fa3_vl_raw(
3133                                    qp.as_ptr(),
3134                                    kp.as_ptr(),
3135                                    vp.as_ptr(),
3136                                    op.as_ptr(),
3137                                    tsv.as_ptr(),
3138                                    b as i32,
3139                                    n_head as i32,
3140                                    n_head_kv as i32,
3141                                    head_dim as i32,
3142                                    fa_scale,
3143                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3144                                )
3145                            };
3146                            if rc != 0 {
3147                                return Err(format!("memra_fa3_vl rc={rc}").into());
3148                            }
3149                        } else {
3150                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3151                                .map(|s| crate::FaSeqVl {
3152                                    q: e.addr_f32(&aps[s].qn),
3153                                    k16: e.addr_u8(&mirrors[s].0),
3154                                    v16: e.addr_u8(&mirrors[s].1),
3155                                    o: e.addr_f32(&attns[s]),
3156                                    kf: e.addr_f32(&aps[s].kn),
3157                                    vf: e.addr_f32v(
3158                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3159                                    ),
3160                                    t: ts[s] as i32,
3161                                    pad: 0,
3162                                })
3163                                .collect();
3164                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3165                        }
3166                        for (s, attn) in attns.into_iter().enumerate() {
3167                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3168                                e,
3169                                attn,
3170                                &aps[s].gate,
3171                                ts[s],
3172                                n_head,
3173                                head_dim,
3174                            )?;
3175                            let mut done = false;
3176                            if let Some(xh) = &ag16 {
3177                                done = e.try_f16_gemm_pre_into_off(
3178                                    &fa.wo,
3179                                    xh,
3180                                    ts[s],
3181                                    &mut mixed,
3182                                    offs[s] * n_embd,
3183                                )?;
3184                            }
3185                            if !done {
3186                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3187                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3188                            }
3189                        }
3190                    } else {
3191                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3192                            (0..b).map(|_| Vec::new()).collect();
3193                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3194                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3195                                parts[s].push(ys);
3196                            }
3197                        }
3198                        for (s, g3s) in parts.into_iter().enumerate() {
3199                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3200                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3201                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3202                            )?;
3203                            let mut done = false;
3204                            if let Some(xh) = &ag16 {
3205                                done = e.try_f16_gemm_pre_into_off(
3206                                    &fa.wo,
3207                                    xh,
3208                                    ts[s],
3209                                    &mut mixed,
3210                                    offs[s] * n_embd,
3211                                )?;
3212                            }
3213                            if !done {
3214                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3215                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3216                            }
3217                        }
3218                    }
3219                }
3220                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3221                Mixer::Linear(la) => {
3222                    // task #16: NO split copies (cores read row-offset views of the concat
3223                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3224                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3225                    // varlen K5 launch for all sequences.
3226                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3227                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3228                    let outs =
3229                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3230                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3231                        let (o, t) = (offs[s], ts[s]);
3232                        let mut done = false;
3233                        if let Some(xh) = &gn16 {
3234                            done = e.try_f16_gemm_pre_into_off(
3235                                &la.ssm_out,
3236                                xh,
3237                                t,
3238                                &mut mixed,
3239                                o * n_embd,
3240                            )?;
3241                        }
3242                        if !done {
3243                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3244                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3245                        }
3246                    }
3247                }
3248            }
3249            let mut x1 = e.uninit(total * n_embd)?;
3250            let mut z = e.uninit(total * n_embd)?;
3251            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3252            e.add_rms_norm_f16out(
3253                &x,
3254                &mixed,
3255                layer.post_attn_norm.float_data(),
3256                &mut x1,
3257                &mut z,
3258                &mut zx16,
3259                n_embd,
3260                total,
3261                eps,
3262            )?;
3263            let ffn_out = match &layer.ffn {
3264                crate::hybrid::Ffn::Dense {
3265                    ffn_gate,
3266                    ffn_up,
3267                    ffn_down,
3268                } => {
3269                    let n_ff = ffn_gate.out_features();
3270                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3271                    let up = g2.pop().unwrap();
3272                    let gate = g2.pop().unwrap();
3273                    let mut act = e.uninit(total * n_ff)?;
3274                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3275                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3276                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3277                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3278                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3279                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3280                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3281                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3282                            Some(y) => y,
3283                            None => e.matmul(ffn_down, &act, total)?,
3284                        }
3285                    } else {
3286                        Self::ffn_act_lim(
3287                            e,
3288                            &self.cfg,
3289                            &gate,
3290                            &up,
3291                            1.0,
3292                            1.0,
3293                            d_lim,
3294                            &mut act,
3295                            total * n_ff,
3296                        )?;
3297                        e.matmul(ffn_down, &act, total)?
3298                    }
3299                }
3300                crate::hybrid::Ffn::Moe(m) => {
3301                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3302                }
3303            };
3304            let mut x2 = e.uninit(total * n_embd)?;
3305            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3306            x = x2;
3307        }
3308        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3309        let mut hn = e.uninit(total * n_embd)?;
3310        e.rms_norm(
3311            &x,
3312            self.output_norm.float_data(),
3313            &mut hn,
3314            n_embd,
3315            total,
3316            eps,
3317        )?;
3318        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3319        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3320        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3321        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3322        // argmax battery arbitrates, same as every other prefill GEMM change.
3323        let mut hcat = e.uninit(b * n_embd)?;
3324        for s in 0..b {
3325            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3326            e.copy_view_into(
3327                &mut hcat,
3328                s * n_embd,
3329                &hn.slice(last0..last0 + n_embd),
3330                n_embd,
3331            )?;
3332        }
3333        let logits_cat = if b >= 2 {
3334            e.try_f16_gemm(&self.output, &hcat, b)?
3335        } else {
3336            None
3337        };
3338        let logits_host: Option<Vec<f32>> = match &logits_cat {
3339            Some(lc) => Some(e.dtoh(lc)?),
3340            None => None,
3341        };
3342        let n_vocab = self.output.out_features();
3343        let mut hidden_all = if crate::spec::spec_hpost() {
3344            split(e, &hn, n_embd)?
3345        } else {
3346            split(e, &x, n_embd)?
3347        };
3348        let mut out = Vec::with_capacity(b);
3349        for s in 0..b {
3350            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3351            let mut h_seed = e.uninit(n_embd)?;
3352            if !crate::spec::spec_hpost() {
3353                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3354            } else {
3355                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3356            }
3357            let logits = match &logits_host {
3358                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3359                None => {
3360                    let mut hlast = e.uninit(n_embd)?;
3361                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3362                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3363                }
3364            };
3365            caches[s].pos += ts[s];
3366            out.push((logits, h_seed, hidden_all.remove(0)));
3367        }
3368        Ok(out)
3369    }
3370
3371    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3372    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3373    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3374    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3375    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3376    ///
3377    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3378    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3379    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3380    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3381    #[allow(clippy::too_many_arguments)]
3382    fn full_attn_prime(
3383        &self,
3384        e: &Engine,
3385        fa: &FullAttnLayer,
3386        h: &CudaSlice<f32>,
3387        hx: Option<&CudaSlice<u8>>,
3388        pos_d: &CudaSlice<i32>,
3389        t: usize,
3390        cache: &mut Cache,
3391        il: usize,
3392        seq_end: usize,
3393    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3394        if self.uses_sliding_gated_moe_program() {
3395            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3396        }
3397        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3398        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3399        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3400        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3401        let g3 = match hx {
3402            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3403            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3404        };
3405        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3406    }
3407
3408    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3409    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3410    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3411    fn full_attn_prime_core(
3412        &self,
3413        e: &Engine,
3414        fa: &FullAttnLayer,
3415        g3: Vec<CudaSlice<f32>>,
3416        pos_d: &CudaSlice<i32>,
3417        t: usize,
3418        cache: &mut Cache,
3419        il: usize,
3420    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3421        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3422        if let Some(xh) = &ag16 {
3423            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3424                return Ok(y);
3425            }
3426        }
3427        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3428    }
3429
3430    fn full_attn_prime_core_inner(
3431        &self,
3432        e: &Engine,
3433        fa: &FullAttnLayer,
3434        g3: Vec<CudaSlice<f32>>,
3435        pos_d: &CudaSlice<i32>,
3436        t: usize,
3437        cache: &mut Cache,
3438        il: usize,
3439    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3440        let cfg = &self.cfg;
3441        let geometry = cfg.full_attention_geometry_at(il as u32);
3442        let n_head = geometry.n_head as usize;
3443        let n_head_kv = geometry.n_head_kv as usize;
3444        let head_dim = geometry.head_dim_k as usize;
3445        let scale = geometry.attention_scale();
3446        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3447        let AttnPre { q, k, v, gate } = pre;
3448        let mut attn = e.uninit(t * n_head * head_dim)?;
3449        self.full_attn_prime_fa_dispatch(
3450            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3451        )?;
3452        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3453    }
3454
3455    /// task #18 (attn side): projections tail through KV append — everything before the
3456    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3457    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3458    #[allow(clippy::type_complexity)]
3459    fn full_attn_prime_pre_fa(
3460        &self,
3461        e: &Engine,
3462        fa: &FullAttnLayer,
3463        mut g3: Vec<CudaSlice<f32>>,
3464        pos_d: &CudaSlice<i32>,
3465        t: usize,
3466        cache: &mut Cache,
3467        il: usize,
3468    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3469        let cfg = &self.cfg;
3470        let geometry = cfg.full_attention_geometry_at(il as u32);
3471        let n_head = geometry.n_head as usize;
3472        let n_head_kv = geometry.n_head_kv as usize;
3473        let head_dim = geometry.head_dim_k as usize;
3474        let eps = cfg.rms_eps;
3475
3476        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3477        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3478        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3479        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3480        let v = g3.pop().unwrap();
3481        let mut k = g3.pop().unwrap();
3482        let qf = g3.pop().unwrap();
3483        let (mut q, gate) = if gated {
3484            let mut q = e.uninit(t * n_head * head_dim)?;
3485            let mut gate = e.uninit(t * n_head * head_dim)?;
3486            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3487            (q, Some(gate))
3488        } else {
3489            (qf, None)
3490        };
3491
3492        let mut qn = e.uninit(t * n_head * head_dim)?;
3493        e.rms_norm(
3494            &q,
3495            fa.q_norm.float_data(),
3496            &mut qn,
3497            head_dim,
3498            n_head * t,
3499            eps,
3500        )?;
3501        q = qn;
3502        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3503        e.rms_norm(
3504            &k,
3505            fa.k_norm.float_data(),
3506            &mut kn,
3507            head_dim,
3508            n_head_kv * t,
3509            eps,
3510        )?;
3511        k = kn;
3512        let rope_dims = geometry.n_rot as usize;
3513        e.rope_neox(
3514            &mut q,
3515            pos_d,
3516            head_dim,
3517            rope_dims,
3518            n_head,
3519            t,
3520            geometry.rope_base,
3521            1.0,
3522        )?;
3523        e.rope_neox(
3524            &mut k,
3525            pos_d,
3526            head_dim,
3527            rope_dims,
3528            n_head_kv,
3529            t,
3530            geometry.rope_base,
3531            1.0,
3532        )?;
3533
3534        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3535        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3536        {
3537            let kvl = cache.kv[il].as_mut().unwrap();
3538            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3539            e.append_kv_quantized_rows(
3540                &k,
3541                &v,
3542                &mut kvl.k,
3543                &mut kvl.v,
3544                kvl.len,
3545                t,
3546                kvl.kv_dim_k,
3547                kvl.kv_dim_v,
3548                kvl.k_tok_bytes,
3549                kvl.v_tok_bytes,
3550                crate::Engine::kv_fp8_on(),
3551            )?;
3552            kvl.len += t;
3553            let new_len = kvl.len as i32;
3554            e.set_i32_one(&mut kvl.len_d, new_len)?;
3555        }
3556
3557        let base_len = {
3558            let kvl = cache.kv[il].as_ref().unwrap();
3559            kvl.len - t // KV rows present BEFORE this chunk's append above
3560        };
3561        Ok((AttnPre { q, k, v, gate }, base_len))
3562    }
3563
3564    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3565    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3566    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3567    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3568    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3569    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3570    #[allow(clippy::too_many_arguments)]
3571    fn full_attn_prime_fa_dispatch(
3572        &self,
3573        e: &Engine,
3574        q: &CudaSlice<f32>,
3575        k: &CudaSlice<f32>,
3576        v: &CudaSlice<f32>,
3577        attn: &mut CudaSlice<f32>,
3578        base_len: usize,
3579        t: usize,
3580        cache: &mut Cache,
3581        il: usize,
3582        head_dim: usize,
3583        n_head: usize,
3584        n_head_kv: usize,
3585        scale: f32,
3586    ) -> Result<(), Box<dyn std::error::Error>> {
3587        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3588        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3589        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3590        // attend through the quantized cache exactly like every later chunk (quantize-then-
3591        // attend). One numeric class for every row => the chunk size cannot decide where a
3592        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3593        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
3594        // pin-the-boundary approach).
3595        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
3596        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
3597        // with the fix unconditional, only re-introducing the class edge can prove the gate
3598        // still detects the mechanism. Never on in a measured default run.
3599        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
3600            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3601                e.sdpa_naive(
3602                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3603                )?;
3604            } else {
3605                e.fa_prefill(
3606                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3607                )?;
3608            }
3609            return Ok(());
3610        }
3611        let kvl = cache.kv[il].as_ref().unwrap();
3612        let t_kv = base_len + t;
3613        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
3614        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
3615        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
3616        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
3617        // same numeric class, so the uniform contract holds on the fallback too.
3618        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3619            e.sdpa_naive_quantized_view(
3620                q,
3621                &k_view,
3622                &v_view,
3623                attn,
3624                head_dim,
3625                n_head,
3626                n_head_kv,
3627                t,
3628                t_kv,
3629                scale,
3630                true,
3631                kvl.k_tok_bytes,
3632                kvl.v_tok_bytes,
3633            )?;
3634            return Ok(());
3635        }
3636        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
3637        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
3638        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
3639        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
3640        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
3641        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
3642        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
3643        let deqw = std::env::var("MEMRA_PRIME_DEQW")
3644            .map(|v| v != "0")
3645            .unwrap_or(true);
3646        if deqw {
3647            e.fa_prefill_view_ws(
3648                q,
3649                &k_view,
3650                &v_view,
3651                attn,
3652                head_dim,
3653                n_head,
3654                n_head_kv,
3655                t,
3656                t_kv,
3657                scale,
3658                true,
3659                kvl.k_tok_bytes,
3660                kvl.v_tok_bytes,
3661                crate::Engine::kv_fp8_on(),
3662            )?;
3663        } else {
3664            e.fa_prefill_view(
3665                q,
3666                &k_view,
3667                &v_view,
3668                attn,
3669                head_dim,
3670                n_head,
3671                n_head_kv,
3672                t,
3673                t_kv,
3674                scale,
3675                true,
3676                kvl.k_tok_bytes,
3677                kvl.v_tok_bytes,
3678                crate::Engine::kv_fp8_on(),
3679            )?;
3680        }
3681        Ok(())
3682    }
3683
3684    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
3685    /// (bit-identical composition) and hands wo its fp16 operand directly.
3686    fn full_attn_prime_post_fa(
3687        &self,
3688        e: &Engine,
3689        attn: CudaSlice<f32>,
3690        gate: &Option<CudaSlice<f32>>,
3691        t: usize,
3692        n_head: usize,
3693        head_dim: usize,
3694    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3695        let (attn_g, ag16) = match gate {
3696            Some(gate) => {
3697                let n = t * n_head * head_dim;
3698                let mut ag = e.uninit(n)?;
3699                if Self::f16out_on(e, t) {
3700                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
3701                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
3702                    (ag, Some(a16))
3703                } else {
3704                    let mut gsig = e.uninit(n)?;
3705                    e.sigmoid(gate, &mut gsig, n)?;
3706                    e.mul(&attn, &gsig, &mut ag, n)?;
3707                    (ag, None)
3708                }
3709            }
3710            None => (attn, None),
3711        };
3712        Ok((attn_g, ag16))
3713    }
3714
3715    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
3716    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
3717    /// carried THROUGH the cache like the spec verify does: carried-ring conv
3718    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
3719    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
3720    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
3721    fn linear_attn_prime(
3722        &self,
3723        e: &Engine,
3724        la: &LinearAttnLayer,
3725        h: &CudaSlice<f32>,
3726        hx: Option<&CudaSlice<u8>>,
3727        t: usize,
3728        cache: &mut Cache,
3729        il: usize,
3730    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3731        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
3732        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3733        let g4 = match hx {
3734            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
3735            None => e.matmul_group(&ws, h, t)?,
3736        };
3737        self.linear_attn_prime_core(e, la, g4, t, cache, il)
3738    }
3739
3740    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
3741    fn linear_attn_prime_core(
3742        &self,
3743        e: &Engine,
3744        la: &LinearAttnLayer,
3745        mut g4: Vec<CudaSlice<f32>>,
3746        t: usize,
3747        cache: &mut Cache,
3748        il: usize,
3749    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3750        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
3751    }
3752
3753    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
3754    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
3755    /// conv ring writes back from the true tail. None = classic path, byte-identical.
3756    #[allow(clippy::too_many_arguments)]
3757    fn linear_attn_prime_core_pad_inner(
3758        &self,
3759        e: &Engine,
3760        la: &LinearAttnLayer,
3761        mut g4: Vec<CudaSlice<f32>>,
3762        t: usize,
3763        cache: &mut Cache,
3764        il: usize,
3765        pad_len: Option<&CudaSlice<i32>>,
3766    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3767        // shim over the view twin (task #16): full-range views of the owned buffers.
3768        let geometry = la.geometry;
3769        let d_state = geometry.key_head_dim as usize;
3770        let num_k = geometry.key_heads as usize;
3771        let num_v = geometry.value_heads as usize;
3772        let key_dim = d_state * num_k;
3773        let value_dim = geometry.value_head_dim as usize * num_v;
3774        let conv_dim = key_dim * 2 + value_dim;
3775        let alpha = g4.pop().unwrap(); // [T, num_v]
3776        let beta_raw = g4.pop().unwrap(); // [T, num_v]
3777        let z = g4.pop().unwrap(); // [T, value_dim]
3778        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
3779        self.linear_attn_prime_core_pad_view(
3780            e,
3781            la,
3782            &qkv_mixed.slice(0..t * conv_dim),
3783            &z.slice(0..t * value_dim),
3784            &beta_raw.slice(0..t * num_v),
3785            &alpha.slice(0..t * num_v),
3786            t,
3787            cache,
3788            il,
3789            pad_len,
3790        )
3791    }
3792
3793    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
3794    /// shared verbatim by the per-seq scan path and the varlen batched path.
3795    #[allow(clippy::too_many_arguments)]
3796    fn linear_attn_gdn_prep(
3797        &self,
3798        e: &Engine,
3799        la: &LinearAttnLayer,
3800        qkv_mixed: &cudarc::driver::CudaView<f32>,
3801        beta_raw: &cudarc::driver::CudaView<f32>,
3802        alpha: &cudarc::driver::CudaView<f32>,
3803        t: usize,
3804        cache: &mut Cache,
3805        il: usize,
3806        pad_len: Option<&CudaSlice<i32>>,
3807    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
3808        let cfg = &self.cfg;
3809        let geometry = la.geometry;
3810        let d_state = geometry.key_head_dim as usize;
3811        let num_k = geometry.key_heads as usize;
3812        let num_v = geometry.value_heads as usize;
3813        let d_conv = geometry.conv_kernel as usize;
3814        let key_dim = d_state * num_k; // 2048
3815        let value_dim = geometry.value_head_dim as usize * num_v;
3816        let conv_dim = key_dim * 2 + value_dim; // 8192
3817        let eps = cfg.rms_eps;
3818        debug_assert!(
3819            t >= d_conv - 1,
3820            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
3821        );
3822
3823        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
3824        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
3825        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
3826        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
3827        let rl = cache.recur[il].as_mut().unwrap();
3828        let hk = Self::gdn_hk(e, t, num_v, num_k);
3829        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
3830        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
3831        let mut q_g = e.uninit(d_state * hk * t)?;
3832        let mut k_g = e.uninit(d_state * hk * t)?;
3833        let mut v_g = e.uninit(d_state * num_v * t)?;
3834        if conv_fuse {
3835            e.ssm_conv1d_gdn_state_pad(
3836                qkv_mixed,
3837                &mut rl.conv_state,
3838                la.ssm_conv1d.float_data(),
3839                &mut q_g,
3840                &mut k_g,
3841                &mut v_g,
3842                conv_dim,
3843                t,
3844                d_conv,
3845                d_state,
3846                num_v,
3847                num_k,
3848                key_dim,
3849                hk,
3850                pad_len,
3851            )?;
3852        } else {
3853            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
3854            e.ssm_conv1d_tm_state_pad_v(
3855                qkv_mixed,
3856                &mut rl.conv_state,
3857                la.ssm_conv1d.float_data(),
3858                &mut conv_out,
3859                conv_dim,
3860                t,
3861                d_conv,
3862                pad_len,
3863            )?;
3864            e.qkv_to_gdn_repack(
3865                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
3866            )?;
3867        }
3868        let mut q_l2 = e.uninit(d_state * hk * t)?;
3869        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
3870        // Emitted only where a consumer exists (the wgmma config) — on other arches the
3871        // alloc + epilogue stores would be pure waste.
3872        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
3873            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
3874            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
3875            Some(qb)
3876        } else {
3877            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
3878            None
3879        };
3880        let mut k_l2 = e.uninit(d_state * hk * t)?;
3881        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
3882        let kb16 = if Engine::l2_v2_on(d_state) {
3883            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
3884            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
3885            Some(kb)
3886        } else {
3887            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
3888            None
3889        };
3890        let mut beta = e.uninit(t * num_v)?;
3891        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
3892        let mut g_log = e.uninit(t * num_v)?;
3893        e.gdn_glog_v(
3894            alpha,
3895            la.ssm_dt.float_data(),
3896            la.ssm_a.float_data(),
3897            &mut g_log,
3898            num_v,
3899            t,
3900        )?;
3901        if let Some(len_d) = pad_len {
3902            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
3903        }
3904        Ok(GdnPrep {
3905            hk,
3906            q_l2,
3907            k_l2,
3908            v_g,
3909            beta,
3910            g_log,
3911            kb16,
3912            qb16,
3913        })
3914    }
3915
3916    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
3917    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
3918    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
3919    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
3920    #[allow(clippy::too_many_arguments)]
3921    fn linear_attn_prime_core_batch(
3922        &self,
3923        e: &Engine,
3924        la: &LinearAttnLayer,
3925        g4: &[CudaSlice<f32>],
3926        offs: &[usize],
3927        ts: &[usize],
3928        caches: &mut [&mut Cache],
3929        il: usize,
3930    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
3931        let geometry = la.geometry;
3932        let d_state = geometry.key_head_dim as usize;
3933        let num_k = geometry.key_heads as usize;
3934        let num_v = geometry.value_heads as usize;
3935        let d_conv = geometry.conv_kernel as usize;
3936        let key_dim = d_state * num_k;
3937        let value_dim = geometry.value_head_dim as usize * num_v;
3938        let conv_dim = key_dim * 2 + value_dim;
3939        let eps = self.cfg.rms_eps;
3940        let scale = 1.0 / (d_state as f32).sqrt();
3941        let b = ts.len();
3942        let c = Engine::gdn_chunk_size();
3943        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
3944        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
3945        let carried = caches.iter().any(|c| c.pos > 0);
3946        let use_vl = !carried
3947            && (2..=8).contains(&b)
3948            && Engine::gdn_chunked_enabled()
3949            && ts.iter().all(|&t| t >= 16)
3950            && e.gdn_mma_enabled(c)
3951            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
3952        if !use_vl {
3953            return (0..b)
3954                .map(|s| {
3955                    let (o, t) = (offs[s], ts[s]);
3956                    self.linear_attn_prime_core_pad_view(
3957                        e,
3958                        la,
3959                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
3960                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
3961                        &g4[2].slice(o * num_v..(o + t) * num_v),
3962                        &g4[3].slice(o * num_v..(o + t) * num_v),
3963                        t,
3964                        caches[s],
3965                        il,
3966                        None,
3967                    )
3968                })
3969                .collect();
3970        }
3971        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
3972        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
3973        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
3974        struct SeqBufs {
3975            conv_out: CudaSlice<f32>,
3976            q_g: CudaSlice<f32>,
3977            k_g: CudaSlice<f32>,
3978            v_g: CudaSlice<f32>,
3979            q_l2: CudaSlice<f32>,
3980            k_l2: CudaSlice<f32>,
3981            beta: CudaSlice<f32>,
3982            g_log: CudaSlice<f32>,
3983            gn: CudaSlice<f32>,
3984            gn16: CudaSlice<u8>,
3985        }
3986        let f16o = Self::f16out_on(e, 16);
3987        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
3988        let mut sb = Vec::with_capacity(b);
3989        let mut pres = Vec::with_capacity(b);
3990        for &t in ts.iter().take(b) {
3991            sb.push(SeqBufs {
3992                conv_out: e.uninit(conv_dim * t)?,
3993                q_g: e.uninit(d_state * hk * t)?,
3994                k_g: e.uninit(d_state * hk * t)?,
3995                v_g: e.uninit(d_state * num_v * t)?,
3996                q_l2: e.uninit(d_state * hk * t)?,
3997                k_l2: e.uninit(d_state * hk * t)?,
3998                beta: e.uninit(t * num_v)?,
3999                g_log: e.uninit(t * num_v)?,
4000                gn: e.uninit(d_state * num_v * t)?,
4001                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4002            });
4003            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4004        }
4005        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4006            .map(|s| {
4007                let (o, t) = (offs[s], ts[s]);
4008                let rl = caches[s].recur[il].as_ref().unwrap();
4009                crate::GdnPrepVl {
4010                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4011                    conv_state: e.addr_f32(&rl.conv_state),
4012                    conv_out: e.addr_f32(&sb[s].conv_out),
4013                    q_g: e.addr_f32(&sb[s].q_g),
4014                    k_g: e.addr_f32(&sb[s].k_g),
4015                    v_g: e.addr_f32(&sb[s].v_g),
4016                    q_l2: e.addr_f32(&sb[s].q_l2),
4017                    k_l2: e.addr_f32(&sb[s].k_l2),
4018                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4019                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4020                    beta: e.addr_f32(&sb[s].beta),
4021                    g_log: e.addr_f32(&sb[s].g_log),
4022                    o: e.addr_f32(&pres[s].o),
4023                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4024                    gn: e.addr_f32(&sb[s].gn),
4025                    gn16: e.addr_u8(&sb[s].gn16),
4026                    kb16: if Engine::l2_v2_on(d_state) {
4027                        e.addr_u8(&pres[s].kb16)
4028                    } else {
4029                        0
4030                    },
4031                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4032                        e.addr_u8(&pres[s].qb16)
4033                    } else {
4034                        0
4035                    },
4036                    t: t as i32,
4037                    pad: 0,
4038                }
4039            })
4040            .collect();
4041        let args: Vec<crate::GdnSeqVl> = (0..b)
4042            .map(|s| {
4043                let rl = caches[s].recur[il].as_ref().unwrap();
4044                crate::GdnSeqVl {
4045                    kb16: e.addr_u8(&pres[s].kb16),
4046                    gcum: e.addr_f32(&pres[s].gcum),
4047                    beta: e.addr_f32(&sb[s].beta),
4048                    u: e.addr_f32(&pres[s].u),
4049                    wb16: e.addr_u8(&pres[s].wb16),
4050                    y: e.addr_u8(&pres[s].y16),
4051                    ssnap: e.addr_u8(&pres[s].ssnap16),
4052                    state_in: e.addr_f32(&rl.ssm_state),
4053                    state_out: e.addr_f32(&rl.ssm_state_alt),
4054                    q: e.addr_f32(&sb[s].q_l2),
4055                    p: e.addr_f32(&pres[s].p),
4056                    o: e.addr_f32(&pres[s].o),
4057                    k: e.addr_f32(&sb[s].k_l2),
4058                    v: e.addr_f32(&sb[s].v_g),
4059                    g: e.addr_f32(&sb[s].g_log),
4060                    a: e.addr_f32(&pres[s].a),
4061                    w: e.addr_f32(&pres[s].w),
4062                    t: ts[s] as i32,
4063                    nc: pres[s].nc as i32,
4064                }
4065            })
4066            .collect();
4067        e.gdn_prep_vl8(
4068            &prep_args,
4069            la.ssm_conv1d.float_data(),
4070            la.ssm_dt.float_data(),
4071            la.ssm_a.float_data(),
4072            conv_dim,
4073            d_conv,
4074            d_state,
4075            num_v,
4076            num_k,
4077            key_dim,
4078            hk,
4079            eps,
4080        )?;
4081        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4082        // both standalone mirror launches vanish on the default config.
4083        if !Engine::l2_v2_on(d_state) {
4084            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4085        }
4086        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4087        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4088            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4089            if !Engine::l2_v2_on(d_state) {
4090                for s in 0..b {
4091                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4092                }
4093            }
4094            let mut wa = [crate::GdnWVl::default(); 8];
4095            for s in 0..b {
4096                wa[s] = crate::GdnWVl {
4097                    qb16: e.addr_u8(&pres[s].qb16),
4098                    pb16: e.addr_u8(&pres[s].pb16),
4099                };
4100            }
4101            Some(crate::GdnWVl8(wa))
4102        } else {
4103            None
4104        };
4105        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4106        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4107        if f16o {
4108            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4109        }
4110        // per-seq state swap (+ non-f16out tail fallback)
4111        let mut out = Vec::with_capacity(b);
4112        for (s, bufs) in sb.into_iter().enumerate() {
4113            let rl = caches[s].recur[il].as_mut().unwrap();
4114            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4115            let (o, t) = (offs[s], ts[s]);
4116            let SeqBufs { mut gn, gn16, .. } = bufs;
4117            if f16o {
4118                out.push((gn, Some(gn16)));
4119            } else {
4120                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4121                e.gated_rmsnorm_zv(
4122                    &pres[s].o,
4123                    la.ssm_norm.float_data(),
4124                    &z_v,
4125                    &mut gn,
4126                    d_state,
4127                    num_v * t,
4128                    eps,
4129                )?;
4130                out.push((gn, None));
4131            }
4132        }
4133        Ok(out)
4134    }
4135
4136    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4137    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4138    /// Same kernels, same values, byte-identical to the Vec shim above.
4139    #[allow(clippy::too_many_arguments)]
4140    fn linear_attn_prime_core_pad_view(
4141        &self,
4142        e: &Engine,
4143        la: &LinearAttnLayer,
4144        qkv_mixed: &cudarc::driver::CudaView<f32>,
4145        z: &cudarc::driver::CudaView<f32>,
4146        beta_raw: &cudarc::driver::CudaView<f32>,
4147        alpha: &cudarc::driver::CudaView<f32>,
4148        t: usize,
4149        cache: &mut Cache,
4150        il: usize,
4151        pad_len: Option<&CudaSlice<i32>>,
4152    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4153        let cfg = &self.cfg;
4154        let geometry = la.geometry;
4155        let d_state = geometry.key_head_dim as usize;
4156        let num_v = geometry.value_heads as usize;
4157        let eps = cfg.rms_eps;
4158        let scale = 1.0 / (d_state as f32).sqrt();
4159
4160        let prep =
4161            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4162
4163        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4164        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4165        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4166        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4167        // verify keep the sequential kernel).
4168        let mut o = e.uninit(d_state * num_v * t)?;
4169        let rl = cache.recur[il].as_mut().unwrap();
4170        {
4171            let crate::cache::RecurLayer {
4172                ssm_state,
4173                ssm_state_alt,
4174                ..
4175            } = rl;
4176            e.gdn_scan_prefill(
4177                &prep.q_l2,
4178                &prep.k_l2,
4179                &prep.v_g,
4180                &prep.g_log,
4181                &prep.beta,
4182                prep.kb16.as_ref(),
4183                prep.qb16.as_ref(),
4184                ssm_state,
4185                ssm_state_alt,
4186                &mut o,
4187                num_v,
4188                t,
4189                scale,
4190                prep.hk,
4191            )?;
4192        }
4193        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4194
4195        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4196        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4197        let mut gn = e.uninit(d_state * num_v * t)?;
4198        let gn16 = if Self::f16out_on(e, t) {
4199            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4200            e.gated_rmsnorm_f16out_zv(
4201                &o,
4202                la.ssm_norm.float_data(),
4203                z,
4204                &mut gn,
4205                &mut g16,
4206                d_state,
4207                num_v * t,
4208                eps,
4209            )?;
4210            Some(g16)
4211        } else {
4212            e.gated_rmsnorm_zv(
4213                &o,
4214                la.ssm_norm.float_data(),
4215                z,
4216                &mut gn,
4217                d_state,
4218                num_v * t,
4219                eps,
4220            )?;
4221            None
4222        };
4223        Ok((gn, gn16))
4224    }
4225
4226    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4227    #[allow(clippy::too_many_arguments)]
4228    fn linear_attn_prime_core_pad(
4229        &self,
4230        e: &Engine,
4231        la: &LinearAttnLayer,
4232        g4: Vec<CudaSlice<f32>>,
4233        t: usize,
4234        cache: &mut Cache,
4235        il: usize,
4236        pad_len: Option<&CudaSlice<i32>>,
4237    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4238        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4239        if let Some(xh) = &gn16 {
4240            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4241                return Ok(y);
4242            }
4243        }
4244        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4245    }
4246
4247    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4248    ///
4249    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4250    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4251    pub fn full_attn(
4252        &self,
4253        e: &Engine,
4254        fa: &FullAttnLayer,
4255        h: &CudaSlice<f32>,
4256        pos_d: &CudaSlice<i32>,
4257        t: usize,
4258        il: usize,
4259    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4260        if self.uses_sliding_gated_moe_program() {
4261            return self.step35_attn(e, fa, h, pos_d, t, il);
4262        }
4263        let cfg = &self.cfg;
4264        let _n_embd = cfg.n_embd as usize;
4265        let geometry = cfg.full_attention_geometry_at(il as u32);
4266        let n_head = geometry.n_head as usize;
4267        let n_head_kv = geometry.n_head_kv as usize;
4268        let head_dim = geometry.head_dim_k as usize;
4269        let eps = cfg.rms_eps;
4270        let scale = geometry.attention_scale();
4271
4272        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4273        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4274        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4275        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4276        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4277        let v = g3.pop().unwrap();
4278        let mut k = g3.pop().unwrap();
4279        let qf = g3.pop().unwrap();
4280        let (mut q, gate) = if gated {
4281            let mut q = e.uninit(t * n_head * head_dim)?;
4282            let mut gate = e.uninit(t * n_head * head_dim)?;
4283            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4284            (q, Some(gate))
4285        } else {
4286            (qf, None)
4287        };
4288
4289        // QK-norm (per head_dim row), then partial RoPE.
4290        let mut qn = e.uninit(t * n_head * head_dim)?;
4291        e.rms_norm(
4292            &q,
4293            fa.q_norm.float_data(),
4294            &mut qn,
4295            head_dim,
4296            n_head * t,
4297            eps,
4298        )?;
4299        q = qn;
4300        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4301        e.rms_norm(
4302            &k,
4303            fa.k_norm.float_data(),
4304            &mut kn,
4305            head_dim,
4306            n_head_kv * t,
4307            eps,
4308        )?;
4309        k = kn;
4310        let rope_dims = geometry.n_rot as usize;
4311        e.rope_neox(
4312            &mut q,
4313            pos_d,
4314            head_dim,
4315            rope_dims,
4316            n_head,
4317            t,
4318            geometry.rope_base,
4319            1.0,
4320        )?;
4321        e.rope_neox(
4322            &mut k,
4323            pos_d,
4324            head_dim,
4325            rope_dims,
4326            n_head_kv,
4327            t,
4328            geometry.rope_base,
4329            1.0,
4330        )?;
4331
4332        // SDPA
4333        let mut attn = e.uninit(t * n_head * head_dim)?;
4334        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4335        // falls back to naive sdpa.
4336        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4337            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4338            e.sdpa_naive(
4339                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4340            )?;
4341        } else {
4342            e.fa_prefill(
4343                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4344            )?;
4345        }
4346
4347        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4348        let attn_g = match &gate {
4349            Some(gate) => {
4350                let mut gsig = e.uninit(t * n_head * head_dim)?;
4351                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4352                let mut ag = e.uninit(t * n_head * head_dim)?;
4353                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4354                ag
4355            }
4356            None => attn,
4357        };
4358
4359        // o projection
4360        let o = e.matmul(&fa.wo, &attn_g, t)?;
4361        Ok(o)
4362    }
4363
4364    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4365    pub fn linear_attn(
4366        &self,
4367        e: &Engine,
4368        la: &LinearAttnLayer,
4369        h: &CudaSlice<f32>,
4370        t: usize,
4371    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4372        let cfg = &self.cfg;
4373        let _n_embd = cfg.n_embd as usize;
4374        let geometry = la.geometry;
4375        let d_state = geometry.key_head_dim as usize;
4376        let num_k = geometry.key_heads as usize;
4377        let num_v = geometry.value_heads as usize;
4378        let d_conv = geometry.conv_kernel as usize;
4379        let head_k = d_state;
4380        let head_v = geometry.value_head_dim as usize;
4381        let key_dim = head_k * num_k; // 2048
4382        let value_dim = head_v * num_v; // 4096
4383        let conv_dim = key_dim * 2 + value_dim; // 8192
4384        let eps = cfg.rms_eps;
4385        let scale = 1.0 / (d_state as f32).sqrt();
4386
4387        // projections
4388        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4389        let mut g4 = e.matmul_group(
4390            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4391            h,
4392            t,
4393        )?;
4394        let alpha = g4.pop().unwrap(); // [T, num_v]
4395        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4396        let z = g4.pop().unwrap(); // [T, value_dim]
4397        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4398
4399        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4400        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4401        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4402        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4403        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4404        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4405        let _ = (head_k, head_v);
4406        let mut q_g = e.uninit(d_state * num_v * t)?;
4407        let mut k_g = e.uninit(d_state * num_v * t)?;
4408        let mut v_g = e.uninit(d_state * num_v * t)?;
4409        e.ssm_conv1d_gdn(
4410            &qkv_mixed,
4411            la.ssm_conv1d.float_data(),
4412            &mut q_g,
4413            &mut k_g,
4414            &mut v_g,
4415            conv_dim,
4416            t,
4417            d_conv,
4418            d_state,
4419            num_v,
4420            num_k,
4421            key_dim,
4422        )?;
4423        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4424        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4425        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4426        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4427        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4428        let v_gd = v_g;
4429
4430        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4431        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4432        let mut beta = e.uninit(t * num_v)?;
4433        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4434        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4435        let mut g_log = e.uninit(t * num_v)?;
4436        e.gdn_glog(
4437            &alpha,
4438            la.ssm_dt.float_data(),
4439            la.ssm_a.float_data(),
4440            &mut g_log,
4441            num_v,
4442            t,
4443        )?;
4444
4445        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4446        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4447        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4448        let mut o = e.uninit(d_state * num_v * t)?;
4449        e.gdn_scan_prefill(
4450            &q_l2,
4451            &k_l2,
4452            &v_gd,
4453            &g_log,
4454            &beta,
4455            None,
4456            None,
4457            &state_in,
4458            &mut state_out,
4459            &mut o,
4460            num_v,
4461            t,
4462            scale,
4463            num_v,
4464        )?;
4465
4466        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4467        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4468        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4469        // o rows are (t*num_v+vh) too. Good.
4470        let mut gn = e.uninit(d_state * num_v * t)?;
4471        e.gated_rmsnorm(
4472            &o,
4473            la.ssm_norm.float_data(),
4474            &z,
4475            &mut gn,
4476            d_state,
4477            num_v * t,
4478            eps,
4479        )?;
4480
4481        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4482        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4483        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4484        let out = e.matmul(&la.ssm_out, &gn, t)?;
4485        Ok(out)
4486    }
4487}
4488
4489impl HybridModel {
4490    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4491    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4492    ///
4493    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4494    /// different 860160-byte block than the same expert of layer 7).
4495    ///
4496    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4497    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4498    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4499    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4500    pub fn moe_ffn_il(
4501        &self,
4502        e: &Engine,
4503        m: &MoeWeights,
4504        z: &CudaSlice<f32>,
4505        t: usize,
4506        il: u16,
4507    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4508        Self::moe_ffn_inner(
4509            e,
4510            m,
4511            z,
4512            None,
4513            t,
4514            &self.cfg,
4515            il,
4516            self.max_moe_block(),
4517            false,
4518            None,
4519            self.uses_sliding_gated_moe_program(),
4520        )
4521    }
4522
4523    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4524    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4525    pub fn moe_ffn_il_prefill(
4526        &self,
4527        e: &Engine,
4528        m: &MoeWeights,
4529        z: &CudaSlice<f32>,
4530        t: usize,
4531        il: u16,
4532    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4533        Self::moe_ffn_inner(
4534            e,
4535            m,
4536            z,
4537            None,
4538            t,
4539            &self.cfg,
4540            il,
4541            self.max_moe_block(),
4542            true,
4543            Some(&self.step_grouped_prefill),
4544            self.uses_sliding_gated_moe_program(),
4545        )
4546    }
4547
4548    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4549    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4550    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4551    pub fn moe_ffn_il_zq8(
4552        &self,
4553        e: &Engine,
4554        m: &MoeWeights,
4555        z: &CudaSlice<f32>,
4556        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4557        t: usize,
4558        il: u16,
4559    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4560        Self::moe_ffn_inner(
4561            e,
4562            m,
4563            z,
4564            zq8,
4565            t,
4566            &self.cfg,
4567            il,
4568            self.max_moe_block(),
4569            false,
4570            None,
4571            self.uses_sliding_gated_moe_program(),
4572        )
4573    }
4574
4575    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4576    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4577    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4578    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4579    ///
4580    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4581    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4582    pub(crate) fn moe_ffn(
4583        e: &Engine,
4584        m: &MoeWeights,
4585        z: &CudaSlice<f32>,
4586        t: usize,
4587        cfg: &ModelConfig,
4588        il: u16,
4589        max_block: usize,
4590    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4591        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4592    }
4593
4594    #[allow(clippy::too_many_arguments)]
4595    pub(crate) fn moe_ffn_inner(
4596        e: &Engine,
4597        m: &MoeWeights,
4598        z: &CudaSlice<f32>,
4599        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4600        t: usize,
4601        cfg: &ModelConfig,
4602        il: u16,
4603        max_block: usize,
4604        prefill: bool,
4605        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
4606        sliding_gated_moe: bool,
4607    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4608        let worker_io = crate::spill_pread::worker_enabled();
4609        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
4610        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
4611            e.with_moe_cache(max_block, |cache, _| {
4612                cache.begin_forward_epoch(il, t);
4613                if worker_io {
4614                    cache.begin_worker_scope();
4615                }
4616                Ok(())
4617            })?;
4618        }
4619        if m.step_ep.is_some() || m.step_tp.is_some() {
4620            let moe = cfg
4621                .moe
4622                .as_ref()
4623                .ok_or("Step distributed execution requires MoE model metadata")?;
4624            let n_embd = cfg.n_embd as usize;
4625            let n_expert = moe.expert_count as usize;
4626            let n_used = moe.expert_used_count as usize;
4627            let sigmoid = cfg
4628                .sigmoid_router()
4629                .ok_or("Step distributed execution requires the Step sigmoid router")?;
4630            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
4631            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
4632            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
4633            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
4634                return Err(
4635                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
4636                );
4637            }
4638            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
4639                return Err(format!(
4640                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
4641                    PRIME_MIN_T,
4642                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
4643                )
4644                .into());
4645            }
4646            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
4647            let grouped_prefill_shape =
4648                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
4649            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
4650                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
4651            }) {
4652                let (selected, route_weights) =
4653                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
4654                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
4655                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
4656                Self::trace_moe_input(e, il, t, n_embd, z)?;
4657                let selected = selected
4658                    .iter()
4659                    .map(|&expert| expert as usize)
4660                    .collect::<Vec<_>>();
4661
4662                // The narrow route readback above orders the owning-stage producer. The grouped
4663                // runtime then copies the resident root activation into its persistent rank inputs.
4664                e.stream().synchronize()?;
4665                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
4666                    state.projection.set_activation_limit(ep.activation_limit)?;
4667                    ep.runtime
4668                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
4669                            ep.experts.e4m3()?,
4670                            &mut state.projection,
4671                            z,
4672                            t,
4673                            &selected,
4674                        )?;
4675                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
4676                        &state.projection,
4677                        &mut state.combine,
4678                        &route_weights,
4679                    )?;
4680                    ep.runtime.execute_step_grouped_expert_parallel_gate(
4681                        ep.experts.e4m3()?,
4682                        &mut state.projection,
4683                    )?;
4684                    ep.runtime.execute_step_grouped_expert_parallel_combine(
4685                        &state.projection,
4686                        &mut state.combine,
4687                    )?;
4688                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
4689                        &state.projection,
4690                        &state.combine,
4691                        e,
4692                    )?;
4693                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
4694                    if prefill {
4695                        // A shared plan may be reused by the next layer on a different runtime
4696                        // stream. Complete the owning-stage copy before its source is overwritten.
4697                        e.stream().synchronize()?;
4698                    }
4699                    eprintln!(
4700                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
4701                         attention_layout=tensor-parallel expert_layout=expert-parallel \
4702                         expert_transport={} native_p2p=true route_control=host-narrow \
4703                         input=root-device projection_workspaces=persistent \
4704                         combine=root-device output=owning-stage-device \
4705                         prefill={prefill} batched_decode=false capacity={} \
4706                         performance_claim=false",
4707                        ep.devices,
4708                        ep.runtime.transport_label(),
4709                        state.projection.max_tokens(),
4710                    );
4711                    Ok::<_, Box<dyn std::error::Error>>(output)
4712                };
4713
4714                if grouped_prefill_shape {
4715                    let grouped_prefill = grouped_prefill
4716                        .ok_or("Step grouped prefill has no model-scoped executor")?;
4717                    let mut shared = grouped_prefill
4718                        .lock()
4719                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
4720                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
4721                        state.devices != ep.devices
4722                            || state.grouped.projection.max_tokens() < t
4723                            || state.grouped.projection.input_width() != n_embd
4724                            || state.grouped.projection.expert_width()
4725                                != moe.expert_ff_length as usize
4726                    });
4727                    if needs_prepare {
4728                        let seed_input = vec![0.0f32; n_embd];
4729                        let seed_selected = &selected[..n_used];
4730                        let seed_weights = &route_weights[..n_used];
4731                        let projection = ep
4732                            .runtime
4733                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
4734                                ep.experts.e4m3()?,
4735                                &seed_input,
4736                                1,
4737                                seed_selected,
4738                                ep.activation_limit,
4739                                t,
4740                            )?;
4741                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
4742                            &projection,
4743                            seed_weights,
4744                        )?;
4745                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
4746                            devices: ep.devices.clone(),
4747                            grouped: crate::hybrid::StepEpGroupedDecode {
4748                                projection,
4749                                combine,
4750                            },
4751                        });
4752                        eprintln!(
4753                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
4754                             shared_across_layers=true performance_claim=false",
4755                            ep.devices,
4756                        );
4757                    }
4758                    return execute(
4759                        &mut shared
4760                            .state
4761                            .as_mut()
4762                            .expect("Step grouped prefill state prepared above")
4763                            .grouped,
4764                    );
4765                }
4766
4767                let mut grouped = ep
4768                    .grouped_decode
4769                    .as_ref()
4770                    .expect("grouped decode presence checked above")
4771                    .lock()
4772                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
4773                return execute(&mut grouped);
4774            }
4775            if grouped_prefill_shape {
4776                return Err(
4777                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
4778                        .into(),
4779                );
4780            }
4781            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
4782            // expert program — the per-layer host logits readback (the last per-layer host
4783            // sync) disappears. Selection tie-breaking may differ from the host router:
4784            // numeric-class door, run-gen argmax gate + boot battery.
4785            if t == 1
4786                && crate::tp::step_nvfp4_dev_routes_enabled()?
4787                && crate::tp::step_tp_dev_router_enabled()?
4788            {
4789                if let Some(tp) = &m.step_tp {
4790                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
4791                        let (sf, route_norm) = sigmoid;
4792                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
4793                        // before the router — the rank streams overlap the gemv+topk.
4794                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
4795                        // from its own z copy (replicated deterministic router — identical
4796                        // bits in, identical sel/w out) and starts its sweep without
4797                        // waiting the root's sel broadcast.
4798                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4799                        let d1_router = *D1_ROUTER.get_or_init(|| {
4800                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
4801                        });
4802                        if d1_router {
4803                            let (sf_h, rn_h) = sigmoid;
4804                            let n_ex = m.gate_exps.n_expert;
4805                            let act_ct = m.active_count();
4806                            let _ = tp.runtime.nvfp4_routes_prestage_with(
4807                                bank,
4808                                e,
4809                                z,
4810                                |rank1, in1, sel1, w1| {
4811                                    let mut guard = DEV1_ROUTER_REPS
4812                                        .lock()
4813                                        .map_err(|_| "dev1 router replica lock")?;
4814                                    let (reps, scratch) =
4815                                        guard.get_or_insert_with(|| (Default::default(), None));
4816                                    if !reps.contains_key(&il) {
4817                                        use cudarc::driver::DevicePtr;
4818                                        let (g1, p1, a1) = (
4819                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
4820                                            rank1.htod(&vec![0.0f32; n_ex])?,
4821                                            rank1.alloc_u8_uninit(n_ex)?,
4822                                        );
4823                                        for (src, dst_len, dst) in [
4824                                            (
4825                                                {
4826                                                    let s = e.stream();
4827                                                    let (p, _g) =
4828                                                        m.gate_inp.float_data().device_ptr(&s);
4829                                                    p as u64
4830                                                },
4831                                                n_ex * n_embd * 4,
4832                                                {
4833                                                    let s = rank1.stream();
4834                                                    let (p, _g) = g1.device_ptr(&s);
4835                                                    p as u64
4836                                                },
4837                                            ),
4838                                            (
4839                                                {
4840                                                    let s = e.stream();
4841                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
4842                                                    p as u64
4843                                                },
4844                                                n_ex * 4,
4845                                                {
4846                                                    let s = rank1.stream();
4847                                                    let (p, _g) = p1.device_ptr(&s);
4848                                                    p as u64
4849                                                },
4850                                            ),
4851                                            (
4852                                                {
4853                                                    let s = e.stream();
4854                                                    let (p, _g) =
4855                                                        m.active_experts_dev.device_ptr(&s);
4856                                                    p as u64
4857                                                },
4858                                                n_ex,
4859                                                {
4860                                                    let s = rank1.stream();
4861                                                    let (p, _g) = a1.device_ptr(&s);
4862                                                    p as u64
4863                                                },
4864                                            ),
4865                                        ] {
4866                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
4867                                        }
4868                                        rank1.stream().synchronize()?;
4869                                        reps.insert(il, (g1, p1, a1));
4870                                    }
4871                                    if scratch.is_none() {
4872                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
4873                                    }
4874                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
4875                                    let logits1 = scratch.as_mut().expect("armed above");
4876                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
4877                                    rank1.moe_router_sigmoid_topk_into(
4878                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
4879                                        w1,
4880                                    )?;
4881                                    Ok(true)
4882                                },
4883                            )?;
4884                        } else {
4885                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
4886                        }
4887                        // Persistent selection buffers: the allocating topk built two fresh
4888                        // slices per layer; sel/w land in process-static rows instead
4889                        // (host-op diet — same kernel, same bytes).
4890                        static SELW: std::sync::Mutex<
4891                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
4892                        > = std::sync::Mutex::new(None);
4893                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
4894                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
4895                            *selw = Some((
4896                                e.ctx().ordinal(),
4897                                e.htod_i32(&vec![0i32; n_used])?,
4898                                e.htod(&vec![0.0f32; n_used])?,
4899                            ));
4900                        }
4901                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
4902                        e.moe_router_sigmoid_topk_into(
4903                            &logits,
4904                            t,
4905                            n_expert,
4906                            n_used,
4907                            m.active_count(),
4908                            &m.exp_probs_b_dev,
4909                            &m.active_experts_dev,
4910                            sf,
4911                            route_norm,
4912                            sel_d,
4913                            w_d,
4914                        )?;
4915                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
4916                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
4917                        // PREJOIN hook so it executes while the peer rank drains its sweep
4918                        // (fills dev0's join wait); apply adds the identical values after.
4919                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4920                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
4921                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
4922                        });
4923                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
4924                        // expert runs on rank1 — the idle device — same kernels, same
4925                        // split program, down row root-resident: bit-identical.
4926                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4927                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
4928                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
4929                        }) && tp.runtime.rank_engine(1).is_some();
4930                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
4931                        // overlap ws + ones row and hand their RAW pointers to the routed
4932                        // run — the join add folds the shexp apply into one launch.
4933                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4934                        let tail3 = *TAIL3
4935                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
4936                        let mut ov_issued = false;
4937                        let mut d1_issued = false;
4938                        let mut tail_folded = false;
4939                        let mut output = if shexp_d1 {
4940                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
4941                            tp.runtime
4942                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
4943                                    bank,
4944                                    e,
4945                                    z,
4946                                    &sel_d,
4947                                    &w_d,
4948                                    n_used,
4949                                    tp.activation_limit,
4950                                    || {
4951                                        d1_issued = Self::shexp_dev1_issue(
4952                                            e, rank1, m, z, cfg, il, n_embd,
4953                                        )?;
4954                                        Ok(())
4955                                    },
4956                                )?
4957                        } else if shexp_ov {
4958                            // Raw sh/ones pointers for the fused tail (persistent statics;
4959                            // pointers stable, no lock held across the routed call). The
4960                            // sh CONTENT is written by the prejoin-issued kernels earlier
4961                            // on e's stream — stream order covers the fused add.
4962                            let post_add = if tail3 {
4963                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
4964                            } else {
4965                                None
4966                            };
4967                            let used_post = post_add.is_some();
4968                            let out = tp
4969                                .runtime
4970                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
4971                                    bank,
4972                                    e,
4973                                    z,
4974                                    &sel_d,
4975                                    &w_d,
4976                                    n_used,
4977                                    tp.activation_limit,
4978                                    || {
4979                                        ov_issued =
4980                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
4981                                        Ok(())
4982                                    },
4983                                    post_add,
4984                                )?;
4985                            // ov_issued false with post_add armed = an early-return arm
4986                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
4987                            // fall through to the normal shexp add (battery v22 receipt:
4988                            // the strict error here failed every graph-door boot).
4989                            if used_post && ov_issued {
4990                                tail_folded = true; // apply folded into the join add
4991                            }
4992                            out
4993                        } else {
4994                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
4995                                bank,
4996                                e,
4997                                z,
4998                                &sel_d,
4999                                &w_d,
5000                                n_used,
5001                                tp.activation_limit,
5002                            )?
5003                        };
5004                        if output.len() != t * n_embd {
5005                            return Err(format!(
5006                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5007                                output.len()
5008                            )
5009                            .into());
5010                        }
5011                        if tail_folded {
5012                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5013                        } else if d1_issued {
5014                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5015                        } else if ov_issued {
5016                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5017                        } else {
5018                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5019                        }
5020                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5021                            std::sync::atomic::AtomicU64::new(0);
5022                        let layer_bit = 1u64 << (il as u64 % 64);
5023                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5024                            & layer_bit
5025                            == 0
5026                        {
5027                            eprintln!(
5028                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5029                                 expert_transport={} native_p2p={} router=device \
5030                                 activation=host-canonical accumulation=host-canonical \
5031                                 output=e-device io=device performance_claim=false \
5032                                 (logged once per layer)",
5033                                tp.devices,
5034                                tp.runtime.transport_label(),
5035                                tp.runtime.native_p2p(),
5036                            );
5037                        }
5038                        return Ok(output);
5039                    }
5040                }
5041            }
5042            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5043            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5044            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5045            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5046            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5047            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5048            let route_started = route_timing.then(std::time::Instant::now);
5049            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5050                e,
5051                &logits,
5052                z,
5053                t,
5054                n_embd,
5055                n_expert,
5056                n_used,
5057                m.exp_probs_b.as_deref(),
5058                sigmoid,
5059                m.active_experts.as_deref(),
5060            )?;
5061            if let Some(started) = route_started {
5062                use std::sync::atomic::Ordering;
5063                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5064                    + started.elapsed().as_nanos() as u64;
5065                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5066                if calls % 430 == 0 {
5067                    eprintln!(
5068                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5069                        ns as f64 / 1.0e6,
5070                        ns as f64 / calls as f64 / 1.0e3,
5071                    );
5072                }
5073            }
5074            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5075            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5076            Self::trace_moe_input(e, il, t, n_embd, z)?;
5077            let selected = selected
5078                .iter()
5079                .map(|&expert| expert as usize)
5080                .collect::<Vec<_>>();
5081            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5082            // combined output comes back as an e-context row — no host round-trip, no host
5083            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5084            // both preserve f32 bits), gated by greedy token identity.
5085            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5086                if let Some(tp) = &m.step_tp {
5087                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5088                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5089                            bank,
5090                            e,
5091                            z,
5092                            &selected,
5093                            &route_weights,
5094                            n_used,
5095                            tp.activation_limit,
5096                        )?;
5097                        if output.len() != t * n_embd {
5098                            return Err(format!(
5099                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5100                                output.len()
5101                            )
5102                            .into());
5103                        }
5104                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5105                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5106                            std::sync::atomic::AtomicU64::new(0);
5107                        let layer_bit = 1u64 << (il as u64 % 64);
5108                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5109                            & layer_bit
5110                            == 0
5111                        {
5112                            eprintln!(
5113                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5114                                 expert_transport={} native_p2p={} activation=host-canonical \
5115                                 accumulation=host-canonical output=e-device io=device \
5116                                 performance_claim=false (logged once per layer)",
5117                                tp.devices,
5118                                tp.runtime.transport_label(),
5119                                tp.runtime.native_p2p(),
5120                            );
5121                        }
5122                        return Ok(output);
5123                    }
5124                }
5125            }
5126            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5127                (
5128                    match &tp.experts {
5129                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5130                            tp.runtime.run_tensor_parallel_routes(
5131                                bank,
5132                                &input,
5133                                t,
5134                                &selected,
5135                                &route_weights,
5136                                n_used,
5137                            )?
5138                        }
5139                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5140                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5141                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5142                                    bank,
5143                                    &input,
5144                                    &selected,
5145                                    &route_weights,
5146                                    n_used,
5147                                    tp.activation_limit,
5148                                )?
5149                            } else {
5150                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5151                                    bank,
5152                                    &input,
5153                                    t,
5154                                    &selected,
5155                                    &route_weights,
5156                                    n_used,
5157                                    tp.activation_limit,
5158                                )?
5159                            }
5160                        }
5161                    },
5162                    "tp",
5163                    &tp.devices,
5164                    tp.runtime.transport_label(),
5165                    tp.runtime.native_p2p(),
5166                )
5167            } else {
5168                let ep = m
5169                    .step_ep
5170                    .as_ref()
5171                    .ok_or("Step distributed runtime has no EP or TP state")?;
5172                (
5173                    match &ep.experts {
5174                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5175                            ep.runtime.run_routed_experts(
5176                                bank,
5177                                &input,
5178                                t,
5179                                &selected,
5180                                &route_weights,
5181                                n_used,
5182                                ep.activation_limit,
5183                            )?
5184                        }
5185                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5186                            ep.runtime.run_routed_experts_nvfp4(
5187                                bank,
5188                                &input,
5189                                t,
5190                                &selected,
5191                                &route_weights,
5192                                n_used,
5193                                ep.activation_limit,
5194                            )?
5195                        }
5196                    },
5197                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5198                    &ep.devices,
5199                    ep.runtime.transport_label(),
5200                    ep.runtime.native_p2p(),
5201                )
5202            };
5203            if routed.len() != t * n_embd {
5204                return Err(format!(
5205                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5206                    routed.len()
5207                )
5208                .into());
5209            }
5210            let mut output = e.htod(&routed)?;
5211            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5212            // Once per layer per process: the topology contract line is a boot receipt, not a
5213            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5214            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5215            let layer_bit = 1u64 << (il as u64 % 64);
5216            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5217                == 0
5218            {
5219                eprintln!(
5220                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5221                     expert_transport={transport} native_p2p={native_p2p} \
5222                     activation={} accumulation={} output={} \
5223                     performance_claim=false (logged once per layer)",
5224                    if let Some(ep) = &m.step_ep {
5225                        ep.runtime.expert_activation_label()
5226                    } else {
5227                        "host-canonical"
5228                    },
5229                    if let Some(ep) = &m.step_ep {
5230                        ep.runtime.expert_accumulation_label()
5231                    } else {
5232                        "host-canonical"
5233                    },
5234                    if let Some(ep) = &m.step_ep {
5235                        ep.runtime.expert_output_label()
5236                    } else {
5237                        "host-accumulated"
5238                    },
5239                );
5240                if let Some(ep) = &m.step_ep {
5241                    if let Some(limit) = ep.activation_limit {
5242                        eprintln!(
5243                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5244                             formula=min-silu-times-clamped-up performance_claim=false"
5245                        );
5246                    }
5247                }
5248            }
5249            return Ok(output);
5250        }
5251        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5252            let moe = cfg.moe.as_ref().unwrap();
5253            let n_expert = moe.expert_count as usize;
5254            let n_used = moe.expert_used_count as usize;
5255            let sigmoid = cfg.sigmoid_router().unwrap();
5256            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5257            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5258            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5259        }
5260        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5261        // current caller into this research arm; the naked default stays on the established path.
5262        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5263            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5264            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5265            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5266            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5267            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5268            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5269                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5270                let g_host = e.dtoh(&grouped_out)?;
5271                let s_host = e.dtoh(&seq_out)?;
5272                let g_bytes: &[u8] = unsafe {
5273                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5274                };
5275                let s_bytes: &[u8] = unsafe {
5276                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5277                };
5278                if g_bytes == s_bytes {
5279                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5280                } else {
5281                    let diffs = g_host
5282                        .iter()
5283                        .zip(s_host.iter())
5284                        .enumerate()
5285                        .filter(|(_, (a, b))| a != b)
5286                        .count();
5287                    let maxdiff = g_host
5288                        .iter()
5289                        .zip(s_host.iter())
5290                        .map(|(a, b)| (a - b).abs())
5291                        .fold(0.0f32, f32::max);
5292                    panic!(
5293                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5294                        g_host.len()
5295                    );
5296                }
5297            }
5298            return Ok(grouped_out);
5299        }
5300        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5301    }
5302
5303    fn sigmoid_resident_dev_eligible(
5304        e: &Engine,
5305        m: &MoeWeights,
5306        cfg: &ModelConfig,
5307        sliding_gated_moe: bool,
5308    ) -> bool {
5309        let Some(moe) = cfg.moe.as_ref() else {
5310            return false;
5311        };
5312        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5313        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5314        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5315        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5316            std::env::var("MEMRA_MOE_STATS").is_ok()
5317                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5318                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5319                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5320                || std::env::var("MEMRA_MOE_GATE").is_ok()
5321        });
5322        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5323            if dev.dev != e.ctx().ordinal() {
5324                return false;
5325            }
5326            let q8 = moe_q8_enabled()
5327                && q8_expert_supported(m.gate_exps.qtype)
5328                && q8_expert_supported(m.up_exps.qtype)
5329                && q8_expert_supported(m.down_exps.qtype);
5330            let fp8 = dev.fp8_blk.is_some()
5331                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5332                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5333                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5334            q8 || fp8
5335        });
5336        sliding_gated_moe
5337            && sigmoid_router_enabled()
5338            && moe_dev_enabled()
5339            && moe_slab_enabled()
5340            && !observation_mode
5341            && moe.expert_used_count <= 8
5342            && m.has_uniform_expert_layout()
5343            && m.gate_exps.macros.is_none()
5344            && m.up_exps.macros.is_none()
5345            && m.down_exps.macros.is_none()
5346            && !m.has_macros
5347            && resident_layout_supported
5348    }
5349
5350    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5351    pub(crate) fn moe_ffn_sequential(
5352        e: &Engine,
5353        m: &MoeWeights,
5354        z: &CudaSlice<f32>,
5355        t: usize,
5356        cfg: &ModelConfig,
5357        il: u16,
5358        max_block: usize,
5359    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5360        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5361    }
5362
5363    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5364    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5365    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5366    fn moe_router_logits(
5367        e: &Engine,
5368        m: &MoeWeights,
5369        z: &CudaSlice<f32>,
5370        t: usize,
5371        cfg: &ModelConfig,
5372    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5373        if t < PRIME_MIN_T {
5374            // Decode and speculative verify use one fixed per-row reduction program.
5375            if crate::router_kernel_on() {
5376                e.router_gemv(
5377                    m.gate_inp.float_data(),
5378                    z,
5379                    cfg.n_embd as usize,
5380                    m.gate_exps.n_expert,
5381                    t,
5382                )
5383            } else {
5384                e.matmul_decode_exact(&m.gate_inp, z, t)
5385            }
5386        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5387            e.router_gemv(
5388                m.gate_inp.float_data(),
5389                z,
5390                cfg.n_embd as usize,
5391                m.gate_exps.n_expert,
5392                t,
5393            )
5394        } else {
5395            e.matmul(&m.gate_inp, z, t)
5396        }
5397    }
5398
5399    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5400    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5401    /// trace is independent of the dispatch optimization selected for the forward.
5402    fn trace_moe_routes(
5403        il: u16,
5404        t: usize,
5405        sel_all: &[u32],
5406        weights: &[f32],
5407    ) -> Result<(), Box<dyn std::error::Error>> {
5408        use std::io::Write as _;
5409        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5410            let mut f = std::fs::OpenOptions::new()
5411                .create(true)
5412                .append(true)
5413                .open(path)?;
5414            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5415            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5416        }
5417        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5418            let mut f = std::fs::OpenOptions::new()
5419                .create(true)
5420                .append(true)
5421                .open(path)?;
5422            let pairs: Vec<String> = sel_all
5423                .iter()
5424                .zip(weights)
5425                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5426                .collect();
5427            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5428        }
5429        Ok(())
5430    }
5431
5432    #[allow(clippy::too_many_arguments)]
5433    fn trace_sigmoid_router_logits(
5434        e: &Engine,
5435        il: u16,
5436        t: usize,
5437        n_expert: usize,
5438        n_used: usize,
5439        logits: &CudaSlice<f32>,
5440        m: &MoeWeights,
5441        (scaling_factor, route_norm): (f32, bool),
5442    ) -> Result<(), Box<dyn std::error::Error>> {
5443        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5444            return Ok(());
5445        }
5446        let logits = e.dtoh(logits)?;
5447        let active: Vec<u8> = m
5448            .active_experts
5449            .as_ref()
5450            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5451            .unwrap_or_else(|| vec![1; n_expert]);
5452        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5453        crate::sigrouter_contract::capture_served_logits(
5454            il as u32,
5455            t,
5456            n_expert,
5457            n_used,
5458            scaling_factor,
5459            route_norm,
5460            &active,
5461            &bias,
5462            &logits,
5463        )?;
5464        Ok(())
5465    }
5466
5467    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
5468    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
5469    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
5470    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
5471    fn trace_moe_input(
5472        e: &Engine,
5473        il: u16,
5474        t: usize,
5475        n_embd: usize,
5476        z: &CudaSlice<f32>,
5477    ) -> Result<(), Box<dyn std::error::Error>> {
5478        use std::io::Write as _;
5479        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
5480            return Ok(());
5481        };
5482        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
5483        let host = e.dtoh_view(&z.slice(0..values))?;
5484        let bytes = unsafe {
5485            std::slice::from_raw_parts(
5486                host.as_ptr().cast::<u8>(),
5487                host.len() * std::mem::size_of::<f32>(),
5488            )
5489        };
5490        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
5491        let mut state = state
5492            .lock()
5493            .map_err(|_| "MoE input trace writer lock is poisoned")?;
5494        if state.is_none() {
5495            let dir = std::path::PathBuf::from(&dir);
5496            std::fs::create_dir_all(&dir)?;
5497            let index = std::fs::OpenOptions::new()
5498                .create(true)
5499                .append(true)
5500                .open(dir.join("index.jsonl"))?;
5501            *state = Some(MoeInputTraceWriter {
5502                dir,
5503                index,
5504                payloads: std::collections::HashMap::new(),
5505            });
5506        }
5507        let writer = state.as_mut().unwrap();
5508        if writer.dir != std::path::Path::new(&dir) {
5509            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
5510        }
5511        let file_name = format!("layer-{il:03}.f32");
5512        if !writer.payloads.contains_key(&il) {
5513            let payload = std::fs::OpenOptions::new()
5514                .create(true)
5515                .append(true)
5516                .open(writer.dir.join(&file_name))?;
5517            let offset = payload.metadata()?.len();
5518            writer.payloads.insert(il, (payload, offset));
5519        }
5520        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
5521        let row_offset = *offset;
5522        payload.write_all(bytes)?;
5523        *offset += bytes.len() as u64;
5524        writeln!(
5525            writer.index,
5526            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
5527             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
5528             \"payload_bytes\":{}}}",
5529            bytes.len()
5530        )?;
5531        Ok(())
5532    }
5533
5534    #[allow(clippy::too_many_arguments)]
5535    pub(crate) fn moe_ffn_sequential_zq8(
5536        e: &Engine,
5537        m: &MoeWeights,
5538        z: &CudaSlice<f32>,
5539        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5540        t: usize,
5541        cfg: &ModelConfig,
5542        il: u16,
5543        max_block: usize,
5544    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5545        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
5546        let moe = cfg.moe.as_ref().unwrap();
5547        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
5548        let n_expert = moe.expert_count as usize; // 256
5549        let n_used = moe.expert_used_count as usize; // 8
5550        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
5551
5552        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
5553        debug_assert_eq!(m.gate_exps.in_f, n_embd);
5554        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
5555        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
5556        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
5557        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
5558
5559        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
5560        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
5561        let lim_exp = cfg.clamp_exp_at(il as u32);
5562        let lim_shexp = cfg.clamp_shexp_at(il as u32);
5563        let use_cache = Engine::moe_cache_enabled();
5564        let uniform_experts = m.has_uniform_expert_layout();
5565        let moe_q8 = uniform_experts
5566            && moe_q8_enabled()
5567            && q8_expert_supported(m.gate_exps.qtype)
5568            && q8_expert_supported(m.up_exps.qtype)
5569            && q8_expert_supported(m.down_exps.qtype);
5570        // Experimental secondary backend: complete experts already resident in the SLRU stay on
5571        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
5572        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
5573        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
5574        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
5575        // commands and CI have no llama.cpp or OpenMP dependency.
5576        let cpu_expert_requested = crate::cpu_experts::configured();
5577        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
5578            return Err(std::io::Error::other(
5579                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
5580            )
5581            .into());
5582        }
5583        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
5584        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
5585        // Those backends are each deterministic but are different numeric configurations, so a
5586        // later prefill eviction can change greedy output. Freeze after the first real prefill;
5587        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
5588        // staging below and cannot change backend assignment.
5589        let freeze_cpu_residency = cpu_expert_requested
5590            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
5591        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
5592            .ok()
5593            .and_then(|value| value.parse::<usize>().ok())
5594            .is_some_and(|tokens| tokens > 0);
5595        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
5596            e.freeze_moe_cache();
5597        }
5598        let cache_frozen = use_cache && e.moe_cache_frozen();
5599        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
5600
5601        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
5602        // cannot change logits, selected expert ids, or routing weights.
5603        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5604        if let Some(sig) = cfg.sigmoid_router() {
5605            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
5606        }
5607
5608        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
5609        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
5610        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
5611        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
5612        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
5613        // per-token host stall that dominated the 35B decode wall after stages 1+2.
5614        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
5615        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
5616        // only difference is where sel/w/pointers are READ from (device instead of params).
5617        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
5618        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
5619        // Any non-resident layer falls through to host routing + the gdec/sequential path.
5620        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
5621        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
5622        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
5623        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
5624        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
5625        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
5626        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
5627        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
5628        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
5629        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
5630        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
5631        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
5632        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
5633        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
5634        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
5635        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
5636        // now rides the dev loop below (same kernels per token as decode); pairs serves real
5637        // prefill (t >= 16, where spec never verifies).
5638        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
5639        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
5640        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
5641        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
5642        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
5643        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
5644        // ride the macro-aware sequential/staged paths below or every expert output is off by
5645        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
5646        let no_exp_macros = m.gate_exps.macros.is_none()
5647            && m.up_exps.macros.is_none()
5648            && m.down_exps.macros.is_none();
5649        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
5650        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
5651        // so it cannot even see the per-layer limit.
5652        if cfg.sigmoid_router().is_none()
5653            && cfg.m3.is_none()
5654            && cfg.hy3.is_none()
5655            && !cfg.swiglu_clamped_at(il as u32)
5656            && no_exp_macros
5657            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
5658            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
5659            // pairs serves real prefill from 17 up.
5660            && t > MOE_DEV_MAX_T
5661            && m.dev_exps.is_some()
5662            && moe_q8_enabled()
5663            && q8_expert_supported(m.gate_exps.qtype)
5664            && q8_expert_supported(m.up_exps.qtype)
5665            && q8_expert_supported(m.down_exps.qtype)
5666            && std::env::var("MEMRA_MOE_PAIRS")
5667                .map(|v| v != "0")
5668                .unwrap_or(true)
5669            && std::env::var("MEMRA_MOE_STATS").is_err()
5670        {
5671            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
5672        }
5673
5674        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
5675        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
5676        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
5677        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
5678        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
5679        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
5680        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
5681        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
5682        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
5683        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
5684        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
5685        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
5686        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
5687        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
5688        // Keyed off sigmoid_router() so arch #4 is denied by construction.
5689        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
5690        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
5691        let dev_ok = uniform_experts
5692            && cfg.sigmoid_router().is_none()
5693            && cfg.m3.is_none()
5694            && cfg.hy3.is_none()
5695            && !cfg.swiglu_clamped_at(il as u32);
5696        // Observation modes must route through the host-visible selection below. Otherwise a fully
5697        // resident layer returns through device dispatch before its trace/stats row is recorded,
5698        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
5699        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
5700            || std::env::var("MEMRA_MOE_TRACE").is_ok()
5701            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5702            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
5703        if dev_ok
5704            && t <= MOE_DEV_MAX_T
5705            && m.dev_exps.is_some()
5706            && n_used <= 8
5707            && moe_dev_enabled()
5708            && !observe_routes
5709        {
5710            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5711        }
5712        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
5713            let row_ok = e.with_moe_cache(max_block, |c, eng| {
5714                if moe_prewarm_enabled() {
5715                    c.prewarm_layer(il, m, eng)?;
5716                }
5717                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
5718            })?;
5719            if row_ok {
5720                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5721            }
5722        }
5723
5724        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
5725        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
5726            if cpu_hybrid {
5727                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
5728                    e,
5729                    &logits,
5730                    z,
5731                    t,
5732                    n_embd,
5733                    n_expert,
5734                    n_used,
5735                    m.exp_probs_b.as_deref(),
5736                    sig,
5737                    m.active_experts.as_deref(),
5738                )?;
5739                (sel, w, Some(input))
5740            } else {
5741                let (sel, w) =
5742                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
5743                (sel, w, None)
5744            }
5745        } else {
5746            let (sel, w) =
5747                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
5748            (sel, w, None)
5749        };
5750        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
5751
5752        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
5753        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
5754        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
5755        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
5756        Self::trace_moe_input(e, il, t, n_embd, z)?;
5757
5758        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
5759        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
5760        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
5761        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
5762        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
5763        // wait for each pending block, so later copies can overlap the earlier expert kernels while
5764        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
5765        // T=1; batched forwards can have token-local consumers still in flight between selections.
5766        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
5767        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
5768        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
5769        let worker_disk_prefetch =
5770            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
5771        let promote_worker_h2d =
5772            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
5773        if promote_worker_h2d {
5774            let mut selected_blocks = Vec::with_capacity(n_used * 3);
5775            for &ex in sel_all.iter().take(n_used) {
5776                let ex = ex as u16;
5777                selected_blocks.extend([
5778                    BlockId::new(il, PROJ_GATE, ex),
5779                    BlockId::new(il, PROJ_UP, ex),
5780                    BlockId::new(il, PROJ_DOWN, ex),
5781                ]);
5782            }
5783            for &ex in sel_all.iter().take(n_used) {
5784                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
5785            }
5786            e.with_moe_cache(max_block, |cache, eng| {
5787                cache.promote_worker_reads_at_safe_boundary(
5788                    &selected_blocks,
5789                    &selected_blocks,
5790                    eng,
5791                )?;
5792                Ok(())
5793            })?;
5794        }
5795
5796        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
5797        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
5798        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
5799            let mut cnt = vec![0u32; n_expert];
5800            for &s in sel_all.iter() {
5801                cnt[s as usize] += 1;
5802            }
5803            let total = sel_all.len() as f64;
5804            let mut h = 0.0f64;
5805            let mut active = 0usize;
5806            for &c in &cnt {
5807                if c > 0 {
5808                    active += 1;
5809                    let p = c as f64 / total;
5810                    h -= p * p.log2();
5811                }
5812            }
5813            let maxc = cnt.iter().copied().max().unwrap_or(0);
5814            println!(
5815                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
5816                il,
5817                t,
5818                sel_all.len(),
5819                active,
5820                n_expert,
5821                h,
5822                (n_expert as f64).log2(),
5823                total / active.max(1) as f64,
5824                maxc
5825            );
5826        }
5827
5828        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
5829        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
5830        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
5831        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
5832        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
5833        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
5834        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
5835        // zeroed-then-accumulated exactly as before (fallback).
5836        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
5837        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
5838        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
5839        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
5840        let gdec_may_fire = uniform_experts
5841            && use_cache
5842            && n_used <= 8
5843            && gdec_enabled()
5844            && !cfg.swiglu_clamped_at(il as u32);
5845        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
5846        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
5847        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
5848        // archs the slabs were uploaded but never read, and every expert went through the
5849        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
5850        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
5851        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
5852        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
5853        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
5854        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
5855        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
5856        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
5857        // strictly worse than staging); under PP-2 without the prime walker this admits
5858        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
5859        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
5860        let slab_local = m
5861            .dev_exps
5862            .as_ref()
5863            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
5864        let slab_bases = slab_local.map(|d| {
5865            use cudarc::driver::DevicePtr;
5866            let s = e.stream();
5867            let (pg, _g0) = d.gate.device_ptr(&s);
5868            let (pu, _g1) = d.up.device_ptr(&s);
5869            let (pd, _g2) = d.down.device_ptr(&s);
5870            (pg as u64, pu as u64, pd as u64)
5871        });
5872        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
5873        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
5874        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
5875        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
5876        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
5877        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
5878        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
5879        // all-resident tokens, staged loop for misses), which is a dispatch-class
5880        // comparison, not a provenance one.
5881        let slab_fused_may_fire = slab_bases.is_some()
5882            && n_used <= 8
5883            && gdec_enabled()
5884            && !cfg.swiglu_clamped_at(il as u32)
5885            && cfg.m3.is_none()
5886            && no_exp_macros
5887            && moe_q8;
5888        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
5889        // uninit; a token that falls through to any accumulating loop zeroes its own row.
5890        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
5891            e.uninit(t * n_embd)?
5892        } else {
5893            e.zeros(t * n_embd)?
5894        };
5895        // The router readback above already established a host boundary. Copy each small-t hidden
5896        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
5897        let cpu_input = if cpu_hybrid {
5898            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
5899        } else {
5900            None
5901        };
5902
5903        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
5904        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
5905        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
5906        // measured ~123 memsets/token of the decode wall).
5907        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
5908        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
5909        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
5910        let mut scratch_g: Option<CudaSlice<u8>> = None;
5911        let mut scratch_u: Option<CudaSlice<u8>> = None;
5912        let mut scratch_d: Option<CudaSlice<u8>> = None;
5913        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
5914        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
5915
5916        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
5917        // the copy stream before launching the current expert's compute. Pending slots stay invisible
5918        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
5919        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
5920        let page_window = moe_page_prefetch_window();
5921
5922        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
5923        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
5924        for tok in 0..t {
5925            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
5926            let w = &w_all[tok * n_used..(tok + 1) * n_used];
5927            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
5928            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
5929
5930            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
5931            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
5932            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
5933            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
5934            // memcpy, zero admission, so no slot can move under the collected pointers) — any
5935            // miss falls through to the sequential loop below, which admits as before. In steady
5936            // state on a fully-resident rig every token-layer takes the grouped path.
5937            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
5938            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
5939            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
5940            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
5941            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
5942            // per-expert macro-scales the fused kernels don't fold — those fall through too.
5943            let no_macros = m.gate_exps.macros.is_none()
5944                && m.up_exps.macros.is_none()
5945                && m.down_exps.macros.is_none();
5946            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
5947            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
5948            // with pointers computed from the resident slab base + ex*stride instead of
5949            // collected SLRU slot addresses. No cache lock, no residency predicate — the
5950            // slab holds every expert by construction, so this arm never falls through
5951            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
5952            // staging both die). Bit-identity class: pointer provenance only, the same
5953            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
5954            // slab exists it is strictly better (no lock, no miss).
5955            if slab_fused_may_fire {
5956                let (pg, pu, pd) = slab_bases.unwrap();
5957                let mut gp = [0u64; 8];
5958                let mut up = [0u64; 8];
5959                let mut dp = [0u64; 8];
5960                for (j, &ex) in sel.iter().enumerate() {
5961                    let ex = ex as usize;
5962                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
5963                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
5964                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
5965                }
5966                let mut wv = [0f32; 8];
5967                wv[..n_used].copy_from_slice(w);
5968                if tok_q8.is_none() {
5969                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
5970                }
5971                let (zq, zd) = tok_q8.as_ref().unwrap();
5972                let act = e.moe_gate_up_silu8_q8(
5973                    crate::WPtr8(gp),
5974                    crate::WPtr8(up),
5975                    zq,
5976                    zd,
5977                    n_embd,
5978                    n_ff_exp,
5979                    n_used,
5980                    m.gate_exps.qtype,
5981                    m.up_exps.qtype,
5982                    m.gate_exps.row_bytes,
5983                    m.up_exps.row_bytes,
5984                )?;
5985                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
5986                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
5987                e.moe_down8_fma_q8(
5988                    crate::WPtr8(dp),
5989                    crate::F32x8(wv),
5990                    &aq2,
5991                    &ad2,
5992                    &mut dst,
5993                    n_ff_exp,
5994                    n_embd,
5995                    n_used,
5996                    m.down_exps.qtype,
5997                    m.down_exps.row_bytes,
5998                )?;
5999                continue;
6000            }
6001            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6002                if tok_q8.is_none() {
6003                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6004                }
6005                let (zq, zd) = tok_q8.as_ref().unwrap();
6006                if Self::moe_gdec_token_q8(
6007                    e,
6008                    m,
6009                    il,
6010                    max_block,
6011                    zq,
6012                    zd,
6013                    sel,
6014                    w,
6015                    &mut moe_out,
6016                    tok,
6017                    n_embd,
6018                    n_ff_exp,
6019                    n_used,
6020                )? {
6021                    continue;
6022                }
6023            } else if gdec_may_fire
6024                && cfg.m3.is_none()
6025                && no_macros
6026                && Self::moe_gdec_token(
6027                    e,
6028                    m,
6029                    il,
6030                    max_block,
6031                    &zt,
6032                    sel,
6033                    w,
6034                    &mut moe_out,
6035                    tok,
6036                    n_embd,
6037                    n_ff_exp,
6038                    n_used,
6039                )?
6040            {
6041                continue;
6042            }
6043
6044            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6045            // slab pair could fire. This token fell through to a sequential axpy loop, which
6046            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6047            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6048            // has no fallible predicate), included for the allocation invariant's symmetry.
6049            if gdec_may_fire || slab_fused_may_fire {
6050                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6051                e.memset_zeros_view(&mut row)?;
6052            }
6053
6054            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6055            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6056            // stall this path exists to remove, while mixing projections would require another
6057            // activation round-trip. Weight addresses remain valid until this worker is joined at
6058            // the bottom of the token scope.
6059            let mut cpu_mask = vec![false; sel.len()];
6060            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6061                let gpu_resident = if use_cache {
6062                    e.with_moe_cache(max_block, |cache, _| {
6063                        Ok(sel
6064                            .iter()
6065                            .map(|&expert| {
6066                                let expert = expert as u16;
6067                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6068                                    .into_iter()
6069                                    .filter(|&projection| {
6070                                        cache
6071                                            .resident(BlockId::new(il, projection, expert))
6072                                            .is_some()
6073                                    })
6074                                    .count()
6075                            })
6076                            .collect::<Vec<_>>())
6077                    })?
6078                } else {
6079                    vec![0; sel.len()]
6080                };
6081                let mut cpu_selected = Vec::new();
6082                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6083                    if gpu_resident[index] != 3 {
6084                        cpu_mask[index] = true;
6085                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6086                        let expert = expert as usize;
6087                        cpu_selected.push((expert, route_weight));
6088                    }
6089                }
6090                if crate::cpu_experts::predictor_enabled() {
6091                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6092                    // from this layer's MoE input and prefetches predicted-and-missing
6093                    // experts into the companion RAM cache. Never blocks this thread.
6094                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6095                    crate::cpu_experts::predictor_submit(il, row);
6096                }
6097                if cpu_selected.is_empty() {
6098                    None
6099                } else {
6100                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6101                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6102                        .map_err(std::io::Error::other)?;
6103                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6104                }
6105            } else {
6106                None
6107            };
6108
6109            let worker_window = worker_disk_prefetch
6110                .then(worker_prefetch_window)
6111                .unwrap_or(0);
6112            for (j, &ex) in sel.iter().enumerate() {
6113                if cpu_mask[j] {
6114                    continue;
6115                }
6116                let ex = ex as usize;
6117                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6118                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6119                // fused form) and macro-carrying artifacts — still have their bytes in the
6120                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6121                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6122                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6123                if let Some(d) = slab_local {
6124                    let gl = m.gate_exps.expert_layout(ex);
6125                    let ul = m.up_exps.expert_layout(ex);
6126                    let dl = m.down_exps.expert_layout(ex);
6127                    let (g0, u0, d0) = (
6128                        ex * m.gate_exps.expert_stride,
6129                        ex * m.up_exps.expert_stride,
6130                        ex * m.down_exps.expert_stride,
6131                    );
6132                    let (gate, up) = if moe_q8 {
6133                        if tok_q8.is_none() {
6134                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6135                        }
6136                        let (zq, zd) = tok_q8.as_ref().unwrap();
6137                        (
6138                            e.qmatvec_expert_q8(
6139                                &d.gate,
6140                                g0..g0 + gl.len,
6141                                zq,
6142                                zd,
6143                                1,
6144                                m.gate_exps.in_f,
6145                                m.gate_exps.out_f,
6146                                gl.qtype,
6147                                gl.row_bytes,
6148                            )?,
6149                            e.qmatvec_expert_q8(
6150                                &d.up,
6151                                u0..u0 + ul.len,
6152                                zq,
6153                                zd,
6154                                1,
6155                                m.up_exps.in_f,
6156                                m.up_exps.out_f,
6157                                ul.qtype,
6158                                ul.row_bytes,
6159                            )?,
6160                        )
6161                    } else {
6162                        (
6163                            e.qmatvec_view(
6164                                &d.gate,
6165                                g0..g0 + gl.len,
6166                                &zt,
6167                                1,
6168                                m.gate_exps.in_f,
6169                                m.gate_exps.out_f,
6170                                gl.qtype,
6171                                gl.row_bytes,
6172                            )?,
6173                            e.qmatvec_view(
6174                                &d.up,
6175                                u0..u0 + ul.len,
6176                                &zt,
6177                                1,
6178                                m.up_exps.in_f,
6179                                m.up_exps.out_f,
6180                                ul.qtype,
6181                                ul.row_bytes,
6182                            )?,
6183                        )
6184                    };
6185                    let mut act = e.uninit(n_ff_exp)?;
6186                    Self::ffn_act_lim(
6187                        e,
6188                        cfg,
6189                        &gate,
6190                        &up,
6191                        m.gate_exps.macro_scale(ex),
6192                        m.up_exps.macro_scale(ex),
6193                        lim_exp,
6194                        &mut act,
6195                        n_ff_exp,
6196                    )?;
6197                    let y = if moe_q8 {
6198                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6199                        e.qmatvec_expert_q8(
6200                            &d.down,
6201                            d0..d0 + dl.len,
6202                            &aq2,
6203                            &ad2,
6204                            1,
6205                            m.down_exps.in_f,
6206                            m.down_exps.out_f,
6207                            dl.qtype,
6208                            dl.row_bytes,
6209                        )?
6210                    } else {
6211                        let actv = act.slice(0..n_ff_exp);
6212                        e.qmatvec_view(
6213                            &d.down,
6214                            d0..d0 + dl.len,
6215                            &actv,
6216                            1,
6217                            m.down_exps.in_f,
6218                            m.down_exps.out_f,
6219                            dl.qtype,
6220                            dl.row_bytes,
6221                        )?
6222                    };
6223                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6224                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6225                    continue;
6226                }
6227                for next in page_prefetch_positions(j, sel.len(), page_window) {
6228                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6229                }
6230                let keep = [
6231                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6232                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6233                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6234                ];
6235                if worker_disk_prefetch && worker_window > 0 {
6236                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6237                        Self::moe_prefetch_disk_expert(
6238                            e,
6239                            il,
6240                            sel[next] as usize,
6241                            m,
6242                            max_block,
6243                            &keep,
6244                        )?;
6245                    }
6246                } else if cache_dispatch
6247                    && !cpu_hybrid
6248                    && moe_prefetch_enabled()
6249                    && j + 1 < sel.len()
6250                {
6251                    let next = sel[j + 1] as usize;
6252                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6253                }
6254                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6255                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6256                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6257                    // layouts stay on the metadata-aware f32 path.
6258                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6259                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6260                    }
6261                    let gate = if gate_q8 {
6262                        let (zq, zd) = tok_q8.as_ref().unwrap();
6263                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6264                    } else {
6265                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6266                    };
6267                    let up = if up_q8 {
6268                        let (zq, zd) = tok_q8.as_ref().unwrap();
6269                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6270                    } else {
6271                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6272                    };
6273                    let mut act = e.uninit(n_ff_exp)?;
6274                    Self::ffn_act_lim(
6275                        e,
6276                        cfg,
6277                        &gate,
6278                        &up,
6279                        m.gate_exps.macro_scale(ex),
6280                        m.up_exps.macro_scale(ex),
6281                        lim_exp,
6282                        &mut act,
6283                        n_ff_exp,
6284                    )?;
6285                    let y = if down_q8 {
6286                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6287                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6288                    } else {
6289                        let actv = act.slice(0..n_ff_exp);
6290                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6291                    };
6292                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6293                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6294                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6295                } else if cache_dispatch {
6296                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6297                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6298                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6299                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6300                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6301                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6302                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6303                    Self::ffn_act_lim(
6304                        e,
6305                        cfg,
6306                        &gate,
6307                        &up,
6308                        m.gate_exps.macro_scale(ex),
6309                        m.up_exps.macro_scale(ex),
6310                        lim_exp,
6311                        &mut act,
6312                        n_ff_exp,
6313                    )?;
6314                    let actv = act.slice(0..n_ff_exp);
6315                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6316                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6317                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6318                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6319                } else if cache_frozen {
6320                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6321                    // first prime. Reuse every fixed resident projection directly and stage only a
6322                    // true miss through the ordinary scratch slot. This preserves the established
6323                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6324                    let gate = Self::moe_frozen_gemm(
6325                        e,
6326                        il,
6327                        PROJ_GATE,
6328                        ex,
6329                        m,
6330                        max_block,
6331                        &zt,
6332                        &mut scratch_g,
6333                        g_len,
6334                    )?;
6335                    let up = Self::moe_frozen_gemm(
6336                        e,
6337                        il,
6338                        PROJ_UP,
6339                        ex,
6340                        m,
6341                        max_block,
6342                        &zt,
6343                        &mut scratch_u,
6344                        u_len,
6345                    )?;
6346                    let mut act = e.uninit(n_ff_exp)?;
6347                    Self::ffn_act_lim(
6348                        e,
6349                        cfg,
6350                        &gate,
6351                        &up,
6352                        m.gate_exps.macro_scale(ex),
6353                        m.up_exps.macro_scale(ex),
6354                        lim_exp,
6355                        &mut act,
6356                        n_ff_exp,
6357                    )?;
6358                    let actv = act.slice(0..n_ff_exp);
6359                    let y = Self::moe_frozen_gemm(
6360                        e,
6361                        il,
6362                        PROJ_DOWN,
6363                        ex,
6364                        m,
6365                        max_block,
6366                        &actv,
6367                        &mut scratch_d,
6368                        d_len,
6369                    )?;
6370                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6371                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6372                } else {
6373                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6374                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6375                    // fully overwrites the byte range the GEMM reads).
6376                    if scratch_g.is_none() {
6377                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6378                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6379                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6380                    }
6381                    let (sg, su, sd) = (
6382                        scratch_g.as_mut().unwrap(),
6383                        scratch_u.as_mut().unwrap(),
6384                        scratch_d.as_mut().unwrap(),
6385                    );
6386                    let gl = m.gate_exps.expert_layout(ex);
6387                    let ul = m.up_exps.expert_layout(ex);
6388                    let dl = m.down_exps.expert_layout(ex);
6389                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6390                    let gate = e.qmatvec_view(
6391                        sg,
6392                        0..gl.len,
6393                        &zt,
6394                        1,
6395                        m.gate_exps.in_f,
6396                        m.gate_exps.out_f,
6397                        gl.qtype,
6398                        gl.row_bytes,
6399                    )?;
6400
6401                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6402                    let up = e.qmatvec_view(
6403                        su,
6404                        0..ul.len,
6405                        &zt,
6406                        1,
6407                        m.up_exps.in_f,
6408                        m.up_exps.out_f,
6409                        ul.qtype,
6410                        ul.row_bytes,
6411                    )?;
6412
6413                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6414                    Self::ffn_act_lim(
6415                        e,
6416                        cfg,
6417                        &gate,
6418                        &up,
6419                        m.gate_exps.macro_scale(ex),
6420                        m.up_exps.macro_scale(ex),
6421                        lim_exp,
6422                        &mut act,
6423                        n_ff_exp,
6424                    )?;
6425
6426                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6427                    let actv = act.slice(0..n_ff_exp);
6428                    let y = e.qmatvec_view(
6429                        sd,
6430                        0..dl.len,
6431                        &actv,
6432                        1,
6433                        m.down_exps.in_f,
6434                        m.down_exps.out_f,
6435                        dl.qtype,
6436                        dl.row_bytes,
6437                    )?;
6438
6439                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6440                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6441                }
6442            }
6443            if let Some(worker) = cpu_worker {
6444                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6445                let cpu_output = e.htod(&cpu_output)?;
6446                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6447                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6448            }
6449            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6450                for (j, &ex) in sel.iter().enumerate() {
6451                    if cpu_mask[j] {
6452                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6453                    }
6454                }
6455            }
6456        }
6457
6458        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6459        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6460        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6461        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6462        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6463            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
6464        {
6465            let n_ff_sh = gate_shexp.out_features(); // 512
6466            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
6467            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
6468            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
6469            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
6470            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
6471            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
6472            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
6473            let verify_t = t > 1 && t < PRIME_MIN_T;
6474            let (sg_gate, sg_up) = if t == 1 {
6475                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
6476            } else if verify_t {
6477                (
6478                    e.matmul_decode_exact(gate_shexp, z, t)?,
6479                    e.matmul_decode_exact(up_shexp, z, t)?,
6480                )
6481            } else {
6482                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
6483            };
6484            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
6485            Self::ffn_act_lim(
6486                e,
6487                cfg,
6488                &sg_gate,
6489                &sg_up,
6490                1.0,
6491                1.0,
6492                lim_shexp,
6493                &mut sa,
6494                t * n_ff_sh,
6495            )?;
6496            let sh = if verify_t {
6497                e.matmul_decode_exact(down_shexp, &sa, t)?
6498            } else {
6499                e.matmul(down_shexp, &sa, t)?
6500            }; // [T, n_embd]
6501
6502            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
6503            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
6504            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
6505            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
6506            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
6507            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
6508            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
6509            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
6510            // expert's contribution into every token's residual, so under cross-request
6511            // concat prefill a session's hidden state depended on its co-arrivals' token
6512            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
6513            let g = match &m.gate_inp_shexp {
6514                Some(gate_inp_shexp) => {
6515                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
6516                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
6517                    } else {
6518                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
6519                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
6520                        e.sigmoid(&gs, &mut g, t)?;
6521                        g
6522                    }
6523                }
6524                None => e.htod(&vec![1.0f32; t])?,
6525            };
6526            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
6527            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
6528        }
6529
6530        Ok(moe_out)
6531    }
6532
6533    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
6534    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
6535    pub fn stage1_h2d_per_token(&self) -> u64 {
6536        use crate::hybrid::Ffn;
6537        let n_used = self
6538            .cfg
6539            .moe
6540            .as_ref()
6541            .map(|m| m.expert_used_count as u64)
6542            .unwrap_or(0);
6543        let mut bytes = 0u64;
6544        for l in self.layers.iter() {
6545            if let Ffn::Moe(m) = &l.ffn {
6546                bytes += n_used
6547                    * (m.gate_exps.max_expert_bytes()
6548                        + m.up_exps.max_expert_bytes()
6549                        + m.down_exps.max_expert_bytes()) as u64;
6550            }
6551        }
6552        bytes
6553    }
6554
6555    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
6556    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
6557    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
6558    pub(crate) fn max_moe_block(&self) -> usize {
6559        use crate::hybrid::Ffn;
6560        let mut mx = 0usize;
6561        let mut scan = |ffn: &Ffn| {
6562            if let Ffn::Moe(m) = ffn {
6563                mx = mx
6564                    .max(m.gate_exps.max_expert_bytes())
6565                    .max(m.up_exps.max_expert_bytes())
6566                    .max(m.down_exps.max_expert_bytes());
6567            }
6568        };
6569        for l in self.layers.iter() {
6570            scan(&l.ffn);
6571        }
6572        if let Some(mtp) = self.mtp.as_ref() {
6573            scan(&mtp.ffn);
6574        }
6575        mx
6576    }
6577
6578    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
6579    /// but have no bytes and therefore consume no residency slot.
6580    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
6581        use crate::hybrid::Ffn;
6582        let mut sizes = Vec::new();
6583        let mut scan = |ffn: &Ffn| {
6584            let Ffn::Moe(m) = ffn else { return };
6585            for ex in 0..m.gate_exps.n_expert {
6586                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
6587                    continue;
6588                }
6589                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
6590                    let len = exps.expert_layout(ex).len;
6591                    if len > 0 {
6592                        sizes.push(len);
6593                    }
6594                }
6595            }
6596        };
6597        for layer in &self.layers {
6598            scan(&layer.ffn);
6599        }
6600        if let Some(mtp) = &self.mtp {
6601            scan(&mtp.ffn);
6602        }
6603        sizes
6604    }
6605
6606    /// Persist the frozen residency set so a later process can restage it directly and skip
6607    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
6608    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
6609    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
6610    /// post-freeze argmax gate still validates the serving assignment.
6611    pub fn save_cpu_expert_residency_profile(
6612        &self,
6613        e: &Engine,
6614        path: &std::path::Path,
6615    ) -> Result<(), Box<dyn std::error::Error>> {
6616        let Some(ids) = e.export_moe_residency() else {
6617            return Err("no MoE residency cache to persist".into());
6618        };
6619        let mut body = format!(
6620            "memra-freeze-profile v1 max_block={} blocks={}\n",
6621            self.max_moe_block(),
6622            ids.len()
6623        );
6624        for (layer, proj, ex) in &ids {
6625            body.push_str(&format!("{layer} {proj} {ex}\n"));
6626        }
6627        let tmp = path.with_extension("tmp");
6628        std::fs::write(&tmp, body)?;
6629        std::fs::rename(&tmp, path)?;
6630        println!(
6631            "[moe-cache] freeze profile saved: {} blocks -> {}",
6632            ids.len(),
6633            path.display()
6634        );
6635        Ok(())
6636    }
6637
6638    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
6639    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
6640    /// missing or its header does not match this model's slot geometry.
6641    pub fn restore_cpu_expert_residency_profile(
6642        &self,
6643        e: &Engine,
6644        path: &std::path::Path,
6645    ) -> Result<bool, Box<dyn std::error::Error>> {
6646        use crate::hybrid::Ffn;
6647        use crate::moe_cache::BlockId;
6648        let Ok(content) = std::fs::read_to_string(path) else {
6649            return Ok(false);
6650        };
6651        let mut lines = content.lines();
6652        let Some(header) = lines.next() else {
6653            return Ok(false);
6654        };
6655        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
6656        if !header.starts_with(&expected) {
6657            println!(
6658                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
6659                path.display()
6660            );
6661            return Ok(false);
6662        }
6663        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
6664            std::collections::HashMap::new();
6665        for line in lines {
6666            let mut fields = line.split_whitespace();
6667            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
6668            else {
6669                continue;
6670            };
6671            let (Ok(layer), Ok(proj), Ok(ex)) =
6672                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
6673            else {
6674                continue;
6675            };
6676            by_layer
6677                .entry(layer)
6678                .or_default()
6679                .push(BlockId::new(layer, proj, ex));
6680        }
6681        let requested: usize = by_layer.values().map(Vec::len).sum();
6682        if requested == 0 {
6683            return Ok(false);
6684        }
6685        let max_block = self.max_moe_block();
6686        let mut restaged = 0usize;
6687        let mut stage_layer =
6688            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
6689                let Ffn::Moe(m) = ffn else { return Ok(()) };
6690                let Some(ids) = by_layer.get(&layer_index) else {
6691                    return Ok(());
6692                };
6693                e.with_moe_cache(max_block, |cache, eng| {
6694                    for id in ids {
6695                        if cache.restage_block(*id, m, eng)? {
6696                            restaged += 1;
6697                        }
6698                    }
6699                    Ok(())
6700                })
6701            };
6702        for (index, layer) in self.layers.iter().enumerate() {
6703            stage_layer(index as u16, &layer.ffn)?;
6704        }
6705        if let Some(mtp) = self.mtp.as_ref() {
6706            stage_layer(u16::MAX, &mtp.ffn)?;
6707        }
6708        e.freeze_moe_cache();
6709        println!(
6710            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
6711            path.display()
6712        );
6713        Ok(true)
6714    }
6715
6716    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
6717    pub fn freeze_cpu_expert_residency(
6718        &self,
6719        e: &Engine,
6720    ) -> Result<(), Box<dyn std::error::Error>> {
6721        e.freeze_moe_cache();
6722        Ok(())
6723    }
6724
6725    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
6726    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
6727    /// the model's activation exactly.
6728    ///
6729    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
6730    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
6731    /// form for anything that can land on a clamped layer.
6732    pub fn ffn_act(
6733        e: &Engine,
6734        cfg: &ModelConfig,
6735        gate: &CudaSlice<f32>,
6736        up: &CudaSlice<f32>,
6737        act: &mut CudaSlice<f32>,
6738        n: usize,
6739    ) -> Result<(), Box<dyn std::error::Error>> {
6740        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
6741    }
6742
6743    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
6744    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
6745    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
6746    #[allow(clippy::too_many_arguments)]
6747    pub(crate) fn ffn_act_scaled(
6748        e: &Engine,
6749        cfg: &ModelConfig,
6750        gate: &CudaSlice<f32>,
6751        up: &CudaSlice<f32>,
6752        gs: f32,
6753        us: f32,
6754        act: &mut CudaSlice<f32>,
6755        n: usize,
6756    ) -> Result<(), Box<dyn std::error::Error>> {
6757        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
6758    }
6759
6760    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
6761    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
6762    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
6763    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
6764    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
6765    ///                 arrays are SEPARATE and a layer can have one without the other.
6766    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
6767    /// already known live.
6768    #[allow(clippy::too_many_arguments)]
6769    pub(crate) fn ffn_act_lim(
6770        e: &Engine,
6771        cfg: &ModelConfig,
6772        gate: &CudaSlice<f32>,
6773        up: &CudaSlice<f32>,
6774        gs: f32,
6775        us: f32,
6776        limit: Option<f32>,
6777        act: &mut CudaSlice<f32>,
6778        n: usize,
6779    ) -> Result<(), Box<dyn std::error::Error>> {
6780        if let Some(m3) = cfg.m3.as_ref() {
6781            debug_assert!(
6782                limit.is_none(),
6783                "m3 swigluoai and step35 clamp are different archs"
6784            );
6785            return e.swigluoai_mul_scaled(
6786                gate,
6787                up,
6788                gs,
6789                us,
6790                m3.swiglu_alpha,
6791                m3.swiglu_limit,
6792                act,
6793                n,
6794            );
6795        }
6796        if let Some(l) = limit {
6797            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
6798        }
6799        if gs == 1.0 && us == 1.0 {
6800            return e.silu_mul(gate, up, act, n);
6801        }
6802        e.silu_mul_scaled(gate, up, gs, us, act, n)
6803    }
6804
6805    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
6806    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
6807    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
6808    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
6809    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
6810    fn moe_route(
6811        e: &Engine,
6812        logits: &CudaSlice<f32>,
6813        t: usize,
6814        n_expert: usize,
6815        n_used: usize,
6816    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6817        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
6818    }
6819
6820    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
6821    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
6822    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
6823    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
6824    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
6825    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
6826    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
6827    #[allow(clippy::too_many_arguments)]
6828    fn moe_route_sigmoid_cfg(
6829        e: &Engine,
6830        logits: &CudaSlice<f32>,
6831        t: usize,
6832        n_expert: usize,
6833        n_used: usize,
6834        m: &MoeWeights,
6835        (sf, route_norm): (f32, bool),
6836    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6837        if sigmoid_router_enabled() {
6838            return e.moe_router_sigmoid_topk_host(
6839                logits,
6840                t,
6841                n_expert,
6842                n_used,
6843                m.active_count(),
6844                &m.exp_probs_b_dev,
6845                &m.active_experts_dev,
6846                sf,
6847                route_norm,
6848            );
6849        }
6850        let lg = e.dtoh(logits)?;
6851        Self::moe_route_sigmoid_host(
6852            &lg,
6853            t,
6854            n_expert,
6855            n_used,
6856            m.exp_probs_b.as_deref(),
6857            sf,
6858            route_norm,
6859            m.active_experts.as_deref(),
6860        )
6861    }
6862
6863    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
6864    /// the existing softmax device kernel has no mask input.
6865    fn moe_route_cfg(
6866        e: &Engine,
6867        logits: &CudaSlice<f32>,
6868        t: usize,
6869        n_expert: usize,
6870        n_used: usize,
6871        active: Option<&[bool]>,
6872    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6873        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
6874        // rollback) via the single-sync pinned readback — softmax arch only.
6875        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
6876            return e.moe_router_topk_host(logits, t, n_expert, n_used);
6877        }
6878        // Host oracle (the §D bit-identity reference).
6879        let lg = e.dtoh(logits)?; // [T*n_expert] host
6880        let mut sel = vec![0u32; t * n_used];
6881        let mut w_out = vec![0f32; t * n_used];
6882        for tok in 0..t {
6883            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
6884            // softmax over ALL n_expert (stable: subtract max)
6885            let maxl = row
6886                .iter()
6887                .enumerate()
6888                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
6889                .map(|(_, &x)| x)
6890                .fold(f32::NEG_INFINITY, f32::max);
6891            let mut probs = vec![0f32; n_expert];
6892            let mut den = 0f32;
6893            for i in 0..n_expert {
6894                if active.is_some_and(|mask| !mask[i]) {
6895                    continue;
6896                }
6897                let x = (row[i] - maxl).exp();
6898                probs[i] = x;
6899                den += x;
6900            }
6901            for p in probs.iter_mut() {
6902                *p /= den;
6903            }
6904            // stable DESC sort: prob DESC, ascending-index tiebreak.
6905            let mut idx: Vec<usize> = (0..n_expert)
6906                .filter(|&i| active.is_none_or(|mask| mask[i]))
6907                .collect();
6908            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
6909            let sl = &idx[..n_used];
6910            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
6911            let mut ws: f32 = wv.iter().sum();
6912            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
6913            for x in wv.iter_mut() {
6914                *x /= ws;
6915            }
6916            for j in 0..n_used {
6917                sel[tok * n_used + j] = sl[j] as u32;
6918                w_out[tok * n_used + j] = wv[j];
6919            }
6920        }
6921        Ok((sel, w_out))
6922    }
6923
6924    #[allow(clippy::too_many_arguments)]
6925    fn moe_route_sigmoid_with_input(
6926        e: &Engine,
6927        logits: &CudaSlice<f32>,
6928        input: &CudaSlice<f32>,
6929        t: usize,
6930        in_features: usize,
6931        n_expert: usize,
6932        n_used: usize,
6933        bias: Option<&[f32]>,
6934        (sf, route_norm): (f32, bool),
6935        active: Option<&[bool]>,
6936    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6937        let logit_values =
6938            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
6939        let input_values =
6940            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
6941        let (lg, input) = e.dtoh_pair_views(
6942            &logits.slice(0..logit_values),
6943            &input.slice(0..input_values),
6944        )?;
6945        let (sel, w) =
6946            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
6947        Ok((sel, w, input))
6948    }
6949
6950    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
6951    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
6952    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
6953    /// active mask, prebuilt projection descriptors) so no model reference escapes.
6954    pub fn start_moe_prefetch_predictor(
6955        &self,
6956        e: &Engine,
6957        cfg: &ModelConfig,
6958    ) -> Result<(), Box<dyn std::error::Error>> {
6959        use crate::hybrid::Ffn;
6960        let Some(sig) = cfg.sigmoid_router() else {
6961            return Err("prefetch predictor requires a sigmoid-router arch".into());
6962        };
6963        let resident: std::collections::HashSet<(u16, u8, u16)> = e
6964            .export_moe_residency()
6965            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
6966            .into_iter()
6967            .collect();
6968        let mut layers = Vec::new();
6969        for (index, layer) in self.layers.iter().enumerate() {
6970            let Ffn::Moe(m) = &layer.ffn else { continue };
6971            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
6972                continue;
6973            };
6974            let router = e.dtoh(data)?;
6975            let n_expert = m.gate_exps.n_expert;
6976            let n_embd = m.gate_exps.in_f;
6977            if router.len() != n_embd * n_expert {
6978                continue;
6979            }
6980            let build = |exps: &crate::model::HostExps| {
6981                (0..n_expert)
6982                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
6983                    .collect::<Vec<_>>()
6984            };
6985            layers.push((
6986                index as u16,
6987                crate::cpu_experts::PredictLayerInit {
6988                    router,
6989                    bias: m.exp_probs_b.clone(),
6990                    active: m.active_experts.clone(),
6991                    n_embd,
6992                    n_used: cfg
6993                        .moe
6994                        .as_ref()
6995                        .map(|moe| moe.expert_used_count as usize)
6996                        .ok_or("prefetch predictor requires MoE config")?,
6997                    sig,
6998                    weights_n_expert: n_expert,
6999                    gate: build(&m.gate_exps),
7000                    up: build(&m.up_exps),
7001                    down: build(&m.down_exps),
7002                },
7003            ));
7004        }
7005        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7006    }
7007
7008    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7009    /// selection math to the rollback runtime, applied to host-computed logits.
7010    #[allow(clippy::too_many_arguments)]
7011    pub fn moe_route_sigmoid_host_public(
7012        logits: &[f32],
7013        t: usize,
7014        n_expert: usize,
7015        n_used: usize,
7016        bias: Option<&[f32]>,
7017        sf: f32,
7018        route_norm: bool,
7019        active: Option<&[bool]>,
7020    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7021        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7022    }
7023
7024    #[allow(clippy::too_many_arguments)]
7025    fn moe_route_sigmoid_host(
7026        lg: &[f32],
7027        t: usize,
7028        n_expert: usize,
7029        n_used: usize,
7030        bias: Option<&[f32]>,
7031        sf: f32,
7032        route_norm: bool,
7033        active: Option<&[bool]>,
7034    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7035        let active_count = active
7036            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7037            .unwrap_or(n_expert);
7038        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7039        if lg.len() != t * n_expert {
7040            return Err(format!(
7041                "sigmoid router logits length mismatch: got {}, expected {}",
7042                lg.len(),
7043                t * n_expert,
7044            )
7045            .into());
7046        }
7047        let mut sel = vec![0u32; t * n_used];
7048        let mut w_out = vec![0f32; t * n_used];
7049        for tok in 0..t {
7050            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7051            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7052            // selection score = sigmoid + bias; weight = plain sigmoid.
7053            let selsc: Vec<f32> = match bias {
7054                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7055                None => scores.clone(),
7056            };
7057            let mut idx: Vec<usize> = (0..n_expert)
7058                .filter(|&i| active.is_none_or(|mask| mask[i]))
7059                .collect();
7060            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7061            let sl = &idx[..n_used];
7062            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7063            if route_norm {
7064                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7065                for x in wv.iter_mut() {
7066                    *x = *x / ws * sf;
7067                }
7068            } else {
7069                for x in wv.iter_mut() {
7070                    *x *= sf;
7071                }
7072            }
7073            for j in 0..n_used {
7074                sel[tok * n_used + j] = sl[j] as u32;
7075                w_out[tok * n_used + j] = wv[j];
7076            }
7077        }
7078        Ok((sel, w_out))
7079    }
7080
7081    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7082    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7083    /// macro-scaled experts, and observation modes are denied by the caller.
7084    #[allow(clippy::too_many_arguments)]
7085    fn moe_ffn_sigmoid_dev(
7086        e: &Engine,
7087        m: &MoeWeights,
7088        z: &CudaSlice<f32>,
7089        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7090        logits: &CudaSlice<f32>,
7091        t: usize,
7092        cfg: &ModelConfig,
7093        il: u16,
7094        (scaling_factor, route_norm): (f32, bool),
7095    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7096        let moe = cfg.moe.as_ref().unwrap();
7097        let n_embd = cfg.n_embd as usize;
7098        let n_expert = moe.expert_count as usize;
7099        let n_used = moe.expert_used_count as usize;
7100        let n_ff_exp = moe.expert_ff_length as usize;
7101        let dev = m.dev_exps.as_ref().unwrap();
7102        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7103        debug_assert!(m.has_uniform_expert_layout());
7104        debug_assert!(!m.has_macros);
7105
7106        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7107            logits,
7108            t,
7109            n_expert,
7110            n_used,
7111            m.active_count(),
7112            &m.exp_probs_b_dev,
7113            &m.active_experts_dev,
7114            scaling_factor,
7115            route_norm,
7116        )?;
7117        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7118        if let Some(fp8) = dev.fp8_blk.as_ref() {
7119            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7120            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7121            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7122            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7123            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7124            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7125
7126            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7127            // activations with block-128 E4M3 weights. This deliberately
7128            // simple resident reference is the correctness oracle for later
7129            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7130            // load-time Q8 diagnostic representation, so one process never
7131            // crosses between numerical programs.
7132            let selected = e.dtoh_i32(&sel_d)?;
7133            let route_weights = e.dtoh(&w_d)?;
7134            let mut moe_out = e.zeros(t * n_embd)?;
7135            for tok in 0..t {
7136                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7137                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7138                for j in 0..n_used {
7139                    let pair = tok * n_used + j;
7140                    let expert = selected[pair] as usize;
7141                    let gate = Self::moe_resident_fp8_e4m3(
7142                        e,
7143                        &m.gate_exps,
7144                        &dev.gate,
7145                        &fp8.gate,
7146                        expert,
7147                        &zt,
7148                        1,
7149                    )?;
7150                    let up = Self::moe_resident_fp8_e4m3(
7151                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7152                    )?;
7153                    let mut act = e.uninit(n_ff_exp)?;
7154                    Self::ffn_act_lim(
7155                        e,
7156                        cfg,
7157                        &gate,
7158                        &up,
7159                        1.0,
7160                        1.0,
7161                        cfg.clamp_exp_at(il as u32),
7162                        &mut act,
7163                        n_ff_exp,
7164                    )?;
7165                    let act = act.slice(0..n_ff_exp);
7166                    let down = Self::moe_resident_fp8_e4m3(
7167                        e,
7168                        &m.down_exps,
7169                        &dev.down,
7170                        &fp8.down,
7171                        expert,
7172                        &act,
7173                        1,
7174                    )?;
7175                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7176                }
7177            }
7178            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7179                eprintln!(
7180                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7181                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7182                    cfg.clamp_exp_at(il as u32).is_some(),
7183                );
7184            }
7185            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7186            return Ok(moe_out);
7187        }
7188        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7189            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7190            (combined, combined)
7191        } else {
7192            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7193        };
7194        let (zq, zd) = match (t, zq8) {
7195            (1, Some((q, d))) => (q.clone(), d.clone()),
7196            _ => e.quantize_q8_1(z, t, n_embd)?,
7197        };
7198        let n_pairs = t * n_used;
7199        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7200            // The final Step layers retain the established separate gate/up -> clamp -> down
7201            // arithmetic. Pair rows are derived from token position; selected expert ids and
7202            // routing weights remain the device router's buffers throughout.
7203            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7204            let pair_tok_d = e.htod_i32(&pair_tok)?;
7205            let gate = e.moe_pairs_matvec_q8(
7206                &dev.ptr_row,
7207                0,
7208                &pair_tok_d,
7209                &sel_d,
7210                &zq,
7211                &zd,
7212                n_embd,
7213                n_ff_exp,
7214                n_expert,
7215                n_pairs,
7216                m.gate_exps.qtype,
7217                gate_row_bytes,
7218            )?;
7219            let up = e.moe_pairs_matvec_q8(
7220                &dev.ptr_row,
7221                1,
7222                &pair_tok_d,
7223                &sel_d,
7224                &zq,
7225                &zd,
7226                n_embd,
7227                n_ff_exp,
7228                n_expert,
7229                n_pairs,
7230                m.up_exps.qtype,
7231                up_row_bytes,
7232            )?;
7233            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7234            Self::ffn_act_lim(
7235                e,
7236                cfg,
7237                &gate,
7238                &up,
7239                1.0,
7240                1.0,
7241                cfg.clamp_exp_at(il as u32),
7242                &mut act,
7243                n_pairs * n_ff_exp,
7244            )?;
7245            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7246            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7247            let pair_self_d = e.htod_i32(&pair_self)?;
7248            let down = e.moe_pairs_matvec_q8(
7249                &dev.ptr_row,
7250                2,
7251                &pair_self_d,
7252                &sel_d,
7253                &aq2,
7254                &ad2,
7255                n_ff_exp,
7256                n_embd,
7257                n_expert,
7258                n_pairs,
7259                m.down_exps.qtype,
7260                m.down_exps.row_bytes,
7261            )?;
7262            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7263            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7264            let tok_off_d = e.htod_i32(&tok_off)?;
7265            let tok_ids_d = e.htod_i32(&tok_ids)?;
7266            let mut output = e.uninit(t * n_embd)?;
7267            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7268            output
7269        } else {
7270            let act = e.moe_gate_up_silu8_dev_q8_rows(
7271                &dev.ptr_row,
7272                &sel_d,
7273                &zq,
7274                &zd,
7275                t,
7276                n_embd,
7277                n_ff_exp,
7278                n_used,
7279                n_expert,
7280                m.gate_exps.qtype,
7281                m.up_exps.qtype,
7282                gate_row_bytes,
7283                up_row_bytes,
7284                &m.dev_macros,
7285            )?;
7286            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7287            let mut output = e.uninit(t * n_embd)?;
7288            e.moe_down8_fma_dev_q8_rows_g(
7289                &dev.ptr_row,
7290                &sel_d,
7291                &w_d,
7292                &aq2,
7293                &ad2,
7294                &mut output,
7295                t,
7296                n_ff_exp,
7297                n_embd,
7298                n_used,
7299                n_expert,
7300                m.down_exps.qtype,
7301                m.down_exps.row_bytes,
7302            )?;
7303            output
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} clamp={} gu_il={}",
7309                cfg.clamp_exp_at(il as u32).is_some(),
7310                dev.gu_il,
7311            );
7312        }
7313        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7314        Ok(moe_out)
7315    }
7316
7317    #[allow(clippy::too_many_arguments)]
7318    fn moe_resident_fp8_e4m3(
7319        e: &Engine,
7320        exps: &crate::model::HostExps,
7321        bytes: &CudaSlice<u8>,
7322        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7323        expert: usize,
7324        x: &cudarc::driver::CudaView<f32>,
7325        m: usize,
7326    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7327        let layout = exps.expert_layout(expert);
7328        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7329        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7330        let byte_start = expert * exps.expert_stride;
7331        let scale_start = expert * scales.expert_stride;
7332        let weight = bytes.slice(byte_start..byte_start + layout.len);
7333        let scale = scales
7334            .scales
7335            .slice(scale_start..scale_start + scales.expert_stride);
7336        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7337    }
7338
7339    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7340    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7341    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7342    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7343    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7344    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7345    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7346    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7347    fn moe_ffn_pairs(
7348        e: &Engine,
7349        m: &MoeWeights,
7350        z: &CudaSlice<f32>,
7351        logits: &CudaSlice<f32>,
7352        t: usize,
7353        cfg: &ModelConfig,
7354    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7355        let moe = cfg.moe.as_ref().unwrap();
7356        let n_embd = cfg.n_embd as usize;
7357        let n_expert = moe.expert_count as usize;
7358        let n_used = moe.expert_used_count as usize;
7359        let n_ff_exp = moe.expert_ff_length as usize;
7360        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7361        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7362        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7363        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7364        debug_assert!(
7365            !cfg.swiglu_clamped_anywhere(),
7366            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7367        );
7368        let dev = m.dev_exps.as_ref().unwrap();
7369        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7370        let (rbg_d, rbu_d) = if dev.gu_il {
7371            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7372            (sxx, sxx)
7373        } else {
7374            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7375        };
7376
7377        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7378        let n_pairs = t * n_used;
7379        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7380        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7381        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7382        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7383        let pair_w: Vec<f32> = w_all.clone();
7384        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7385        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7386        let pt = e.htod_i32(&pair_tok)?;
7387        let px = e.htod_i32(&pair_ex)?;
7388        let pw = e.htod(&pair_w)?;
7389        let toff = e.htod_i32(&tok_off)?;
7390        let tids = e.htod_i32(&tok_ids)?;
7391
7392        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7393        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7394        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7395        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7396        for p in 0..n_pairs {
7397            by_ex[pair_ex[p] as usize].push(p as i32);
7398        }
7399        let mut ex_ids: Vec<i32> = Vec::new();
7400        let mut ex_off: Vec<i32> = vec![0];
7401        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7402        for (ex, list) in by_ex.iter().enumerate() {
7403            if list.is_empty() {
7404                continue;
7405            }
7406            ex_ids.push(ex as i32);
7407            ex_pairs.extend_from_slice(list);
7408            ex_off.push(ex_pairs.len() as i32);
7409        }
7410        let n_active = ex_ids.len();
7411        let exi = e.htod_i32(&ex_ids)?;
7412        let exo = e.htod_i32(&ex_off)?;
7413        let exp_d = e.htod_i32(&ex_pairs)?;
7414        let _ = &px; // pair-major twin keeps it; em path uses CSR
7415
7416        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7417        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7418        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7419        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7420        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7421        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7422        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7423        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7424        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7425        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7426        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7427        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7428        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7429        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7430        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7431        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7432        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7433        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7434        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7435        let mma_t = *MMA_T.get_or_init(|| {
7436            std::env::var("MEMRA_MOE_MMA_T")
7437                .ok()
7438                .and_then(|v| v.parse().ok())
7439                .unwrap_or(16)
7440        });
7441        let use_mma = std::env::var("MEMRA_MOE_MMA")
7442            .map(|v| v != "0")
7443            .unwrap_or(true)
7444            && t >= mma_t
7445            && q8_expert_dec_supported(m.gate_exps.qtype)
7446            && q8_expert_dec_supported(m.up_exps.qtype)
7447            && q8_expert_dec_supported(m.down_exps.qtype)
7448            && n_embd % 256 == 0
7449            && n_ff_exp % 256 == 0;
7450        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7451        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7452        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7453        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7454        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7455        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7456        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7457        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7458        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7459        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7460        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7461        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7462        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7463        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
7464        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
7465        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
7466            && q8_expert_dec_supported(m.up_exps.qtype)
7467            && q8_expert_dec_supported(m.down_exps.qtype)
7468            && n_embd % 256 == 0
7469            && n_ff_exp % 256 == 0;
7470        let f16g_mode = crate::moe_f16g_mode();
7471        let f16g = f16g_mode != 0
7472            && t >= mma_t
7473            && (f16g_mode != 3 || !mma_capable)
7474            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
7475            && f16g_proj_ok(m.up_exps.qtype, n_embd)
7476            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
7477        if use_mma || f16g {
7478            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
7479            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
7480            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
7481            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
7482            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
7483            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
7484            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
7485            let y_down = if f16g {
7486                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
7487                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
7488                // permute at the very end back to pair-id order for the scatter.
7489                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
7490                let csr_tok_d = e.htod_i32(&csr_tok)?;
7491                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
7492                let g_csr = e.moe_f16_grouped(
7493                    &dev.ptr_row,
7494                    0,
7495                    n_expert,
7496                    &exi,
7497                    &ex_off,
7498                    &exo,
7499                    &z_f16,
7500                    &z_s,
7501                    n_embd,
7502                    n_ff_exp,
7503                    n_active,
7504                    n_pairs,
7505                    m.gate_exps.qtype,
7506                    rbg_d,
7507                )?;
7508                let u_csr = e.moe_f16_grouped(
7509                    &dev.ptr_row,
7510                    1,
7511                    n_expert,
7512                    &exi,
7513                    &ex_off,
7514                    &exo,
7515                    &z_f16,
7516                    &z_s,
7517                    n_embd,
7518                    n_ff_exp,
7519                    n_active,
7520                    n_pairs,
7521                    m.up_exps.qtype,
7522                    rbu_d,
7523                )?;
7524                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
7525                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
7526                let d_csr = e.moe_f16_grouped(
7527                    &dev.ptr_row,
7528                    2,
7529                    n_expert,
7530                    &exi,
7531                    &ex_off,
7532                    &exo,
7533                    &a_f16,
7534                    &a_s,
7535                    n_ff_exp,
7536                    n_embd,
7537                    n_active,
7538                    n_pairs,
7539                    m.down_exps.qtype,
7540                    m.down_exps.row_bytes,
7541                )?;
7542                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
7543            } else {
7544                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
7545                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
7546                let gate = e.mmq_iq_experts(
7547                    &dev.ptr_row,
7548                    0,
7549                    n_expert,
7550                    &exi,
7551                    &exo,
7552                    &exp_d,
7553                    &pt,
7554                    &z_scr,
7555                    n_embd,
7556                    n_ff_exp,
7557                    n_active,
7558                    n_pairs,
7559                    t,
7560                    m.gate_exps.qtype,
7561                    rbg_d,
7562                )?;
7563                let up = e.mmq_iq_experts(
7564                    &dev.ptr_row,
7565                    1,
7566                    n_expert,
7567                    &exi,
7568                    &exo,
7569                    &exp_d,
7570                    &pt,
7571                    &z_scr,
7572                    n_embd,
7573                    n_ff_exp,
7574                    n_active,
7575                    n_pairs,
7576                    t,
7577                    m.up_exps.qtype,
7578                    rbu_d,
7579                )?;
7580                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
7581                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
7582                // registers and writes ONLY the quantized scratch — the two-pass chain
7583                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
7584                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
7585                let a_scr = if crate::moe_fuse_actq_on() {
7586                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
7587                } else {
7588                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7589                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
7590                };
7591                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7592                let pself = e.htod_i32(&pair_self)?;
7593                e.mmq_iq_experts(
7594                    &dev.ptr_row,
7595                    2,
7596                    n_expert,
7597                    &exi,
7598                    &exo,
7599                    &exp_d,
7600                    &pself,
7601                    &a_scr,
7602                    n_ff_exp,
7603                    n_embd,
7604                    n_active,
7605                    n_pairs,
7606                    n_pairs,
7607                    m.down_exps.qtype,
7608                    m.down_exps.row_bytes,
7609                )?
7610            };
7611            let mut moe_out = e.uninit(t * n_embd)?;
7612            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7613            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7614                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7615            {
7616                let n_ff_sh = gate_shexp.out_features();
7617                let sg_gate = e.matmul(gate_shexp, z, t)?;
7618                let sg_up = e.matmul(up_shexp, z, t)?;
7619                let mut sa = e.uninit(t * n_ff_sh)?;
7620                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7621                let sh = e.matmul(down_shexp, &sa, t)?;
7622                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7623                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
7624                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
7625                // i.e. the one real prefill actually takes on a resident-expert MoE model,
7626                // so the concat-prime isolation fix has to land here as well.
7627                let g = match &m.gate_inp_shexp {
7628                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7629                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7630                    }
7631                    Some(gate_inp_shexp) => {
7632                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7633                        let mut g = e.uninit(t)?;
7634                        e.sigmoid(&gs, &mut g, t)?;
7635                        g
7636                    }
7637                    None => e.htod(&vec![1.0f32; t])?,
7638                };
7639                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7640            }
7641            return Ok(moe_out);
7642        }
7643
7644        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
7645        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
7646        let dec = std::env::var("MEMRA_MOE_DEC")
7647            .map(|v| v != "0")
7648            .unwrap_or(true);
7649        let matvec = |proj,
7650                      exi: &_,
7651                      exo: &_,
7652                      exp_d: &_,
7653                      pt: &_,
7654                      aq: &_,
7655                      ad: &_,
7656                      inf,
7657                      outf,
7658                      qtype,
7659                      rb|
7660         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7661            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
7662            let dec = dec && q8_expert_dec_supported(qtype);
7663            if dec {
7664                e.moe_pairs_matvec_q8_dec(
7665                    &dev.ptr_row,
7666                    proj,
7667                    exi,
7668                    exo,
7669                    exp_d,
7670                    pt,
7671                    aq,
7672                    ad,
7673                    inf,
7674                    outf,
7675                    n_expert,
7676                    n_active,
7677                    n_pairs,
7678                    qtype,
7679                    rb,
7680                )
7681            } else {
7682                e.moe_pairs_matvec_q8_em(
7683                    &dev.ptr_row,
7684                    proj,
7685                    exi,
7686                    exo,
7687                    exp_d,
7688                    pt,
7689                    aq,
7690                    ad,
7691                    inf,
7692                    outf,
7693                    n_expert,
7694                    n_active,
7695                    n_pairs,
7696                    qtype,
7697                    rb,
7698                )
7699            }
7700        };
7701        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7702        let gate = matvec(
7703            0,
7704            &exi,
7705            &exo,
7706            &exp_d,
7707            &pt,
7708            &zq,
7709            &zd,
7710            n_embd,
7711            n_ff_exp,
7712            m.gate_exps.qtype,
7713            rbg_d,
7714        )?;
7715        let up = matvec(
7716            1,
7717            &exi,
7718            &exo,
7719            &exp_d,
7720            &pt,
7721            &zq,
7722            &zd,
7723            n_embd,
7724            n_ff_exp,
7725            m.up_exps.qtype,
7726            rbu_d,
7727        )?;
7728        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7729        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7730        // down consumes PAIR-major activation rows: pair_tok = identity.
7731        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7732        let pself = e.htod_i32(&pair_self)?;
7733        let y_down = matvec(
7734            2,
7735            &exi,
7736            &exo,
7737            &exp_d,
7738            &pself,
7739            &aq2,
7740            &ad2,
7741            n_ff_exp,
7742            n_embd,
7743            m.down_exps.qtype,
7744            m.down_exps.row_bytes,
7745        )?;
7746        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
7747        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7748
7749        // SHARED EXPERT epilogue — same as the other paths.
7750        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7751        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7752        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7753            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7754        {
7755            let n_ff_sh = gate_shexp.out_features();
7756            // These decode-exact forms are required by the new Step resident arm. Keep the
7757            // established grouped shared-expert program for every other architecture: widening
7758            // this to Gemma changed its speculative acceptance despite green argmax gates.
7759            let step_exact = true;
7760            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
7761            let (sg_gate, sg_up) = if step_exact && t == 1 {
7762                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
7763            } else if verify_t {
7764                let mut fused = None;
7765                if crate::spec::spec_fused_t()
7766                    && (2..=4).contains(&t)
7767                    && e.uses_q8_1_fast(gate_shexp)
7768                    && e.uses_q8_1_fast(up_shexp)
7769                {
7770                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7771                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
7772                }
7773                match fused {
7774                    Some(pair) => pair,
7775                    None => (
7776                        e.matmul_decode_exact(gate_shexp, z, t)?,
7777                        e.matmul_decode_exact(up_shexp, z, t)?,
7778                    ),
7779                }
7780            } else {
7781                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
7782            };
7783            let mut sa = e.uninit(t * n_ff_sh)?;
7784            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7785            let sh = if verify_t {
7786                e.matmul_decode_exact(down_shexp, &sa, t)?
7787            } else {
7788                e.matmul(down_shexp, &sa, t)?
7789            };
7790            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7791            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
7792            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
7793            // dispatch choice cannot change bits.
7794            let g = match &m.gate_inp_shexp {
7795                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7796                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7797                }
7798                Some(gate_inp_shexp) => {
7799                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7800                    let mut g = e.uninit(t)?;
7801                    e.sigmoid(&gs, &mut g, t)?;
7802                    g
7803                }
7804                None => e.htod(&vec![1.0f32; t])?,
7805            };
7806            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7807        }
7808        Ok(moe_out)
7809    }
7810
7811    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
7812    #[allow(clippy::too_many_arguments)]
7813    #[allow(clippy::too_many_arguments)]
7814    fn moe_ffn_dev(
7815        e: &Engine,
7816        m: &MoeWeights,
7817        z: &CudaSlice<f32>,
7818        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7819        logits: &CudaSlice<f32>,
7820        t: usize,
7821        cfg: &ModelConfig,
7822        il: u16,
7823        max_block: usize,
7824    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7825        let moe = cfg.moe.as_ref().unwrap();
7826        let n_embd = cfg.n_embd as usize;
7827        let n_expert = moe.expert_count as usize;
7828        let n_used = moe.expert_used_count as usize;
7829        let n_ff_exp = moe.expert_ff_length as usize;
7830        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
7831        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
7832        // clamped layers; assert both so a future caller that skips the gate fails loudly.
7833        debug_assert!(
7834            cfg.sigmoid_router().is_none(),
7835            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
7836        );
7837        debug_assert!(
7838            !cfg.swiglu_clamped_at(il as u32),
7839            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
7840        );
7841
7842        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
7843        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
7844        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
7845        // skipped entirely for macro-free experts (every k-quant GGUF).
7846        if m.has_macros {
7847            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
7848        }
7849
7850        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
7851        let mut moe_out = e.uninit(t * n_embd)?;
7852
7853        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
7854        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
7855        if let Some(dev) = m.dev_exps.as_ref() {
7856            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
7857            // the combined stride; up's base is offset in the ptr table. Down unchanged.
7858            let (rbg_d, rbu_d) = if dev.gu_il {
7859                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7860                (sxx, sxx)
7861            } else {
7862                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7863            };
7864            let q8 = moe_q8_enabled()
7865                && q8_expert_supported(m.gate_exps.qtype)
7866                && q8_expert_supported(m.up_exps.qtype)
7867                && q8_expert_supported(m.down_exps.qtype);
7868            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
7869            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
7870            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
7871            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
7872            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
7873            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
7874            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
7875            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
7876            let rows_arm = q8
7877                && t > 1
7878                && crate::spec::spec_m2()
7879                && n_ff_exp == 512
7880                && n_used <= 8
7881                && std::env::var("MEMRA_MOE_DEVQ8_GU")
7882                    .map(|v| v.is_empty() || v == "v")
7883                    .unwrap_or(true)
7884                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
7885                    .map(|v| v.is_empty() || v == "w8h2v")
7886                    .unwrap_or(true);
7887            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
7888            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
7889            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
7890            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
7891            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
7892            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
7893            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
7894            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
7895            let csr_mode = std::env::var("MEMRA_MOE_CSR")
7896                .ok()
7897                .and_then(|v| v.parse::<i32>().ok())
7898                .unwrap_or(1);
7899            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
7900            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
7901            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
7902            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
7903            // axis. Three chain-pinning attempts did not close it (receipts,
7904            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
7905            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
7906            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
7907            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
7908            // never decode-batch-gate at B=8 on the MoE model itself.
7909            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
7910            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
7911            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
7912            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
7913            // de-admission verdict above stands until those gates are GREEN on the MoE
7914            // artifact; this door must never default on.
7915            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
7916            let csr_qt = |qt: i32| {
7917                qt == crate::QT_IQ4_XS
7918                    || qt == crate::QT_IQ3_S
7919                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
7920            };
7921            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
7922            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
7923            let csr_arm = rows_arm
7924                && csr_mode > 0
7925                && t <= csr_t_max
7926                && csr_uniform
7927                && csr_qt(m.gate_exps.qtype)
7928                && csr_qt(m.up_exps.qtype)
7929                && csr_qt(m.down_exps.qtype);
7930            if csr_arm {
7931                if csr_mode == 2 {
7932                    static ENGAGED: std::sync::Once = std::sync::Once::new();
7933                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
7934                }
7935                let n_pairs = t * n_used;
7936                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7937                let act = e.moe_gate_up_silu8_dev_q8_csr(
7938                    &dev.ptr_row,
7939                    &sel_d,
7940                    &zq,
7941                    &zd,
7942                    n_pairs,
7943                    n_embd,
7944                    n_ff_exp,
7945                    n_used,
7946                    n_expert,
7947                    m.gate_exps.qtype,
7948                    m.up_exps.qtype,
7949                    rbg_d,
7950                    rbu_d,
7951                )?;
7952                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7953                // down stays on the _rows twin — BOTH CSR down variants measured negative
7954                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
7955                // 16-group rows have too little decode to amortize any dedup structure.
7956                e.moe_down8_fma_dev_q8_rows(
7957                    &dev.ptr_row,
7958                    &sel_d,
7959                    &w_d,
7960                    &aq2,
7961                    &ad2,
7962                    &mut moe_out,
7963                    t,
7964                    n_ff_exp,
7965                    n_embd,
7966                    n_used,
7967                    n_expert,
7968                    m.down_exps.qtype,
7969                    m.down_exps.row_bytes,
7970                )?;
7971                if csr_mode == 2 {
7972                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
7973                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
7974                        &dev.ptr_row,
7975                        &sel_d,
7976                        &zq,
7977                        &zd,
7978                        t,
7979                        n_embd,
7980                        n_ff_exp,
7981                        n_used,
7982                        n_expert,
7983                        m.gate_exps.qtype,
7984                        m.up_exps.qtype,
7985                        rbg_d,
7986                        rbu_d,
7987                        &m.dev_macros,
7988                    )?;
7989                    let mut out_r = e.uninit(t * n_embd)?;
7990                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
7991                    e.moe_down8_fma_dev_q8_rows(
7992                        &dev.ptr_row,
7993                        &sel_d,
7994                        &w_d,
7995                        &aq2r,
7996                        &ad2r,
7997                        &mut out_r,
7998                        t,
7999                        n_ff_exp,
8000                        n_embd,
8001                        n_used,
8002                        n_expert,
8003                        m.down_exps.qtype,
8004                        m.down_exps.row_bytes,
8005                    )?;
8006                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8007                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8008                    let ba = a1
8009                        .iter()
8010                        .zip(&a2)
8011                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8012                        .count();
8013                    let bo = o1
8014                        .iter()
8015                        .zip(&o2)
8016                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8017                        .count();
8018                    if ba + bo > 0 {
8019                        eprintln!(
8020                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8021                            a1.len(),
8022                            o1.len()
8023                        );
8024                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8025                        let sel_h = e.dtoh_i32(&sel_d)?;
8026                        let mut shown = 0;
8027                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8028                            if x.to_bits() != y.to_bits() && shown < 4 {
8029                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8030                                let ex = sel_h[p];
8031                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8032                                eprintln!(
8033                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8034                                );
8035                                shown += 1;
8036                            }
8037                        }
8038                        std::process::exit(3);
8039                    }
8040                }
8041            } else if rows_arm {
8042                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8043                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8044                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8045                    use std::sync::atomic::{AtomicU64, Ordering};
8046                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8047                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8048                    static CALLS: AtomicU64 = AtomicU64::new(0);
8049                    let sel_h = e.dtoh_i32(&sel_d)?;
8050                    let mut u: Vec<i32> = sel_h.clone();
8051                    u.sort_unstable();
8052                    u.dedup();
8053                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8054                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8055                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8056                    if c % 480 == 0 {
8057                        let p = PAIRS.load(Ordering::Relaxed);
8058                        let q = UNIQ.load(Ordering::Relaxed);
8059                        eprintln!(
8060                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8061                            q as f64 / p as f64
8062                        );
8063                    }
8064                }
8065                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8066                let act = e.moe_gate_up_silu8_dev_q8_rows(
8067                    &dev.ptr_row,
8068                    &sel_d,
8069                    &zq,
8070                    &zd,
8071                    t,
8072                    n_embd,
8073                    n_ff_exp,
8074                    n_used,
8075                    n_expert,
8076                    m.gate_exps.qtype,
8077                    m.up_exps.qtype,
8078                    rbg_d,
8079                    rbu_d,
8080                    &m.dev_macros,
8081                )?;
8082                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8083                e.moe_down8_fma_dev_q8_rows(
8084                    &dev.ptr_row,
8085                    &sel_d,
8086                    &w_d,
8087                    &aq2,
8088                    &ad2,
8089                    &mut moe_out,
8090                    t,
8091                    n_ff_exp,
8092                    n_embd,
8093                    n_used,
8094                    n_expert,
8095                    m.down_exps.qtype,
8096                    m.down_exps.row_bytes,
8097                )?;
8098            } else {
8099                for tok in 0..t {
8100                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8101                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8102                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8103                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8104                    if q8 {
8105                        let (zq, zd) = match (t, zq8) {
8106                            (1, Some((q, d))) => (q.clone(), d.clone()),
8107                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8108                        };
8109                        let act = e.moe_gate_up_silu8_dev_q8(
8110                            &dev.ptr_row,
8111                            &selt,
8112                            &zq,
8113                            &zd,
8114                            n_embd,
8115                            n_ff_exp,
8116                            n_used,
8117                            n_expert,
8118                            m.gate_exps.qtype,
8119                            m.up_exps.qtype,
8120                            rbg_d,
8121                            rbu_d,
8122                            &m.dev_macros,
8123                        )?;
8124                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8125                        e.moe_down8_fma_dev_q8(
8126                            &dev.ptr_row,
8127                            &selt,
8128                            &wt,
8129                            &aq2,
8130                            &ad2,
8131                            &mut dst,
8132                            n_ff_exp,
8133                            n_embd,
8134                            n_used,
8135                            n_expert,
8136                            m.down_exps.qtype,
8137                            m.down_exps.row_bytes,
8138                        )?;
8139                    } else {
8140                        let act = e.moe_gate_up_silu8_dev(
8141                            &dev.ptr_row,
8142                            &selt,
8143                            &zt,
8144                            n_embd,
8145                            n_ff_exp,
8146                            n_used,
8147                            n_expert,
8148                            m.gate_exps.qtype,
8149                            m.up_exps.qtype,
8150                            rbg_d,
8151                            rbu_d,
8152                            &m.dev_macros,
8153                        )?;
8154                        e.moe_down8_fma_dev(
8155                            &dev.ptr_row,
8156                            &selt,
8157                            &wt,
8158                            &act,
8159                            &mut dst,
8160                            n_ff_exp,
8161                            n_embd,
8162                            n_used,
8163                            n_expert,
8164                            m.down_exps.qtype,
8165                            m.down_exps.row_bytes,
8166                        )?;
8167                    }
8168                }
8169            }
8170        } else {
8171            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8172            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8173            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8174            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8175            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8176            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8177            let q8 = moe_q8_enabled()
8178                && q8_expert_supported(m.gate_exps.qtype)
8179                && q8_expert_supported(m.up_exps.qtype)
8180                && q8_expert_supported(m.down_exps.qtype);
8181            e.with_moe_cache(max_block, |c, eng| {
8182                let row = c
8183                    .layer_dev_row(il, n_expert, eng)?
8184                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8185                for tok in 0..t {
8186                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8187                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8188                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8189                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8190                    if q8 {
8191                        let (zq, zd) = match (t, zq8) {
8192                            (1, Some((q, d))) => (q.clone(), d.clone()),
8193                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8194                        };
8195                        let act = eng.moe_gate_up_silu8_dev_q8(
8196                            row,
8197                            &selt,
8198                            &zq,
8199                            &zd,
8200                            n_embd,
8201                            n_ff_exp,
8202                            n_used,
8203                            n_expert,
8204                            m.gate_exps.qtype,
8205                            m.up_exps.qtype,
8206                            m.gate_exps.row_bytes,
8207                            m.up_exps.row_bytes,
8208                            &m.dev_macros,
8209                        )?;
8210                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8211                        eng.moe_down8_fma_dev_q8(
8212                            row,
8213                            &selt,
8214                            &wt,
8215                            &aq2,
8216                            &ad2,
8217                            &mut dst,
8218                            n_ff_exp,
8219                            n_embd,
8220                            n_used,
8221                            n_expert,
8222                            m.down_exps.qtype,
8223                            m.down_exps.row_bytes,
8224                        )?;
8225                    } else {
8226                        let act = eng.moe_gate_up_silu8_dev(
8227                            row,
8228                            &selt,
8229                            &zt,
8230                            n_embd,
8231                            n_ff_exp,
8232                            n_used,
8233                            n_expert,
8234                            m.gate_exps.qtype,
8235                            m.up_exps.qtype,
8236                            m.gate_exps.row_bytes,
8237                            m.up_exps.row_bytes,
8238                            &m.dev_macros,
8239                        )?;
8240                        eng.moe_down8_fma_dev(
8241                            row,
8242                            &selt,
8243                            &wt,
8244                            &act,
8245                            &mut dst,
8246                            n_ff_exp,
8247                            n_embd,
8248                            n_used,
8249                            n_expert,
8250                            m.down_exps.qtype,
8251                            m.down_exps.row_bytes,
8252                        )?;
8253                    }
8254                }
8255                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8256                c.hits += (t * 3 * n_used) as u64;
8257                Ok(())
8258            })?;
8259        }
8260
8261        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8262        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8263        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8264        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8265        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8266            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8267        {
8268            let n_ff_sh = gate_shexp.out_features();
8269            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8270            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8271            let verify_t = t > 1 && t < PRIME_MIN_T;
8272            let (sg_gate, sg_up) = if t == 1 {
8273                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8274            } else if verify_t {
8275                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8276                // rides one shared quantize + one fused2 batched launch instead of two
8277                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8278                let mut fused = None;
8279                if crate::spec::spec_fused_t()
8280                    && (2..=4).contains(&t)
8281                    && e.uses_q8_1_fast(gate_shexp)
8282                    && e.uses_q8_1_fast(up_shexp)
8283                {
8284                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8285                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8286                }
8287                match fused {
8288                    Some(pair) => pair,
8289                    None => (
8290                        e.matmul_decode_exact(gate_shexp, z, t)?,
8291                        e.matmul_decode_exact(up_shexp, z, t)?,
8292                    ),
8293                }
8294            } else {
8295                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8296            };
8297            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8298            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8299            let sh = if verify_t {
8300                e.matmul_decode_exact(down_shexp, &sa, t)?
8301            } else {
8302                e.matmul(down_shexp, &sa, t)?
8303            };
8304            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8305            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8306            // between the two arms; prefill keeps the batched cuBLASLt linear).
8307            let g = match &m.gate_inp_shexp {
8308                Some(gate_inp_shexp) => {
8309                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8310                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8311                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8312                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8313                    } else {
8314                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8315                        let mut g = e.uninit(t)?;
8316                        e.sigmoid(&gs, &mut g, t)?;
8317                        g
8318                    }
8319                }
8320                None => e.htod(&vec![1.0f32; t])?,
8321            };
8322            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8323        }
8324
8325        Ok(moe_out)
8326    }
8327
8328    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8329    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8330    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8331    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8332    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8333    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8334    /// the collected raw pointers cannot move between collection and launch (single-threaded
8335    /// decode; the lock is held only for collection, launches are stream-ordered after any
8336    /// prior same-stream staging writes).
8337    #[allow(clippy::too_many_arguments)]
8338    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8339    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8340    #[allow(clippy::too_many_arguments)]
8341    fn moe_gdec_token_q8(
8342        e: &Engine,
8343        m: &MoeWeights,
8344        il: u16,
8345        max_block: usize,
8346        zq: &CudaSlice<i8>,
8347        zd: &CudaSlice<f32>,
8348        sel: &[u32],
8349        w: &[f32],
8350        moe_out: &mut CudaSlice<f32>,
8351        tok: usize,
8352        n_embd: usize,
8353        n_ff_exp: usize,
8354        n_used: usize,
8355    ) -> Result<bool, Box<dyn std::error::Error>> {
8356        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8357        use cudarc::driver::DevicePtr;
8358        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8359            let mut g = [0u64; 8];
8360            let mut u = [0u64; 8];
8361            let mut d = [0u64; 8];
8362            for (j, &ex) in sel.iter().enumerate() {
8363                let ex = ex as u16;
8364                let (Some(sg), Some(su), Some(sd)) = (
8365                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8366                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8367                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8368                ) else {
8369                    return Ok(None);
8370                };
8371                let __s = eng.stream();
8372                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8373                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8374                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8375                g[j] = pg as u64;
8376                u[j] = pu as u64;
8377                d[j] = pd as u64;
8378            }
8379            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8380                for &ex in sel {
8381                    let ex = ex as u16;
8382                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8383                        c.note_profile_hit(BlockId::new(il, proj, ex));
8384                    }
8385                }
8386            }
8387            c.hits += (3 * n_used) as u64;
8388            Ok(Some((g, u, d)))
8389        })?;
8390        let Some((g, u, d)) = ptrs else {
8391            return Ok(false);
8392        };
8393        let mut wv = [0f32; 8];
8394        wv[..n_used].copy_from_slice(w);
8395        let act = e.moe_gate_up_silu8_q8(
8396            crate::WPtr8(g),
8397            crate::WPtr8(u),
8398            zq,
8399            zd,
8400            n_embd,
8401            n_ff_exp,
8402            n_used,
8403            m.gate_exps.qtype,
8404            m.up_exps.qtype,
8405            m.gate_exps.row_bytes,
8406            m.up_exps.row_bytes,
8407        )?;
8408        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8409        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8410        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8411        e.moe_down8_fma_q8(
8412            crate::WPtr8(d),
8413            crate::F32x8(wv),
8414            &aq2,
8415            &ad2,
8416            &mut dst,
8417            n_ff_exp,
8418            n_embd,
8419            n_used,
8420            m.down_exps.qtype,
8421            m.down_exps.row_bytes,
8422        )?;
8423        Ok(true)
8424    }
8425
8426    fn moe_gdec_token(
8427        e: &Engine,
8428        m: &MoeWeights,
8429        il: u16,
8430        max_block: usize,
8431        zt: &cudarc::driver::CudaView<f32>,
8432        sel: &[u32],
8433        w: &[f32],
8434        moe_out: &mut CudaSlice<f32>,
8435        tok: usize,
8436        n_embd: usize,
8437        n_ff_exp: usize,
8438        n_used: usize,
8439    ) -> Result<bool, Box<dyn std::error::Error>> {
8440        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8441        use cudarc::driver::DevicePtr;
8442        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8443        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8444            let mut g = [0u64; 8];
8445            let mut u = [0u64; 8];
8446            let mut d = [0u64; 8];
8447            for (j, &ex) in sel.iter().enumerate() {
8448                let ex = ex as u16;
8449                let (Some(sg), Some(su), Some(sd)) = (
8450                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8451                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8452                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8453                ) else {
8454                    return Ok(None);
8455                };
8456                let __s = eng.stream();
8457                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8458                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8459                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8460                g[j] = pg as u64;
8461                u[j] = pu as u64;
8462                d[j] = pd as u64;
8463            }
8464            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8465                for &ex in sel {
8466                    let ex = ex as u16;
8467                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8468                        c.note_profile_hit(BlockId::new(il, proj, ex));
8469                    }
8470                }
8471            }
8472            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
8473            Ok(Some((g, u, d)))
8474        })?;
8475        let Some((g, u, d)) = ptrs else {
8476            return Ok(false);
8477        };
8478        let mut wv = [0f32; 8];
8479        wv[..n_used].copy_from_slice(w);
8480        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
8481        let act = e.moe_gate_up_silu8(
8482            crate::WPtr8(g),
8483            crate::WPtr8(u),
8484            zt,
8485            n_embd,
8486            n_ff_exp,
8487            n_used,
8488            m.gate_exps.qtype,
8489            m.up_exps.qtype,
8490            m.gate_exps.row_bytes,
8491            m.up_exps.row_bytes,
8492        )?;
8493        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8494        e.moe_down8_fma_into(
8495            crate::WPtr8(d),
8496            crate::F32x8(wv),
8497            &act,
8498            &mut dst,
8499            n_ff_exp,
8500            n_embd,
8501            n_used,
8502            m.down_exps.qtype,
8503            m.down_exps.row_bytes,
8504        )?;
8505        Ok(true)
8506    }
8507
8508    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
8509    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
8510    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
8511    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
8512    fn moe_cached_gemm_q8(
8513        e: &Engine,
8514        il: u16,
8515        proj: u8,
8516        ex: usize,
8517        m: &MoeWeights,
8518        max_block: usize,
8519        aq: &CudaSlice<i8>,
8520        ad: &CudaSlice<f32>,
8521    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8522        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8523        let exps = match proj {
8524            PROJ_GATE => &m.gate_exps,
8525            PROJ_UP => &m.up_exps,
8526            _ => &m.down_exps,
8527        };
8528        let layout = exps.expert_layout(ex);
8529        let id = BlockId::new(il, proj, ex as u16);
8530        let source = exps.expert_source(ex);
8531        e.with_moe_cache(max_block, |c, eng| {
8532            let slot = c.dispatch_source(id, source, eng)?;
8533            let DispatchSlot::Resident(sl) = slot;
8534            let buf = c.slot(sl);
8535            eng.qmatvec_expert_q8(
8536                buf,
8537                0..layout.len,
8538                aq,
8539                ad,
8540                1,
8541                exps.in_f,
8542                exps.out_f,
8543                layout.qtype,
8544                layout.row_bytes,
8545            )
8546        })
8547    }
8548
8549    fn moe_cached_gemm(
8550        e: &Engine,
8551        il: u16,
8552        proj: u8,
8553        ex: usize,
8554        m: &MoeWeights,
8555        max_block: usize,
8556        x: &cudarc::driver::CudaView<f32>,
8557    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8558        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8559        let exps = match proj {
8560            PROJ_GATE => &m.gate_exps,
8561            PROJ_UP => &m.up_exps,
8562            _ => &m.down_exps,
8563        };
8564        let layout = exps.expert_layout(ex);
8565        let id = BlockId::new(il, proj, ex as u16);
8566        let source = exps.expert_source(ex);
8567        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
8568        e.with_moe_cache(max_block, |c, eng| {
8569            let slot = c.dispatch_source(id, source, eng)?;
8570            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
8571            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
8572            let DispatchSlot::Resident(sl) = slot;
8573            let buf = c.slot(sl);
8574            eng.qmatvec_view(
8575                buf,
8576                0..layout.len,
8577                x,
8578                1,
8579                exps.in_f,
8580                exps.out_f,
8581                layout.qtype,
8582                layout.row_bytes,
8583            )
8584        })
8585    }
8586
8587    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
8588    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
8589    /// so the current forward's backend assignment and output remain unchanged.
8590    fn moe_profile_admit_expert(
8591        e: &Engine,
8592        il: u16,
8593        ex: usize,
8594        m: &MoeWeights,
8595        max_block: usize,
8596    ) -> Result<(), Box<dyn std::error::Error>> {
8597        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8598        e.with_moe_cache(max_block, |cache, eng| {
8599            for (proj, exps) in [
8600                (PROJ_GATE, &m.gate_exps),
8601                (PROJ_UP, &m.up_exps),
8602                (PROJ_DOWN, &m.down_exps),
8603            ] {
8604                let id = BlockId::new(il, proj, ex as u16);
8605                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
8606            }
8607            Ok(())
8608        })
8609    }
8610
8611    /// Read a projection from the immutable residency set when present; otherwise use one
8612    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
8613    #[allow(clippy::too_many_arguments)]
8614    fn moe_frozen_gemm(
8615        e: &Engine,
8616        il: u16,
8617        proj: u8,
8618        ex: usize,
8619        m: &MoeWeights,
8620        max_block: usize,
8621        x: &cudarc::driver::CudaView<f32>,
8622        scratch: &mut Option<CudaSlice<u8>>,
8623        scratch_len: usize,
8624    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8625        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
8626        let exps = match proj {
8627            PROJ_GATE => &m.gate_exps,
8628            PROJ_UP => &m.up_exps,
8629            _ => &m.down_exps,
8630        };
8631        let layout = exps.expert_layout(ex);
8632        let id = BlockId::new(il, proj, ex as u16);
8633        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
8634            let Some(slot) = cache.resident(id) else {
8635                return Ok(None);
8636            };
8637            let buf = cache.slot(slot);
8638            Ok(Some(eng.qmatvec_view(
8639                buf,
8640                0..layout.len,
8641                x,
8642                1,
8643                exps.in_f,
8644                exps.out_f,
8645                layout.qtype,
8646                layout.row_bytes,
8647            )?))
8648        })? {
8649            return Ok(output);
8650        }
8651        if scratch.is_none() {
8652            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
8653        }
8654        let scratch = scratch.as_mut().unwrap();
8655        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
8656        e.qmatvec_view(
8657            scratch,
8658            0..layout.len,
8659            x,
8660            1,
8661            exps.in_f,
8662            exps.out_f,
8663            layout.qtype,
8664            layout.row_bytes,
8665        )
8666    }
8667
8668    fn moe_prefetch_expert(
8669        e: &Engine,
8670        il: u16,
8671        ex: usize,
8672        m: &MoeWeights,
8673        max_block: usize,
8674        keep: &[crate::moe_cache::BlockId],
8675    ) -> Result<(), Box<dyn std::error::Error>> {
8676        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8677        e.with_moe_cache(max_block, |c, eng| {
8678            for (proj, exps) in [
8679                (PROJ_GATE, &m.gate_exps),
8680                (PROJ_UP, &m.up_exps),
8681                (PROJ_DOWN, &m.down_exps),
8682            ] {
8683                let id = BlockId::new(il, proj, ex as u16);
8684                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
8685            }
8686            Ok(())
8687        })
8688    }
8689
8690    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
8691    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
8692    fn moe_prefetch_disk_expert(
8693        e: &Engine,
8694        il: u16,
8695        ex: usize,
8696        m: &MoeWeights,
8697        max_block: usize,
8698        keep: &[crate::moe_cache::BlockId],
8699    ) -> Result<(), Box<dyn std::error::Error>> {
8700        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8701        e.with_moe_cache(max_block, |c, eng| {
8702            for (proj, exps) in [
8703                (PROJ_GATE, &m.gate_exps),
8704                (PROJ_UP, &m.up_exps),
8705                (PROJ_DOWN, &m.down_exps),
8706            ] {
8707                let source = exps.expert_source(ex);
8708                if let crate::model::ExpertSource::Disk { .. } = &source {
8709                    let id = BlockId::new(il, proj, ex as u16);
8710                    let _ = c.prefetch_source(id, source, keep, eng)?;
8711                }
8712            }
8713            Ok(())
8714        })
8715    }
8716
8717    #[inline]
8718    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
8719        let _ = m.gate_exps.prefetch_expert_pages(ex);
8720        let _ = m.up_exps.prefetch_expert_pages(ex);
8721        let _ = m.down_exps.prefetch_expert_pages(ex);
8722    }
8723}
8724
8725// ================================================================================================
8726// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
8727//
8728// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
8729// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
8730// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
8731//
8732// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
8733// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
8734// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
8735// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
8736// identical to the per-token loop regardless of expert processing order.
8737//
8738// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
8739// ================================================================================================
8740
8741impl HybridModel {
8742    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
8743    /// sequential fused q8 program over the token axis; clamped layers use the separate
8744    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
8745    #[allow(clippy::too_many_arguments)]
8746    fn moe_ffn_grouped_resident_q8(
8747        e: &Engine,
8748        m: &MoeWeights,
8749        z: &CudaSlice<f32>,
8750        t: usize,
8751        cfg: &ModelConfig,
8752        il: u16,
8753        sel_all: &[u32],
8754        w_all: &[f32],
8755        table: &CudaSlice<u64>,
8756        gu_il: bool,
8757    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8758        let moe = cfg.moe.as_ref().unwrap();
8759        let n_embd = cfg.n_embd as usize;
8760        let n_expert = moe.expert_count as usize;
8761        let n_used = moe.expert_used_count as usize;
8762        let n_ff_exp = moe.expert_ff_length as usize;
8763        let n_pairs = t * n_used;
8764        debug_assert_eq!(sel_all.len(), n_pairs);
8765        debug_assert_eq!(w_all.len(), n_pairs);
8766        debug_assert!(
8767            m.gate_exps.macros.is_none()
8768                && m.up_exps.macros.is_none()
8769                && m.down_exps.macros.is_none(),
8770            "resident grouped q8 does not fold per-expert macro scales",
8771        );
8772
8773        // The rows twins run the resident sequential program verbatim on grid.z = token:
8774        // fused gate/up/SiLU per slot, batched activation quantization, then the original
8775        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
8776        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
8777        // never enter the softmax router.
8778        if !cfg.swiglu_clamped_at(il as u32) {
8779            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8780            let sel_d = e.htod_i32(&sel)?;
8781            let w_d = e.htod(w_all)?;
8782            let (gate_row_bytes, up_row_bytes) = if gu_il {
8783                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8784                (combined, combined)
8785            } else {
8786                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8787            };
8788            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8789            let act = e.moe_gate_up_silu8_dev_q8_rows(
8790                table,
8791                &sel_d,
8792                &zq,
8793                &zd,
8794                t,
8795                n_embd,
8796                n_ff_exp,
8797                n_used,
8798                n_expert,
8799                m.gate_exps.qtype,
8800                m.up_exps.qtype,
8801                gate_row_bytes,
8802                up_row_bytes,
8803                &m.dev_macros,
8804            )?;
8805            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8806            let mut moe_out = e.uninit(t * n_embd)?;
8807            e.moe_down8_fma_dev_q8_rows_g(
8808                table,
8809                &sel_d,
8810                &w_d,
8811                &aq2,
8812                &ad2,
8813                &mut moe_out,
8814                t,
8815                n_ff_exp,
8816                n_embd,
8817                n_used,
8818                n_expert,
8819                m.down_exps.qtype,
8820                m.down_exps.row_bytes,
8821            )?;
8822
8823            if std::env::var("MEMRA_MOE_STATS").is_ok() {
8824                let mut counts = vec![0usize; n_expert];
8825                for &expert in sel_all {
8826                    counts[expert as usize] += 1;
8827                }
8828                let mut sizes: Vec<usize> =
8829                    counts.into_iter().filter(|&count| count != 0).collect();
8830                sizes.sort_unstable();
8831                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
8832                println!(
8833                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
8834                     m_e: min={} median={} mean={mean:.1} max={}",
8835                    sizes.len(),
8836                    n_expert,
8837                    sizes.first().copied().unwrap_or(0),
8838                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
8839                    sizes.last().copied().unwrap_or(0),
8840                );
8841            }
8842            return Ok(moe_out);
8843        }
8844
8845        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
8846        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
8847        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
8848        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
8849        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8850        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
8851        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
8852
8853        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
8854        for (pair, &expert) in pair_ex.iter().enumerate() {
8855            by_expert[expert as usize].push(pair as i32);
8856        }
8857
8858        let pair_tok_d = e.htod_i32(&pair_tok)?;
8859        let pair_ex_d = e.htod_i32(&pair_ex)?;
8860        let pair_w_d = e.htod(w_all)?;
8861        let tok_off_d = e.htod_i32(&tok_off)?;
8862        let tok_ids_d = e.htod_i32(&tok_ids)?;
8863
8864        let matvec = |proj: i32,
8865                      pair_rows: &CudaSlice<i32>,
8866                      aq: &CudaSlice<i8>,
8867                      ad: &CudaSlice<f32>,
8868                      in_f: usize,
8869                      out_f: usize,
8870                      qtype: i32,
8871                      row_bytes: usize|
8872         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8873            e.moe_pairs_matvec_q8(
8874                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
8875                row_bytes,
8876            )
8877        };
8878
8879        let (gate_row_bytes, up_row_bytes) = if gu_il {
8880            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8881            (combined, combined)
8882        } else {
8883            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8884        };
8885        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8886        let gate = matvec(
8887            0,
8888            &pair_tok_d,
8889            &zq,
8890            &zd,
8891            n_embd,
8892            n_ff_exp,
8893            m.gate_exps.qtype,
8894            gate_row_bytes,
8895        )?;
8896        let up = matvec(
8897            1,
8898            &pair_tok_d,
8899            &zq,
8900            &zd,
8901            n_embd,
8902            n_ff_exp,
8903            m.up_exps.qtype,
8904            up_row_bytes,
8905        )?;
8906        let mut act = e.uninit(n_pairs * n_ff_exp)?;
8907        Self::ffn_act_lim(
8908            e,
8909            cfg,
8910            &gate,
8911            &up,
8912            1.0,
8913            1.0,
8914            cfg.clamp_exp_at(il as u32),
8915            &mut act,
8916            n_pairs * n_ff_exp,
8917        )?;
8918        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8919        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
8920        let pair_self_d = e.htod_i32(&pair_self)?;
8921        let down = matvec(
8922            2,
8923            &pair_self_d,
8924            &aq2,
8925            &ad2,
8926            n_ff_exp,
8927            n_embd,
8928            m.down_exps.qtype,
8929            m.down_exps.row_bytes,
8930        )?;
8931        let mut moe_out = e.uninit(t * n_embd)?;
8932        e.moe_pairs_scatter(
8933            &down,
8934            &pair_w_d,
8935            &tok_off_d,
8936            &tok_ids_d,
8937            &mut moe_out,
8938            t,
8939            n_embd,
8940        )?;
8941
8942        if std::env::var("MEMRA_MOE_STATS").is_ok() {
8943            let mut sizes: Vec<usize> = by_expert
8944                .iter()
8945                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
8946                .collect();
8947            sizes.sort_unstable();
8948            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
8949            println!(
8950                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
8951                 m_e: min={} median={} mean={mean:.1} max={}",
8952                sizes.len(),
8953                n_expert,
8954                sizes.first().copied().unwrap_or(0),
8955                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
8956                sizes.last().copied().unwrap_or(0),
8957            );
8958        }
8959        Ok(moe_out)
8960    }
8961
8962    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
8963    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
8964    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
8965    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
8966    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
8967    #[allow(clippy::too_many_arguments)]
8968    fn shexp_split_matvec(
8969        e: &Engine,
8970        rank1: &Engine,
8971        wg: &CudaSlice<u8>,
8972        wu: &CudaSlice<u8>,
8973        wd: &CudaSlice<u8>,
8974        z: &CudaSlice<f32>,
8975        lim: Option<f32>,
8976        cfg: &ModelConfig,
8977        il: u16,
8978        n_embd: usize,
8979        n_ff_sh: usize,
8980    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
8981        use cudarc::driver::DevicePtr;
8982        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
8983            return Ok(None);
8984        }
8985        let hf = n_ff_sh / 2;
8986        let nd = n_embd / 2;
8987        struct Rep {
8988            wg1: CudaSlice<u8>,
8989            wu1: CudaSlice<u8>,
8990            wd1: CudaSlice<u8>,
8991        }
8992        struct SplitWs {
8993            pin_dev: usize,
8994            // e side
8995            gate0: CudaSlice<f32>,
8996            up0: CudaSlice<f32>,
8997            act: CudaSlice<f32>,
8998            sh_buf: CudaSlice<f32>,
8999            ev_z: cudarc::driver::CudaEvent,
9000            ev_act0: cudarc::driver::CudaEvent,
9001            // rank1 side
9002            z1: CudaSlice<f32>,
9003            g1: CudaSlice<f32>,
9004            u1: CudaSlice<f32>,
9005            a1h: CudaSlice<f32>,
9006            act1: CudaSlice<f32>,
9007            y1: CudaSlice<f32>,
9008            ev_act1: cudarc::driver::CudaEvent,
9009            ev_y1: cudarc::driver::CudaEvent,
9010            raw_act_e: u64,
9011            raw_sh_e: u64,
9012            raw_z1: u64,
9013            raw_a1h: u64,
9014            raw_act1: u64,
9015            raw_y1: u64,
9016        }
9017        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9018        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9019            std::sync::Mutex::new(None);
9020        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9021        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9022        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9023        let pins = e.ctx().ordinal();
9024        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9025            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9026                let _m = e.gpu.enter_main()?;
9027                (
9028                    e.htod(&vec![0.0f32; hf])?,
9029                    e.htod(&vec![0.0f32; hf])?,
9030                    e.htod(&vec![0.0f32; n_ff_sh])?,
9031                    e.htod(&vec![0.0f32; n_embd])?,
9032                    e.ctx().new_event(None)?,
9033                    e.ctx().new_event(None)?,
9034                )
9035            };
9036            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9037                let _r = rank1.gpu.enter_main()?;
9038                (
9039                    rank1.htod(&vec![0.0f32; n_embd])?,
9040                    rank1.htod(&vec![0.0f32; hf])?,
9041                    rank1.htod(&vec![0.0f32; hf])?,
9042                    rank1.htod(&vec![0.0f32; hf])?,
9043                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9044                    rank1.htod(&vec![0.0f32; nd])?,
9045                    rank1.ctx().new_event(None)?,
9046                    rank1.ctx().new_event(None)?,
9047                )
9048            };
9049            let (raw_act_e, raw_sh_e) = {
9050                let _m = e.gpu.enter_main()?;
9051                let stream = e.stream();
9052                let (a, _g0) = act.device_ptr(&stream);
9053                let (b, _g1) = sh_buf.device_ptr(&stream);
9054                (a as u64, b as u64)
9055            };
9056            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9057                let _r = rank1.gpu.enter_main()?;
9058                let rs = rank1.stream();
9059                let (a, _g0) = z1.device_ptr(&rs);
9060                let (b, _g1) = a1h.device_ptr(&rs);
9061                let (c, _g2) = act1.device_ptr(&rs);
9062                let (d, _g3) = y1.device_ptr(&rs);
9063                (a as u64, b as u64, c as u64, d as u64)
9064            };
9065            *guard = Some(SplitWs {
9066                pin_dev: pins,
9067                gate0,
9068                up0,
9069                act,
9070                sh_buf,
9071                ev_z,
9072                ev_act0,
9073                z1,
9074                g1,
9075                u1,
9076                a1h,
9077                act1,
9078                y1,
9079                ev_act1,
9080                ev_y1,
9081                raw_act_e,
9082                raw_sh_e,
9083                raw_z1,
9084                raw_a1h,
9085                raw_act1,
9086                raw_y1,
9087            });
9088        }
9089        let ws = guard.as_mut().expect("armed above");
9090        let wg_pin = {
9091            let _m = e.gpu.enter_main()?;
9092            let stream = e.stream();
9093            let (p, _g) = wg.device_ptr(&stream);
9094            p as u64
9095        };
9096        if !reps.contains_key(&wg_pin) {
9097            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9098            let mut up = |src: &CudaSlice<u8>,
9099                          off_bytes: usize,
9100                          len: usize|
9101             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9102                use cudarc::driver::sys;
9103                let sptr = {
9104                    let _m = e.gpu.enter_main()?;
9105                    let stream = e.stream();
9106                    let (p, _g) = src.device_ptr(&stream);
9107                    p as u64 + off_bytes as u64
9108                };
9109                let dst = {
9110                    let _r = rank1.gpu.enter_main()?;
9111                    rank1.alloc_u8_uninit(len)?
9112                };
9113                let dptr = {
9114                    let _r = rank1.gpu.enter_main()?;
9115                    let rs = rank1.stream();
9116                    let (p, _g) = dst.device_ptr(&rs);
9117                    p as u64
9118                };
9119                let _r = rank1.gpu.enter_main()?;
9120                let r = unsafe {
9121                    sys::cuMemcpyAsync(
9122                        dptr as sys::CUdeviceptr,
9123                        sptr as sys::CUdeviceptr,
9124                        len,
9125                        rank1.stream().cu_stream() as sys::CUstream,
9126                    )
9127                };
9128                if r != sys::CUresult::CUDA_SUCCESS {
9129                    return Err(format!("shexp split replica upload: {r:?}").into());
9130                }
9131                rank1.stream().synchronize()?;
9132                Ok(dst)
9133            };
9134            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9135            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9136            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9137            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9138        }
9139        let _ = il;
9140        // Per token, evented split flow.
9141        let raw_z = {
9142            let _m = e.gpu.enter_main()?;
9143            let stream = e.stream();
9144            let (p, _g) = z.device_ptr(&stream);
9145            ws.ev_z.record(&stream)?;
9146            p as u64
9147        };
9148        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9149        {
9150            let rep = reps.get(&wg_pin).expect("uploaded above");
9151            let _r = rank1.gpu.enter_main()?;
9152            rank1.stream().wait(&ws.ev_z)?;
9153            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9154            let SplitWs {
9155                z1, g1, u1, a1h, ..
9156            } = &mut *ws;
9157            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9158            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9159            // local place into act1[hf..] + P2P push into e's act[hf..]
9160            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9161            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9162            ws.ev_act1.record(&rank1.stream())?;
9163        }
9164        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9165        {
9166            let _m = e.gpu.enter_main()?;
9167            let SplitWs {
9168                gate0, up0, act, ..
9169            } = &mut *ws;
9170            let wg_lo = wg.slice(0..hf * n_embd * 2);
9171            let wu_lo = wu.slice(0..hf * n_embd * 2);
9172            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9173            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9174            ws.ev_act0.record(&e.stream())?;
9175        }
9176        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9177        {
9178            let rep = reps.get(&wg_pin).expect("uploaded above");
9179            let _r = rank1.gpu.enter_main()?;
9180            rank1.stream().wait(&ws.ev_act0)?;
9181            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9182            let SplitWs { act1, y1, .. } = &mut *ws;
9183            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9184            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9185            ws.ev_y1.record(&rank1.stream())?;
9186        }
9187        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9188        {
9189            let _m = e.gpu.enter_main()?;
9190            e.stream().wait(&ws.ev_act1)?;
9191            let SplitWs { act, sh_buf, .. } = &mut *ws;
9192            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9193            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9194            e.stream().wait(&ws.ev_y1)?;
9195            let mut sh = e.uninit(n_embd)?;
9196            {
9197                let mut dst = sh.slice_mut(0..n_embd);
9198                e.stream()
9199                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9200            }
9201            Ok(Some(sh))
9202        }
9203    }
9204
9205    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9206    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9207    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9208    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9209    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9210    /// the join with the exact add_scaled_rows expression: values unchanged.
9211    fn shexp_overlap_issue(
9212        e: &Engine,
9213        m: &MoeWeights,
9214        z: &CudaSlice<f32>,
9215        cfg: &ModelConfig,
9216        il: u16,
9217        n_embd: usize,
9218    ) -> Result<bool, Box<dyn std::error::Error>> {
9219        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9220            return Ok(false);
9221        }
9222        let (
9223            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9224            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9225            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9226        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9227        else {
9228            return Ok(false);
9229        };
9230        let n_ff_sh = m
9231            .gate_shexp
9232            .as_ref()
9233            .expect("matched Some above")
9234            .out_features();
9235        let lim = cfg.clamp_shexp_at(il as u32);
9236        let mut guard = SHEXP_OV_WS
9237            .lock()
9238            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9239        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9240        if guard
9241            .as_ref()
9242            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9243        {
9244            *guard = Some((
9245                pins.0,
9246                pins.1,
9247                pins.2,
9248                e.uninit(n_ff_sh)?,
9249                e.uninit(n_embd)?,
9250            ));
9251        }
9252        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9253        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9254        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9255        drop(guard);
9256        Ok(true)
9257    }
9258
9259    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9260    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9261    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9262    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9263    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9264    #[allow(clippy::too_many_arguments)]
9265    fn shexp_dev1_issue(
9266        e: &Engine,
9267        rank1: &Engine,
9268        m: &MoeWeights,
9269        z: &CudaSlice<f32>,
9270        cfg: &ModelConfig,
9271        il: u16,
9272        n_embd: usize,
9273    ) -> Result<bool, Box<dyn std::error::Error>> {
9274        use cudarc::driver::DevicePtr;
9275        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9276            return Ok(false);
9277        }
9278        let (
9279            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9280            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9281            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9282        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9283        else {
9284            return Ok(false);
9285        };
9286        let n_ff_sh = m
9287            .gate_shexp
9288            .as_ref()
9289            .expect("matched Some above")
9290            .out_features();
9291        let lim = cfg.clamp_shexp_at(il as u32);
9292        // Shared scratch, geometry-keyed.
9293        let mut ws_guard = SHEXP_D1_WS
9294            .lock()
9295            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9296        if ws_guard
9297            .as_ref()
9298            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9299        {
9300            let (act1, z1, ev_done) = {
9301                let _r1 = rank1.gpu.enter_main()?;
9302                (
9303                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9304                    rank1.htod(&vec![0.0f32; n_embd])?,
9305                    rank1.ctx().new_event(None)?,
9306                )
9307            };
9308            let (sh_root, ev_z) = {
9309                let _main = e.gpu.enter_main()?;
9310                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9311            };
9312            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9313        }
9314        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9315        let mut reps_guard = SHEXP_D1_REPS
9316            .lock()
9317            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9318        let reps = reps_guard.get_or_insert_with(Default::default);
9319        if !reps.contains_key(&il) {
9320            let (wg1, wu1, wd1) = {
9321                let _r1 = rank1.gpu.enter_main()?;
9322                (
9323                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9324                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9325                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9326                )
9327            };
9328            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9329                let s_ptr = {
9330                    let _main = e.gpu.enter_main()?;
9331                    let stream = e.stream();
9332                    let (p, _g) = src.device_ptr(&stream);
9333                    p as u64
9334                };
9335                let d_ptr = {
9336                    let _r1 = rank1.gpu.enter_main()?;
9337                    let stream = rank1.stream();
9338                    let (p, _g) = dst.device_ptr(&stream);
9339                    p as u64
9340                };
9341                let _r1 = rank1.gpu.enter_main()?;
9342                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9343            }
9344            {
9345                let _r1 = rank1.gpu.enter_main()?;
9346                rank1.stream().synchronize()?;
9347            }
9348            reps.insert(il, (wg1, wu1, wd1));
9349        }
9350        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9351        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9352        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9353        // row root-side (single store pass), rings ev_done.
9354        let (raw_z, raw_sh) = {
9355            let _main = e.gpu.enter_main()?;
9356            let stream = e.stream();
9357            let (a, _g0) = z.device_ptr(&stream);
9358            let (b, _g1) = sh_root.device_ptr(&stream);
9359            ev_z.record(&stream)?;
9360            (a as u64, b as u64)
9361        };
9362        {
9363            let _r1 = rank1.gpu.enter_main()?;
9364            rank1.stream().wait(ev_z)?;
9365            let raw_z1 = {
9366                let stream = rank1.stream();
9367                let (p, _g) = z1.device_ptr(&stream);
9368                p as u64
9369            };
9370            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9371            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9372            // down writes the ROOT-resident row over P2P via the raw-output twin of
9373            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9374            // cross-device, so launch on the raw pointer.
9375            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9376            ev_done.record(&rank1.stream())?;
9377        }
9378        Ok(true)
9379    }
9380
9381    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9382    fn shexp_dev1_apply(
9383        e: &Engine,
9384        output: &mut CudaSlice<f32>,
9385        n_embd: usize,
9386    ) -> Result<(), Box<dyn std::error::Error>> {
9387        let guard = SHEXP_D1_WS
9388            .lock()
9389            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9390        let (pin, _, _, sh_root, _, ev_done) =
9391            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9392        if pin.0 != n_embd {
9393            return Err("shexp dev1 width drifted".into());
9394        }
9395        let _main = e.gpu.enter_main()?;
9396        e.stream().wait(ev_done)?;
9397        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9398            std::sync::Mutex::new(None);
9399        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9400        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9401            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9402        }
9403        let ones = &og.as_ref().expect("armed above").1;
9404        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9405        Ok(())
9406    }
9407
9408    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9409    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9410    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9411    fn shexp_overlap_tail_ptrs(
9412        e: &Engine,
9413        m: &MoeWeights,
9414        cfg: &ModelConfig,
9415        n_embd: usize,
9416    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9417        use cudarc::driver::DevicePtr;
9418        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9419            return Ok(None);
9420        }
9421        let (
9422            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9423            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9424            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9425        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9426        else {
9427            return Ok(None);
9428        };
9429        let n_ff_sh = m
9430            .gate_shexp
9431            .as_ref()
9432            .expect("matched Some above")
9433            .out_features();
9434        let mut guard = SHEXP_OV_WS
9435            .lock()
9436            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9437        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9438        if guard
9439            .as_ref()
9440            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9441        {
9442            *guard = Some((
9443                pins.0,
9444                pins.1,
9445                pins.2,
9446                e.uninit(n_ff_sh)?,
9447                e.uninit(n_embd)?,
9448            ));
9449        }
9450        let sh_raw = {
9451            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9452            let stream = e.stream();
9453            let (p, _g) = sh.device_ptr(&stream);
9454            p as u64
9455        };
9456        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9457            std::sync::Mutex::new(None);
9458        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9459        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9460            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9461        }
9462        let ones_raw = {
9463            let stream = e.stream();
9464            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
9465            p as u64
9466        };
9467        Ok(Some((sh_raw, ones_raw)))
9468    }
9469
9470    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
9471    /// add_scaled_rows program the split path used (persistent ones row, no htod).
9472    fn shexp_overlap_apply(
9473        e: &Engine,
9474        output: &mut CudaSlice<f32>,
9475        n_embd: usize,
9476    ) -> Result<(), Box<dyn std::error::Error>> {
9477        let guard = SHEXP_OV_WS
9478            .lock()
9479            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9480        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
9481        if *ne != n_embd {
9482            return Err("shexp overlap width drifted".into());
9483        }
9484        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9485            std::sync::Mutex::new(None);
9486        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
9487        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9488            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9489        }
9490        let ones = &og.as_ref().expect("armed above").1;
9491        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
9492        Ok(())
9493    }
9494
9495    fn moe_ffn_grouped_add_shared(
9496        e: &Engine,
9497        m: &MoeWeights,
9498        z: &CudaSlice<f32>,
9499        t: usize,
9500        cfg: &ModelConfig,
9501        il: u16,
9502        moe_out: &mut CudaSlice<f32>,
9503    ) -> Result<(), Box<dyn std::error::Error>> {
9504        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
9505        // queued matmuls here rather than at the next host readback).
9506        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9507        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9508        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9509        let shexp_started = shexp_timing.then(std::time::Instant::now);
9510        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
9511        if let Some(started) = shexp_started {
9512            use std::sync::atomic::Ordering;
9513            e.stream().synchronize()?;
9514            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9515                + started.elapsed().as_nanos() as u64;
9516            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9517            if calls % 430 == 0 {
9518                eprintln!(
9519                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9520                    ns as f64 / 1.0e6,
9521                    ns as f64 / calls as f64 / 1.0e3,
9522                );
9523            }
9524        }
9525        result
9526    }
9527
9528    #[allow(clippy::too_many_arguments)]
9529    fn moe_ffn_grouped_add_shared_inner(
9530        e: &Engine,
9531        m: &MoeWeights,
9532        z: &CudaSlice<f32>,
9533        t: usize,
9534        cfg: &ModelConfig,
9535        il: u16,
9536        moe_out: &mut CudaSlice<f32>,
9537    ) -> Result<(), Box<dyn std::error::Error>> {
9538        let n_embd = cfg.n_embd as usize;
9539        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
9540            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9541        {
9542            let n_ff_sh = gate_shexp.out_features();
9543            let lim = cfg.clamp_shexp_at(il as u32);
9544            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
9545            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
9546            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
9547            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
9548            // operand pre-quantized (kernel_check-proven identities). This path measured
9549            // 167us/layer as separate matmuls + 5 allocs at decode.
9550            let fused = t == 1
9551                && lim.is_none()
9552                && cfg.m3.is_none()
9553                && e.uses_q8_1_fast(gate_shexp)
9554                && e.uses_q8_1_fast(up_shexp);
9555            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
9556            // the two matvec_bf16 launches matmul would issue).
9557            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
9558                match (gate_shexp, up_shexp) {
9559                    (
9560                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
9561                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
9562                    ) => Some((wg, wu)),
9563                    _ => None,
9564                }
9565            } else {
9566                None
9567            };
9568            let sh = if let Some((wg, wu)) = bf16_dual {
9569                // Persistent shared-expert workspace: sizes are constant across every MoE
9570                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
9571                // the four per-layer allocations. Buffers are fully overwritten each call.
9572                static SHEXP_WS: std::sync::Mutex<
9573                    Option<(
9574                        usize,
9575                        usize,
9576                        usize,
9577                        CudaSlice<f32>,
9578                        CudaSlice<f32>,
9579                        CudaSlice<f32>,
9580                        CudaSlice<f32>,
9581                    )>,
9582                > = std::sync::Mutex::new(None);
9583                let down_bf16 = match down_shexp {
9584                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9585                    _ => None,
9586                };
9587                let mut guard = SHEXP_WS
9588                    .lock()
9589                    .map_err(|_| "shexp workspace lock is poisoned")?;
9590                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9591                if guard
9592                    .as_ref()
9593                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9594                {
9595                    *guard = Some((
9596                        pins.0,
9597                        pins.1,
9598                        pins.2,
9599                        e.uninit(n_ff_sh)?,
9600                        e.uninit(n_ff_sh)?,
9601                        e.uninit(n_ff_sh)?,
9602                        e.uninit(n_embd)?,
9603                    ));
9604                }
9605                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
9606                // through to the single-device arm when ineligible.
9607                {
9608                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9609                    let split_on = *ON
9610                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
9611                    if split_on {
9612                        if let (Some(wd), Some(rank1)) = (
9613                            match down_shexp {
9614                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9615                                _ => None,
9616                            },
9617                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
9618                        ) {
9619                            if let Some(sh) = Self::shexp_split_matvec(
9620                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
9621                            )? {
9622                                drop(guard);
9623                                let gate = match &m.gate_inp_shexp {
9624                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
9625                                        z,
9626                                        gate_inp_shexp.float_data(),
9627                                        n_embd,
9628                                        t,
9629                                    )?,
9630                                    None => e.htod(&vec![1.0f32; t])?,
9631                                };
9632                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9633                                return Ok(());
9634                            }
9635                        }
9636                    }
9637                }
9638                let (_, _, _, gate, up, act, sh_buf) =
9639                    guard.as_mut().expect("shexp workspace initialized above");
9640                if cfg.m3.is_none() {
9641                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
9642                    // per-row program + exact silu/clamped expression, bit-identical.
9643                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9644                    let _ = (&gate, &up);
9645                } else {
9646                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
9647                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
9648                }
9649                if let Some(down) = down_bf16 {
9650                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
9651                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
9652                    // exact f32acc per-row program + the exact add_scaled_rows expression
9653                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
9654                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
9655                    // accumulate consumes the same f32 the split path stored and reloaded.
9656                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9657                    let fuse_da = *FUSE_DA.get_or_init(|| {
9658                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
9659                    });
9660                    if fuse_da && m.gate_inp_shexp.is_none() {
9661                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9662                            std::sync::Mutex::new(None);
9663                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
9664                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9665                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9666                        }
9667                        let ones = &og.as_ref().expect("armed above").1;
9668                        e.matvec_bf16_down_addscale_into(
9669                            down, act, ones, moe_out, n_ff_sh, n_embd,
9670                        )?;
9671                        return Ok(());
9672                    }
9673                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
9674                    let sh = e.uninit(n_embd)?;
9675                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
9676                    let mut sh = sh;
9677                    {
9678                        let mut dst = sh.slice_mut(0..n_embd);
9679                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
9680                    }
9681                    sh
9682                } else {
9683                    e.matmul(down_shexp, act, 1)?
9684                }
9685            } else if fused {
9686                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
9687                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
9688                    Some((gate, up)) => Some((gate, up)),
9689                    None => {
9690                        match (
9691                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
9692                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
9693                        ) {
9694                            (Some(gate), Some(up)) => Some((gate, up)),
9695                            _ => None,
9696                        }
9697                    }
9698                };
9699                match pair {
9700                    Some(((gate, gs), (up, us))) => {
9701                        if e.uses_q8_1_fast(down_shexp) {
9702                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
9703                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
9704                        } else {
9705                            let mut act = e.uninit(n_ff_sh)?;
9706                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
9707                            e.matmul(down_shexp, &act, 1)?
9708                        }
9709                    }
9710                    None => {
9711                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
9712                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
9713                        let mut act = e.uninit(n_ff_sh)?;
9714                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
9715                        e.matmul(down_shexp, &act, 1)?
9716                    }
9717                }
9718            } else {
9719                let sg_gate = e.matmul(gate_shexp, z, t)?;
9720                let sg_up = e.matmul(up_shexp, z, t)?;
9721                let mut sa = e.uninit(t * n_ff_sh)?;
9722                Self::ffn_act_lim(
9723                    e,
9724                    cfg,
9725                    &sg_gate,
9726                    &sg_up,
9727                    1.0,
9728                    1.0,
9729                    lim,
9730                    &mut sa,
9731                    t * n_ff_sh,
9732                )?;
9733                e.matmul(down_shexp, &sa, t)?
9734            };
9735            let gate = match &m.gate_inp_shexp {
9736                Some(gate_inp_shexp) => {
9737                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
9738                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
9739                    } else {
9740                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
9741                        let mut gate = e.uninit(t)?;
9742                        e.sigmoid(&raw, &mut gate, t)?;
9743                        gate
9744                    }
9745                }
9746                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
9747                // synchronizes the stream — measured as the biggest per-layer host gap
9748                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
9749                // device serves every layer; larger t (prefill) keeps the plain htod.
9750                None if t == 1 => {
9751                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9752                        std::sync::Mutex::new(None);
9753                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
9754                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9755                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9756                    }
9757                    let ones = &guard.as_ref().expect("armed above").1;
9758                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
9759                    return Ok(());
9760                }
9761                None => e.htod(&vec![1.0f32; t])?,
9762            };
9763            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9764        }
9765        Ok(())
9766    }
9767
9768    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
9769    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
9770    pub(crate) fn moe_ffn_grouped(
9771        e: &Engine,
9772        m: &MoeWeights,
9773        z: &CudaSlice<f32>,
9774        t: usize,
9775        cfg: &ModelConfig,
9776        il: u16,
9777        max_block: usize,
9778    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9779        let moe = cfg.moe.as_ref().unwrap();
9780        let n_embd = cfg.n_embd as usize;
9781        let n_expert = moe.expert_count as usize;
9782        let n_used = moe.expert_used_count as usize;
9783        let n_ff_exp = moe.expert_ff_length as usize;
9784        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
9785        let lim_exp = cfg.clamp_exp_at(il as u32);
9786
9787        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
9788        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
9789        // enters the softmax-only pairs/dev router.
9790        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
9791        if let Some(sig) = cfg.sigmoid_router() {
9792            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
9793        }
9794        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
9795            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
9796        } else {
9797            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
9798        };
9799        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
9800        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
9801        Self::trace_moe_input(e, il, t, n_embd, z)?;
9802
9803        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
9804        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
9805        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
9806        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
9807        let no_exp_macros = m.gate_exps.macros.is_none()
9808            && m.up_exps.macros.is_none()
9809            && m.down_exps.macros.is_none();
9810        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
9811            m.has_uniform_expert_layout()
9812                && no_exp_macros
9813                && moe_q8_enabled()
9814                && q8_expert_supported(m.gate_exps.qtype)
9815                && q8_expert_supported(m.up_exps.qtype)
9816                && q8_expert_supported(m.down_exps.qtype)
9817                && moe_slab_enabled()
9818                && dev.dev == e.ctx().ordinal()
9819        });
9820        if let Some(dev) = resident_q8 {
9821            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
9822                e,
9823                m,
9824                z,
9825                t,
9826                cfg,
9827                il,
9828                &sel_all,
9829                &w_all,
9830                &dev.ptr_row,
9831                dev.gu_il,
9832            )?;
9833            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
9834            return Ok(moe_out);
9835        }
9836
9837        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
9838        // For each expert e, we need: which tokens use it, their positions in z, their top-k
9839        // slot index (for bit-identical accumulation), and their weights.
9840        struct ExpertGroup {
9841            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
9842            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
9843            weights: Vec<f32>,      // renormalized weight for that token-expert pair
9844        }
9845        let mut groups: Vec<ExpertGroup> = (0..n_expert)
9846            .map(|_| ExpertGroup {
9847                tok_indices: Vec::new(),
9848                slot_indices: Vec::new(),
9849                weights: Vec::new(),
9850            })
9851            .collect();
9852
9853        for tok in 0..t {
9854            for j in 0..n_used {
9855                let ex = sel_all[tok * n_used + j] as usize;
9856                let w = w_all[tok * n_used + j];
9857                groups[ex].tok_indices.push(tok as i32);
9858                groups[ex].slot_indices.push(j as i32);
9859                groups[ex].weights.push(w);
9860            }
9861        }
9862
9863        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
9864        // Each token's 8 expert contributions land in their respective slots.
9865        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
9866        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
9867
9868        // Expert weight dimensions (used in both cache and staging paths).
9869        let g_len = m.gate_exps.max_expert_bytes();
9870        let u_len = m.up_exps.max_expert_bytes();
9871        let d_len = m.down_exps.max_expert_bytes();
9872        let moe_q8 = m.has_uniform_expert_layout()
9873            && moe_q8_enabled()
9874            && q8_expert_supported(m.gate_exps.qtype)
9875            && q8_expert_supported(m.up_exps.qtype)
9876            && q8_expert_supported(m.down_exps.qtype);
9877        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
9878        // Interleaved GU slabs require the pointer-table fast path above.
9879        let slab_local = m
9880            .dev_exps
9881            .as_ref()
9882            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
9883        let use_cache =
9884            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
9885        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
9886        // also does: a local resident slab or a live SLRU dispatch.
9887        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
9888
9889        // GPU scratch for staging (only allocated without a local slab or cache).
9890        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
9891            (
9892                Some(e.alloc_u8(g_len)?),
9893                Some(e.alloc_u8(u_len)?),
9894                Some(e.alloc_u8(d_len)?),
9895            )
9896        } else {
9897            (None, None, None)
9898        };
9899
9900        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
9901        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
9902        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
9903        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
9904        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
9905        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
9906        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
9907        // at long prompts where every expert stages regardless. Order is FREE to change without
9908        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
9909        // regardless of expert processing order (the whole point of the slots).
9910        let mut order: Vec<usize> = (0..n_expert)
9911            .filter(|&ex| !groups[ex].tok_indices.is_empty())
9912            .collect();
9913        order.sort_by(|&a, &b| {
9914            groups[b]
9915                .tok_indices
9916                .len()
9917                .cmp(&groups[a].tok_indices.len())
9918                .then(a.cmp(&b))
9919        });
9920        let mut m_dist: Vec<usize> = Vec::new(); // for stats
9921        let page_window = moe_page_prefetch_window();
9922        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
9923        if worker_disk_prefetch {
9924            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
9925                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
9926            }
9927        }
9928        for (order_pos, &ex) in order.iter().enumerate() {
9929            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
9930                Self::moe_prefetch_host_expert(order[next], m);
9931            }
9932            if worker_disk_prefetch {
9933                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
9934                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9935                    let keep = [
9936                        BlockId::new(il, PROJ_GATE, ex as u16),
9937                        BlockId::new(il, PROJ_UP, ex as u16),
9938                        BlockId::new(il, PROJ_DOWN, ex as u16),
9939                    ];
9940                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
9941                }
9942            }
9943            let grp = &groups[ex];
9944            let m_e = grp.tok_indices.len();
9945            m_dist.push(m_e);
9946            let gl = m.gate_exps.expert_layout(ex);
9947            let ul = m.up_exps.expert_layout(ex);
9948            let dl = m.down_exps.expert_layout(ex);
9949
9950            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
9951            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
9952            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
9953            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
9954            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
9955            let dmac = m.down_exps.macro_scale(ex);
9956            let weight_d = if dmac == 1.0 {
9957                e.htod(&grp.weights)?
9958            } else {
9959                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
9960                e.htod(&scaled)?
9961            };
9962
9963            // GATHER: collect m_e activation rows from z into a contiguous buffer.
9964            let mut gathered = e.zeros(m_e * n_embd)?;
9965            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
9966            let gv = gathered.slice(0..m_e * n_embd);
9967
9968            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
9969            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
9970            let y = if let Some(dev) = slab_local {
9971                let gate_start = ex * m.gate_exps.expert_stride;
9972                let up_start = ex * m.up_exps.expert_stride;
9973                let down_start = ex * m.down_exps.expert_stride;
9974                if grouped_q8 {
9975                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
9976                    let gate = e.qmatvec_expert_q8(
9977                        &dev.gate,
9978                        gate_start..gate_start + gl.len,
9979                        &zq,
9980                        &zd,
9981                        m_e,
9982                        m.gate_exps.in_f,
9983                        m.gate_exps.out_f,
9984                        gl.qtype,
9985                        gl.row_bytes,
9986                    )?;
9987                    let up = e.qmatvec_expert_q8(
9988                        &dev.up,
9989                        up_start..up_start + ul.len,
9990                        &zq,
9991                        &zd,
9992                        m_e,
9993                        m.up_exps.in_f,
9994                        m.up_exps.out_f,
9995                        ul.qtype,
9996                        ul.row_bytes,
9997                    )?;
9998                    let mut act = e.uninit(m_e * n_ff_exp)?;
9999                    Self::ffn_act_lim(
10000                        e,
10001                        cfg,
10002                        &gate,
10003                        &up,
10004                        m.gate_exps.macro_scale(ex),
10005                        m.up_exps.macro_scale(ex),
10006                        lim_exp,
10007                        &mut act,
10008                        m_e * n_ff_exp,
10009                    )?;
10010                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10011                    e.qmatvec_expert_q8(
10012                        &dev.down,
10013                        down_start..down_start + dl.len,
10014                        &aq2,
10015                        &ad2,
10016                        m_e,
10017                        m.down_exps.in_f,
10018                        m.down_exps.out_f,
10019                        dl.qtype,
10020                        dl.row_bytes,
10021                    )?
10022                } else {
10023                    let gate = e.qmatvec_view(
10024                        &dev.gate,
10025                        gate_start..gate_start + gl.len,
10026                        &gv,
10027                        m_e,
10028                        m.gate_exps.in_f,
10029                        m.gate_exps.out_f,
10030                        gl.qtype,
10031                        gl.row_bytes,
10032                    )?;
10033                    let up = e.qmatvec_view(
10034                        &dev.up,
10035                        up_start..up_start + ul.len,
10036                        &gv,
10037                        m_e,
10038                        m.up_exps.in_f,
10039                        m.up_exps.out_f,
10040                        ul.qtype,
10041                        ul.row_bytes,
10042                    )?;
10043                    let mut act = e.uninit(m_e * n_ff_exp)?;
10044                    Self::ffn_act_lim(
10045                        e,
10046                        cfg,
10047                        &gate,
10048                        &up,
10049                        m.gate_exps.macro_scale(ex),
10050                        m.up_exps.macro_scale(ex),
10051                        lim_exp,
10052                        &mut act,
10053                        m_e * n_ff_exp,
10054                    )?;
10055                    let actv = act.slice(0..m_e * n_ff_exp);
10056                    e.qmatvec_view(
10057                        &dev.down,
10058                        down_start..down_start + dl.len,
10059                        &actv,
10060                        m_e,
10061                        m.down_exps.in_f,
10062                        m.down_exps.out_f,
10063                        dl.qtype,
10064                        dl.row_bytes,
10065                    )?
10066                }
10067            } else if use_cache {
10068                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10069                if grouped_q8 {
10070                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10071                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10072                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10073                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10074                        eng.qmatvec_expert_q8(
10075                            cache.buf(slot),
10076                            0..gl.len,
10077                            &zq,
10078                            &zd,
10079                            m_e,
10080                            m.gate_exps.in_f,
10081                            m.gate_exps.out_f,
10082                            gl.qtype,
10083                            gl.row_bytes,
10084                        )
10085                    })?;
10086                    let up = e.with_moe_cache(max_block, |cache, eng| {
10087                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10088                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10089                        eng.qmatvec_expert_q8(
10090                            cache.buf(slot),
10091                            0..ul.len,
10092                            &zq,
10093                            &zd,
10094                            m_e,
10095                            m.up_exps.in_f,
10096                            m.up_exps.out_f,
10097                            ul.qtype,
10098                            ul.row_bytes,
10099                        )
10100                    })?;
10101                    let mut act = e.uninit(m_e * n_ff_exp)?;
10102                    Self::ffn_act_lim(
10103                        e,
10104                        cfg,
10105                        &gate,
10106                        &up,
10107                        m.gate_exps.macro_scale(ex),
10108                        m.up_exps.macro_scale(ex),
10109                        lim_exp,
10110                        &mut act,
10111                        m_e * n_ff_exp,
10112                    )?;
10113                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10114                    e.with_moe_cache(max_block, |cache, eng| {
10115                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10116                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10117                        eng.qmatvec_expert_q8(
10118                            cache.buf(slot),
10119                            0..dl.len,
10120                            &aq2,
10121                            &ad2,
10122                            m_e,
10123                            m.down_exps.in_f,
10124                            m.down_exps.out_f,
10125                            dl.qtype,
10126                            dl.row_bytes,
10127                        )
10128                    })?
10129                } else {
10130                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10131                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10132                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10133                        eng.qmatvec_view(
10134                            cache.buf(slot),
10135                            0..gl.len,
10136                            &gv,
10137                            m_e,
10138                            m.gate_exps.in_f,
10139                            m.gate_exps.out_f,
10140                            gl.qtype,
10141                            gl.row_bytes,
10142                        )
10143                    })?;
10144                    let up = e.with_moe_cache(max_block, |cache, eng| {
10145                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10146                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10147                        eng.qmatvec_view(
10148                            cache.buf(slot),
10149                            0..ul.len,
10150                            &gv,
10151                            m_e,
10152                            m.up_exps.in_f,
10153                            m.up_exps.out_f,
10154                            ul.qtype,
10155                            ul.row_bytes,
10156                        )
10157                    })?;
10158                    let mut act = e.uninit(m_e * n_ff_exp)?;
10159                    Self::ffn_act_lim(
10160                        e,
10161                        cfg,
10162                        &gate,
10163                        &up,
10164                        m.gate_exps.macro_scale(ex),
10165                        m.up_exps.macro_scale(ex),
10166                        lim_exp,
10167                        &mut act,
10168                        m_e * n_ff_exp,
10169                    )?;
10170                    let actv = act.slice(0..m_e * n_ff_exp);
10171                    e.with_moe_cache(max_block, |cache, eng| {
10172                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10173                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10174                        eng.qmatvec_view(
10175                            cache.buf(slot),
10176                            0..dl.len,
10177                            &actv,
10178                            m_e,
10179                            m.down_exps.in_f,
10180                            m.down_exps.out_f,
10181                            dl.qtype,
10182                            dl.row_bytes,
10183                        )
10184                    })?
10185                }
10186            } else {
10187                let sg = scratch_g.as_mut().unwrap();
10188                let su = scratch_u.as_mut().unwrap();
10189                let sd = scratch_d.as_mut().unwrap();
10190                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10191                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10192                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10193                if grouped_q8 {
10194                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10195                    let gate = e.qmatvec_expert_q8(
10196                        sg,
10197                        0..gl.len,
10198                        &zq,
10199                        &zd,
10200                        m_e,
10201                        m.gate_exps.in_f,
10202                        m.gate_exps.out_f,
10203                        gl.qtype,
10204                        gl.row_bytes,
10205                    )?;
10206                    let up = e.qmatvec_expert_q8(
10207                        su,
10208                        0..ul.len,
10209                        &zq,
10210                        &zd,
10211                        m_e,
10212                        m.up_exps.in_f,
10213                        m.up_exps.out_f,
10214                        ul.qtype,
10215                        ul.row_bytes,
10216                    )?;
10217                    let mut act = e.uninit(m_e * n_ff_exp)?;
10218                    Self::ffn_act_lim(
10219                        e,
10220                        cfg,
10221                        &gate,
10222                        &up,
10223                        m.gate_exps.macro_scale(ex),
10224                        m.up_exps.macro_scale(ex),
10225                        lim_exp,
10226                        &mut act,
10227                        m_e * n_ff_exp,
10228                    )?;
10229                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10230                    e.qmatvec_expert_q8(
10231                        sd,
10232                        0..dl.len,
10233                        &aq2,
10234                        &ad2,
10235                        m_e,
10236                        m.down_exps.in_f,
10237                        m.down_exps.out_f,
10238                        dl.qtype,
10239                        dl.row_bytes,
10240                    )?
10241                } else {
10242                    let gate = e.qmatvec_view(
10243                        sg,
10244                        0..gl.len,
10245                        &gv,
10246                        m_e,
10247                        m.gate_exps.in_f,
10248                        m.gate_exps.out_f,
10249                        gl.qtype,
10250                        gl.row_bytes,
10251                    )?;
10252                    let up = e.qmatvec_view(
10253                        su,
10254                        0..ul.len,
10255                        &gv,
10256                        m_e,
10257                        m.up_exps.in_f,
10258                        m.up_exps.out_f,
10259                        ul.qtype,
10260                        ul.row_bytes,
10261                    )?;
10262                    let mut act = e.uninit(m_e * n_ff_exp)?;
10263                    Self::ffn_act_lim(
10264                        e,
10265                        cfg,
10266                        &gate,
10267                        &up,
10268                        m.gate_exps.macro_scale(ex),
10269                        m.up_exps.macro_scale(ex),
10270                        lim_exp,
10271                        &mut act,
10272                        m_e * n_ff_exp,
10273                    )?;
10274                    let actv = act.slice(0..m_e * n_ff_exp);
10275                    e.qmatvec_view(
10276                        sd,
10277                        0..dl.len,
10278                        &actv,
10279                        m_e,
10280                        m.down_exps.in_f,
10281                        m.down_exps.out_f,
10282                        dl.qtype,
10283                        dl.row_bytes,
10284                    )?
10285                }
10286            };
10287
10288            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10289            e.scatter_slot(
10290                &y,
10291                &tok_idx_d,
10292                &slot_idx_d,
10293                &weight_d,
10294                &mut slot_buf,
10295                &mut wbuf,
10296                n_embd,
10297                n_used,
10298                m_e,
10299            )?;
10300        }
10301
10302        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10303        let mut moe_out = e.zeros(t * n_embd)?;
10304        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10305
10306        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10307        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10308            m_dist.sort_unstable();
10309            let active = m_dist.len();
10310            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10311            let median = m_dist[active / 2];
10312            let max_m = *m_dist.last().unwrap();
10313            let min_m = m_dist[0];
10314            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10315            println!(
10316                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10317                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10318                      above_gemm_threshold(>=16)={above16}/{active}"
10319            );
10320        }
10321
10322        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10323        Ok(moe_out)
10324    }
10325
10326    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10327    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10328    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10329    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10330    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10331    /// expert-sum order identical to the sequential path.
10332    pub(crate) fn moe_ffn_lockstep(
10333        &self,
10334        e: &Engine,
10335        m: &MoeWeights,
10336        zbatch: &CudaSlice<f32>,
10337        mrows: usize,
10338        il: u16,
10339        max_block: usize,
10340    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10341        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10342        let cfg = &self.cfg;
10343        let moe = cfg.moe.as_ref().unwrap();
10344        let n_embd = cfg.n_embd as usize;
10345        let n_expert = moe.expert_count as usize;
10346        let n_used = moe.expert_used_count as usize;
10347        let n_ff_exp = moe.expert_ff_length as usize;
10348        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10349        let lim_exp = cfg.clamp_exp_at(il as u32);
10350        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10351
10352        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10353        if let Some(sig) = cfg.sigmoid_router() {
10354            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10355        }
10356        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10357            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10358        } else {
10359            Self::moe_route_cfg(
10360                e,
10361                &logits,
10362                mrows,
10363                n_expert,
10364                n_used,
10365                m.active_experts.as_deref(),
10366            )?
10367        };
10368        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10369
10370        // Residency split at whole-expert granularity against the (frozen) cache.
10371        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10372            Ok((0..n_expert)
10373                .map(|ex| {
10374                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10375                        .into_iter()
10376                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10377                })
10378                .collect())
10379        })?;
10380
10381        struct Group {
10382            rows: Vec<i32>,
10383            slots: Vec<i32>,
10384            weights: Vec<f32>,
10385        }
10386        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10387        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10388        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10389            Default::default();
10390        for row in 0..mrows {
10391            for j in 0..n_used {
10392                let ex = sel_all[row * n_used + j] as usize;
10393                let w = w_all[row * n_used + j];
10394                if resident_expert[ex] {
10395                    let group = groups.entry(ex).or_insert_with(|| Group {
10396                        rows: Vec::new(),
10397                        slots: Vec::new(),
10398                        weights: Vec::new(),
10399                    });
10400                    group.rows.push(row as i32);
10401                    group.slots.push(j as i32);
10402                    group.weights.push(w);
10403                } else {
10404                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10405                    cpu_rows[row].push((ex, w));
10406                    cpu_by_expert.entry(ex).or_default().push((row, w));
10407                }
10408            }
10409        }
10410
10411        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10412        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10413        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10414        // order per row differs from the sequential single-call chunk — part of the
10415        // documented lockstep numeric class.
10416        let host_rows = e.dtoh(zbatch)?;
10417        let rows_ok = crate::cpu_experts::rows_supported();
10418        enum CpuPart {
10419            Single { row: usize },
10420            Rows { rows: Vec<usize> },
10421        }
10422        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10423        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10424        if rows_ok {
10425            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10426                .into_iter()
10427                .filter(|(_, rows)| rows.len() >= 2)
10428                .collect();
10429            shared.sort_by_key(|(ex, _)| *ex);
10430            for (ex, mut row_weights) in shared {
10431                row_weights.sort_by_key(|(row, _)| *row);
10432                let inputs: Vec<(&[f32], f32)> = row_weights
10433                    .iter()
10434                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10435                    .collect();
10436                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10437                    .map_err(std::io::Error::other)?;
10438                for &(row, _) in &row_weights {
10439                    rows_served.insert((row, ex));
10440                }
10441                tickets.push((
10442                    CpuPart::Rows {
10443                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10444                    },
10445                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10446                ));
10447            }
10448        }
10449        for (row, selected) in cpu_rows.iter().enumerate() {
10450            let leftover: Vec<(usize, f32)> = selected
10451                .iter()
10452                .copied()
10453                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10454                .collect();
10455            if leftover.is_empty() {
10456                continue;
10457            }
10458            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10459            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10460                .map_err(std::io::Error::other)?;
10461            tickets.push((
10462                CpuPart::Single { row },
10463                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
10464            ));
10465        }
10466
10467        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
10468        let mut wbuf = e.zeros(mrows * n_used)?;
10469        let mut order: Vec<usize> = groups.keys().copied().collect();
10470        order.sort_by(|&a, &b| {
10471            groups[&b]
10472                .rows
10473                .len()
10474                .cmp(&groups[&a].rows.len())
10475                .then(a.cmp(&b))
10476        });
10477        for &ex in &order {
10478            let group = &groups[&ex];
10479            let m_e = group.rows.len();
10480            let gl = m.gate_exps.expert_layout(ex);
10481            let ul = m.up_exps.expert_layout(ex);
10482            let dl = m.down_exps.expert_layout(ex);
10483            let row_idx_d = e.htod_i32(&group.rows)?;
10484            let slot_idx_d = e.htod_i32(&group.slots)?;
10485            let dmac = m.down_exps.macro_scale(ex);
10486            let weight_d = if dmac == 1.0 {
10487                e.htod(&group.weights)?
10488            } else {
10489                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
10490                e.htod(&scaled)?
10491            };
10492            let mut gathered = e.zeros(m_e * n_embd)?;
10493            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
10494            let gv = gathered.slice(0..m_e * n_embd);
10495            let gate = e.with_moe_cache(max_block, |c, eng| {
10496                let slot = c
10497                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
10498                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10499                eng.qmatvec_view(
10500                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10501                    0..gl.len,
10502                    &gv,
10503                    m_e,
10504                    m.gate_exps.in_f,
10505                    m.gate_exps.out_f,
10506                    gl.qtype,
10507                    gl.row_bytes,
10508                )
10509            })?;
10510            let up = e.with_moe_cache(max_block, |c, eng| {
10511                let slot = c
10512                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
10513                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10514                eng.qmatvec_view(
10515                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10516                    0..ul.len,
10517                    &gv,
10518                    m_e,
10519                    m.up_exps.in_f,
10520                    m.up_exps.out_f,
10521                    ul.qtype,
10522                    ul.row_bytes,
10523                )
10524            })?;
10525            let mut act = e.zeros(m_e * n_ff_exp)?;
10526            Self::ffn_act_lim(
10527                e,
10528                cfg,
10529                &gate,
10530                &up,
10531                m.gate_exps.macro_scale(ex),
10532                m.up_exps.macro_scale(ex),
10533                lim_exp,
10534                &mut act,
10535                m_e * n_ff_exp,
10536            )?;
10537            let actv = act.slice(0..m_e * n_ff_exp);
10538            let y = e.with_moe_cache(max_block, |c, eng| {
10539                let slot = c
10540                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
10541                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10542                eng.qmatvec_view(
10543                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10544                    0..dl.len,
10545                    &actv,
10546                    m_e,
10547                    m.down_exps.in_f,
10548                    m.down_exps.out_f,
10549                    dl.qtype,
10550                    dl.row_bytes,
10551                )
10552            })?;
10553            e.scatter_slot(
10554                &y,
10555                &row_idx_d,
10556                &slot_idx_d,
10557                &weight_d,
10558                &mut slot_buf,
10559                &mut wbuf,
10560                n_embd,
10561                n_used,
10562                m_e,
10563            )?;
10564        }
10565        let mut moe_out = e.zeros(mrows * n_embd)?;
10566        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
10567
10568        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
10569        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
10570        for (part, ticket) in tickets {
10571            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
10572            let mut add_row = |row: usize, chunk: &[f32]| {
10573                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
10574                for (accumulator, value) in sum.iter_mut().zip(chunk) {
10575                    *accumulator += value;
10576                }
10577            };
10578            match part {
10579                CpuPart::Single { row } => add_row(row, &cpu_output),
10580                CpuPart::Rows { rows } => {
10581                    for (slot, row) in rows.into_iter().enumerate() {
10582                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
10583                    }
10584                }
10585            }
10586        }
10587        for (row, sum) in row_sums.into_iter().enumerate() {
10588            let Some(sum) = sum else { continue };
10589            let cpu_output = e.htod(&sum)?;
10590            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
10591            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
10592        }
10593
10594        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10595            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10596        {
10597            let n_ff_sh = gate_shexp.out_features();
10598            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
10599            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
10600            let mut sa = e.zeros(mrows * n_ff_sh)?;
10601            Self::ffn_act_lim(
10602                e,
10603                cfg,
10604                &sg_gate,
10605                &sg_up,
10606                1.0,
10607                1.0,
10608                lim_shexp,
10609                &mut sa,
10610                mrows * n_ff_sh,
10611            )?;
10612            let sh = e.matmul(down_shexp, &sa, mrows)?;
10613            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
10614            // decode matches the single-sequence decode chain bit-for-bit.
10615            let g = match &m.gate_inp_shexp {
10616                Some(gate_inp_shexp) => {
10617                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
10618                }
10619                None => e.htod(&vec![1.0f32; mrows])?,
10620            };
10621            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
10622        }
10623
10624        Ok(moe_out)
10625    }
10626}
10627
10628// ============================ gemma4 (R8 verified wiring) ==================================
10629// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
10630// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
10631// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
10632// gemma variants after the correctness gate).
10633impl HybridModel {
10634    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
10635    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
10636    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
10637    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
10638    ///
10639    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
10640    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
10641    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
10642    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
10643    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
10644    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
10645    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
10646    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
10647    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
10648        let g = self
10649            .cfg
10650            .gemma4
10651            .as_ref()
10652            .expect("gemma4_rope_dims on a non-gemma4 config");
10653        if g.swa_pattern[il] {
10654            g.rope_dims_swa as usize
10655        } else {
10656            g.rope_dims_global as usize
10657        }
10658    }
10659
10660    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
10661        let g = self.cfg.gemma4.as_ref().unwrap();
10662        let swa = g.swa_pattern[il];
10663        let hd = if swa {
10664            g.key_length_swa
10665        } else {
10666            g.key_length_global
10667        } as usize;
10668        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
10669        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
10670        // rows exact (softmax over one element) while every later position drifted).
10671        (
10672            hd,
10673            g.head_count_kv[il] as usize,
10674            self.cfg.n_head as usize,
10675            if swa {
10676                g.rope_base_swa
10677            } else {
10678                g.rope_base_global
10679            },
10680            1.0,
10681            swa,
10682        )
10683    }
10684
10685    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
10686    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
10687    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
10688    pub(crate) fn gemma4_suppress(
10689        &self,
10690        e: &Engine,
10691        ld: &mut CudaSlice<f32>,
10692        t: usize,
10693    ) -> Result<(), Box<dyn std::error::Error>> {
10694        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
10695            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
10696            // stage as primary, and this tail runs only after the last stage). The assert turns
10697            // that argued invariant into a checked one: any topology violating primary==head
10698            // trips here in debug instead of silently peer-reading a device-0 buffer.
10699            #[cfg(debug_assertions)]
10700            crate::debug_assert_tensor_stream_device(
10701                ids,
10702                &e.stream(),
10703                "gemma4_suppress.suppress_d",
10704            );
10705            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
10706        }
10707        Ok(())
10708    }
10709
10710    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
10711    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
10712    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
10713    /// only (v0): attends within `tokens` via the f32 sdpa.
10714    #[allow(clippy::too_many_arguments)]
10715    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
10716    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
10717    /// switching program at `t > sliding_window`. The door is the measured cause of the
10718    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
10719    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
10720    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
10721    /// published prefix KV stops depending on the total prompt length. Off by default because
10722    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
10723    fn gemma_fa_one_program() -> bool {
10724        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10725        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
10726    }
10727
10728    fn gemma4_attn_prime(
10729        &self,
10730        e: &Engine,
10731        fa: &crate::hybrid::FullAttnLayer,
10732        il: usize,
10733        h: &CudaSlice<f32>,
10734        pos_d: &CudaSlice<i32>,
10735        t: usize,
10736        cache: Option<&mut Cache>,
10737        island: Option<&CudaSlice<i32>>,
10738    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10739        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
10740        let eps = self.cfg.rms_eps;
10741        let aux = self.gemma4_aux.as_ref().unwrap();
10742        let ones = aux.ones(e);
10743        #[cfg(debug_assertions)]
10744        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
10745
10746        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
10747        // (h stays borrowed across the triple, so the cache key can't go stale).
10748        e.mmq_act_begin();
10749        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
10750        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10751            let v = e.dtoh(&q0)?;
10752            let nan = v.iter().filter(|x| x.is_nan()).count();
10753            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10754            eprintln!(
10755                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
10756                v.len()
10757            );
10758        }
10759        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
10760        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
10761        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
10762        let v0 = if swa {
10763            e.matmul(&fa.wv, h, t)?
10764        } else {
10765            e.clone_dtod(&k0)?
10766        };
10767        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10768            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
10769                let v = e.dtoh(buf)?;
10770                let nan = v.iter().filter(|x| x.is_nan()).count();
10771                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10772                eprintln!(
10773                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
10774                    v.len()
10775                );
10776            }
10777        }
10778
10779        let mut q = e.uninit(t * nh * hd)?;
10780        let mut k = e.uninit(t * nkv * hd)?;
10781        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
10782        let mut v = e.uninit(t * nkv * hd)?;
10783        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
10784        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
10785        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
10786        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10787        // Island primes take the mask-capable naive kernel below; keep the operands f32
10788        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
10789        let emit = island.is_none()
10790            && t >= 16
10791            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
10792            && *EMIT.get_or_init(|| {
10793                std::env::var("MEMRA_FA_EMIT")
10794                    .map(|s| s != "0")
10795                    .unwrap_or(true)
10796            });
10797        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
10798        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10799        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10800        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
10801        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
10802        let v_f16 = emit
10803            && crate::fa_f16pv_on()
10804            && match hd {
10805                512 => true,
10806                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
10807                _ => false,
10808            };
10809        if emit {
10810            e.rms_norm_qkv_w4b(
10811                &q0,
10812                &k0,
10813                &v0,
10814                fa.q_norm.float_data(),
10815                fa.k_norm.float_data(),
10816                ones,
10817                &mut q,
10818                &mut k,
10819                &mut v,
10820                &mut vb,
10821                hd,
10822                nh * t,
10823                nkv * t,
10824                eps,
10825                v_f16,
10826            )?;
10827        } else {
10828            e.rms_norm_qkv(
10829                &q0,
10830                &k0,
10831                &v0,
10832                fa.q_norm.float_data(),
10833                fa.k_norm.float_data(),
10834                ones,
10835                &mut q,
10836                &mut k,
10837                &mut v,
10838                hd,
10839                nh * t,
10840                nkv * t,
10841                eps,
10842            )?;
10843        }
10844
10845        let ff = if swa {
10846            None
10847        } else {
10848            Some(
10849                aux.rope_freqs(e)
10850                    .expect("gemma4 global rope needs rope_freqs.weight"),
10851            )
10852        };
10853        #[cfg(debug_assertions)]
10854        if let Some(ff) = ff {
10855            crate::debug_assert_tensor_stream_device(
10856                ff,
10857                &e.stream(),
10858                "gemma4_attn_prime.rope_freqs",
10859            );
10860        }
10861        if emit {
10862            e.rope_neox2_bf16e(
10863                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
10864            )?;
10865        } else {
10866            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
10867        }
10868
10869        if let Some(cache) = cache {
10870            let kvl = cache.kv[il].as_mut().unwrap();
10871            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
10872            e.append_kv_quantized_rows(
10873                &k,
10874                &v,
10875                &mut kvl.k,
10876                &mut kvl.v,
10877                kvl.len,
10878                t,
10879                kvl.kv_dim_k,
10880                kvl.kv_dim_v,
10881                kvl.k_tok_bytes,
10882                kvl.v_tok_bytes,
10883                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
10884            )?;
10885            kvl.len += t;
10886        }
10887        let mut attn = e.zeros(t * nh * hd)?;
10888        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
10889        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
10890        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
10891        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
10892        if let Some(span) = island {
10893            // Masked-prefill arm: every layer routes through the island-aware naive
10894            // kernel (correctness-first, same posture as the vision tower v1). The
10895            // window argument keeps the R6 shortcut: 0 while the prompt fits the
10896            // window, the real window beyond it.
10897            let w = if swa && t > win { win } else { 0 };
10898            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
10899        } else if swa && (t > win || Self::gemma_fa_one_program()) {
10900            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
10901                if emit {
10902                    e.fa_prefill_w_pre(
10903                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
10904                    )?;
10905                } else {
10906                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
10907                }
10908            } else {
10909                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
10910            }
10911        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
10912            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
10913        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
10914            if emit {
10915                e.fa_prefill_hd512_pre(
10916                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
10917                )?;
10918            } else {
10919                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
10920            }
10921        } else {
10922            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
10923        }
10924        Ok(e.matmul(&fa.wo, &attn, t)?)
10925    }
10926
10927    /// Back-compat wrapper (pure prefill, no cache).
10928    fn gemma4_attn(
10929        &self,
10930        e: &Engine,
10931        fa: &crate::hybrid::FullAttnLayer,
10932        il: usize,
10933        h: &CudaSlice<f32>,
10934        pos_d: &CudaSlice<i32>,
10935        t: usize,
10936    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10937        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
10938    }
10939
10940    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
10941    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
10942    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
10943    /// the q8z epilogue is quantize_q8_1 verbatim).
10944    fn gemma4_moe_q8(
10945        &self,
10946        e: &Engine,
10947        m: &crate::hybrid::MoeWeights,
10948        bits: &crate::hybrid::Gemma4MoeBits,
10949        mq: &(CudaSlice<i8>, CudaSlice<f32>),
10950        router_in: &CudaSlice<f32>,
10951        t: usize,
10952    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10953        let cfg = &self.cfg;
10954        let moe = cfg.moe.as_ref().unwrap();
10955        let n_embd = cfg.n_embd as usize;
10956        let n_expert = moe.expert_count as usize;
10957        let n_used = moe.expert_used_count as usize;
10958        let n_ff_exp = moe.expert_ff_length as usize;
10959        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
10960        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
10961        // the pair's 12us is kernel time, not launch gaps.
10962        let logits = if crate::router_kernel_on() {
10963            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
10964        } else {
10965            e.matmul(&m.gate_inp, router_in, t)?
10966        };
10967        let dev = m.dev_exps.as_ref().unwrap();
10968        let (sel_d, w_d) =
10969            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
10970        let (zq, zd) = mq;
10971        if t == 1 {
10972            let selv = sel_d.slice(0..n_used);
10973            let wv = w_d.slice(0..n_used);
10974            let act = e.moe_gate_up_gelu8_dev_q8(
10975                &dev.ptr_row,
10976                &selv,
10977                zq,
10978                zd,
10979                n_embd,
10980                n_ff_exp,
10981                n_used,
10982                n_expert,
10983                m.gate_exps.qtype,
10984                m.up_exps.qtype,
10985                m.gate_exps.row_bytes,
10986                m.up_exps.row_bytes,
10987            )?;
10988            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
10989            let mut moe_out = e.uninit(n_embd)?;
10990            e.moe_down8_fma_dev_q8(
10991                &dev.ptr_row,
10992                &selv,
10993                &wv,
10994                &aq2,
10995                &ad2,
10996                &mut moe_out.slice_mut(0..n_embd),
10997                n_ff_exp,
10998                n_embd,
10999                n_used,
11000                n_expert,
11001                m.down_exps.qtype,
11002                m.down_exps.row_bytes,
11003            )?;
11004            return Ok(moe_out);
11005        }
11006        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11007        let act = if csr {
11008            e.moe_gate_up_gelu8_dev_q8_csr(
11009                &dev.ptr_row,
11010                &sel_d,
11011                zq,
11012                zd,
11013                t * n_used,
11014                n_embd,
11015                n_ff_exp,
11016                n_used,
11017                n_expert,
11018                m.gate_exps.qtype,
11019                m.up_exps.qtype,
11020                m.gate_exps.row_bytes,
11021                m.up_exps.row_bytes,
11022            )?
11023        } else {
11024            e.moe_gate_up_gelu8_dev_q8_rows(
11025                &dev.ptr_row,
11026                &sel_d,
11027                zq,
11028                zd,
11029                t,
11030                n_embd,
11031                n_ff_exp,
11032                n_used,
11033                n_expert,
11034                m.gate_exps.qtype,
11035                m.up_exps.qtype,
11036                m.gate_exps.row_bytes,
11037                m.up_exps.row_bytes,
11038            )?
11039        };
11040        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11041        let mut moe_out = e.uninit(t * n_embd)?;
11042        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11043        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11044        e.moe_down8_fma_dev_q8_rows_g(
11045            &dev.ptr_row,
11046            &sel_d,
11047            &w_d,
11048            &aq2,
11049            &ad2,
11050            &mut moe_out,
11051            t,
11052            n_ff_exp,
11053            n_embd,
11054            n_used,
11055            n_expert,
11056            m.down_exps.qtype,
11057            m.down_exps.row_bytes,
11058        )?;
11059        Ok(moe_out)
11060    }
11061
11062    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11063    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11064    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11065    fn gemma4_moe(
11066        &self,
11067        e: &Engine,
11068        m: &crate::hybrid::MoeWeights,
11069        bits: &crate::hybrid::Gemma4MoeBits,
11070        moe_in: &CudaSlice<f32>,
11071        router_in: &CudaSlice<f32>,
11072        t: usize,
11073    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11074        let cfg = &self.cfg;
11075        let moe = cfg.moe.as_ref().unwrap();
11076        let n_embd = cfg.n_embd as usize;
11077        let n_expert = moe.expert_count as usize;
11078        let n_used = moe.expert_used_count as usize;
11079        let n_ff_exp = moe.expert_ff_length as usize;
11080
11081        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11082        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11083        // batched matmul only at real prefill.
11084        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11085            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11086        } else {
11087            e.matmul(&m.gate_inp, router_in, t)?
11088        };
11089
11090        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11091        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11092        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11093        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11094        if t < PRIME_MIN_T
11095            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11096            && expert_dp4a_supported(m.gate_exps.qtype)
11097            && expert_dp4a_supported(m.up_exps.qtype)
11098            && expert_dp4a_supported(m.down_exps.qtype)
11099            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11100        {
11101            let dev = m.dev_exps.as_ref().unwrap();
11102            let (sel_d, w_d) =
11103                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11104            if t == 1 {
11105                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11106                let selv = sel_d.slice(0..n_used);
11107                let wv = w_d.slice(0..n_used);
11108                let act = e.moe_gate_up_gelu8_dev_q8(
11109                    &dev.ptr_row,
11110                    &selv,
11111                    &zq,
11112                    &zd,
11113                    n_embd,
11114                    n_ff_exp,
11115                    n_used,
11116                    n_expert,
11117                    m.gate_exps.qtype,
11118                    m.up_exps.qtype,
11119                    m.gate_exps.row_bytes,
11120                    m.up_exps.row_bytes,
11121                )?;
11122                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11123                let mut moe_out = e.uninit(n_embd)?;
11124                e.moe_down8_fma_dev_q8(
11125                    &dev.ptr_row,
11126                    &selv,
11127                    &wv,
11128                    &aq2,
11129                    &ad2,
11130                    &mut moe_out.slice_mut(0..n_embd),
11131                    n_ff_exp,
11132                    n_embd,
11133                    n_used,
11134                    n_expert,
11135                    m.down_exps.qtype,
11136                    m.down_exps.row_bytes,
11137                )?;
11138                return Ok(moe_out);
11139            }
11140            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11141            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11142            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11143            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11144            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11145            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11146            let act = if csr {
11147                e.moe_gate_up_gelu8_dev_q8_csr(
11148                    &dev.ptr_row,
11149                    &sel_d,
11150                    &zq,
11151                    &zd,
11152                    t * n_used,
11153                    n_embd,
11154                    n_ff_exp,
11155                    n_used,
11156                    n_expert,
11157                    m.gate_exps.qtype,
11158                    m.up_exps.qtype,
11159                    m.gate_exps.row_bytes,
11160                    m.up_exps.row_bytes,
11161                )?
11162            } else {
11163                e.moe_gate_up_gelu8_dev_q8_rows(
11164                    &dev.ptr_row,
11165                    &sel_d,
11166                    &zq,
11167                    &zd,
11168                    t,
11169                    n_embd,
11170                    n_ff_exp,
11171                    n_used,
11172                    n_expert,
11173                    m.gate_exps.qtype,
11174                    m.up_exps.qtype,
11175                    m.gate_exps.row_bytes,
11176                    m.up_exps.row_bytes,
11177                )?
11178            };
11179            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11180            let mut moe_out = e.uninit(t * n_embd)?;
11181            e.moe_down8_fma_dev_q8_rows_g(
11182                &dev.ptr_row,
11183                &sel_d,
11184                &w_d,
11185                &aq2,
11186                &ad2,
11187                &mut moe_out,
11188                t,
11189                n_ff_exp,
11190                n_embd,
11191                n_used,
11192                n_expert,
11193                m.down_exps.qtype,
11194                m.down_exps.row_bytes,
11195            )?;
11196            return Ok(moe_out);
11197        }
11198
11199        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11200        for (i, &sx) in sel_all.iter().enumerate() {
11201            w_all[i] *= bits.per_expert_scale[sx as usize];
11202        }
11203
11204        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11205        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11206        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11207        if t >= PRIME_MIN_T
11208            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11209            && expert_dp4a_supported(m.gate_exps.qtype)
11210            && expert_dp4a_supported(m.up_exps.qtype)
11211            && expert_dp4a_supported(m.down_exps.qtype)
11212            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11213        {
11214            let dev = m.dev_exps.as_ref().unwrap();
11215            let n_pairs = t * n_used;
11216            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11217            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11218            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11219            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11220            let pt = e.htod_i32(&pair_tok)?;
11221            let pw = e.htod(&w_all)?;
11222            let toff = e.htod_i32(&tok_off)?;
11223            let tids = e.htod_i32(&tok_ids)?;
11224            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11225            for p in 0..n_pairs {
11226                by_ex[pair_ex[p] as usize].push(p as i32);
11227            }
11228            let mut ex_ids: Vec<i32> = Vec::new();
11229            let mut ex_off: Vec<i32> = vec![0];
11230            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11231            for (ex, list) in by_ex.iter().enumerate() {
11232                if list.is_empty() {
11233                    continue;
11234                }
11235                ex_ids.push(ex as i32);
11236                ex_pairs.extend_from_slice(list);
11237                ex_off.push(ex_pairs.len() as i32);
11238            }
11239            let n_active = ex_ids.len();
11240            let exi = e.htod_i32(&ex_ids)?;
11241            let exo = e.htod_i32(&ex_off)?;
11242            let exp_d = e.htod_i32(&ex_pairs)?;
11243            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11244            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11245            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11246            // ragged down k (704) needs no padding here — cublas takes any k.
11247            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11248            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11249            // Hopper default — see moe_f16g_gemma_on.
11250            if crate::moe_f16g_gemma_on()
11251                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11252                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11253                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11254            {
11255                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11256                let csr_tok_d = e.htod_i32(&csr_tok)?;
11257                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11258                let g_csr = e.moe_f16_grouped(
11259                    &dev.ptr_row,
11260                    0,
11261                    n_expert,
11262                    &exi,
11263                    &ex_off,
11264                    &exo,
11265                    &z_f16,
11266                    &z_s,
11267                    n_embd,
11268                    n_ff_exp,
11269                    n_active,
11270                    n_pairs,
11271                    m.gate_exps.qtype,
11272                    m.gate_exps.row_bytes,
11273                )?;
11274                let u_csr = e.moe_f16_grouped(
11275                    &dev.ptr_row,
11276                    1,
11277                    n_expert,
11278                    &exi,
11279                    &ex_off,
11280                    &exo,
11281                    &z_f16,
11282                    &z_s,
11283                    n_embd,
11284                    n_ff_exp,
11285                    n_active,
11286                    n_pairs,
11287                    m.up_exps.qtype,
11288                    m.up_exps.row_bytes,
11289                )?;
11290                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11291                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11292                let d_csr = e.moe_f16_grouped(
11293                    &dev.ptr_row,
11294                    2,
11295                    n_expert,
11296                    &exi,
11297                    &ex_off,
11298                    &exo,
11299                    &a_f16,
11300                    &a_s,
11301                    n_ff_exp,
11302                    n_embd,
11303                    n_active,
11304                    n_pairs,
11305                    m.down_exps.qtype,
11306                    m.down_exps.row_bytes,
11307                )?;
11308                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11309                let mut moe_out = e.uninit(t * n_embd)?;
11310                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11311                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11312                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11313                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11314                    eprintln!(
11315                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11316                        scan(&yd),
11317                        scan(&mo)
11318                    );
11319                }
11320                return Ok(moe_out);
11321            }
11322            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11323            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11324            let mma =
11325                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11326            let (gate, up) = if mma {
11327                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11328                (
11329                    e.mmq_iq_experts(
11330                        &dev.ptr_row,
11331                        0,
11332                        n_expert,
11333                        &exi,
11334                        &exo,
11335                        &exp_d,
11336                        &pt,
11337                        &z_scr,
11338                        n_embd,
11339                        n_ff_exp,
11340                        n_active,
11341                        n_pairs,
11342                        t,
11343                        m.gate_exps.qtype,
11344                        m.gate_exps.row_bytes,
11345                    )?,
11346                    e.mmq_iq_experts(
11347                        &dev.ptr_row,
11348                        1,
11349                        n_expert,
11350                        &exi,
11351                        &exo,
11352                        &exp_d,
11353                        &pt,
11354                        &z_scr,
11355                        n_embd,
11356                        n_ff_exp,
11357                        n_active,
11358                        n_pairs,
11359                        t,
11360                        m.up_exps.qtype,
11361                        m.up_exps.row_bytes,
11362                    )?,
11363                )
11364            } else {
11365                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11366                (
11367                    e.moe_pairs_matvec_q8_dec(
11368                        &dev.ptr_row,
11369                        0,
11370                        &exi,
11371                        &exo,
11372                        &exp_d,
11373                        &pt,
11374                        &zq,
11375                        &zd,
11376                        n_embd,
11377                        n_ff_exp,
11378                        n_expert,
11379                        n_active,
11380                        n_pairs,
11381                        m.gate_exps.qtype,
11382                        m.gate_exps.row_bytes,
11383                    )?,
11384                    e.moe_pairs_matvec_q8_dec(
11385                        &dev.ptr_row,
11386                        1,
11387                        &exi,
11388                        &exo,
11389                        &exp_d,
11390                        &pt,
11391                        &zq,
11392                        &zd,
11393                        n_embd,
11394                        n_ff_exp,
11395                        n_expert,
11396                        n_active,
11397                        n_pairs,
11398                        m.up_exps.qtype,
11399                        m.up_exps.row_bytes,
11400                    )?,
11401                )
11402            };
11403            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11404            let pself = e.htod_i32(&pair_self)?;
11405            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11406            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11407            // to the 256-val superblock (768) while the act quantizer's zero padding
11408            // makes every padded-k product exactly zero (weight overread bytes multiply
11409            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11410            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11411            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11412            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11413            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11414            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11415            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11416            let y_down = if mma {
11417                let in_pad = n_ff_exp.div_ceil(256) * 256;
11418                let a_scr = if crate::moe_fuse_actq_on() {
11419                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11420                } else {
11421                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11422                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11423                };
11424                e.mmq_iq_experts(
11425                    &dev.ptr_row,
11426                    2,
11427                    n_expert,
11428                    &exi,
11429                    &exo,
11430                    &exp_d,
11431                    &pself,
11432                    &a_scr,
11433                    in_pad,
11434                    n_embd,
11435                    n_active,
11436                    n_pairs,
11437                    n_pairs,
11438                    m.down_exps.qtype,
11439                    m.down_exps.row_bytes,
11440                )?
11441            } else {
11442                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11443                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11444                e.moe_pairs_matvec_q8_dec(
11445                    &dev.ptr_row,
11446                    2,
11447                    &exi,
11448                    &exo,
11449                    &exp_d,
11450                    &pself,
11451                    &aq2,
11452                    &ad2,
11453                    n_ff_exp,
11454                    n_embd,
11455                    n_expert,
11456                    n_active,
11457                    n_pairs,
11458                    m.down_exps.qtype,
11459                    m.down_exps.row_bytes,
11460                )?
11461            };
11462            let mut moe_out = e.uninit(t * n_embd)?;
11463            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11464            return Ok(moe_out);
11465        }
11466
11467        let g_len = m.gate_exps.expert_stride;
11468        let u_len = m.up_exps.expert_stride;
11469        let d_len = m.down_exps.expert_stride;
11470        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
11471        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
11472        // the spill fallback.
11473        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
11474        let (mut sg, mut su, mut sd) = if dev.is_some() {
11475            (None, None, None)
11476        } else {
11477            (
11478                Some(e.alloc_u8_uninit(g_len)?),
11479                Some(e.alloc_u8_uninit(u_len)?),
11480                Some(e.alloc_u8_uninit(d_len)?),
11481            )
11482        };
11483        let mut moe_out = e.zeros(t * n_embd)?;
11484        for tok in 0..t {
11485            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
11486            let w = &w_all[tok * n_used..(tok + 1) * n_used];
11487            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
11488            for (j, &ex) in sel.iter().enumerate() {
11489                let ex = ex as usize;
11490                let gate = match dev {
11491                    Some(d) => e.qmatvec_view(
11492                        &d.gate,
11493                        ex * g_len..(ex + 1) * g_len,
11494                        &zt,
11495                        1,
11496                        m.gate_exps.in_f,
11497                        m.gate_exps.out_f,
11498                        m.gate_exps.qtype,
11499                        m.gate_exps.row_bytes,
11500                    )?,
11501                    None => {
11502                        let sg = sg.as_mut().unwrap();
11503                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
11504                        e.qmatvec_view(
11505                            sg,
11506                            0..g_len,
11507                            &zt,
11508                            1,
11509                            m.gate_exps.in_f,
11510                            m.gate_exps.out_f,
11511                            m.gate_exps.qtype,
11512                            m.gate_exps.row_bytes,
11513                        )?
11514                    }
11515                };
11516                let up = match dev {
11517                    Some(d) => e.qmatvec_view(
11518                        &d.up,
11519                        ex * u_len..(ex + 1) * u_len,
11520                        &zt,
11521                        1,
11522                        m.up_exps.in_f,
11523                        m.up_exps.out_f,
11524                        m.up_exps.qtype,
11525                        m.up_exps.row_bytes,
11526                    )?,
11527                    None => {
11528                        let su = su.as_mut().unwrap();
11529                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
11530                        e.qmatvec_view(
11531                            su,
11532                            0..u_len,
11533                            &zt,
11534                            1,
11535                            m.up_exps.in_f,
11536                            m.up_exps.out_f,
11537                            m.up_exps.qtype,
11538                            m.up_exps.row_bytes,
11539                        )?
11540                    }
11541                };
11542                let mut act = e.uninit(n_ff_exp)?;
11543                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
11544                let actv = act.slice(0..n_ff_exp);
11545                let y = match dev {
11546                    Some(d) => e.qmatvec_view(
11547                        &d.down,
11548                        ex * d_len..(ex + 1) * d_len,
11549                        &actv,
11550                        1,
11551                        m.down_exps.in_f,
11552                        m.down_exps.out_f,
11553                        m.down_exps.qtype,
11554                        m.down_exps.row_bytes,
11555                    )?,
11556                    None => {
11557                        let sd = sd.as_mut().unwrap();
11558                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
11559                        e.qmatvec_view(
11560                            sd,
11561                            0..d_len,
11562                            &actv,
11563                            1,
11564                            m.down_exps.in_f,
11565                            m.down_exps.out_f,
11566                            m.down_exps.qtype,
11567                            m.down_exps.row_bytes,
11568                        )?
11569                    }
11570                };
11571                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
11572                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
11573            }
11574        }
11575        Ok(moe_out)
11576    }
11577
11578    /// One gemma4 trunk layer (R8): x -> x_next.
11579    fn gemma4_layer(
11580        &self,
11581        e: &Engine,
11582        il: usize,
11583        layer: &crate::hybrid::HybridLayer,
11584        x: &CudaSlice<f32>,
11585        pos_d: &CudaSlice<i32>,
11586        t: usize,
11587    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11588        let n_embd = self.cfg.n_embd as usize;
11589        let eps = self.cfg.rms_eps;
11590
11591        let mut h = e.zeros(t * n_embd)?;
11592        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
11593        let Mixer::Full(fa) = &layer.mixer else {
11594            panic!("gemma4 layer {il} not full-attn")
11595        };
11596        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
11597        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
11598        let mut cur = e.zeros(t * n_embd)?;
11599        e.rms_norm(
11600            &o,
11601            layer.post_attn_norm.float_data(),
11602            &mut cur,
11603            n_embd,
11604            t,
11605            eps,
11606        )?;
11607        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
11608    }
11609
11610    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
11611    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
11612    /// layer scale — shared verbatim by the prefill, decode and verify paths.
11613    fn gemma4_layer_tail_add(
11614        &self,
11615        e: &Engine,
11616        layer: &crate::hybrid::HybridLayer,
11617        cur: &CudaSlice<f32>,
11618        x: &CudaSlice<f32>,
11619        t: usize,
11620    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11621        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
11622    }
11623
11624    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
11625    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
11626    fn gemma4_layer_tail_add_n(
11627        &self,
11628        e: &Engine,
11629        layer: &crate::hybrid::HybridLayer,
11630        cur: &CudaSlice<f32>,
11631        x: &CudaSlice<f32>,
11632        t: usize,
11633        next_norm: Option<&CudaSlice<f32>>,
11634    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
11635        let n_embd = self.cfg.n_embd as usize;
11636        let bits = layer.gemma4.as_ref().unwrap();
11637        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
11638        let mut xn = e.uninit(t * n_embd)?;
11639        match next_norm {
11640            Some(w) => {
11641                let mut hn = e.uninit(t * n_embd)?;
11642                e.add_scale_rms_norm(
11643                    &sn,
11644                    &attn_out,
11645                    bits.layer_scale,
11646                    w,
11647                    &mut xn,
11648                    &mut hn,
11649                    n_embd,
11650                    t,
11651                    self.cfg.rms_eps,
11652                )?;
11653                Ok((xn, Some(hn)))
11654            }
11655            None => {
11656                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
11657                Ok((xn, None))
11658            }
11659        }
11660    }
11661
11662    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
11663    /// norm — returns (sn, attn_out) for the closing add+scale variants.
11664    fn gemma4_layer_tail_core(
11665        &self,
11666        e: &Engine,
11667        layer: &crate::hybrid::HybridLayer,
11668        cur: &CudaSlice<f32>,
11669        x: &CudaSlice<f32>,
11670        t: usize,
11671    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11672        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
11673    }
11674
11675    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
11676    /// means `cur` is the RAW attention output and the dense entry runs
11677    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
11678    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
11679    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
11680    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
11681    fn gemma4_layer_tail_core_pn(
11682        &self,
11683        e: &Engine,
11684        layer: &crate::hybrid::HybridLayer,
11685        cur: &CudaSlice<f32>,
11686        x: &CudaSlice<f32>,
11687        t: usize,
11688        pre_norm: Option<&CudaSlice<f32>>,
11689        defer_post_norm: bool,
11690    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11691        let n_embd = self.cfg.n_embd as usize;
11692        let eps = self.cfg.rms_eps;
11693        let bits = layer.gemma4.as_ref().unwrap();
11694
11695        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
11696        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
11697        let Some(mbits) = bits.moe_bits.as_ref() else {
11698            let crate::hybrid::Ffn::Dense {
11699                ffn_gate,
11700                ffn_up,
11701                ffn_down,
11702            } = &layer.ffn
11703            else {
11704                panic!("gemma4 dense layer without Dense ffn")
11705            };
11706            let mut attn_out = e.uninit(t * n_embd)?;
11707            let mut zsh = e.uninit(t * n_embd)?;
11708            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
11709            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
11710            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
11711            match pre_norm {
11712                Some(wa) if t == 1 => {
11713                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
11714                        cur,
11715                        wa,
11716                        x,
11717                        bits.ffn_norm.float_data(),
11718                        &mut attn_out,
11719                        &mut zsh,
11720                        n_embd,
11721                        t,
11722                        eps,
11723                    )?);
11724                }
11725                Some(wa) => e.rms_pre_add_rms_norm(
11726                    cur,
11727                    wa,
11728                    x,
11729                    bits.ffn_norm.float_data(),
11730                    &mut attn_out,
11731                    &mut zsh,
11732                    n_embd,
11733                    t,
11734                    eps,
11735                )?,
11736                None => e.add_rms_norm(
11737                    cur,
11738                    x,
11739                    bits.ffn_norm.float_data(),
11740                    &mut attn_out,
11741                    &mut zsh,
11742                    n_embd,
11743                    t,
11744                    eps,
11745                )?,
11746            }
11747            let n_ff = ffn_gate.out_features();
11748            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
11749            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
11750            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
11751            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
11752            // rescue segment C — the megakernel front is closed for the dense tail.
11753            let (gate, up) = if t == 1 {
11754                let (zq, zd) = match zpair {
11755                    Some(p) => p,
11756                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
11757                };
11758                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
11759                    Some(p) => p,
11760                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
11761                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
11762                        Some(p) => p,
11763                        None => (
11764                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
11765                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
11766                        ),
11767                    },
11768                }
11769            } else {
11770                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
11771                // launch for the verify's gate+up — the up segment's blocks fill SMs as
11772                // the gate segment drains (the launch-tail mechanism behind the b-tier
11773                // plateau; first positive after six falsified in-kernel variants).
11774                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11775                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
11776                let fused = if f2b {
11777                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
11778                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
11779                } else {
11780                    None
11781                };
11782                match fused {
11783                    Some(p) => p,
11784                    None => {
11785                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
11786                        e.mmq_act_begin();
11787                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
11788                    }
11789                }
11790            };
11791            let mut act = e.uninit(t * n_ff)?;
11792            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
11793            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
11794            let f0 = if e.uses_q8_1_fast(ffn_down) {
11795                let upv = e.view(&up, t * n_ff);
11796                let up_all = upv.slice(0..t * n_ff);
11797                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
11798                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
11799            } else {
11800                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11801                e.matmul(ffn_down, &act, t)?
11802            };
11803            if defer_post_norm {
11804                return Ok((f0, attn_out));
11805            }
11806            let mut sn = e.uninit(t * n_embd)?;
11807            e.rms_norm(
11808                &f0,
11809                bits.post_ffw_norm.float_data(),
11810                &mut sn,
11811                n_embd,
11812                t,
11813                eps,
11814            )?;
11815            return Ok((sn, attn_out));
11816        };
11817
11818        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
11819        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
11820        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
11821        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
11822        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
11823        let mut attn_out = e.uninit(t * n_embd)?;
11824        let mut router_in = e.uninit(t * n_embd)?;
11825        let fast_moe = match &layer.ffn {
11826            crate::hybrid::Ffn::Moe(m) => {
11827                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11828                    && expert_dp4a_supported(m.gate_exps.qtype)
11829                    && expert_dp4a_supported(m.up_exps.qtype)
11830                    && expert_dp4a_supported(m.down_exps.qtype)
11831                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11832            }
11833            _ => false,
11834        };
11835        let q8z = t < PRIME_MIN_T && fast_moe;
11836        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
11837            let (z0, m2) = e.add_rms_norm3_q8z(
11838                cur,
11839                x,
11840                bits.ffn_norm.float_data(),
11841                &mbits.router_scale_pre,
11842                mbits.pre_ffw_norm_2.float_data(),
11843                &mut attn_out,
11844                &mut router_in,
11845                n_embd,
11846                t,
11847                eps,
11848            )?;
11849            (None, Some(z0), Some(m2))
11850        } else {
11851            let mut zsh = e.uninit(t * n_embd)?;
11852            let mut moe_in = e.uninit(t * n_embd)?;
11853            e.add_rms_norm3(
11854                cur,
11855                x,
11856                bits.ffn_norm.float_data(),
11857                &mbits.router_scale_pre,
11858                mbits.pre_ffw_norm_2.float_data(),
11859                &mut attn_out,
11860                &mut zsh,
11861                &mut router_in,
11862                &mut moe_in,
11863                n_embd,
11864                t,
11865                eps,
11866            )?;
11867            (Some((zsh, moe_in)), None, None)
11868        };
11869        let attn_out2 = attn_out;
11870        #[allow(unused_variables)]
11871        let attn_out = &attn_out2;
11872        let n_ff = mbits.shared_gate.out_features();
11873        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
11874            if t == 1 {
11875                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
11876                    Some(p) => p,
11877                    None => match e.matmul_nvfp4_fused2(
11878                        &mbits.shared_gate,
11879                        &mbits.shared_up,
11880                        zq,
11881                        zd,
11882                        1,
11883                    )? {
11884                        Some(p) => p,
11885                        None => {
11886                            let h0 = e.zeros(0)?;
11887                            (
11888                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
11889                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
11890                            )
11891                        }
11892                    },
11893                }
11894            } else {
11895                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
11896                let h0 = e.zeros(0)?;
11897                (
11898                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
11899                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
11900                )
11901            }
11902        } else {
11903            let (zsh, _) = zsh_f32.as_ref().unwrap();
11904            (
11905                e.matmul(&mbits.shared_gate, zsh, t)?,
11906                e.matmul(&mbits.shared_up, zsh, t)?,
11907            )
11908        };
11909        let mut act = e.uninit(t * n_ff)?;
11910        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11911        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
11912        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
11913            panic!("gemma4 layer not MoE")
11914        };
11915        let moe0 = match (&moe_q8, &zsh_f32) {
11916            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
11917            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
11918            _ => unreachable!(),
11919        };
11920        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
11921        let mut mlp = e.uninit(t * n_embd)?;
11922        let mut moe = e.uninit(t * n_embd)?;
11923        e.rms_norm2x(
11924            &mlp0,
11925            &moe0,
11926            mbits.post_ffw_norm_1.float_data(),
11927            mbits.post_ffw_norm_2.float_data(),
11928            &mut mlp,
11929            &mut moe,
11930            n_embd,
11931            t,
11932            eps,
11933        )?;
11934
11935        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
11936        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
11937        let mut sum = e.uninit(t * n_embd)?;
11938        let mut sn = e.uninit(t * n_embd)?;
11939        e.add_rms_norm(
11940            &mlp,
11941            &moe,
11942            bits.post_ffw_norm.float_data(),
11943            &mut sum,
11944            &mut sn,
11945            n_embd,
11946            t,
11947            eps,
11948        )?;
11949        Ok((sn, attn_out2))
11950    }
11951
11952    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
11953    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
11954    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
11955    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
11956    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
11957    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
11958    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
11959    /// decode == verify == graph parity holds by construction at either seam value.
11960    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
11961    pub(crate) fn gemma4_layer_tail_add_nq_pn(
11962        &self,
11963        e: &Engine,
11964        layer: &crate::hybrid::HybridLayer,
11965        o: &CudaSlice<f32>,
11966        x: &CudaSlice<f32>,
11967        t: usize,
11968        next_norm: Option<&CudaSlice<f32>>,
11969    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
11970    {
11971        let n_embd = self.cfg.n_embd as usize;
11972        let eps = self.cfg.rms_eps;
11973        let bits = layer.gemma4.as_ref().unwrap();
11974        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
11975            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
11976                e,
11977                layer,
11978                o,
11979                x,
11980                t,
11981                Some(layer.post_attn_norm.float_data()),
11982                true,
11983            )?;
11984            let mut xn = e.uninit(t * n_embd)?;
11985            return match next_norm {
11986                Some(w) => {
11987                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
11988                        &f0,
11989                        bits.post_ffw_norm.float_data(),
11990                        &attn_out,
11991                        bits.layer_scale,
11992                        w,
11993                        &mut xn,
11994                        n_embd,
11995                        t,
11996                        eps,
11997                    )?;
11998                    Ok((xn, Some(pair)))
11999                }
12000                None => {
12001                    let mut sn = e.uninit(t * n_embd)?;
12002                    e.rms_norm(
12003                        &f0,
12004                        bits.post_ffw_norm.float_data(),
12005                        &mut sn,
12006                        n_embd,
12007                        t,
12008                        eps,
12009                    )?;
12010                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12011                    Ok((xn, None))
12012                }
12013            };
12014        }
12015        let mut cur = e.uninit(t * n_embd)?;
12016        e.rms_norm(
12017            o,
12018            layer.post_attn_norm.float_data(),
12019            &mut cur,
12020            n_embd,
12021            t,
12022            eps,
12023        )?;
12024        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12025    }
12026
12027    pub(crate) fn gemma4_layer_tail_add_nq(
12028        &self,
12029        e: &Engine,
12030        layer: &crate::hybrid::HybridLayer,
12031        cur: &CudaSlice<f32>,
12032        x: &CudaSlice<f32>,
12033        t: usize,
12034        next_norm: Option<&CudaSlice<f32>>,
12035    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12036    {
12037        let n_embd = self.cfg.n_embd as usize;
12038        let bits = layer.gemma4.as_ref().unwrap();
12039        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12040        let mut xn = e.uninit(t * n_embd)?;
12041        match next_norm {
12042            Some(w) => {
12043                let pair = e.add_scale_rms_norm_q8_1(
12044                    &sn,
12045                    &attn_out,
12046                    bits.layer_scale,
12047                    w,
12048                    &mut xn,
12049                    n_embd,
12050                    t,
12051                    self.cfg.rms_eps,
12052                )?;
12053                Ok((xn, Some(pair)))
12054            }
12055            None => {
12056                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12057                Ok((xn, None))
12058            }
12059        }
12060    }
12061
12062    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12063    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12064    fn gemma4_forward(
12065        &self,
12066        e: &Engine,
12067        tokens: &[u32],
12068        last_only: bool,
12069    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12070        // E4B routes to its own forward regardless of the caller's entry point (forward /
12071        // forward_last / prime paths all funnel here for gemma4).
12072        if self.is_gemma4_e4b() {
12073            return self.gemma4_e4b_forward(e, tokens, last_only);
12074        }
12075        let n_embd = self.cfg.n_embd as usize;
12076        let t = tokens.len();
12077        let pos: Vec<i32> = (0..t as i32).collect();
12078        let pos_d = e.htod_i32(&pos)?;
12079
12080        let mut x = self.embed(e, tokens)?;
12081        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12082        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12083        // the bring-up bisect vs llama-eval-callback node stats.
12084        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12085        let stat =
12086            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12087                let h = e.dtoh(x)?;
12088                let bad = h.iter().filter(|v| !v.is_finite()).count();
12089                let mx = h
12090                    .iter()
12091                    .filter(|v| v.is_finite())
12092                    .fold(0.0f32, |m, v| m.max(v.abs()));
12093                eprintln!(
12094                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12095                    &h[..3]
12096                );
12097                Ok(())
12098            };
12099        if probe {
12100            stat(e, &x, "embed")?;
12101        }
12102        for (il, layer) in self.layers.iter().enumerate() {
12103            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12104            if probe {
12105                stat(e, &x, &format!("L{il}"))?;
12106            }
12107        }
12108        let mut hn = e.zeros(t * n_embd)?;
12109        e.rms_norm(
12110            &x,
12111            self.output_norm.float_data(),
12112            &mut hn,
12113            n_embd,
12114            t,
12115            self.cfg.rms_eps,
12116        )?;
12117        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12118        let n_vocab = self.output.out_features();
12119        let logits = if last_only {
12120            let hv = e.view(&hn, t * n_embd);
12121            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12122            let mut hlast = e.zeros(n_embd)?;
12123            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12124            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12125            e.softcap(&mut ld, cap, n_vocab)?;
12126            self.gemma4_suppress(e, &mut ld, 1)?;
12127            e.dtoh(&ld)?
12128        } else {
12129            let mut ld = e.matmul(&self.output, &hn, t)?;
12130            e.softcap(&mut ld, cap, t * n_vocab)?;
12131            self.gemma4_suppress(e, &mut ld, t)?;
12132            e.dtoh(&ld)?
12133        };
12134        Ok(logits)
12135    }
12136
12137    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12138    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12139    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12140    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12141    pub(crate) fn gemma4_prime(
12142        &self,
12143        e: &Engine,
12144        tokens: &[u32],
12145        cache: &mut Cache,
12146        overlay: Option<&crate::vision::EmbedOverlay>,
12147    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12148        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12149        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12150        // whole worker process on this line. The worker now primes gemma4 monolithically and
12151        // routes continuation suffixes tokenwise; this is the per-request backstop.
12152        if cache.pos != 0 {
12153            return Err(
12154                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12155                        — prime the full prompt in one call or decode tokenwise"
12156                    .into(),
12157            );
12158        }
12159        let n_embd = self.cfg.n_embd as usize;
12160        let eps = self.cfg.rms_eps;
12161        let t = tokens.len();
12162        let pos: Vec<i32> = (0..t as i32).collect();
12163        let pos_d = e.htod_i32(&pos)?;
12164        let mut x = self.embed(e, tokens)?;
12165        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12166        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12167        // sqrt(n_embd) text scale — the reference scales token batches only
12168        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12169        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12170        // bidirectional within itself, causal+SWA everywhere else, matching the
12171        // reference's llama_set_causal_attn(false) image batch exactly.
12172        let island: Option<CudaSlice<i32>> = match overlay {
12173            Some(ov) => {
12174                let mut span_id = vec![-1i32; t];
12175                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12176                    if pos + n_rows > t {
12177                        return Err(format!(
12178                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12179                            pos + n_rows
12180                        )
12181                        .into());
12182                    }
12183                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12184                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12185                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12186                        *s = i as i32;
12187                    }
12188                }
12189                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12190                // keep the plain causal mask. Exists only so the decisive probe can show
12191                // the island mask itself changes the answer; never on in serving.
12192                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12193                    None
12194                } else {
12195                    Some(e.htod_i32(&span_id)?)
12196                }
12197            }
12198            None => None,
12199        };
12200        for (il, layer) in self.layers.iter().enumerate() {
12201            let mut h = e.zeros(t * n_embd)?;
12202            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12203            let Mixer::Full(fa) = &layer.mixer else {
12204                panic!("gemma4 layer not full-attn")
12205            };
12206            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12207            if trace {
12208                let v = e.dtoh(&h)?;
12209                let nan = v.iter().filter(|x| x.is_nan()).count();
12210                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12211            }
12212            let o =
12213                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12214            if trace {
12215                let v = e.dtoh(&o)?;
12216                let nan = v.iter().filter(|x| x.is_nan()).count();
12217                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12218            }
12219            let mut cur = e.zeros(t * n_embd)?;
12220            e.rms_norm(
12221                &o,
12222                layer.post_attn_norm.float_data(),
12223                &mut cur,
12224                n_embd,
12225                t,
12226                eps,
12227            )?;
12228            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12229            self.dflash_tap(e, cache, il, &x, t)?;
12230            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12231            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12232                let h = e.dtoh(&x)?;
12233                let nan = h.iter().filter(|v| v.is_nan()).count();
12234                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12235                eprintln!(
12236                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12237                    h.len()
12238                );
12239                if nan > 0 {
12240                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12241                }
12242            }
12243        }
12244        cache.pos += t;
12245        let hiddens = e.clone_dtod(&x)?;
12246        let xv = e.view(&x, t * n_embd);
12247        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12248        let mut h_seed = e.zeros(n_embd)?;
12249        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12250        let mut hn = e.uninit(n_embd)?;
12251        e.rms_norm(
12252            &h_seed,
12253            self.output_norm.float_data(),
12254            &mut hn,
12255            n_embd,
12256            1,
12257            eps,
12258        )?;
12259        let mut ld = e.matmul(&self.output, &hn, 1)?;
12260        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12261        e.softcap(&mut ld, cap, self.output.out_features())?;
12262        self.gemma4_suppress(e, &mut ld, 1)?;
12263        let logits = e.dtoh(&ld)?;
12264        Ok((logits, h_seed, hiddens))
12265    }
12266
12267    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12268    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12269    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12270    /// fused norm emits q8 directly — the f32 h never materializes).
12271    fn gemma4_decode_attn(
12272        &self,
12273        e: &Engine,
12274        fa: &crate::hybrid::FullAttnLayer,
12275        il: usize,
12276        hq: &CudaSlice<i8>,
12277        hdq: &CudaSlice<f32>,
12278        pos_d: &CudaSlice<i32>,
12279        cache: &mut Cache,
12280    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12281        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12282        let eps = self.cfg.rms_eps;
12283        let aux = self.gemma4_aux.as_ref().unwrap();
12284        let ones = aux.ones(e);
12285        #[cfg(debug_assertions)]
12286        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12287        let (hq, hdq) = (hq, hdq);
12288        let h0 = e.zeros(0)?;
12289        let h = &h0;
12290        let (q0, k0, v0) = if swa {
12291            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12292                Some(t3) => t3,
12293                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12294                // match — fuse the uniform (q,k) pair and take v as its own single.
12295                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12296                    Some((q0, k0)) => {
12297                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12298                        (q0, k0, v0)
12299                    }
12300                    None => (
12301                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12302                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12303                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12304                    ),
12305                },
12306            }
12307        } else {
12308            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12309                Some(p) => p,
12310                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12311                    Some(p) => p,
12312                    None => (
12313                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12314                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12315                    ),
12316                },
12317            };
12318            let v0 = e.clone_dtod(&k0)?;
12319            (q0, k0, v0)
12320        };
12321        let mut q = e.uninit(nh * hd)?;
12322        let mut k = e.uninit(nkv * hd)?;
12323        let mut v = e.uninit(nkv * hd)?;
12324        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12325        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12326        let ff = if swa {
12327            None
12328        } else {
12329            Some(
12330                aux.rope_freqs(e)
12331                    .expect("gemma4 global rope needs rope_freqs.weight"),
12332            )
12333        };
12334        #[cfg(debug_assertions)]
12335        if let Some(ff) = ff {
12336            crate::debug_assert_tensor_stream_device(
12337                ff,
12338                &e.stream(),
12339                "gemma4_decode_attn.rope_freqs",
12340            );
12341        }
12342        let kvl = cache.kv[il].as_mut().unwrap();
12343        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12344        if crate::Engine::qkv_append_on() {
12345            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12346            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12347            // twin of the dc fold — bit-identical bodies, one launch per layer.
12348            e.rms_norm_qkv_rope_append(
12349                &q0,
12350                &k0,
12351                &v0,
12352                fa.q_norm.float_data(),
12353                fa.k_norm.float_data(),
12354                ones,
12355                &mut q,
12356                &mut k,
12357                &mut v,
12358                hd,
12359                self.gemma4_rope_dims(il),
12360                nh,
12361                nkv,
12362                pos_d,
12363                nh,
12364                nkv,
12365                base,
12366                1.0,
12367                ff,
12368                eps,
12369                &mut kvl.k,
12370                &mut kvl.v,
12371                kvl.len,
12372                kvl.k_tok_bytes,
12373                kvl.v_tok_bytes,
12374                kv_fp8,
12375            )?;
12376        } else {
12377            e.rms_norm_qkv_rope(
12378                &q0,
12379                &k0,
12380                &v0,
12381                fa.q_norm.float_data(),
12382                fa.k_norm.float_data(),
12383                ones,
12384                &mut q,
12385                &mut k,
12386                &mut v,
12387                hd,
12388                self.gemma4_rope_dims(il),
12389                nh,
12390                nkv,
12391                pos_d,
12392                nh,
12393                nkv,
12394                base,
12395                1.0,
12396                ff,
12397                eps,
12398            )?;
12399            e.append_kv_quantized(
12400                &k,
12401                &v,
12402                &mut kvl.k,
12403                &mut kvl.v,
12404                kvl.len,
12405                kvl.kv_dim_k,
12406                kvl.kv_dim_v,
12407                kvl.k_tok_bytes,
12408                kvl.v_tok_bytes,
12409                kv_fp8,
12410            )?;
12411        }
12412        kvl.len += 1;
12413        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12414        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12415        // positional). Globals attend the full history.
12416        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12417        let mut attn = e.uninit(nh * hd)?;
12418        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12419        if !swa
12420            && hd == 512
12421            && kvl.len >= crate::fa512_min_tkv()
12422            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12423        {
12424            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12425            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12426            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12427            let base = kvl.len as i32;
12428            e.i32_set_k(&mut kvl.len_d, base)?;
12429            e.fa_decode_rows(
12430                &q,
12431                &kp,
12432                &vp,
12433                &mut attn,
12434                hd,
12435                nh,
12436                nkv,
12437                kvl.len - 1,
12438                1,
12439                scale,
12440                kvl.k_tok_bytes,
12441                kvl.v_tok_bytes,
12442                Some((&kvl.len_d, -1)),
12443                false,
12444                false,
12445                None,
12446            )?;
12447            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12448        }
12449        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12450        if swa
12451            && kvl.len > win
12452            && hd == 256
12453            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12454        {
12455            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12456            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12457            let base = kvl.len as i32;
12458            e.i32_set_k(&mut kvl.len_d, base)?;
12459            e.fa_decode_rows_w(
12460                &q,
12461                &kp,
12462                &vp,
12463                &mut attn,
12464                hd,
12465                nh,
12466                nkv,
12467                &kvl.len_d,
12468                -1,
12469                1,
12470                scale,
12471                win,
12472                kvl.k_tok_bytes,
12473                kvl.v_tok_bytes,
12474                None,
12475            )?;
12476            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12477        }
12478        let (off_tok, t_kv) = if swa && kvl.len > win {
12479            (kvl.len - win, win)
12480        } else {
12481            (0, kvl.len)
12482        };
12483        let k_view = e.view_u8_range(
12484            &kvl.k,
12485            off_tok * kvl.k_tok_bytes,
12486            (off_tok + t_kv) * kvl.k_tok_bytes,
12487        );
12488        let v_view = e.view_u8_range(
12489            &kvl.v,
12490            off_tok * kvl.v_tok_bytes,
12491            (off_tok + t_kv) * kvl.v_tok_bytes,
12492        );
12493        e.fa_decode_kvmod(
12494            &q,
12495            &k_view,
12496            &v_view,
12497            &mut attn,
12498            hd,
12499            nh,
12500            nkv,
12501            t_kv,
12502            scale,
12503            kvl.k_tok_bytes,
12504            kvl.v_tok_bytes,
12505            swa && crate::Engine::wkv_on(),
12506        )?;
12507        Ok(e.matmul(&fa.wo, &attn, 1)?)
12508    }
12509
12510    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
12511    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
12512    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
12513    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
12514    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
12515    /// in-graph; the driver gates).
12516    #[allow(clippy::too_many_arguments)]
12517    pub fn gemma4_decode_step_dc(
12518        &self,
12519        e: &Engine,
12520        token_d: &CudaSlice<u32>,
12521        pos_d: &mut CudaSlice<i32>,
12522        embd_gpu: &CudaSlice<u8>,
12523        embd_qt: i32,
12524        embd_rb: usize,
12525        cache: &mut Cache,
12526        n_vocab: usize,
12527        cap_bucket_max: Option<(usize, usize)>,
12528    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
12529        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
12530        self.gemma4_decode_step_dc_into(
12531            e,
12532            token_d,
12533            pos_d,
12534            embd_gpu,
12535            embd_qt,
12536            embd_rb,
12537            cache,
12538            n_vocab,
12539            cap_bucket_max,
12540            &mut tok_out,
12541        )?;
12542        Ok(tok_out)
12543    }
12544
12545    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
12546    /// every replay; pass `token_d` itself for the self-feeding graph loop).
12547    #[allow(clippy::too_many_arguments)]
12548    pub fn gemma4_decode_step_dc_into(
12549        &self,
12550        e: &Engine,
12551        token_d: &CudaSlice<u32>,
12552        pos_d: &mut CudaSlice<i32>,
12553        embd_gpu: &CudaSlice<u8>,
12554        embd_qt: i32,
12555        embd_rb: usize,
12556        cache: &mut Cache,
12557        n_vocab: usize,
12558        cap_bucket_max: Option<(usize, usize)>,
12559        tok_out: &mut CudaSlice<u32>,
12560    ) -> Result<(), Box<dyn std::error::Error>> {
12561        let n_embd = self.cfg.n_embd as usize;
12562        let eps = self.cfg.rms_eps;
12563        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
12564        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
12565        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12566        let n_layers = self.layers.len();
12567        for (il, layer) in self.layers.iter().enumerate() {
12568            let (hq, hdq) = match h_carry.take() {
12569                Some(p) => p,
12570                None => {
12571                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
12572                }
12573            };
12574            let Mixer::Full(fa) = &layer.mixer else {
12575                panic!("gemma4 layer {il} not full-attn")
12576            };
12577            let o =
12578                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
12579            let next_norm = if il + 1 < n_layers {
12580                Some(self.layers[il + 1].attn_norm.float_data())
12581            } else {
12582                None
12583            };
12584            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
12585            x = xn;
12586            h_carry = hn;
12587        }
12588        let mut hn = e.uninit(n_embd)?;
12589        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
12590        let mut logits = e.matmul(&self.output, &hn, 1)?;
12591        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
12592        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
12593        e.inc_seqlen(pos_d)?;
12594        if cap_bucket_max.is_none() {
12595            cache.pos += 1;
12596        }
12597        Ok(())
12598    }
12599
12600    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
12601    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
12602    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
12603    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
12604
12605    /// Build the slot set (call OUTSIDE any capture).
12606    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
12607        let n_embd = self.cfg.n_embd as usize;
12608        let n_vocab = self.output.out_features();
12609        let n_layers = self.layers.len();
12610        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
12611        for il in 0..n_layers {
12612            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
12613            qmax = qmax.max(nh * hd);
12614            kvmax = kvmax.max(nkv * hd);
12615            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
12616                ffmax = ffmax.max(ffn_gate.out_features());
12617            }
12618        }
12619        Ok(G4DcSlots {
12620            x: e.uninit(n_embd)?,
12621            xn: e.uninit(n_embd)?,
12622            cur: e.uninit(n_embd)?,
12623            hq: e.alloc_i8_uninit(n_embd)?,
12624            hd_: e.uninit(n_embd / 32)?,
12625            q0: e.uninit(qmax)?,
12626            k0: e.uninit(kvmax)?,
12627            v0: e.uninit(kvmax)?,
12628            q: e.uninit(qmax)?,
12629            k: e.uninit(kvmax)?,
12630            v: e.uninit(kvmax)?,
12631            attn: e.uninit(qmax)?,
12632            o: e.uninit(n_embd)?,
12633            attn_out: e.uninit(n_embd)?,
12634            zsh: e.uninit(n_embd)?,
12635            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
12636            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
12637            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
12638            zd: e.uninit(n_embd.max(qmax) / 32)?,
12639            gate: e.uninit(ffmax)?,
12640            up: e.uninit(ffmax)?,
12641            act: e.uninit(ffmax)?,
12642            actq: e.alloc_i8_uninit(ffmax)?,
12643            actd: e.uninit(ffmax / 32)?,
12644            f0: e.uninit(n_embd)?,
12645            sn: e.uninit(n_embd)?,
12646            hn: e.uninit(n_embd)?,
12647            logits: e.uninit(n_vocab)?,
12648        })
12649    }
12650
12651    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
12652    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
12653    fn g4_matvec_m1_into(
12654        &self,
12655        e: &Engine,
12656        w: &crate::model::GpuTensor,
12657        aq: &CudaSlice<i8>,
12658        ad: &CudaSlice<f32>,
12659        y: &mut CudaSlice<f32>,
12660    ) -> Result<(), Box<dyn std::error::Error>> {
12661        use crate::model::GpuTensor;
12662        let (bytes, qtype, row_bytes, scale, rp) = match w {
12663            GpuTensor::Quant {
12664                bytes,
12665                qtype,
12666                row_bytes,
12667                scale,
12668                rp,
12669                ..
12670            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12671            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
12672        };
12673        let (mbytes, mrp) = match w {
12674            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12675            _ => (bytes, rp),
12676        };
12677        e.qmatvec_mmvq_into(
12678            mbytes,
12679            aq,
12680            ad,
12681            1,
12682            w.in_features(),
12683            w.out_features(),
12684            qtype,
12685            row_bytes,
12686            scale,
12687            mrp,
12688            y,
12689        )
12690    }
12691
12692    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
12693    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
12694    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
12695    #[allow(clippy::too_many_arguments)]
12696    pub fn gemma4_decode_step_dc_slotted(
12697        &self,
12698        e: &Engine,
12699        token_d: &CudaSlice<u32>,
12700        pos_d: &mut CudaSlice<i32>,
12701        embd_gpu: &CudaSlice<u8>,
12702        embd_qt: i32,
12703        embd_rb: usize,
12704        cache: &mut Cache,
12705        n_vocab: usize,
12706        cap_bucket_max: Option<(usize, usize)>,
12707        sl: &mut G4DcSlots,
12708        tok_out: &mut CudaSlice<u32>,
12709        ring: Option<(&mut CudaSlice<u32>, usize)>,
12710    ) -> Result<(), Box<dyn std::error::Error>> {
12711        let n_embd = self.cfg.n_embd as usize;
12712        let eps = self.cfg.rms_eps;
12713        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
12714        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
12715        let n_layers = self.layers.len();
12716        let mut has_carry = false;
12717        for il in 0..n_layers {
12718            if !has_carry {
12719                e.rms_norm_q8_1_into(
12720                    &sl.x,
12721                    self.layers[il].attn_norm.float_data(),
12722                    n_embd,
12723                    1,
12724                    eps,
12725                    &mut sl.hq,
12726                    &mut sl.hd_,
12727                )?;
12728            }
12729            has_carry = true;
12730            let layer = &self.layers[il];
12731            let Mixer::Full(fa) = &layer.mixer else {
12732                panic!("gemma4 layer {il} not full-attn")
12733            };
12734            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
12735            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
12736            // the standalone norm only survives on the unfused seam arm.
12737            if !Engine::g4_pnfold_on() {
12738                e.rms_norm(
12739                    &sl.o,
12740                    layer.post_attn_norm.float_data(),
12741                    &mut sl.cur,
12742                    n_embd,
12743                    1,
12744                    eps,
12745                )?;
12746            }
12747            let next_norm = if il + 1 < n_layers {
12748                Some(self.layers[il + 1].attn_norm.float_data())
12749            } else {
12750                None
12751            };
12752            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
12753            std::mem::swap(&mut sl.x, &mut sl.xn);
12754        }
12755        e.rms_norm(
12756            &sl.x,
12757            self.output_norm.float_data(),
12758            &mut sl.hn,
12759            n_embd,
12760            1,
12761            eps,
12762        )?;
12763        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
12764        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
12765        {
12766            let (zq, zd) = (&sl.zq, &sl.zd);
12767            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
12768            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
12769            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
12770        }
12771        self.gemma4_suppress(e, &mut sl.logits, 1)?;
12772        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
12773        if let Some((ring, base)) = ring {
12774            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
12775            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
12776            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
12777            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
12778        }
12779        e.inc_seqlen(pos_d)?;
12780        if cap_bucket_max.is_none() {
12781            cache.pos += 1;
12782        }
12783        Ok(())
12784    }
12785
12786    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
12787    #[allow(clippy::too_many_arguments)]
12788    fn gemma4_decode_attn_dc_slotted(
12789        &self,
12790        e: &Engine,
12791        fa: &crate::hybrid::FullAttnLayer,
12792        il: usize,
12793        pos_d: &CudaSlice<i32>,
12794        cache: &mut Cache,
12795        cap_bucket_max: Option<(usize, usize)>,
12796        sl: &mut G4DcSlots,
12797    ) -> Result<(), Box<dyn std::error::Error>> {
12798        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12799        let eps = self.cfg.rms_eps;
12800        let aux = self.gemma4_aux.as_ref().unwrap();
12801        let ones = aux.ones(e);
12802        #[cfg(debug_assertions)]
12803        crate::debug_assert_tensor_stream_device(
12804            ones,
12805            &e.stream(),
12806            "gemma4_decode_attn_dc_slotted.ones",
12807        );
12808        {
12809            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
12810            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
12811            if swa {
12812                if !e.matmul_q4_fused3_into(
12813                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
12814                )? {
12815                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
12816                    // (q,k) pair, v through the generic m1 slot matvec — the same two
12817                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
12818                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12819                    {
12820                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
12821                    } else {
12822                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
12823                    }
12824                }
12825            } else {
12826                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12827                    && !e
12828                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12829                {
12830                    return Err("slotted step: fused2 unavailable".into());
12831                }
12832                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
12833                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
12834            }
12835        }
12836        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
12837        // kernel-for-kernel (graph stream-identity gate).
12838        let ff = if swa {
12839            None
12840        } else {
12841            Some(
12842                aux.rope_freqs(e)
12843                    .expect("gemma4 global rope needs rope_freqs.weight"),
12844            )
12845        };
12846        #[cfg(debug_assertions)]
12847        if let Some(ff) = ff {
12848            crate::debug_assert_tensor_stream_device(
12849                ff,
12850                &e.stream(),
12851                "gemma4_decode_attn_dc_slotted.rope_freqs",
12852            );
12853        }
12854        let kvl = cache.kv[il].as_mut().unwrap();
12855        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12856        if crate::Engine::qkv_append_on() {
12857            // append fold (2026-07-23): mirrors dc_into.
12858            e.rms_norm_qkv_rope_append_dc(
12859                &sl.q0,
12860                &sl.k0,
12861                &sl.v0,
12862                fa.q_norm.float_data(),
12863                fa.k_norm.float_data(),
12864                ones,
12865                &mut sl.q,
12866                &mut sl.k,
12867                &mut sl.v,
12868                hd,
12869                self.gemma4_rope_dims(il),
12870                nh,
12871                nkv,
12872                pos_d,
12873                nh,
12874                nkv,
12875                base,
12876                1.0,
12877                ff,
12878                eps,
12879                &mut kvl.k,
12880                &mut kvl.v,
12881                &kvl.len_d,
12882                kvl.k_tok_bytes,
12883                kvl.v_tok_bytes,
12884                kv_fp8,
12885            )?;
12886        } else {
12887            e.rms_norm_qkv_rope(
12888                &sl.q0,
12889                &sl.k0,
12890                &sl.v0,
12891                fa.q_norm.float_data(),
12892                fa.k_norm.float_data(),
12893                ones,
12894                &mut sl.q,
12895                &mut sl.k,
12896                &mut sl.v,
12897                hd,
12898                self.gemma4_rope_dims(il),
12899                nh,
12900                nkv,
12901                pos_d,
12902                nh,
12903                nkv,
12904                base,
12905                1.0,
12906                ff,
12907                eps,
12908            )?;
12909            e.append_kv_quantized_dc(
12910                &sl.k,
12911                &sl.v,
12912                &mut kvl.k,
12913                &mut kvl.v,
12914                &kvl.len_d,
12915                kvl.kv_dim_k,
12916                kvl.kv_dim_v,
12917                kvl.k_tok_bytes,
12918                kvl.v_tok_bytes,
12919                kv_fp8,
12920            )?;
12921        }
12922        e.inc_seqlen(&mut kvl.len_d)?;
12923        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
12924        let k_view = e.view_u8(&kvl.k, kvl.k.len());
12925        let v_view = e.view_u8(&kvl.v, kvl.v.len());
12926        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
12927        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12928        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
12929        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
12930        // the dc_into arm branch-for-branch (stream gate).
12931        let mut fa_q8 = false;
12932        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
12933            e.fa_decode_rows(
12934                &sl.q,
12935                &k_view,
12936                &v_view,
12937                &mut sl.attn,
12938                hd,
12939                nh,
12940                nkv,
12941                b_glob - 1,
12942                1,
12943                scale,
12944                kvl.k_tok_bytes,
12945                kvl.v_tok_bytes,
12946                Some((&kvl.len_d, -1)),
12947                false,
12948                false,
12949                Some((&mut sl.zq, &mut sl.zd)),
12950            )?;
12951            fa_q8 = true;
12952        } else if swa && b_swa > win && hd == 256 && rows_on {
12953            e.fa_decode_rows_w(
12954                &sl.q,
12955                &k_view,
12956                &v_view,
12957                &mut sl.attn,
12958                hd,
12959                nh,
12960                nkv,
12961                &kvl.len_d,
12962                -1,
12963                1,
12964                scale,
12965                win,
12966                kvl.k_tok_bytes,
12967                kvl.v_tok_bytes,
12968                Some((&mut sl.zq, &mut sl.zd)),
12969            )?;
12970            fa_q8 = true;
12971        } else {
12972            let b = if swa { b_swa } else { b_glob };
12973            e.fa_decode_dc(
12974                &sl.q,
12975                &k_view,
12976                &v_view,
12977                &mut sl.attn,
12978                hd,
12979                nh,
12980                nkv,
12981                &kvl.len_d,
12982                b,
12983                scale,
12984                kvl.k_tok_bytes,
12985                kvl.v_tok_bytes,
12986                swa && crate::Engine::wkv_on(),
12987            )?;
12988        }
12989        if !fa_q8 {
12990            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
12991            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
12992        }
12993        {
12994            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
12995            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
12996            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
12997        }
12998        Ok(())
12999    }
13000
13001    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13002    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13003    fn gemma4_layer_tail_slotted(
13004        &self,
13005        e: &Engine,
13006        layer: &crate::hybrid::HybridLayer,
13007        next_norm: Option<&CudaSlice<f32>>,
13008        sl: &mut G4DcSlots,
13009    ) -> Result<(), Box<dyn std::error::Error>> {
13010        let n_embd = self.cfg.n_embd as usize;
13011        let eps = self.cfg.rms_eps;
13012        let bits = layer.gemma4.as_ref().unwrap();
13013        let crate::hybrid::Ffn::Dense {
13014            ffn_gate,
13015            ffn_up,
13016            ffn_down,
13017        } = &layer.ffn
13018        else {
13019            return Err("slotted tail: dense ffn only".into());
13020        };
13021        let pnfold = Engine::g4_pnfold_on();
13022        if pnfold {
13023            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13024            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13025            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13026            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13027            e.rms_pre_add_rms_norm_q8z_into(
13028                or,
13029                layer.post_attn_norm.float_data(),
13030                xr,
13031                bits.ffn_norm.float_data(),
13032                &mut sl.attn_out,
13033                &mut sl.zsh,
13034                n_embd,
13035                1,
13036                eps,
13037                &mut sl.zq,
13038                &mut sl.zd,
13039            )?;
13040        } else {
13041            e.add_rms_norm(
13042                &sl.cur,
13043                &sl.x,
13044                bits.ffn_norm.float_data(),
13045                &mut sl.attn_out,
13046                &mut sl.zsh,
13047                n_embd,
13048                1,
13049                eps,
13050            )?;
13051        }
13052        let n_ff = ffn_gate.out_features();
13053        if !pnfold {
13054            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13055            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13056        }
13057        {
13058            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13059            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13060            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13061                && !e.matmul_nvfp4_fused2_into(
13062                    ffn_gate,
13063                    ffn_up,
13064                    zq,
13065                    zd,
13066                    &mut sl.gate,
13067                    &mut sl.up,
13068                )?
13069            {
13070                return Err("slotted tail: ffn fused2 unavailable".into());
13071            }
13072        }
13073        debug_assert!(e.uses_q8_1_fast(ffn_down));
13074        {
13075            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13076            let upv = e.view(upr, n_ff);
13077            let up_all = upv.slice(0..n_ff);
13078            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13079            e.gelu_tanh_mul_q8_1_into(
13080                gr,
13081                &up_all,
13082                &mut sl.act,
13083                n_ff,
13084                1,
13085                &mut sl.actq,
13086                &mut sl.actd,
13087            )?;
13088        }
13089        {
13090            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13091            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13092            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13093        }
13094        if pnfold {
13095            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13096            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13097            if let Some(w) = next_norm {
13098                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13099                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13100                e.rms_pre_add_scale_rms_norm_q8_1_into(
13101                    f0r,
13102                    bits.post_ffw_norm.float_data(),
13103                    aor,
13104                    bits.layer_scale,
13105                    w,
13106                    &mut sl.xn,
13107                    n_embd,
13108                    1,
13109                    eps,
13110                    &mut sl.hq,
13111                    &mut sl.hd_,
13112                )?;
13113                return Ok(());
13114            }
13115        }
13116        e.rms_norm(
13117            &sl.f0,
13118            bits.post_ffw_norm.float_data(),
13119            &mut sl.sn,
13120            n_embd,
13121            1,
13122            eps,
13123        )?;
13124        match next_norm {
13125            Some(w) => {
13126                e.add_scale_rms_norm_q8_1_into(
13127                    &sl.sn,
13128                    &sl.attn_out,
13129                    bits.layer_scale,
13130                    w,
13131                    &mut sl.xn,
13132                    n_embd,
13133                    1,
13134                    eps,
13135                    &mut sl.hq,
13136                    &mut sl.hd_,
13137                )?;
13138            }
13139            None => {
13140                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13141            }
13142        }
13143        Ok(())
13144    }
13145
13146    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13147    #[allow(clippy::too_many_arguments)]
13148    fn gemma4_decode_attn_dc(
13149        &self,
13150        e: &Engine,
13151        fa: &crate::hybrid::FullAttnLayer,
13152        il: usize,
13153        hq: &CudaSlice<i8>,
13154        hdq: &CudaSlice<f32>,
13155        pos_d: &CudaSlice<i32>,
13156        cache: &mut Cache,
13157        cap_bucket_max: Option<(usize, usize)>,
13158    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13159        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13160        let eps = self.cfg.rms_eps;
13161        let aux = self.gemma4_aux.as_ref().unwrap();
13162        let ones = aux.ones(e);
13163        #[cfg(debug_assertions)]
13164        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13165        let (q0, k0, v0) = if swa {
13166            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13167                Some(t3) => t3,
13168                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13169                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13170                    Some((q0, k0)) => {
13171                        let h0 = e.zeros(0)?;
13172                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13173                        (q0, k0, v0)
13174                    }
13175                    None => {
13176                        let h0 = e.zeros(0)?;
13177                        (
13178                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13179                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13180                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13181                        )
13182                    }
13183                },
13184            }
13185        } else {
13186            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13187                Some(p) => p,
13188                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13189                    Some(p) => p,
13190                    None => {
13191                        let h0 = e.zeros(0)?;
13192                        (
13193                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13194                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13195                        )
13196                    }
13197                },
13198            };
13199            let v0 = e.clone_dtod(&k0)?;
13200            (q0, k0, v0)
13201        };
13202        let mut q = e.uninit(nh * hd)?;
13203        let mut k = e.uninit(nkv * hd)?;
13204        let mut v = e.uninit(nkv * hd)?;
13205        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13206        let ff = if swa {
13207            None
13208        } else {
13209            Some(
13210                aux.rope_freqs(e)
13211                    .expect("gemma4 global rope needs rope_freqs.weight"),
13212            )
13213        };
13214        #[cfg(debug_assertions)]
13215        if let Some(ff) = ff {
13216            crate::debug_assert_tensor_stream_device(
13217                ff,
13218                &e.stream(),
13219                "gemma4_decode_attn_dc.rope_freqs",
13220            );
13221        }
13222        let kvl = cache.kv[il].as_mut().unwrap();
13223        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13224        if crate::Engine::qkv_append_on() {
13225            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13226            e.rms_norm_qkv_rope_append_dc(
13227                &q0,
13228                &k0,
13229                &v0,
13230                fa.q_norm.float_data(),
13231                fa.k_norm.float_data(),
13232                ones,
13233                &mut q,
13234                &mut k,
13235                &mut v,
13236                hd,
13237                self.gemma4_rope_dims(il),
13238                nh,
13239                nkv,
13240                pos_d,
13241                nh,
13242                nkv,
13243                base,
13244                1.0,
13245                ff,
13246                eps,
13247                &mut kvl.k,
13248                &mut kvl.v,
13249                &kvl.len_d,
13250                kvl.k_tok_bytes,
13251                kvl.v_tok_bytes,
13252                kv_fp8,
13253            )?;
13254        } else {
13255            e.rms_norm_qkv_rope(
13256                &q0,
13257                &k0,
13258                &v0,
13259                fa.q_norm.float_data(),
13260                fa.k_norm.float_data(),
13261                ones,
13262                &mut q,
13263                &mut k,
13264                &mut v,
13265                hd,
13266                self.gemma4_rope_dims(il),
13267                nh,
13268                nkv,
13269                pos_d,
13270                nh,
13271                nkv,
13272                base,
13273                1.0,
13274                ff,
13275                eps,
13276            )?;
13277            e.append_kv_quantized_dc(
13278                &k,
13279                &v,
13280                &mut kvl.k,
13281                &mut kvl.v,
13282                &kvl.len_d,
13283                kvl.kv_dim_k,
13284                kvl.kv_dim_v,
13285                kvl.k_tok_bytes,
13286                kvl.v_tok_bytes,
13287                kv_fp8,
13288            )?;
13289        }
13290        e.inc_seqlen(&mut kvl.len_d)?;
13291        let mut attn = e.uninit(nh * hd)?;
13292        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13293        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13294        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13295        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13296        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13297        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13298        // (gemma4_e4b_attn, +0.65% valid window).
13299        match cap_bucket_max {
13300            None => {
13301                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13302                // decode (SWA layers attend the last `sliding_window` keys); the device
13303                // counters carry only the append slot + the graph seam.
13304                kvl.len += 1;
13305                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13306                if !swa
13307                    && hd == 512
13308                    && kvl.len >= crate::fa512_min_tkv()
13309                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13310                {
13311                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13312                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13313                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13314                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13315                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13316                    e.fa_decode_rows(
13317                        &q,
13318                        &kp,
13319                        &vp,
13320                        &mut attn,
13321                        hd,
13322                        nh,
13323                        nkv,
13324                        kvl.len - 1,
13325                        1,
13326                        scale,
13327                        kvl.k_tok_bytes,
13328                        kvl.v_tok_bytes,
13329                        Some((&kvl.len_d, -1)),
13330                        false,
13331                        false,
13332                        Some((&mut aq8, &mut ad8)),
13333                    )?;
13334                    fa_q8 = Some((aq8, ad8));
13335                } else if swa
13336                    && kvl.len > win
13337                    && hd == 256
13338                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13339                {
13340                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13341                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13342                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13343                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13344                    e.fa_decode_rows_w(
13345                        &q,
13346                        &kp,
13347                        &vp,
13348                        &mut attn,
13349                        hd,
13350                        nh,
13351                        nkv,
13352                        &kvl.len_d,
13353                        -1,
13354                        1,
13355                        scale,
13356                        win,
13357                        kvl.k_tok_bytes,
13358                        kvl.v_tok_bytes,
13359                        Some((&mut aq8, &mut ad8)),
13360                    )?;
13361                    fa_q8 = Some((aq8, ad8));
13362                } else {
13363                    let (off_tok, t_kv) = if swa && kvl.len > win {
13364                        (kvl.len - win, win)
13365                    } else {
13366                        (0, kvl.len)
13367                    };
13368                    let k_view = e.view_u8_range(
13369                        &kvl.k,
13370                        off_tok * kvl.k_tok_bytes,
13371                        (off_tok + t_kv) * kvl.k_tok_bytes,
13372                    );
13373                    let v_view = e.view_u8_range(
13374                        &kvl.v,
13375                        off_tok * kvl.v_tok_bytes,
13376                        (off_tok + t_kv) * kvl.v_tok_bytes,
13377                    );
13378                    e.fa_decode_kvmod(
13379                        &q,
13380                        &k_view,
13381                        &v_view,
13382                        &mut attn,
13383                        hd,
13384                        nh,
13385                        nkv,
13386                        t_kv,
13387                        scale,
13388                        kvl.k_tok_bytes,
13389                        kvl.v_tok_bytes,
13390                        swa && crate::Engine::wkv_on(),
13391                    )?;
13392                }
13393            }
13394            Some((b_swa, b_glob)) => {
13395                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13396                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13397                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13398                // the RUNG max for the rows family (kernels derive per-replay splits from
13399                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13400                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13401                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13402                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13403                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13404                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13405                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13406                    e.fa_decode_rows(
13407                        &q,
13408                        &k_view,
13409                        &v_view,
13410                        &mut attn,
13411                        hd,
13412                        nh,
13413                        nkv,
13414                        b_glob - 1,
13415                        1,
13416                        scale,
13417                        kvl.k_tok_bytes,
13418                        kvl.v_tok_bytes,
13419                        Some((&kvl.len_d, -1)),
13420                        false,
13421                        false,
13422                        Some((&mut aq8, &mut ad8)),
13423                    )?;
13424                    fa_q8 = Some((aq8, ad8));
13425                } else if swa && b_swa > win && hd == 256 && rows_on {
13426                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13427                    e.fa_decode_rows_w(
13428                        &q,
13429                        &k_view,
13430                        &v_view,
13431                        &mut attn,
13432                        hd,
13433                        nh,
13434                        nkv,
13435                        &kvl.len_d,
13436                        -1,
13437                        1,
13438                        scale,
13439                        win,
13440                        kvl.k_tok_bytes,
13441                        kvl.v_tok_bytes,
13442                        Some((&mut aq8, &mut ad8)),
13443                    )?;
13444                    fa_q8 = Some((aq8, ad8));
13445                } else {
13446                    let b = if swa { b_swa } else { b_glob };
13447                    e.fa_decode_dc(
13448                        &q,
13449                        &k_view,
13450                        &v_view,
13451                        &mut attn,
13452                        hd,
13453                        nh,
13454                        nkv,
13455                        &kvl.len_d,
13456                        b,
13457                        scale,
13458                        kvl.k_tok_bytes,
13459                        kvl.v_tok_bytes,
13460                        swa && crate::Engine::wkv_on(),
13461                    )?;
13462                }
13463            }
13464        }
13465        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
13466        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
13467        if let Some((aq8, ad8)) = fa_q8 {
13468            let mut y = e.uninit(fa.wo.out_features())?;
13469            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
13470            return Ok(y);
13471        }
13472        Ok(e.matmul(&fa.wo, &attn, 1)?)
13473    }
13474
13475    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
13476    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
13477    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
13478    /// views in-graph); caller gates and falls back to the dc-eager loop.
13479    pub fn gemma4_generate_graph(
13480        &self,
13481        e: &Engine,
13482        prompt_pos: usize,
13483        first_token: u32,
13484        cache: &mut Cache,
13485        max_new: usize,
13486        eos: &[u32],
13487        mut on_token: impl FnMut(u32) -> bool,
13488    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
13489        if self.is_gemma4_e4b() {
13490            return Err(
13491                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
13492                    .into(),
13493            );
13494        }
13495        use crate::decode::StopReason;
13496        let n_vocab = self.output.out_features();
13497        let n_embd = self.cfg.n_embd as usize;
13498        let embd_gpu = self
13499            .embd_gpu
13500            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13501        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13502        for kvl in cache.kv.iter_mut().flatten() {
13503            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
13504        }
13505        let mut token_d = e.stream().clone_htod(&[first_token])?;
13506        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
13507        let g4 = self.cfg.gemma4.as_ref().unwrap();
13508        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
13509        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
13510        let nkv_s = g4
13511            .head_count_kv
13512            .iter()
13513            .zip(g4.swa_pattern.iter())
13514            .find(|p| *p.1)
13515            .map(|p| *p.0 as usize)
13516            .unwrap_or(8);
13517        let nkv_g = g4
13518            .head_count_kv
13519            .iter()
13520            .zip(g4.swa_pattern.iter())
13521            .find(|p| !*p.1)
13522            .map(|p| *p.0 as usize)
13523            .unwrap_or(2);
13524        let mut graphs: std::collections::HashMap<
13525            ((bool, usize), (bool, usize), bool, bool),
13526            (
13527                cudarc::driver::CudaGraph,
13528                Vec<Box<dyn std::any::Any + Send>>,
13529            ),
13530        > = Default::default();
13531        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
13532        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
13533        let mut slots = self.g4_dc_slots(e)?;
13534        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
13535        // baked at the door entry (the modulo keeps every capture valid indefinitely).
13536        const RING: usize = 64;
13537        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
13538        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
13539        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
13540        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
13541        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
13542        const DRAIN: usize = 1;
13543        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
13544        let ring_base = prompt_pos;
13545        let mut out = Vec::with_capacity(max_new);
13546        let mut reason = StopReason::MaxNew;
13547        let mut next = first_token;
13548        let mut captures = 0usize;
13549        for _ in 0..max_new {
13550            out.push(next);
13551            if eos.contains(&next) {
13552                reason = StopReason::Eos;
13553                break;
13554            }
13555            if !on_token(next) {
13556                reason = StopReason::Callback;
13557                break;
13558            }
13559            let t_kv = cache.pos + 1;
13560            // Bucket key per ARM (graph arc step 3):
13561            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
13562            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
13563            //    the component collapses to a single marker).
13564            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
13565            //    at/above it — the kernel derives splits from len_d per replay, so buckets
13566            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
13567            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13568            let f512 = crate::fa512_min_tkv();
13569            let key_s = if t_kv > win {
13570                (true, usize::MAX)
13571            } else {
13572                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
13573            };
13574            let (key_g, rung_end) = if t_kv >= f512 {
13575                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
13576                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
13577                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
13578                ((true, end), end)
13579            } else {
13580                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
13581            };
13582            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
13583            if !graphs.contains_key(&key) {
13584                let bucket_max = (t_kv, rung_end);
13585                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
13586                let snap = cache.snapshot(e)?;
13587                let pos_save = e.dtoh_i32_one(&pos_d)?;
13588                let len_save: Vec<Option<i32>> = cache
13589                    .kv
13590                    .iter()
13591                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
13592                    .collect();
13593                let tok_save = e.dtoh_u32_one(&token_d)?;
13594                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
13595                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
13596                // regression class, and this door's measured -8.8%. The keeper pins warmup
13597                // transients so the captured graph holds kernel nodes only.
13598                let graph = {
13599                    let tok_ref = &mut token_d;
13600                    let pos_ref = &mut pos_d;
13601                    let cache_ref = &mut *cache;
13602                    let slots_ref = &mut slots;
13603                    let ring_ref = &mut ring;
13604                    e.capture_graph_retained_flags(
13605                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
13606                        |e| {
13607                        // self-feeding: the argmax writes token_d itself.
13608                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
13609                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
13610                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
13611                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
13612                                                           cache_ref, n_vocab, Some(bucket_max),
13613                                                           sl, tok_ref, Some((rg, ring_base)))
13614                    })?
13615                };
13616                cache.rollback(e, &snap, 0)?;
13617                e.set_i32_one(&mut pos_d, pos_save)?;
13618                for (il, ls) in len_save.iter().enumerate() {
13619                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
13620                        e.set_i32_one(&mut kvl.len_d, *v)?;
13621                    }
13622                }
13623                e.set_u32_one(&mut token_d, tok_save)?;
13624                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
13625                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
13626                        eprintln!("[graph-census] {c:?}");
13627                    }
13628                }
13629                graphs.insert(key, graph);
13630                captures += 1;
13631            }
13632            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
13633            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
13634            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
13635            // the budget; capture warmups already emitted their tokens through the ring.
13636            let mut chunk = 1usize;
13637            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
13638                .ok()
13639                .and_then(|v| v.parse().ok())
13640                .unwrap_or(DRAIN);
13641            while chunk < drain_cap && out.len() + chunk < max_new {
13642                let t_next = cache.pos + 1 + chunk;
13643                let key_s2 = if t_next > win {
13644                    (true, usize::MAX)
13645                } else {
13646                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
13647                };
13648                let key_g2 = if t_next >= f512 {
13649                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
13650                } else {
13651                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
13652                };
13653                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
13654                    break;
13655                }
13656                chunk += 1;
13657            }
13658            let g = &graphs.get(&key).unwrap().0;
13659            for _ in 0..chunk {
13660                g.launch()?;
13661            }
13662            e.stream().synchronize()?;
13663            let ringh = e.dtoh_u32(&ring)?;
13664            for j in 0..chunk {
13665                let pos_j = cache.pos + j;
13666                let tok_j = ringh[(pos_j - ring_base) % RING];
13667                cache.pos += 0; // advanced below in one shot
13668                if j + 1 == chunk {
13669                    next = tok_j;
13670                } else {
13671                    out.push(tok_j);
13672                    if eos.contains(&tok_j) || !on_token(tok_j) {
13673                        reason = if eos.contains(&tok_j) {
13674                            StopReason::Eos
13675                        } else {
13676                            StopReason::Callback
13677                        };
13678                        // roll device/host state back to the stop point.
13679                        let keep = cache.pos + j + 1;
13680                        e.set_i32_one(&mut pos_d, keep as i32)?;
13681                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13682                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
13683                            kvl.len = keep;
13684                        }
13685                        cache.pos = keep;
13686                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13687                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13688                        }
13689                        return Ok((out, reason));
13690                    }
13691                }
13692            }
13693            cache.pos += chunk;
13694            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13695                kvl.len += chunk;
13696            }
13697        }
13698        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13699            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13700        }
13701        Ok((out, reason))
13702    }
13703
13704    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
13705    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
13706    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
13707    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
13708    /// logits (host) + advances cache.pos by t.
13709    pub(crate) fn gemma4_decode_step_t(
13710        &self,
13711        e: &Engine,
13712        tokens: &[u32],
13713        pos0: usize,
13714        cache: &mut Cache,
13715    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13716        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
13717    }
13718
13719    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
13720    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
13721    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
13722    pub(crate) fn gemma4_decode_step_t_am(
13723        &self,
13724        e: &Engine,
13725        tokens: &[u32],
13726        pos0: usize,
13727        cache: &mut Cache,
13728    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13729        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13730        let t = tokens.len();
13731        let n_vocab = self.output.out_features();
13732        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
13733        for i in 0..t {
13734            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
13735        }
13736        Ok((e.dtoh_u32(&toks)?, hn))
13737    }
13738
13739    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
13740    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
13741    pub(crate) fn gemma4_decode_step_t_am_dev(
13742        &self,
13743        e: &Engine,
13744        tok_d: &CudaSlice<u32>,
13745        t: usize,
13746        pos0: usize,
13747        cache: &mut Cache,
13748    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13749        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
13750        let n_vocab = self.output.out_features();
13751        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13752        for i in 0..t {
13753            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13754        }
13755        Ok((vam, hn))
13756    }
13757
13758    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
13759    /// llama's h_nextn convention).
13760    pub(crate) fn gemma4_decode_step_t_h(
13761        &self,
13762        e: &Engine,
13763        tokens: &[u32],
13764        pos0: usize,
13765        cache: &mut Cache,
13766    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13767        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13768        let t = tokens.len();
13769        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
13770        e.softcap(&mut ld, cap, t * self.output.out_features())?;
13771        Ok((e.dtoh(&ld)?, hn))
13772    }
13773
13774    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
13775    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
13776    pub(crate) fn verify_stream_scratch(
13777        &self,
13778        e: &Engine,
13779        cap: usize,
13780    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
13781        Ok(VerifyStreamScratch {
13782            pos_d: e.htod_i32(&vec![0i32; cap])?,
13783            row_ctrs: (0..cap)
13784                .map(|_| e.htod_i32(&[0]))
13785                .collect::<Result<_, _>>()?,
13786        })
13787    }
13788
13789    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
13790    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
13791    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
13792    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
13793    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
13794    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
13795    /// sync, exactly the turnaround the burst exists to remove.
13796    pub(crate) fn gemma4_verify_t_am_stream(
13797        &self,
13798        e: &Engine,
13799        tok_d: &CudaSlice<u32>,
13800        t: usize,
13801        ctr: &CudaSlice<i32>,
13802        hint: usize,
13803        cache: &mut Cache,
13804        scr: &mut VerifyStreamScratch,
13805    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13806        let n_embd = self.cfg.n_embd as usize;
13807        let eps = self.cfg.rms_eps;
13808        assert!(t <= scr.row_ctrs.len() && t <= 64);
13809        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
13810        for i in 0..t {
13811            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
13812        }
13813        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
13814        let embd_gpu = self
13815            .embd_gpu
13816            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13817        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13818        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
13819        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
13820        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13821        let n_layers = self.layers.len();
13822        for (il, layer) in self.layers.iter().enumerate() {
13823            let (hq, hdq) = match h_carry.take() {
13824                Some(p) => p,
13825                None => {
13826                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
13827                }
13828            };
13829            let Mixer::Full(fa) = &layer.mixer else {
13830                panic!("gemma4 layer {il} not full-attn")
13831            };
13832            let o = self
13833                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
13834            let next_norm = if il + 1 < n_layers {
13835                Some(self.layers[il + 1].attn_norm.float_data())
13836            } else {
13837                None
13838            };
13839            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
13840            x = xn;
13841            h_carry = hn;
13842            self.dflash_tap(e, cache, il, &x, t)?;
13843        }
13844        let mut hn = e.uninit(t * n_embd)?;
13845        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
13846        let ld = e.matmul(&self.output, &hn, t)?;
13847        let n_vocab = self.output.out_features();
13848        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13849        for i in 0..t {
13850            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13851        }
13852        Ok((vam, hn))
13853    }
13854
13855    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
13856    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
13857    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
13858    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
13859    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
13860    /// kernel later if it shows in the profile).
13861    pub(crate) fn dflash_tap(
13862        &self,
13863        e: &Engine,
13864        cache: &mut Cache,
13865        il: usize,
13866        x: &CudaSlice<f32>,
13867        t: usize,
13868    ) -> Result<(), Box<dyn std::error::Error>> {
13869        let Some(taps) = cache.dflash_taps.as_mut() else {
13870            return Ok(());
13871        };
13872        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
13873            return Ok(());
13874        };
13875        let h = taps.hidden;
13876        let n_taps = taps.layer_ids.len();
13877        let base = taps.base;
13878        debug_assert!(
13879            base + t <= taps.t,
13880            "tap window {base}+{t} exceeds sink {}",
13881            taps.t
13882        );
13883        let xv = e.view(x, t * h);
13884        for r in 0..t {
13885            let row = xv.slice(r * h..(r + 1) * h);
13886            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
13887        }
13888        Ok(())
13889    }
13890
13891    fn gemma4_verify_trunk(
13892        &self,
13893        e: &Engine,
13894        tokens: &[u32],
13895        pos0: usize,
13896        cache: &mut Cache,
13897        tok_dev: Option<&CudaSlice<u32>>,
13898    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13899        let n_embd = self.cfg.n_embd as usize;
13900        let eps = self.cfg.rms_eps;
13901        let t = tokens.len();
13902        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
13903        let pos_d = e.htod_i32(&pos)?;
13904        let mut x = match tok_dev {
13905            Some(td) => {
13906                let embd_gpu = self
13907                    .embd_gpu
13908                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13909                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13910                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
13911            }
13912            None => e.htod(&self.embd.gather(n_embd, tokens))?,
13913        };
13914        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
13915        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13916        let n_layers = self.layers.len();
13917        for (il, layer) in self.layers.iter().enumerate() {
13918            let (hq, hdq) = match h_carry.take() {
13919                Some(p) => p,
13920                None => {
13921                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
13922                }
13923            };
13924            let Mixer::Full(fa) = &layer.mixer else {
13925                panic!("gemma4 layer {il} not full-attn")
13926            };
13927            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
13928            let next_norm = if il + 1 < n_layers {
13929                Some(self.layers[il + 1].attn_norm.float_data())
13930            } else {
13931                None
13932            };
13933            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
13934            x = xn;
13935            h_carry = hn;
13936            self.dflash_tap(e, cache, il, &x, t)?;
13937        }
13938        let mut hn = e.uninit(t * n_embd)?;
13939        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
13940        let mut ld = e.matmul(&self.output, &hn, t)?;
13941        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
13942        cache.pos += t;
13943        Ok((ld, hn))
13944    }
13945
13946    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
13947    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
13948    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
13949    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
13950    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
13951    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
13952    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
13953    #[allow(clippy::too_many_arguments)]
13954    fn gemma4_verify_attn_stream(
13955        &self,
13956        e: &Engine,
13957        fa: &crate::hybrid::FullAttnLayer,
13958        il: usize,
13959        hq: &CudaSlice<i8>,
13960        hdq: &CudaSlice<f32>,
13961        pos_d: &CudaSlice<i32>,
13962        t: usize,
13963        cache: &mut Cache,
13964        hint: usize,
13965        row_ctrs: &[CudaSlice<i32>],
13966    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13967        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13968        let eps = self.cfg.rms_eps;
13969        let aux = self.gemma4_aux.as_ref().unwrap();
13970        let ones = aux.ones(e);
13971        #[cfg(debug_assertions)]
13972        crate::debug_assert_tensor_stream_device(
13973            ones,
13974            &e.stream(),
13975            "gemma4_verify_attn_stream.ones",
13976        );
13977        let h0 = e.zeros(0)?;
13978        let h = &h0;
13979        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
13980        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
13981        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13982        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
13983        let fused_qkv = if f2b {
13984            if swa {
13985                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
13986                    .map(|(a, b, c)| (a, b, Some(c)))
13987            } else {
13988                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
13989                    .map(|(a, b)| (a, b, None))
13990            }
13991        } else {
13992            None
13993        };
13994        let (q0, k0, v0) = match fused_qkv {
13995            Some((a, b, cv)) => {
13996                let v = match cv {
13997                    Some(c) => c,
13998                    None => e.clone_dtod(&b)?,
13999                };
14000                (a, b, v)
14001            }
14002            None => {
14003                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14004                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14005                let v0 = if swa {
14006                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14007                } else {
14008                    e.clone_dtod(&k0)?
14009                };
14010                (q0, k0, v0)
14011            }
14012        };
14013        let mut q = e.uninit(t * nh * hd)?;
14014        let mut k = e.uninit(t * nkv * hd)?;
14015        let mut v = e.uninit(t * nkv * hd)?;
14016        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14017        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14018        let ff = if swa {
14019            None
14020        } else {
14021            Some(
14022                aux.rope_freqs(e)
14023                    .expect("gemma4 global rope needs rope_freqs.weight"),
14024            )
14025        };
14026        #[cfg(debug_assertions)]
14027        if let Some(ff) = ff {
14028            crate::debug_assert_tensor_stream_device(
14029                ff,
14030                &e.stream(),
14031                "gemma4_verify_attn_stream.rope_freqs",
14032            );
14033        }
14034        e.rms_norm_qkv_rope(
14035            &q0,
14036            &k0,
14037            &v0,
14038            fa.q_norm.float_data(),
14039            fa.k_norm.float_data(),
14040            ones,
14041            &mut q,
14042            &mut k,
14043            &mut v,
14044            hd,
14045            self.gemma4_rope_dims(il),
14046            nh * t,
14047            nkv * t,
14048            pos_d,
14049            nh,
14050            nkv,
14051            base,
14052            1.0,
14053            ff,
14054            eps,
14055        )?;
14056        let kvl = cache.kv[il].as_mut().unwrap();
14057        // append at the DEVICE slot; the counter advances by t on-device.
14058        e.append_kv_quantized_rows_dc(
14059            &k,
14060            &v,
14061            &mut kvl.k,
14062            &mut kvl.v,
14063            &kvl.len_d,
14064            t,
14065            kvl.kv_dim_k,
14066            kvl.kv_dim_v,
14067            kvl.k_tok_bytes,
14068            kvl.v_tok_bytes,
14069            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14070        )?;
14071        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14072        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14073        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14074        let mut attn = e.uninit(t * nh * hd)?;
14075        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14076        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14077        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14078        // and a stable window regime — the same rung/regime keys as the draft graph).
14079        if swa && hint + 1 >= win {
14080            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14081            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14082            e.fa_decode_rows_w(
14083                &q,
14084                &k_view,
14085                &v_view,
14086                &mut attn,
14087                hd,
14088                nh,
14089                nkv,
14090                &kvl.len_d,
14091                0,
14092                t,
14093                scale,
14094                win,
14095                kvl.k_tok_bytes,
14096                kvl.v_tok_bytes,
14097                None,
14098            )?;
14099        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14100            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14101            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14102            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14103            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14104            // for every row.
14105            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14106            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14107            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14108            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14109            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14110            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14111            // any bucket >= the live length is exact.
14112            let bucket = (hint + t + 2)
14113                .next_power_of_two()
14114                .min(crate::fa512_min_tkv().saturating_sub(1));
14115            let qv = e.view(&q, t * nh * hd);
14116            for i in 0..t {
14117                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14118                let mut q_one = e.uninit(nh * hd)?;
14119                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14120                let mut a_one = e.uninit(nh * hd)?;
14121                e.fa_decode_dc(
14122                    &q_one,
14123                    &k_view,
14124                    &v_view,
14125                    &mut a_one,
14126                    hd,
14127                    nh,
14128                    nkv,
14129                    &row_ctrs[i],
14130                    bucket,
14131                    scale,
14132                    kvl.k_tok_bytes,
14133                    kvl.v_tok_bytes,
14134                    false,
14135                )?;
14136                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14137            }
14138        } else if hd == 512 {
14139            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14140            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14141            e.fa_decode_rows(
14142                &q,
14143                &k_view,
14144                &v_view,
14145                &mut attn,
14146                hd,
14147                nh,
14148                nkv,
14149                hint,
14150                t,
14151                scale,
14152                kvl.k_tok_bytes,
14153                kvl.v_tok_bytes,
14154                Some((&kvl.len_d, 0)),
14155                false,
14156                false,
14157                None,
14158            )?;
14159        } else {
14160            // hd256 under-window: v4 device-len rows twin.
14161            e.fa_decode_rows_dc(
14162                &q,
14163                &k_view,
14164                &v_view,
14165                &mut attn,
14166                hd,
14167                nh,
14168                nkv,
14169                &kvl.len_d,
14170                hint + t,
14171                t,
14172                scale,
14173                kvl.k_tok_bytes,
14174                kvl.v_tok_bytes,
14175                0,
14176                swa && crate::Engine::wkv_on(),
14177            )?;
14178        }
14179        Ok(e.matmul(&fa.wo, &attn, t)?)
14180    }
14181
14182    fn gemma4_verify_attn(
14183        &self,
14184        e: &Engine,
14185        fa: &crate::hybrid::FullAttnLayer,
14186        il: usize,
14187        hq: &CudaSlice<i8>,
14188        hdq: &CudaSlice<f32>,
14189        pos_d: &CudaSlice<i32>,
14190        t: usize,
14191        cache: &mut Cache,
14192    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14193        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14194        let eps = self.cfg.rms_eps;
14195        let aux = self.gemma4_aux.as_ref().unwrap();
14196        let ones = aux.ones(e);
14197        #[cfg(debug_assertions)]
14198        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14199        let n_embd = self.cfg.n_embd as usize;
14200        let _ = n_embd;
14201
14202        let h0 = e.zeros(0)?;
14203        let h = &h0;
14204        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14205        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14206        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14207        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14208        let fused_qkv = if f2b {
14209            if swa {
14210                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14211                    .map(|(a, b, c)| (a, b, Some(c)))
14212            } else {
14213                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14214                    .map(|(a, b)| (a, b, None))
14215            }
14216        } else {
14217            None
14218        };
14219        let (q0, k0, v0) = match fused_qkv {
14220            Some((a, b, cv)) => {
14221                let v = match cv {
14222                    Some(c) => c,
14223                    None => e.clone_dtod(&b)?,
14224                };
14225                (a, b, v)
14226            }
14227            None => {
14228                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14229                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14230                let v0 = if swa {
14231                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14232                } else {
14233                    e.clone_dtod(&k0)?
14234                };
14235                (q0, k0, v0)
14236            }
14237        };
14238        let mut q = e.uninit(t * nh * hd)?;
14239        let mut k = e.uninit(t * nkv * hd)?;
14240        let mut v = e.uninit(t * nkv * hd)?;
14241        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14242        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14243        let ff = if swa {
14244            None
14245        } else {
14246            Some(
14247                aux.rope_freqs(e)
14248                    .expect("gemma4 global rope needs rope_freqs.weight"),
14249            )
14250        };
14251        #[cfg(debug_assertions)]
14252        if let Some(ff) = ff {
14253            crate::debug_assert_tensor_stream_device(
14254                ff,
14255                &e.stream(),
14256                "gemma4_verify_attn.rope_freqs",
14257            );
14258        }
14259        e.rms_norm_qkv_rope(
14260            &q0,
14261            &k0,
14262            &v0,
14263            fa.q_norm.float_data(),
14264            fa.k_norm.float_data(),
14265            ones,
14266            &mut q,
14267            &mut k,
14268            &mut v,
14269            hd,
14270            self.gemma4_rope_dims(il),
14271            nh * t,
14272            nkv * t,
14273            pos_d,
14274            nh,
14275            nkv,
14276            base,
14277            1.0,
14278            ff,
14279            eps,
14280        )?;
14281        let kvl = cache.kv[il].as_mut().unwrap();
14282        let base_len = kvl.len;
14283        e.append_kv_quantized_rows(
14284            &k,
14285            &v,
14286            &mut kvl.k,
14287            &mut kvl.v,
14288            base_len,
14289            t,
14290            kvl.kv_dim_k,
14291            kvl.kv_dim_v,
14292            kvl.k_tok_bytes,
14293            kvl.v_tok_bytes,
14294            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14295        )?;
14296        kvl.len += t;
14297        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14298        let mut attn = e.uninit(t * nh * hd)?;
14299        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14300        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14301        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14302            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14303            // decode rides the SAME symbol at t=1 (parity law).
14304            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14305        if rows_ok && (!swa || base_len + t <= win) {
14306            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14307            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14308            if hd == 512 {
14309                // device-len twin: sync the counter to the verify base (async arg-store).
14310                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14311                e.fa_decode_rows(
14312                    &q,
14313                    &k_view,
14314                    &v_view,
14315                    &mut attn,
14316                    hd,
14317                    nh,
14318                    nkv,
14319                    base_len,
14320                    t,
14321                    scale,
14322                    kvl.k_tok_bytes,
14323                    kvl.v_tok_bytes,
14324                    Some((&kvl.len_d, 0)),
14325                    false,
14326                    swa && crate::Engine::wkv_on(),
14327                    None,
14328                )?;
14329            } else {
14330                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14331                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14332                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14333                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14334                e.fa_decode_rows_dc(
14335                    &q,
14336                    &k_view,
14337                    &v_view,
14338                    &mut attn,
14339                    hd,
14340                    nh,
14341                    nkv,
14342                    &kvl.len_d,
14343                    base_len + t,
14344                    t,
14345                    scale,
14346                    kvl.k_tok_bytes,
14347                    kvl.v_tok_bytes,
14348                    0,
14349                    swa && crate::Engine::wkv_on(),
14350                )?;
14351            }
14352            return Ok(e.matmul(&fa.wo, &attn, t)?);
14353        }
14354        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14355        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14356        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14357        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14358        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14359        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14360        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14361        if hd == 256
14362            && swa
14363            && base_len + 1 >= win
14364            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14365        {
14366            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14367            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14368            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14369            e.fa_decode_rows_w(
14370                &q,
14371                &k_view,
14372                &v_view,
14373                &mut attn,
14374                hd,
14375                nh,
14376                nkv,
14377                &kvl.len_d,
14378                0,
14379                t,
14380                scale,
14381                win,
14382                kvl.k_tok_bytes,
14383                kvl.v_tok_bytes,
14384                None,
14385            )?;
14386            return Ok(e.matmul(&fa.wo, &attn, t)?);
14387        }
14388        for i in 0..t {
14389            let avail = base_len + i + 1;
14390            let (off_tok, t_kv) = if swa && avail > win {
14391                (avail - win, win)
14392            } else {
14393                (0, avail)
14394            };
14395            let k_view = e.view_u8_range(
14396                &kvl.k,
14397                off_tok * kvl.k_tok_bytes,
14398                (off_tok + t_kv) * kvl.k_tok_bytes,
14399            );
14400            let v_view = e.view_u8_range(
14401                &kvl.v,
14402                off_tok * kvl.v_tok_bytes,
14403                (off_tok + t_kv) * kvl.v_tok_bytes,
14404            );
14405            let qi = e.view(&q, t * nh * hd);
14406            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14407            let mut q_one = e.uninit(nh * hd)?;
14408            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14409            let mut a_one = e.uninit(nh * hd)?;
14410            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14411            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14412            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14413            if swa
14414                && avail > win
14415                && hd == 256
14416                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14417            {
14418                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14419                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14420                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14421                e.fa_decode_rows_w(
14422                    &q_one,
14423                    &kp,
14424                    &vp,
14425                    &mut a_one,
14426                    hd,
14427                    nh,
14428                    nkv,
14429                    &kvl.len_d,
14430                    0,
14431                    1,
14432                    scale,
14433                    win,
14434                    kvl.k_tok_bytes,
14435                    kvl.v_tok_bytes,
14436                    None,
14437                )?;
14438            } else if !swa
14439                && hd == 512
14440                && avail >= crate::fa512_min_tkv()
14441                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14442            {
14443                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14444                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14445                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14446                e.fa_decode_rows(
14447                    &q_one,
14448                    &kp,
14449                    &vp,
14450                    &mut a_one,
14451                    hd,
14452                    nh,
14453                    nkv,
14454                    avail - 1,
14455                    1,
14456                    scale,
14457                    kvl.k_tok_bytes,
14458                    kvl.v_tok_bytes,
14459                    Some((&kvl.len_d, 0)),
14460                    false,
14461                    false,
14462                    None,
14463                )?;
14464            } else {
14465                e.fa_decode_kvmod(
14466                    &q_one,
14467                    &k_view,
14468                    &v_view,
14469                    &mut a_one,
14470                    hd,
14471                    nh,
14472                    nkv,
14473                    t_kv,
14474                    scale,
14475                    kvl.k_tok_bytes,
14476                    kvl.v_tok_bytes,
14477                    swa && crate::Engine::wkv_on(),
14478                )?;
14479            }
14480            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14481        }
14482        Ok(e.matmul(&fa.wo, &attn, t)?)
14483    }
14484
14485    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
14486    /// h_seed = pre-output_norm hidden). Advances cache.pos.
14487    pub(crate) fn gemma4_decode_step_h(
14488        &self,
14489        e: &Engine,
14490        token: u32,
14491        cache: &mut Cache,
14492    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14493        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
14494        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
14495        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
14496        // unsplit rather than guessing a fence.
14497        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
14498            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
14499        }
14500        if crate::pp::pp_cuts(self.layers.len()).is_some() {
14501            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
14502        }
14503        let n_embd = self.cfg.n_embd as usize;
14504        let eps = self.cfg.rms_eps;
14505        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14506        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14507        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14508        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
14509        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
14510        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14511        let n_layers = self.layers.len();
14512        for (il, layer) in self.layers.iter().enumerate() {
14513            let (hq, hdq) = match h_carry.take() {
14514                Some(p) => p,
14515                None => {
14516                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
14517                }
14518            };
14519            let Mixer::Full(fa) = &layer.mixer else {
14520                panic!("gemma4 layer {il} not full-attn")
14521            };
14522            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
14523            let next_norm = if il + 1 < n_layers {
14524                Some(self.layers[il + 1].attn_norm.float_data())
14525            } else {
14526                None
14527            };
14528            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14529            x = xn;
14530            h_carry = hn;
14531        }
14532        let mut hn = e.uninit(n_embd)?;
14533        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14534        let h_seed = e.clone_dtod(&x)?;
14535        let mut ld = e.matmul(&self.output, &hn, 1)?;
14536        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14537        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
14538        self.gemma4_suppress(e, &mut ld, 1)?;
14539        let logits = e.dtoh(&ld)?;
14540        cache.pos += 1;
14541        Ok((logits, h_seed))
14542    }
14543
14544    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
14545    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
14546    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
14547    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
14548    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
14549    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
14550    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
14551    fn gemma4_decode_layers(
14552        &self,
14553        e: &Engine,
14554        mut x: CudaSlice<f32>,
14555        lo: usize,
14556        hi: usize,
14557        pos_d: &CudaSlice<i32>,
14558        cache: &mut Cache,
14559    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14560        let n_embd = self.cfg.n_embd as usize;
14561        let eps = self.cfg.rms_eps;
14562        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14563        for il in lo..hi {
14564            let layer = &self.layers[il];
14565            let (hq, hdq) = match h_carry.take() {
14566                Some(p) => p,
14567                // range head: il == lo — norm against THIS layer's attn_norm.
14568                None => {
14569                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
14570                }
14571            };
14572            let Mixer::Full(fa) = &layer.mixer else {
14573                panic!("gemma4 layer {il} not full-attn")
14574            };
14575            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
14576            let next_norm = if il + 1 < hi {
14577                Some(self.layers[il + 1].attn_norm.float_data())
14578            } else {
14579                None
14580            };
14581            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14582            x = xn;
14583            h_carry = hn;
14584        }
14585        Ok(x)
14586    }
14587
14588    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
14589    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
14590    /// boundary handoff — same choreography as the generic arm (decode.rs), same
14591    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
14592    /// stage 1 = layers [split, n) + output_norm + softcapped head.
14593    /// Each stage uploads its own copy of the step's position scalar on its own stream.
14594    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
14595    fn gemma4_decode_step_h_pp2(
14596        &self,
14597        e: &Engine,
14598        token: u32,
14599        cache: &mut Cache,
14600        split: usize,
14601    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14602        if crate::pp::pp2_streams_off() {
14603            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
14604        }
14605        let rt = crate::pp::Pp2Rt::get(e)?;
14606        let e0 = rt.engine(0, e);
14607        let e1 = rt.engine(1, e);
14608        let n_embd = self.cfg.n_embd as usize;
14609        let eps = self.cfg.rms_eps;
14610        let pos = cache.pos as i32;
14611
14612        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
14613        let slot = {
14614            let _st0 = rt.enter(0);
14615            let pos_d = e0.htod_i32(&[pos])?;
14616            #[cfg(debug_assertions)]
14617            crate::debug_assert_tensor_stream_device(
14618                &pos_d,
14619                &e0.stream(),
14620                "gemma4_decode_step_h_pp2.stage0.pos_d",
14621            );
14622            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
14623            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14624            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
14625            rt.tx(0, &x, n_embd)?
14626        };
14627
14628        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
14629        let _st1 = rt.enter(1);
14630        let pos_d = e1.htod_i32(&[pos])?;
14631        #[cfg(debug_assertions)]
14632        crate::debug_assert_tensor_stream_device(
14633            &pos_d,
14634            &e1.stream(),
14635            "gemma4_decode_step_h_pp2.stage1.pos_d",
14636        );
14637        let x = rt.rx(0, slot, n_embd)?;
14638        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
14639
14640        let mut hn = e1.uninit(n_embd)?;
14641        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14642        let h_seed = e1.clone_dtod(&x)?;
14643        let mut ld = e1.matmul(&self.output, &hn, 1)?;
14644        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14645        e1.softcap(&mut ld, cap, self.output.out_features())?;
14646        self.gemma4_suppress(e1, &mut ld, 1)?;
14647        let logits = e1.dtoh(&ld)?;
14648        cache.pos += 1;
14649        Ok((logits, h_seed))
14650    }
14651
14652    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
14653    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
14654    fn gemma4_decode_step_h_pp2_samestream(
14655        &self,
14656        e: &Engine,
14657        token: u32,
14658        cache: &mut Cache,
14659        split: usize,
14660    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14661        let n_embd = self.cfg.n_embd as usize;
14662        let eps = self.cfg.rms_eps;
14663        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14664
14665        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
14666        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14667        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14668        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
14669
14670        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
14671        let boundary_tx = e.clone_dtod(&x)?;
14672        let boundary_rx = e.clone_dtod(&boundary_tx)?;
14673
14674        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
14675        let x =
14676            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
14677
14678        let mut hn = e.uninit(n_embd)?;
14679        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14680        let h_seed = e.clone_dtod(&x)?;
14681        let mut ld = e.matmul(&self.output, &hn, 1)?;
14682        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14683        e.softcap(&mut ld, cap, self.output.out_features())?;
14684        self.gemma4_suppress(e, &mut ld, 1)?;
14685        let logits = e.dtoh(&ld)?;
14686        cache.pos += 1;
14687        Ok((logits, h_seed))
14688    }
14689}
14690
14691// ============================ step35 (Step-3.7-Flash) ==================================
14692// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
14693// FAMILY and not a few branches inside the generic `full_attn*` chain:
14694//
14695//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
14696//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
14697//      shapes and the FA head counts would be wrong on 33 of 45 layers.
14698//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
14699//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
14700//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
14701//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
14702//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
14703//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
14704//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
14705//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
14706//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
14707//
14708// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
14709impl HybridModel {
14710    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
14711    /// synthesize a drafter or trunk layer from a neighboring class.
14712    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
14713        let geometry = self
14714            .cfg
14715            .layer_geometry(il as u32)
14716            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
14717        debug_assert_eq!(
14718            geometry.attention_gate,
14719            memra_gguf::config::AttentionGateKind::SeparateHead
14720        );
14721        geometry
14722    }
14723
14724    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
14725    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
14726    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
14727    ///
14728    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
14729    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
14730    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
14731    /// `cache`:
14732    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
14733    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
14734    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
14735    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
14736    ///     contract, lane/chunkinv-flip).
14737    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
14738    ///     q/k/v, no cache side effect.
14739    ///
14740    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
14741    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
14742    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
14743    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
14744    /// still contains must be masked per query. memra's window convention
14745    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
14746    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
14747    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
14748    ///
14749    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
14750    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
14751    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
14752    ///
14753    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
14754    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
14755    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
14756    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
14757    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
14758    /// hidden rows, and the generated text — a function of the chunk size:
14759    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
14760    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
14761    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
14762    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
14763    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
14764    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
14765    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
14766    ///   one-token change in a documented machine-config knob changed the answer.
14767    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
14768    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
14769    /// the same rows moves the logits by ~1.8.
14770    ///
14771    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
14772    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
14773    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
14774    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
14775    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
14776    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
14777    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
14778    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
14779    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
14780    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
14781    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
14782    /// those with t_kv <= win = 512.
14783    #[allow(clippy::too_many_arguments)]
14784    fn step35_attn_pre_wo(
14785        &self,
14786        e: &Engine,
14787        fa: &FullAttnLayer,
14788        mut g3: Vec<CudaSlice<f32>>,
14789        hg: Option<&CudaSlice<f32>>,
14790        gt_pre: Option<&CudaSlice<f32>>,
14791        pos_d: &CudaSlice<i32>,
14792        t: usize,
14793        cache: Option<&mut Cache>,
14794        il: usize,
14795        seq_end: usize,
14796    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14797        let geometry = self.step35_geom(il);
14798        let hd = geometry.head_dim_k as usize;
14799        let nkv = geometry.n_head_kv as usize;
14800        let nh = geometry.n_head as usize;
14801        let rbase = geometry.rope_base;
14802        let scale = geometry.attention_scale();
14803        let swa = geometry.window.is_some();
14804        let eps = self.cfg.rms_eps;
14805        let win = geometry.window.unwrap_or(0) as usize;
14806        let n_rot = geometry.n_rot as usize;
14807
14808        let v = g3.pop().unwrap();
14809        let k0 = g3.pop().unwrap();
14810        let q0 = g3.pop().unwrap();
14811
14812        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
14813        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
14814        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
14815        let mut q = e.uninit(t * nh * hd)?;
14816        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
14817        let mut k = e.uninit(t * nkv * hd)?;
14818        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
14819        let ff = if geometry.rope_factors {
14820            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
14821        } else {
14822            None
14823        };
14824        #[cfg(debug_assertions)]
14825        if let Some(ff) = ff {
14826            crate::debug_assert_tensor_stream_device(
14827                ff,
14828                &e.stream(),
14829                "step35_attn_pre_wo.rope_freqs",
14830            );
14831        }
14832        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
14833
14834        let mut attn = e.uninit(t * nh * hd)?;
14835        match cache {
14836            Some(cache) => {
14837                let base_len = cache.kv[il].as_ref().unwrap().len;
14838                // Read per layer call, never in a measured default.
14839                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
14840                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
14841                let off = if swa {
14842                    let raw = base_len.saturating_sub(win - 1);
14843                    if legacy_tkv || legacy_calllocal {
14844                        raw
14845                    } else {
14846                        raw & !31usize
14847                    }
14848                } else {
14849                    0
14850                };
14851                {
14852                    let kvl = cache.kv[il].as_mut().unwrap();
14853                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
14854                    let write_row = e.prepare_kv_append(kvl, off, t)?;
14855                    e.append_kv_quantized_rows(
14856                        &k,
14857                        &v,
14858                        &mut kvl.k,
14859                        &mut kvl.v,
14860                        write_row,
14861                        t,
14862                        kvl.kv_dim_k,
14863                        kvl.kv_dim_v,
14864                        kvl.k_tok_bytes,
14865                        kvl.v_tok_bytes,
14866                        crate::Engine::kv_fp8_on(),
14867                    )?;
14868                    kvl.len += t;
14869                    let new_len = kvl.len as i32;
14870                    e.set_i32_one(&mut kvl.len_d, new_len)?;
14871                }
14872                let kvl = cache.kv[il].as_ref().unwrap();
14873                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
14874                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
14875                // unaligned view offset here. Both halves are load-bearing for the canaries:
14876                // on the FA default the predicate arms agree bitwise wherever they can differ
14877                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
14878                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
14879                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
14880                // on the current FA path: its tile grid starts at the chunk/call boundary.
14881                // SWA: trim the view to the oldest key any query in this chunk can reach —
14882                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
14883                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
14884                // kernel's online-softmax recurrence groups keys into BK tiles relative to
14885                // the VIEW START — so an unaligned off regroups the same absolute keys into
14886                // different tiles at different chunk sizes = different (m,l) rounding =
14887                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
14888                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
14889                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
14890                // size; the <=31 extra leading keys are older than EVERY query's window
14891                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
14892                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
14893                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
14894                // the floor arm's bits do not move either (gated: G2f, battery 2).
14895                let t_kv = base_len + t - off;
14896                let physical = kvl.physical_rows(off, off + t_kv)?;
14897                let k_view = e.view_u8_range(
14898                    &kvl.k,
14899                    physical.start * kvl.k_tok_bytes,
14900                    physical.end * kvl.k_tok_bytes,
14901                );
14902                let v_view = e.view_u8_range(
14903                    &kvl.v,
14904                    physical.start * kvl.v_tok_bytes,
14905                    physical.end * kvl.v_tok_bytes,
14906                );
14907                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
14908                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
14909                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
14910                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
14911                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
14912                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
14913                // construction, so the invariance assertion MUST break under it (the seam whose
14914                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
14915                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
14916                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
14917                // cached (probes flip it in-process). Never on in a measured default run.
14918                let swa_naive = if legacy_tkv {
14919                    t_kv > win
14920                } else {
14921                    seq_end > win
14922                };
14923                if swa && swa_naive {
14924                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
14925                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
14926                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
14927                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
14928                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
14929                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
14930                    // identically to the unwindowed one modulo the mask, which is the point.
14931                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
14932                    // selected on `seq_end` like every arm here, so the class is uniform for
14933                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
14934                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
14935                    // the f32 floor (the previous numeric config, kept as the A/B seam).
14936                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
14937                        e.sdpa_naive_w_quantized_view(
14938                            &q,
14939                            &k_view,
14940                            &v_view,
14941                            &mut attn,
14942                            hd,
14943                            nh,
14944                            nkv,
14945                            t,
14946                            t_kv,
14947                            scale,
14948                            true,
14949                            win,
14950                            kvl.k_tok_bytes,
14951                            kvl.v_tok_bytes,
14952                        )?;
14953                    } else {
14954                        e.fa_prefill_view_ws_w_hd128(
14955                            &q,
14956                            &k_view,
14957                            &v_view,
14958                            &mut attn,
14959                            hd,
14960                            nh,
14961                            nkv,
14962                            t,
14963                            t_kv,
14964                            scale,
14965                            true,
14966                            win,
14967                            kvl.k_tok_bytes,
14968                            kvl.v_tok_bytes,
14969                        )?;
14970                    }
14971                } else if std::env::var("MEMRA_NOFA").is_ok() {
14972                    e.sdpa_naive_quantized_view(
14973                        &q,
14974                        &k_view,
14975                        &v_view,
14976                        &mut attn,
14977                        hd,
14978                        nh,
14979                        nkv,
14980                        t,
14981                        t_kv,
14982                        scale,
14983                        true,
14984                        kvl.k_tok_bytes,
14985                        kvl.v_tok_bytes,
14986                    )?;
14987                } else {
14988                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
14989                    // reach past the window, so the window mask is a no-op under causal and every
14990                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
14991                    // request either way, which is what makes the chunk size arithmetic-free.
14992                    e.fa_prefill_view_ws(
14993                        &q,
14994                        &k_view,
14995                        &v_view,
14996                        &mut attn,
14997                        hd,
14998                        nh,
14999                        nkv,
15000                        t,
15001                        t_kv,
15002                        scale,
15003                        true,
15004                        kvl.k_tok_bytes,
15005                        kvl.v_tok_bytes,
15006                        crate::Engine::kv_fp8_on(),
15007                    )?;
15008                }
15009            }
15010            None => {
15011                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15012                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15013                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15014                // seq_end here too or it re-opens the same door.
15015                debug_assert_eq!(
15016                    seq_end, t,
15017                    "step35 cacheless prefill is monolithic (seq_end == t)"
15018                );
15019                if swa && seq_end > win {
15020                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15021                } else if std::env::var("MEMRA_NOFA").is_ok() {
15022                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15023                } else {
15024                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15025                }
15026            }
15027        }
15028
15029        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15030        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15031        let gw = fa
15032            .attn_gate
15033            .as_ref()
15034            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15035        let gt_owned = if gt_pre.is_none() {
15036            Some(e.matmul(
15037                gw,
15038                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15039                t,
15040            )?)
15041        } else {
15042            None
15043        };
15044        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15045        let mut ag = e.uninit(t * nh * hd)?;
15046        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15047        Ok(ag)
15048    }
15049
15050    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15051    /// `forward_last`, t2probe). Post-`wo`.
15052    pub(crate) fn step35_attn(
15053        &self,
15054        e: &Engine,
15055        fa: &FullAttnLayer,
15056        h: &CudaSlice<f32>,
15057        pos_d: &CudaSlice<i32>,
15058        t: usize,
15059        il: usize,
15060    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15061        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15062            Some(g3) => g3,
15063            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15064        };
15065        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15066        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15067        self.step35_o(e, fa, &ag, t)
15068    }
15069
15070    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15071    /// resident quantized cache, attend through the cache view). Post-`wo`.
15072    ///
15073    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15074    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15075    /// own extent.
15076    #[allow(clippy::too_many_arguments)]
15077    pub(crate) fn step35_attn_prime(
15078        &self,
15079        e: &Engine,
15080        fa: &FullAttnLayer,
15081        h: &CudaSlice<f32>,
15082        hx: Option<&CudaSlice<u8>>,
15083        pos_d: &CudaSlice<i32>,
15084        t: usize,
15085        cache: &mut Cache,
15086        il: usize,
15087        seq_end: usize,
15088    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15089        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15090            if hx.is_some() {
15091                return Err(
15092                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15093                     pre-quantized prime path"
15094                        .into(),
15095                );
15096            }
15097            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15098        }
15099        let g3 = if fa.step_tp_qkv.is_some() {
15100            if hx.is_some() {
15101                return Err(
15102                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15103                     pre-quantized prime path"
15104                        .into(),
15105                );
15106            }
15107            self.step35_tp_qkv(e, fa, h, t)?
15108                .expect("Step Q/K/V TP disappeared after the presence check")
15109        } else {
15110            match hx {
15111                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15112                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15113            }
15114        };
15115        let ag =
15116            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15117        self.step35_o(e, fa, &ag, t)
15118    }
15119
15120    fn ensure_step_tp_kv_cache(
15121        &self,
15122        e: &Engine,
15123        fa: &FullAttnLayer,
15124        il: usize,
15125        cache: &mut Cache,
15126    ) -> Result<bool, Box<dyn std::error::Error>> {
15127        let tp = fa
15128            .step_tp_qkv
15129            .as_ref()
15130            .ok_or("Step TP cache hydration lost its resident projections")?;
15131        let geometry = self.step35_geom(il);
15132        let window = geometry.window.map(|window| window as usize);
15133        let ranks = tp.runtime.devices().len();
15134        let head_dim = geometry.head_dim_k as usize;
15135        let kv_heads = geometry.n_head_kv as usize;
15136        let max_ctx = cache.max_ctx;
15137
15138        if cache.tp_kv[il].is_some() {
15139            return Ok(false);
15140        }
15141        let local = cache.kv[il]
15142            .as_ref()
15143            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15144        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15145            return Err(format!(
15146                "Step TP layer {il} local KV geometry k={} v={} != {}",
15147                local.kv_dim_k,
15148                local.kv_dim_v,
15149                kv_heads * head_dim
15150            )
15151            .into());
15152        }
15153        let resident_start = window
15154            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15155            .unwrap_or(0);
15156        let resident_rows = local.len - resident_start;
15157        let physical = local.physical_rows(resident_start, local.len)?;
15158        let k_rows = if resident_rows == 0 {
15159            Vec::new()
15160        } else {
15161            e.dtoh_u8_view(&e.view_u8_range(
15162                &local.k,
15163                physical.start * local.k_tok_bytes,
15164                physical.end * local.k_tok_bytes,
15165            ))?
15166        };
15167        let v_rows = if resident_rows == 0 {
15168            Vec::new()
15169        } else {
15170            e.dtoh_u8_view(&e.view_u8_range(
15171                &local.v,
15172                physical.start * local.v_tok_bytes,
15173                physical.end * local.v_tok_bytes,
15174            ))?
15175        };
15176        let mut distributed = match window {
15177            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15178                kv_heads * head_dim,
15179                kv_heads * head_dim,
15180                max_ctx,
15181                window,
15182            )?,
15183            None => tp.runtime.allocate_tp_kv_cache(
15184                kv_heads * head_dim,
15185                kv_heads * head_dim,
15186                max_ctx,
15187            )?,
15188        };
15189        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15190            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15191        {
15192            return Err(format!(
15193                "Step TP layer {il} distributed/local KV token bytes disagree: \
15194                 k={}x{ranks}/{} v={}x{ranks}/{}",
15195                distributed.k_tok_bytes(),
15196                local.k_tok_bytes,
15197                distributed.v_tok_bytes(),
15198                local.v_tok_bytes,
15199            )
15200            .into());
15201        }
15202        tp.runtime.hydrate_tp_kv_cache_from(
15203            &mut distributed,
15204            local.len,
15205            resident_start,
15206            &k_rows,
15207            &v_rows,
15208        )?;
15209        cache.tp_kv[il] = Some(distributed);
15210        Ok(true)
15211    }
15212
15213    #[allow(clippy::too_many_arguments)]
15214    fn step35_tp_prefill_attn_resident(
15215        &self,
15216        e: &Engine,
15217        fa: &FullAttnLayer,
15218        il: usize,
15219        h: &CudaSlice<f32>,
15220        pos_d: &CudaSlice<i32>,
15221        tokens: usize,
15222        cache: &mut Cache,
15223        seq_end: usize,
15224    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15225        let tp = fa
15226            .step_tp_qkv
15227            .as_ref()
15228            .ok_or("Step TP prefill lost its resident projections")?;
15229        let attention = tp
15230            .attention
15231            .as_ref()
15232            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15233        let ranks = tp.runtime.devices().len();
15234        if !step_tp_prefill_shape(
15235            true,
15236            tokens,
15237            ranks,
15238            tp.runtime.native_p2p(),
15239            true,
15240            crate::Engine::kv_fp8_on(),
15241        ) {
15242            return Err(format!(
15243                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP4 native P2P, \
15244                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15245                 native_p2p={} fp8_kv={}",
15246                tp.runtime.native_p2p(),
15247                crate::Engine::kv_fp8_on(),
15248            )
15249            .into());
15250        }
15251        for seam in [
15252            "MEMRA_STEP35_SWA_TKV",
15253            "MEMRA_PRIME_CALLLOCAL",
15254            "MEMRA_PRIME_F32CHUNK0",
15255        ] {
15256            if std::env::var(seam).as_deref() == Ok("1") {
15257                return Err(format!(
15258                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15259                )
15260                .into());
15261            }
15262        }
15263
15264        let geometry = self.step35_geom(il);
15265        let window = geometry.window.map(|window| window as usize);
15266        let head_dim = geometry.head_dim_k as usize;
15267        let heads = geometry.n_head as usize;
15268        let kv_heads = geometry.n_head_kv as usize;
15269        if heads % ranks != 0 || kv_heads % ranks != 0 {
15270            return Err(format!(
15271                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15272            )
15273            .into());
15274        }
15275        let local_heads = heads / ranks;
15276        let local_kv_heads = kv_heads / ranks;
15277        let local_kv_dim = local_kv_heads * head_dim;
15278        let hidden = self.cfg.n_embd as usize;
15279        let expected_input = tokens
15280            .checked_mul(hidden)
15281            .ok_or("Step TP prefill input size overflow")?;
15282        if h.len() < expected_input {
15283            return Err(format!(
15284                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15285                h.len()
15286            )
15287            .into());
15288        }
15289        let positions = e.dtoh_i32(pos_d)?;
15290        if positions.len() != tokens {
15291            return Err(format!(
15292                "rank-local Step prefill positions {} != tokens {tokens}",
15293                positions.len()
15294            )
15295            .into());
15296        }
15297
15298        let mut active_input = e.uninit(expected_input)?;
15299        e.copy_view_into(
15300            &mut active_input,
15301            0,
15302            &h.slice(0..expected_input),
15303            expected_input,
15304        )?;
15305        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15306        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15307        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15308        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15309        // layer-count-amplified arm of the boot flake.
15310        e.stream().synchronize()?;
15311        tp.runtime
15312            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15313        let q_raw = tp
15314            .runtime
15315            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15316        let k_raw = tp
15317            .runtime
15318            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15319        let v_raw = tp
15320            .runtime
15321            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15322        let mut q = Vec::with_capacity(ranks);
15323        let mut k = Vec::with_capacity(ranks);
15324        for rank in 0..ranks {
15325            let engine = tp
15326                .runtime
15327                .rank_engine(rank)
15328                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15329            let _main = engine.gpu.enter_main()?;
15330            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15331            engine.rms_norm(
15332                &q_raw[rank],
15333                &attention.q_norm[rank],
15334                &mut q_rank,
15335                head_dim,
15336                tokens * local_heads,
15337                self.cfg.rms_eps,
15338            )?;
15339            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15340            engine.rms_norm(
15341                &k_raw[rank],
15342                &attention.k_norm[rank],
15343                &mut k_rank,
15344                head_dim,
15345                tokens * local_kv_heads,
15346                self.cfg.rms_eps,
15347            )?;
15348            let position = engine.htod_i32(&positions)?;
15349            let rope_freqs = if geometry.rope_factors {
15350                self.step35_aux
15351                    .as_ref()
15352                    .and_then(|aux| aux.rope_freqs(engine))
15353            } else {
15354                None
15355            };
15356            engine.rope_neox2(
15357                &mut q_rank,
15358                &mut k_rank,
15359                &position,
15360                head_dim,
15361                geometry.n_rot as usize,
15362                local_heads,
15363                local_kv_heads,
15364                tokens,
15365                geometry.rope_base,
15366                1.0,
15367                rope_freqs,
15368            )?;
15369            q.push(q_rank);
15370            k.push(k_rank);
15371        }
15372
15373        let gate_weight = fa
15374            .attn_gate
15375            .as_ref()
15376            .ok_or("step35 layer is missing attn_gate.weight")?;
15377        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15378        if gate.len() != tokens * heads {
15379            return Err(format!(
15380                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15381                gate.len()
15382            )
15383            .into());
15384        }
15385
15386        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15387        let base_len = cache.kv[il]
15388            .as_ref()
15389            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15390            .len;
15391        let distributed = cache.tp_kv[il]
15392            .as_ref()
15393            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15394        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15395            return Err(format!(
15396                "Step TP layer {il} cache lengths diverged before prefill: \
15397                 local={base_len} distributed={}/{}",
15398                distributed.committed_len(),
15399                distributed.staged_len()
15400            )
15401            .into());
15402        }
15403        let target_len = base_len
15404            .checked_add(tokens)
15405            .ok_or("Step TP prefill cache length overflow")?;
15406        if target_len > cache.max_ctx {
15407            return Err(format!(
15408                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15409                cache.max_ctx
15410            )
15411            .into());
15412        }
15413        if seq_end < target_len {
15414            return Err(format!(
15415                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15416            )
15417            .into());
15418        }
15419
15420        let transaction = cache.tp_kv[il]
15421            .as_mut()
15422            .expect("distributed cache checked above")
15423            .begin_transaction()?;
15424        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15425            cache.tp_kv[il]
15426                .as_mut()
15427                .expect("distributed cache checked above"),
15428            transaction,
15429            &k,
15430            &v_raw,
15431            tokens,
15432        ) {
15433            let _ = tp.runtime.rollback_tp_kv_transaction(
15434                cache.tp_kv[il]
15435                    .as_mut()
15436                    .expect("distributed cache checked above"),
15437                transaction,
15438            );
15439            return Err(error);
15440        }
15441
15442        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15443            let distributed = cache.tp_kv[il]
15444                .as_ref()
15445                .expect("distributed cache checked above");
15446            let staged_len = distributed.staged_len();
15447            let view_start = window
15448                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15449                .unwrap_or(0);
15450            let physical = distributed.physical_range(view_start, staged_len)?;
15451            let t_kv = staged_len - view_start;
15452            let swa_naive = window.is_some_and(|window| seq_end > window);
15453            let mut gated = Vec::with_capacity(ranks);
15454            for rank in 0..ranks {
15455                let engine = tp
15456                    .runtime
15457                    .rank_engine(rank)
15458                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15459                let _main = engine.gpu.enter_main()?;
15460                let rank_cache = distributed
15461                    .rank(rank)
15462                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15463                let k_view = engine.view_u8_range(
15464                    rank_cache.k(),
15465                    physical.start * distributed.k_tok_bytes(),
15466                    physical.end * distributed.k_tok_bytes(),
15467                );
15468                let v_view = engine.view_u8_range(
15469                    rank_cache.v(),
15470                    physical.start * distributed.v_tok_bytes(),
15471                    physical.end * distributed.v_tok_bytes(),
15472                );
15473                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
15474                if swa_naive {
15475                    let window = window.expect("SWA predicate requires a window");
15476                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15477                        engine.sdpa_naive_w_quantized_view(
15478                            &q[rank],
15479                            &k_view,
15480                            &v_view,
15481                            &mut attention_out,
15482                            head_dim,
15483                            local_heads,
15484                            local_kv_heads,
15485                            tokens,
15486                            t_kv,
15487                            geometry.attention_scale(),
15488                            true,
15489                            window,
15490                            distributed.k_tok_bytes(),
15491                            distributed.v_tok_bytes(),
15492                        )?;
15493                    } else {
15494                        engine.fa_prefill_view_ws_w_hd128(
15495                            &q[rank],
15496                            &k_view,
15497                            &v_view,
15498                            &mut attention_out,
15499                            head_dim,
15500                            local_heads,
15501                            local_kv_heads,
15502                            tokens,
15503                            t_kv,
15504                            geometry.attention_scale(),
15505                            true,
15506                            window,
15507                            distributed.k_tok_bytes(),
15508                            distributed.v_tok_bytes(),
15509                        )?;
15510                    }
15511                } else if std::env::var("MEMRA_NOFA").is_ok() {
15512                    engine.sdpa_naive_quantized_view(
15513                        &q[rank],
15514                        &k_view,
15515                        &v_view,
15516                        &mut attention_out,
15517                        head_dim,
15518                        local_heads,
15519                        local_kv_heads,
15520                        tokens,
15521                        t_kv,
15522                        geometry.attention_scale(),
15523                        true,
15524                        distributed.k_tok_bytes(),
15525                        distributed.v_tok_bytes(),
15526                    )?;
15527                } else {
15528                    engine.fa_prefill_view_ws(
15529                        &q[rank],
15530                        &k_view,
15531                        &v_view,
15532                        &mut attention_out,
15533                        head_dim,
15534                        local_heads,
15535                        local_kv_heads,
15536                        tokens,
15537                        t_kv,
15538                        geometry.attention_scale(),
15539                        true,
15540                        distributed.k_tok_bytes(),
15541                        distributed.v_tok_bytes(),
15542                        false,
15543                    )?;
15544                }
15545
15546                let gate_start = rank * local_heads;
15547                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
15548                for token in 0..tokens {
15549                    let start = token * heads + gate_start;
15550                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
15551                }
15552                let gate_rank = engine.htod(&gate_rank)?;
15553                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
15554                engine.attn_head_gate(
15555                    &attention_out,
15556                    &gate_rank,
15557                    &mut gated_rank,
15558                    None,
15559                    head_dim,
15560                    local_heads,
15561                    tokens,
15562                )?;
15563                gated.push(gated_rank);
15564            }
15565            for rank in 1..ranks {
15566                let engine = tp
15567                    .runtime
15568                    .rank_engine(rank)
15569                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15570                let _main = engine.gpu.enter_main()?;
15571                engine.stream().synchronize()?;
15572            }
15573
15574            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
15575                let output = tp
15576                    .runtime
15577                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
15578                let k_shadow =
15579                    tp.runtime
15580                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
15581                let v_shadow =
15582                    tp.runtime
15583                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
15584                let root = tp
15585                    .runtime
15586                    .rank_engine(0)
15587                    .ok_or("Step TP prefill lost its root engine")?;
15588                let _main = root.gpu.enter_main()?;
15589                root.stream().synchronize()?;
15590                (output, k_shadow, v_shadow)
15591            } else {
15592                let attention = tp.runtime.gather_native_column_shards(
15593                    &gated,
15594                    tokens,
15595                    local_heads * head_dim,
15596                )?;
15597                let output = tp
15598                    .runtime
15599                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
15600                let k_shadow = tp
15601                    .runtime
15602                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
15603                let v_shadow =
15604                    tp.runtime
15605                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
15606                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
15607            };
15608            let local = cache.kv[il]
15609                .as_mut()
15610                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
15611            if local.len != base_len {
15612                return Err(format!(
15613                    "Step TP layer {il} local cache changed during prefill: \
15614                     len={} base={base_len}",
15615                    local.len
15616                )
15617                .into());
15618            }
15619            let retain_from = window
15620                .map(|window| {
15621                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
15622                    let rollback_retain =
15623                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
15624                    staged_retain.min(rollback_retain)
15625                })
15626                .unwrap_or(0);
15627            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
15628            e.append_kv_quantized_rows(
15629                &k_shadow,
15630                &v_shadow,
15631                &mut local.k,
15632                &mut local.v,
15633                write_row,
15634                tokens,
15635                local.kv_dim_k,
15636                local.kv_dim_v,
15637                local.k_tok_bytes,
15638                local.v_tok_bytes,
15639                false,
15640            )?;
15641            local.len = staged_len;
15642            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
15643            Ok(output)
15644        })();
15645
15646        let output = match staged {
15647            Ok(output) => output,
15648            Err(error) => {
15649                let _ = tp.runtime.rollback_tp_kv_transaction(
15650                    cache.tp_kv[il]
15651                        .as_mut()
15652                        .expect("distributed cache checked above"),
15653                    transaction,
15654                );
15655                if let Some(local) = cache.kv[il].as_mut() {
15656                    local.len = base_len;
15657                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
15658                }
15659                return Err(error);
15660            }
15661        };
15662        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
15663            cache.tp_kv[il]
15664                .as_mut()
15665                .expect("distributed cache checked above"),
15666            transaction,
15667            tokens,
15668        ) {
15669            let _ = tp.runtime.rollback_tp_kv_transaction(
15670                cache.tp_kv[il]
15671                    .as_mut()
15672                    .expect("distributed cache checked above"),
15673                transaction,
15674            );
15675            let local = cache.kv[il].as_mut().expect("local cache checked above");
15676            local.len = base_len;
15677            e.set_i32_one(&mut local.len_d, base_len as i32)?;
15678            return Err(error);
15679        }
15680
15681        let committed = cache.tp_kv[il]
15682            .as_ref()
15683            .expect("distributed cache checked above")
15684            .committed_len();
15685        let local_len = cache.kv[il]
15686            .as_ref()
15687            .expect("local cache checked above")
15688            .len;
15689        if committed != local_len {
15690            return Err(format!(
15691                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
15692            )
15693            .into());
15694        }
15695        eprintln!(
15696            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
15697             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
15698             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
15699             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
15700             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
15701             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
15702             output={} performance_claim=false",
15703            tp.layer,
15704            tp.devices,
15705            hydrated,
15706            if window.is_some() {
15707                "rank-local-swa-ring"
15708            } else {
15709                "rank-local-global"
15710            },
15711            tp.runtime.transport_label(),
15712            tp.runtime.bulk_p2p(),
15713            if tp.runtime.bulk_p2p() {
15714                "root-device"
15715            } else {
15716                "root-readback"
15717            },
15718        );
15719        Ok(output)
15720    }
15721
15722    fn step35_tp_decode_attn_resident(
15723        &self,
15724        e: &Engine,
15725        fa: &FullAttnLayer,
15726        il: usize,
15727        h: &CudaSlice<f32>,
15728        pos_d: &CudaSlice<i32>,
15729        cache: &mut Cache,
15730    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15731        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
15732        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
15733        // nvfp4-dev-routes counter.
15734        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15735        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15736        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15737        let started = timing.then(std::time::Instant::now);
15738        let result = if crate::tp::step_tp_decode_v2_enabled()? {
15739            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
15740        } else {
15741            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
15742        };
15743        if let Some(started) = started {
15744            use std::sync::atomic::Ordering;
15745            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
15746                + started.elapsed().as_nanos() as u64;
15747            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
15748            if calls % 430 == 0 {
15749                eprintln!(
15750                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
15751                    ns as f64 / 1.0e6,
15752                    ns as f64 / calls as f64 / 1.0e3,
15753                );
15754            }
15755        }
15756        result
15757    }
15758
15759    #[allow(clippy::too_many_arguments)]
15760    fn step35_tp_decode_attn_resident_inner(
15761        &self,
15762        e: &Engine,
15763        fa: &FullAttnLayer,
15764        il: usize,
15765        h: &CudaSlice<f32>,
15766        pos_d: &CudaSlice<i32>,
15767        cache: &mut Cache,
15768    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15769        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
15770        // drains every stream so queued async work is billed to the phase that queued it — the
15771        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
15772        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
15773        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15774        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15775        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15776        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15777        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15778        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15779        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15780        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15781        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15782        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15783        fn lap(
15784            runtime: &crate::tp::TpE4m3HostBounce,
15785            e: &Engine,
15786            timer: &std::sync::atomic::AtomicU64,
15787            started: &mut Option<std::time::Instant>,
15788        ) -> Result<(), Box<dyn std::error::Error>> {
15789            let Some(start) = started.as_mut() else {
15790                return Ok(());
15791            };
15792            for rank in 0..runtime.devices().len() {
15793                if let Some(engine) = runtime.rank_engine(rank) {
15794                    let _main = engine.gpu.enter_main()?;
15795                    engine.stream().synchronize()?;
15796                }
15797            }
15798            e.stream().synchronize()?;
15799            timer.fetch_add(
15800                start.elapsed().as_nanos() as u64,
15801                std::sync::atomic::Ordering::Relaxed,
15802            );
15803            *start = std::time::Instant::now();
15804            Ok(())
15805        }
15806        let tp = fa
15807            .step_tp_qkv
15808            .as_ref()
15809            .ok_or("Step TP decode lost its resident projections")?;
15810        let attention = tp
15811            .attention
15812            .as_ref()
15813            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
15814        if !tp.runtime.native_p2p() {
15815            return Err("rank-local Step attention requires native P2P".into());
15816        }
15817        if crate::Engine::kv_fp8_on() {
15818            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
15819        }
15820
15821        let geometry = self.step35_geom(il);
15822        let window = geometry.window.map(|window| window as usize);
15823        let ranks = tp.runtime.devices().len();
15824        let head_dim = geometry.head_dim_k as usize;
15825        let heads = geometry.n_head as usize;
15826        let kv_heads = geometry.n_head_kv as usize;
15827        if heads % ranks != 0 || kv_heads % ranks != 0 {
15828            return Err(format!(
15829                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15830            )
15831            .into());
15832        }
15833        let local_heads = heads / ranks;
15834        let local_kv_heads = kv_heads / ranks;
15835        let local_kv_dim = local_kv_heads * head_dim;
15836        let max_ctx = cache.max_ctx;
15837
15838        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15839
15840        let base_len = cache.kv[il]
15841            .as_ref()
15842            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15843            .len;
15844        let distributed = cache.tp_kv[il]
15845            .as_ref()
15846            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15847        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15848            return Err(format!(
15849                "Step TP layer {il} cache lengths diverged before decode: \
15850                 local={base_len} distributed={}/{}",
15851                distributed.committed_len(),
15852                distributed.staged_len()
15853            )
15854            .into());
15855        }
15856
15857        let mut lap_start = timing.then(std::time::Instant::now);
15858        let positions = e.dtoh_i32(pos_d)?;
15859        if positions.len() != 1 {
15860            return Err(format!(
15861                "rank-local Step decode requires one position, got {}",
15862                positions.len()
15863            )
15864            .into());
15865        }
15866        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
15867        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
15868            attention.decode_input.as_ref()
15869        {
15870            let mut decode_input = decode_input
15871                .lock()
15872                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
15873            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
15874            // engine's stream; the refresh reads it from the runtime root engine's stream. This
15875            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
15876            e.stream().synchronize()?;
15877            tp.runtime
15878                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
15879            let q_raw = tp
15880                .runtime
15881                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
15882            let k_raw = tp
15883                .runtime
15884                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
15885            let v_raw = tp
15886                .runtime
15887                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
15888            (q_raw, k_raw, v_raw, "root-device-replicated")
15889        } else {
15890            let activation = e.dtoh(h)?;
15891            let q_raw =
15892                tp.runtime
15893                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
15894            let k_raw =
15895                tp.runtime
15896                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
15897            let v_raw =
15898                tp.runtime
15899                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
15900            (q_raw, k_raw, v_raw, "host-replicated")
15901        };
15902        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
15903        let mut q = Vec::with_capacity(ranks);
15904        let mut k = Vec::with_capacity(ranks);
15905        for rank in 0..ranks {
15906            let engine = tp
15907                .runtime
15908                .rank_engine(rank)
15909                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15910            let _main = engine.gpu.enter_main()?;
15911            let mut q_rank = engine.uninit(local_heads * head_dim)?;
15912            engine.rms_norm(
15913                &q_raw[rank],
15914                &attention.q_norm[rank],
15915                &mut q_rank,
15916                head_dim,
15917                local_heads,
15918                self.cfg.rms_eps,
15919            )?;
15920            let mut k_rank = engine.uninit(local_kv_dim)?;
15921            engine.rms_norm(
15922                &k_raw[rank],
15923                &attention.k_norm[rank],
15924                &mut k_rank,
15925                head_dim,
15926                local_kv_heads,
15927                self.cfg.rms_eps,
15928            )?;
15929            let position = engine.htod_i32(&positions)?;
15930            let rope_freqs = if geometry.rope_factors {
15931                self.step35_aux
15932                    .as_ref()
15933                    .and_then(|aux| aux.rope_freqs(engine))
15934            } else {
15935                None
15936            };
15937            engine.rope_neox2(
15938                &mut q_rank,
15939                &mut k_rank,
15940                &position,
15941                head_dim,
15942                geometry.n_rot as usize,
15943                local_heads,
15944                local_kv_heads,
15945                1,
15946                geometry.rope_base,
15947                1.0,
15948                rope_freqs,
15949            )?;
15950            q.push(q_rank);
15951            k.push(k_rank);
15952        }
15953        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
15954
15955        let gate_weight = fa
15956            .attn_gate
15957            .as_ref()
15958            .ok_or("step35 layer is missing attn_gate.weight")?;
15959        let gate = e.matmul(gate_weight, h, 1)?;
15960        let gate = e.dtoh(&gate)?;
15961        if gate.len() != heads {
15962            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
15963        }
15964        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
15965
15966        let transaction = cache.tp_kv[il]
15967            .as_mut()
15968            .expect("distributed cache checked above")
15969            .begin_transaction()?;
15970        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15971            cache.tp_kv[il]
15972                .as_mut()
15973                .expect("distributed cache checked above"),
15974            transaction,
15975            &k,
15976            &v_raw,
15977            1,
15978        ) {
15979            let _ = tp.runtime.rollback_tp_kv_transaction(
15980                cache.tp_kv[il]
15981                    .as_mut()
15982                    .expect("distributed cache checked above"),
15983                transaction,
15984            );
15985            return Err(error);
15986        }
15987        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
15988
15989        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15990            let distributed = cache.tp_kv[il]
15991                .as_ref()
15992                .expect("distributed cache checked above");
15993            let staged_len = distributed.staged_len();
15994            let view_start = window
15995                .map(|window| staged_len.saturating_sub(window))
15996                .unwrap_or(0);
15997            let physical = distributed.physical_range(view_start, staged_len)?;
15998            let t_kv = staged_len - view_start;
15999            let mut gated = Vec::with_capacity(ranks);
16000            for rank in 0..ranks {
16001                let engine = tp
16002                    .runtime
16003                    .rank_engine(rank)
16004                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16005                let _main = engine.gpu.enter_main()?;
16006                let rank_cache = distributed
16007                    .rank(rank)
16008                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16009                let k_view = engine.view_u8_range(
16010                    rank_cache.k(),
16011                    physical.start * distributed.k_tok_bytes(),
16012                    physical.end * distributed.k_tok_bytes(),
16013                );
16014                let v_view = engine.view_u8_range(
16015                    rank_cache.v(),
16016                    physical.start * distributed.v_tok_bytes(),
16017                    physical.end * distributed.v_tok_bytes(),
16018                );
16019                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16020                engine.fa_decode_kvmod(
16021                    &q[rank],
16022                    &k_view,
16023                    &v_view,
16024                    &mut attention_out,
16025                    head_dim,
16026                    local_heads,
16027                    local_kv_heads,
16028                    t_kv,
16029                    geometry.attention_scale(),
16030                    distributed.k_tok_bytes(),
16031                    distributed.v_tok_bytes(),
16032                    false,
16033                )?;
16034                let gate_start = rank * local_heads;
16035                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16036                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16037                engine.attn_head_gate(
16038                    &attention_out,
16039                    &gate_rank,
16040                    &mut gated_rank,
16041                    None,
16042                    head_dim,
16043                    local_heads,
16044                    1,
16045                )?;
16046                gated.push(gated_rank);
16047            }
16048            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16049
16050            let gathered =
16051                tp.runtime
16052                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16053            let output = tp
16054                .runtime
16055                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16056            let output = e.htod(&output)?;
16057            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16058
16059            let k_shadow = tp
16060                .runtime
16061                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16062            let v_shadow = tp
16063                .runtime
16064                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16065            let k_shadow = e.htod(&k_shadow)?;
16066            let v_shadow = e.htod(&v_shadow)?;
16067            let local = cache.kv[il]
16068                .as_mut()
16069                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16070            if local.len != base_len || base_len + 1 > max_ctx {
16071                return Err(format!(
16072                    "Step TP layer {il} local cache changed during decode: \
16073                     len={} base={base_len} max={max_ctx}",
16074                    local.len
16075                )
16076                .into());
16077            }
16078            let retain_from = window
16079                .map(|window| {
16080                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16081                    let rollback_retain =
16082                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16083                    staged_retain.min(rollback_retain)
16084                })
16085                .unwrap_or(0);
16086            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16087            e.append_kv_quantized(
16088                &k_shadow,
16089                &v_shadow,
16090                &mut local.k,
16091                &mut local.v,
16092                write_row,
16093                local.kv_dim_k,
16094                local.kv_dim_v,
16095                local.k_tok_bytes,
16096                local.v_tok_bytes,
16097                false,
16098            )?;
16099            local.len = base_len + 1;
16100            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16101            Ok(output)
16102        })();
16103
16104        let output = match staged {
16105            Ok(output) => output,
16106            Err(error) => {
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                if let Some(local) = cache.kv[il].as_mut() {
16114                    local.len = base_len;
16115                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16116                }
16117                return Err(error);
16118            }
16119        };
16120        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16121            cache.tp_kv[il]
16122                .as_mut()
16123                .expect("distributed cache checked above"),
16124            transaction,
16125            1,
16126        ) {
16127            let _ = tp.runtime.rollback_tp_kv_transaction(
16128                cache.tp_kv[il]
16129                    .as_mut()
16130                    .expect("distributed cache checked above"),
16131                transaction,
16132            );
16133            let local = cache.kv[il].as_mut().expect("local cache checked above");
16134            local.len = base_len;
16135            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16136            return Err(error);
16137        }
16138
16139        let committed = cache.tp_kv[il]
16140            .as_ref()
16141            .expect("distributed cache checked above")
16142            .committed_len();
16143        let local_len = cache.kv[il]
16144            .as_ref()
16145            .expect("local cache checked above")
16146            .len;
16147        if committed != local_len {
16148            return Err(format!(
16149                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16150            )
16151            .into());
16152        }
16153        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16154        if timing {
16155            use std::sync::atomic::Ordering;
16156            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16157            if calls % 430 == 0 {
16158                let avg = |t: &std::sync::atomic::AtomicU64| {
16159                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16160                };
16161                eprintln!(
16162                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16163                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16164                    avg(&T_POS),
16165                    avg(&T_QKV),
16166                    avg(&T_NORMROPE),
16167                    avg(&T_GATE),
16168                    avg(&T_APPEND),
16169                    avg(&T_ATTN),
16170                    avg(&T_OPROJ),
16171                    avg(&T_SHADOW),
16172                );
16173            }
16174        }
16175        eprintln!(
16176            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16177             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16178             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16179             attention_scope={} input_path={} kv_physical_rows={} \
16180             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16181             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16182             bulk_p2p={} output=root-readback performance_claim=false",
16183            tp.layer,
16184            tp.devices,
16185            hydrated,
16186            if window.is_some() {
16187                "rank-local-swa-ring"
16188            } else {
16189                "rank-local-global"
16190            },
16191            input_path,
16192            cache.tp_kv[il]
16193                .as_ref()
16194                .expect("distributed cache checked above")
16195                .physical_capacity(),
16196            tp.runtime.transport_label(),
16197            tp.runtime.bulk_p2p(),
16198        );
16199        Ok(output)
16200    }
16201
16202    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16203    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16204    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16205    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16206    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16207    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16208    #[allow(clippy::too_many_arguments)]
16209    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16210    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16211    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16212    /// the resident fused TP2 class (caller falls back to the per-row walk).
16213    pub(crate) fn step35_verify_qkv_precompute(
16214        &self,
16215        e: &Engine,
16216        il: usize,
16217        h_t: &CudaSlice<f32>,
16218        t: usize,
16219    ) -> Result<bool, Box<dyn std::error::Error>> {
16220        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16221            return Ok(false);
16222        };
16223        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16224            return Ok(false);
16225        };
16226        let Some(attention) = tp.attention.as_ref() else {
16227            return Ok(false);
16228        };
16229        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16230            return Ok(false);
16231        }
16232        let geometry = self.step35_geom(il);
16233        let heads = geometry.n_head as usize;
16234        let ws_index = tp
16235            .runtime
16236            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16237        let gate_shards = attention
16238            .gate_shards_bf16
16239            .as_deref()
16240            .map(crate::tp::StepTpGateShards::Bf16);
16241        tp.runtime.decode_v2_input_qkv_tcol(
16242            ws_index,
16243            e,
16244            h_t,
16245            t,
16246            &tp.q,
16247            &tp.k,
16248            &tp.v,
16249            gate_shards,
16250        )?;
16251        Ok(true)
16252    }
16253
16254    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16255    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16256    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16257    /// flag confirmed the defer engaged for every column.
16258    pub(crate) fn step35_verify_oproj_tcol(
16259        &self,
16260        e: &Engine,
16261        il: usize,
16262        t: usize,
16263    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16264        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16265            return Err("tcol o_proj join expects full attention".into());
16266        };
16267        let tp = fa
16268            .step_tp_qkv
16269            .as_ref()
16270            .ok_or("tcol o_proj join lost its resident projections")?;
16271        let heads = self.step35_geom(il).n_head as usize;
16272        let ws_index = tp
16273            .runtime
16274            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16275        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16276    }
16277
16278    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
16279    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
16280    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
16281    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
16282    /// (caller falls back to the per-column walk).
16283    pub(crate) fn step35_verify_moe_t2(
16284        &self,
16285        e: &Engine,
16286        il: usize,
16287        z2: &CudaSlice<f32>,
16288    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16289        let layer = &self.layers[il];
16290        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
16291            return Ok(None);
16292        };
16293        let Some(tp) = m.step_tp.as_ref() else {
16294            return Ok(None);
16295        };
16296        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
16297            return Ok(None);
16298        };
16299        if !crate::tp::step_nvfp4_dev_routes_enabled()?
16300            || !crate::tp::step_tp_dev_router_enabled()?
16301            || !crate::tp::nvfp4_bank_v2_on()
16302            || bank.ep2
16303        {
16304            return Ok(None);
16305        }
16306        let cfg = &self.cfg;
16307        let Some(moe) = cfg.moe.as_ref() else {
16308            return Ok(None);
16309        };
16310        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
16311            return Ok(None);
16312        };
16313        let n_embd = cfg.n_embd as usize;
16314        let n_expert = moe.expert_count as usize;
16315        let n_used = moe.expert_used_count as usize;
16316        if z2.len() < 2 * n_embd {
16317            return Err("verify moe t2 geometry".into());
16318        }
16319        let logits = Self::moe_router_logits(e, m, z2, 2, cfg)?;
16320        // Persistent t=2 selection rows (host-op diet, same shape law as the t=1 SELW).
16321        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
16322            std::sync::Mutex::new(None);
16323        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
16324        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
16325            *selw = Some((
16326                e.ctx().ordinal(),
16327                e.htod_i32(&vec![0i32; 2 * n_used])?,
16328                e.htod(&vec![0.0f32; 2 * n_used])?,
16329            ));
16330        }
16331        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
16332        e.moe_router_sigmoid_topk_into(
16333            &logits,
16334            2,
16335            n_expert,
16336            n_used,
16337            m.active_count(),
16338            &m.exp_probs_b_dev,
16339            &m.active_experts_dev,
16340            sf,
16341            route_norm,
16342            sel_d,
16343            w_d,
16344        )?;
16345        let mut out2 = tp
16346            .runtime
16347            .run_tensor_parallel_routes_nvfp4_device_routed_t2(
16348                bank,
16349                e,
16350                z2,
16351                sel_d,
16352                w_d,
16353                n_used,
16354                tp.activation_limit,
16355            )?;
16356        // Shared expert: the exact t=1 program per column, added into that column's row.
16357        let mut z_row = e.uninit(n_embd)?;
16358        let mut out_row = e.uninit(n_embd)?;
16359        for c in 0..2 {
16360            e.dtod_copy_view(&z2.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
16361            e.dtod_copy_view(&out2.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
16362            Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
16363            e.dtod_copy_into(&out_row, &mut out2, c * n_embd)?;
16364        }
16365        Ok(Some(out2))
16366    }
16367
16368    fn step35_tp_decode_attn_resident_v2(
16369        &self,
16370        e: &Engine,
16371        fa: &FullAttnLayer,
16372        il: usize,
16373        h: &CudaSlice<f32>,
16374        pos_d: &CudaSlice<i32>,
16375        cache: &mut Cache,
16376    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16377        let tp = fa
16378            .step_tp_qkv
16379            .as_ref()
16380            .ok_or("Step TP decode lost its resident projections")?;
16381        let attention = tp
16382            .attention
16383            .as_ref()
16384            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
16385        if !tp.runtime.native_p2p() {
16386            return Err("rank-local Step attention requires native P2P".into());
16387        }
16388        if crate::Engine::kv_fp8_on() {
16389            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
16390        }
16391
16392        let geometry = self.step35_geom(il);
16393        let window = geometry.window.map(|window| window as usize);
16394        let ranks = tp.runtime.devices().len();
16395        let head_dim = geometry.head_dim_k as usize;
16396        let heads = geometry.n_head as usize;
16397        let kv_heads = geometry.n_head_kv as usize;
16398        if heads % ranks != 0 || kv_heads % ranks != 0 {
16399            return Err(format!(
16400                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
16401            )
16402            .into());
16403        }
16404        let local_heads = heads / ranks;
16405        let local_kv_heads = kv_heads / ranks;
16406        let max_ctx = cache.max_ctx;
16407
16408        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
16409
16410        let base_len = cache.kv[il]
16411            .as_ref()
16412            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
16413            .len;
16414        {
16415            let distributed = cache.tp_kv[il]
16416                .as_ref()
16417                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16418            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16419                return Err(format!(
16420                    "Step TP layer {il} cache lengths diverged before decode: \
16421                     local={base_len} distributed={}/{}",
16422                    distributed.committed_len(),
16423                    distributed.staged_len()
16424                )
16425                .into());
16426            }
16427        }
16428        if pos_d.len() != 1 {
16429            return Err(format!(
16430                "rank-local Step decode requires one position, got {}",
16431                pos_d.len()
16432            )
16433            .into());
16434        }
16435
16436        let decode_input = attention
16437            .decode_input
16438            .as_ref()
16439            .ok_or("Step TP decode v2 requires the replicated decode input")?;
16440        let mut decode_input = decode_input
16441            .lock()
16442            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16443
16444        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
16445        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
16446        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
16447        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
16448        let use_gate_shards = (attention.gate_shards.is_some()
16449            || attention.gate_shards_bf16.is_some())
16450            && crate::tp::step_tp_qkv_fused_enabled()?;
16451        let gate_raw = if use_gate_shards {
16452            None
16453        } else {
16454            let gate_weight = fa
16455                .attn_gate
16456                .as_ref()
16457                .ok_or("step35 layer is missing attn_gate.weight")?;
16458            let gate_raw = e.matmul(gate_weight, h, 1)?;
16459            if gate_raw.len() != heads {
16460                return Err(format!(
16461                    "Step TP layer {il} gate output {} != {heads}",
16462                    gate_raw.len()
16463                )
16464                .into());
16465            }
16466            Some(gate_raw)
16467        };
16468
16469        let ws_index = tp
16470            .runtime
16471            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16472        let mut ws_guard = tp
16473            .runtime
16474            .decode_v2_workspace()
16475            .lock()
16476            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
16477        let ws = ws_guard
16478            .get_mut(ws_index)
16479            .ok_or("Step TP decode v2 workspace missing after ensure")?;
16480
16481        let mut rope_freqs = Vec::with_capacity(ranks);
16482        for rank in 0..ranks {
16483            let engine = tp
16484                .runtime
16485                .rank_engine(rank)
16486                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16487            rope_freqs.push(if geometry.rope_factors {
16488                self.step35_aux
16489                    .as_ref()
16490                    .and_then(|aux| aux.rope_freqs(engine))
16491            } else {
16492                None
16493            });
16494        }
16495        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
16496        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
16497        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
16498        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
16499        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
16500        // the fused rope+append+inc launch on dcw tokens.)
16501        let staged_next = base_len + 1;
16502        let t_kv_eff = window
16503            .map(|window| staged_next.min(window))
16504            .unwrap_or(staged_next);
16505        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
16506            let (write_row, would_rebase) = cache.tp_kv[il]
16507                .as_ref()
16508                .expect("distributed cache checked above")
16509                .peek_append_ring(1)?;
16510            if !would_rebase {
16511                // Arm the base mirrors on first use: base = logical staged - physical row.
16512                let base = (base_len - write_row) as i32;
16513                let distributed = cache.tp_kv[il]
16514                    .as_mut()
16515                    .expect("distributed cache checked above");
16516                for rank in 0..ranks {
16517                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
16518                        format!("Step TP layer {il} has no engine for rank {rank}")
16519                    })?;
16520                    let _main = engine.gpu.enter_main()?;
16521                    let rank_cache = distributed
16522                        .rank_mut(rank)
16523                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16524                    if rank_cache.base_d().is_none() {
16525                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
16526                    }
16527                }
16528            }
16529            !would_rebase
16530        };
16531        let fuse_rope = dcw
16532            && crate::tp::fuse_rope_append_on()
16533            && head_dim == 128
16534            && cache.tp_kv[il]
16535                .as_ref()
16536                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
16537                .unwrap_or(false);
16538
16539        let tcol_col = crate::tp::take_verify_tcol();
16540        tp.runtime.decode_v2_input_qkv(
16541            ws,
16542            e,
16543            h,
16544            pos_d,
16545            gate_raw.as_ref(),
16546            if !use_gate_shards {
16547                None
16548            } else if let Some(shards) = attention.gate_shards.as_deref() {
16549                Some(crate::tp::StepTpGateShards::F32(shards))
16550            } else {
16551                attention
16552                    .gate_shards_bf16
16553                    .as_deref()
16554                    .map(crate::tp::StepTpGateShards::Bf16)
16555            },
16556            &mut decode_input,
16557            &tp.q,
16558            &tp.k,
16559            &tp.v,
16560            &attention.q_norm,
16561            &attention.k_norm,
16562            head_dim,
16563            geometry.n_rot as usize,
16564            geometry.rope_base,
16565            &rope_freqs,
16566            self.cfg.rms_eps,
16567            fuse_rope,
16568            tcol_col,
16569        )?;
16570
16571        let transaction = cache.tp_kv[il]
16572            .as_mut()
16573            .expect("distributed cache checked above")
16574            .begin_transaction()?;
16575        let append_result = tp.runtime.append_tp_kv_transaction_inner(
16576            cache.tp_kv[il]
16577                .as_mut()
16578                .expect("distributed cache checked above"),
16579            transaction,
16580            &ws.k,
16581            &ws.v_raw,
16582            1,
16583            dcw,
16584        );
16585        if let Err(error) = append_result {
16586            let _ = tp.runtime.rollback_tp_kv_transaction(
16587                cache.tp_kv[il]
16588                    .as_mut()
16589                    .expect("distributed cache checked above"),
16590                transaction,
16591            );
16592            return Err(error);
16593        }
16594
16595        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16596            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
16597            // reborrows the cache mutably per rank.
16598            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
16599                let distributed = cache.tp_kv[il]
16600                    .as_ref()
16601                    .expect("distributed cache checked above");
16602                let staged_len = distributed.staged_len();
16603                let view_start = window
16604                    .map(|window| staged_len.saturating_sub(window))
16605                    .unwrap_or(0);
16606                (
16607                    staged_len,
16608                    distributed.physical_range(view_start, staged_len)?,
16609                    distributed.k_tok_bytes(),
16610                    distributed.v_tok_bytes(),
16611                    distributed.physical_capacity(),
16612                )
16613            };
16614            let view_start = window
16615                .map(|window| staged_len.saturating_sub(window))
16616                .unwrap_or(0);
16617            let t_kv = staged_len - view_start;
16618            for rank in 0..ranks {
16619                let engine = tp
16620                    .runtime
16621                    .rank_engine(rank)
16622                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16623                let _main = engine.gpu.enter_main()?;
16624                if dcw {
16625                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
16626                    // stream visit. distributed is borrowed shared here; the planes need mut —
16627                    // reborrow through the cache Option (the closure holds cache mutably).
16628                    {
16629                        let distributed_mut = cache.tp_kv[il]
16630                            .as_mut()
16631                            .expect("distributed cache checked above");
16632                        let (kv_dim_k, kv_dim_v) =
16633                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
16634                        let (k_tok_bytes, v_tok_bytes) =
16635                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
16636                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
16637                            format!("Step TP layer {il} has no KV cache rank {rank}")
16638                        })?;
16639                        let (k_plane, v_plane, len_d, base_d) =
16640                            rank_cache.planes_and_counters_mut();
16641                        if fuse_rope {
16642                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
16643                            // + last-block len inc in ONE launch. Bit-identical bodies.
16644                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
16645                            let crate::tp::StepTpDecodeV2Ws {
16646                                q_raw,
16647                                k_raw,
16648                                v_raw,
16649                                q,
16650                                k,
16651                                pos,
16652                                pos_stage,
16653                                fuse_ctr,
16654                                ..
16655                            } = &mut *ws;
16656                            // Same-device rank: the staged-copy elision leaves pos[rank]
16657                            // stale — read the e-context pos stage directly (mirrors the
16658                            // rope elision in input_qkv_rank).
16659                            let pos_ref: &CudaSlice<i32> = if same_dev {
16660                                pos_stage
16661                                    .as_ref()
16662                                    .ok_or("step TP decode v2 pos stage not armed")?
16663                            } else {
16664                                &pos[rank]
16665                            };
16666                            engine.qk_norm_rope_append_inc_dcw(
16667                                &q_raw[rank],
16668                                &k_raw[rank],
16669                                &v_raw[rank],
16670                                &attention.q_norm[rank],
16671                                &attention.k_norm[rank],
16672                                &mut q[rank],
16673                                &mut k[rank],
16674                                pos_ref,
16675                                k_plane,
16676                                v_plane,
16677                                len_d,
16678                                base_d,
16679                                &mut fuse_ctr[rank],
16680                                kv_dim_k,
16681                                kv_dim_v,
16682                                k_tok_bytes,
16683                                v_tok_bytes,
16684                                head_dim,
16685                                geometry.n_rot as usize,
16686                                local_heads,
16687                                local_kv_heads,
16688                                self.cfg.rms_eps,
16689                                geometry.rope_base,
16690                                1.0,
16691                                rope_freqs[rank],
16692                            )?;
16693                        } else {
16694                            engine.append_kv_quantized_dcw(
16695                                &ws.k[rank],
16696                                &ws.v_raw[rank],
16697                                k_plane,
16698                                v_plane,
16699                                len_d,
16700                                base_d,
16701                                kv_dim_k,
16702                                kv_dim_v,
16703                                k_tok_bytes,
16704                                v_tok_bytes,
16705                            )?;
16706                        }
16707                        if !fuse_rope {
16708                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
16709                                format!("Step TP layer {il} has no KV cache rank {rank}")
16710                            })?;
16711                            engine.inc_i32(rank_cache.len_d_mut())?;
16712                        }
16713                    }
16714                    let distributed = cache.tp_kv[il]
16715                        .as_ref()
16716                        .expect("distributed cache checked above");
16717                    let rank_cache = distributed
16718                        .rank(rank)
16719                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16720                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
16721                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
16722                    {
16723                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
16724                        // the gated output directly (bit-identical; one launch saved).
16725                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
16726                        engine.fa_decode_dcw(
16727                            &q[rank],
16728                            &k_ring,
16729                            &v_ring,
16730                            &mut gated[rank],
16731                            head_dim,
16732                            local_heads,
16733                            local_kv_heads,
16734                            rank_cache.len_d(),
16735                            rank_cache.base_d(),
16736                            window.unwrap_or(0),
16737                            t_kv,
16738                            geometry.attention_scale(),
16739                            k_tok_bytes_c,
16740                            v_tok_bytes_c,
16741                            Some(&gate[rank]),
16742                        )?;
16743                    }
16744                    continue;
16745                }
16746                let distributed = cache.tp_kv[il]
16747                    .as_ref()
16748                    .expect("distributed cache checked above");
16749                let rank_cache = distributed
16750                    .rank(rank)
16751                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16752                let k_view = engine.view_u8_range(
16753                    rank_cache.k(),
16754                    physical.start * k_tok_bytes_c,
16755                    physical.end * k_tok_bytes_c,
16756                );
16757                let v_view = engine.view_u8_range(
16758                    rank_cache.v(),
16759                    physical.start * v_tok_bytes_c,
16760                    physical.end * v_tok_bytes_c,
16761                );
16762                engine.fa_decode_kvmod(
16763                    &ws.q[rank],
16764                    &k_view,
16765                    &v_view,
16766                    &mut ws.attn_out[rank],
16767                    head_dim,
16768                    local_heads,
16769                    local_kv_heads,
16770                    t_kv,
16771                    geometry.attention_scale(),
16772                    k_tok_bytes_c,
16773                    v_tok_bytes_c,
16774                    false,
16775                )?;
16776                engine.attn_head_gate(
16777                    &ws.attn_out[rank],
16778                    &ws.gate[rank],
16779                    &mut ws.gated[rank],
16780                    None,
16781                    head_dim,
16782                    local_heads,
16783                    1,
16784                )?;
16785            }
16786
16787            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
16788            // column's `gated` rows and skip the per-column finish choreography entirely
16789            // (the batched b4_tcol + join runs after every column). The returned buffer
16790            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
16791            // stashed flag, never this buffer. Ineligible configs fall back to the
16792            // normal finish and the driver consumes the real `mixed` per column.
16793            let output = if let Some(col) = crate::tp::take_tcol_oproj_defer() {
16794                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
16795                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
16796                    crate::tp::set_tcol_oproj_stashed();
16797                    e.uninit(ws.o_out)?
16798                } else {
16799                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
16800                }
16801            } else {
16802                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
16803            };
16804
16805            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
16806            // decode_v2_finish ordered behind the root event. Same math and cache state
16807            // transitions as v1.
16808            let local = cache.kv[il]
16809                .as_mut()
16810                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16811            if local.len != base_len || base_len + 1 > max_ctx {
16812                return Err(format!(
16813                    "Step TP layer {il} local cache changed during decode: \
16814                     len={} base={base_len} max={max_ctx}",
16815                    local.len
16816                )
16817                .into());
16818            }
16819            if crate::tp::no_local_shadow_on() {
16820                // Lengths advance, contents stay stale (graph-door precedent: decode reads
16821                // only the distributed TP caches; local contents feed spec/MTP scratch).
16822                local.len = base_len + 1;
16823                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
16824                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
16825                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
16826                if !crate::tp::len_mirror_lazy_on() {
16827                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
16828                }
16829            } else {
16830                let retain_from = window
16831                    .map(|window| {
16832                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16833                        let rollback_retain =
16834                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16835                        staged_retain.min(rollback_retain)
16836                    })
16837                    .unwrap_or(0);
16838                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16839                e.append_kv_quantized(
16840                    &ws.k_shadow,
16841                    &ws.v_shadow,
16842                    &mut local.k,
16843                    &mut local.v,
16844                    write_row,
16845                    local.kv_dim_k,
16846                    local.kv_dim_v,
16847                    local.k_tok_bytes,
16848                    local.v_tok_bytes,
16849                    false,
16850                )?;
16851                local.len = base_len + 1;
16852                e.set_i32_one(&mut local.len_d, local.len as i32)?;
16853            }
16854            Ok(output)
16855        })();
16856
16857        let output = match staged {
16858            Ok(output) => output,
16859            Err(error) => {
16860                let _ = tp.runtime.rollback_tp_kv_transaction(
16861                    cache.tp_kv[il]
16862                        .as_mut()
16863                        .expect("distributed cache checked above"),
16864                    transaction,
16865                );
16866                if let Some(local) = cache.kv[il].as_mut() {
16867                    local.len = base_len;
16868                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16869                }
16870                return Err(error);
16871            }
16872        };
16873        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
16874        // the rank counters (same value as the absolute re-set on full accept), so commit
16875        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
16876        // keeps the absolute set (its appends do NOT inc).
16877        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
16878        if lazy_commit {
16879            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
16880                cache.tp_kv[il]
16881                    .as_mut()
16882                    .expect("distributed cache checked above"),
16883                transaction,
16884                1,
16885            ) {
16886                let _ = tp.runtime.rollback_tp_kv_transaction(
16887                    cache.tp_kv[il]
16888                        .as_mut()
16889                        .expect("distributed cache checked above"),
16890                    transaction,
16891                );
16892                let local = cache.kv[il].as_mut().expect("local cache checked above");
16893                local.len = base_len;
16894                e.set_i32_one(&mut local.len_d, base_len as i32)?;
16895                return Err(error);
16896            }
16897        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16898            cache.tp_kv[il]
16899                .as_mut()
16900                .expect("distributed cache checked above"),
16901            transaction,
16902            1,
16903        ) {
16904            let _ = tp.runtime.rollback_tp_kv_transaction(
16905                cache.tp_kv[il]
16906                    .as_mut()
16907                    .expect("distributed cache checked above"),
16908                transaction,
16909            );
16910            let local = cache.kv[il].as_mut().expect("local cache checked above");
16911            local.len = base_len;
16912            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16913            return Err(error);
16914        }
16915
16916        let committed = cache.tp_kv[il]
16917            .as_ref()
16918            .expect("distributed cache checked above")
16919            .committed_len();
16920        let local_len = cache.kv[il]
16921            .as_ref()
16922            .expect("local cache checked above")
16923            .len;
16924        if committed != local_len {
16925            return Err(format!(
16926                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16927            )
16928            .into());
16929        }
16930        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
16931        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
16932            eprintln!(
16933                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
16934                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16935                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
16936                 attention_tensor_parallel=true attention_scope={} \
16937                 input_path=root-device-replicated gate_tensor_parallel=false \
16938                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
16939                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16940                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
16941                 performance_claim=false (logged once; every decode layer runs this driver)",
16942                tp.layer,
16943                tp.devices,
16944                if window.is_some() {
16945                    "rank-local-swa-ring"
16946                } else {
16947                    "rank-local-global"
16948                },
16949                tp.runtime.transport_label(),
16950                tp.runtime.bulk_p2p(),
16951            );
16952        }
16953        Ok(output)
16954    }
16955
16956    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
16957    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
16958    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
16959    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
16960    /// requiring `attn_gate`).
16961    ///
16962    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
16963    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
16964    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
16965    #[allow(clippy::too_many_arguments)]
16966    pub(crate) fn step35_decode_attn(
16967        &self,
16968        e: &Engine,
16969        fa: &FullAttnLayer,
16970        il: usize,
16971        h: &CudaSlice<f32>,
16972        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
16973        pos_d: &CudaSlice<i32>,
16974        cache: &mut Cache,
16975    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16976        if fa
16977            .step_tp_qkv
16978            .as_ref()
16979            .is_some_and(|tp| tp.attention.is_some())
16980        {
16981            if pre_q.is_some() {
16982                return Err(
16983                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
16984                     pre-quantized decode path"
16985                        .into(),
16986                );
16987            }
16988            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
16989        }
16990
16991        let geometry = self.step35_geom(il);
16992        let hd = geometry.head_dim_k as usize;
16993        let nkv = geometry.n_head_kv as usize;
16994        let nh = geometry.n_head as usize;
16995        let rbase = geometry.rope_base;
16996        let scale = geometry.attention_scale();
16997        let swa = geometry.window.is_some();
16998        let eps = self.cfg.rms_eps;
16999        let win = geometry.window.unwrap_or(0) as usize;
17000        let n_rot = geometry.n_rot as usize;
17001        let n_embd = self.cfg.n_embd as usize;
17002        let gw = fa
17003            .attn_gate
17004            .as_ref()
17005            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
17006
17007        let tp_qkv = if fa.step_tp_qkv.is_some() {
17008            if pre_q.is_some() {
17009                return Err(
17010                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
17011                     pre-quantized decode path"
17012                        .into(),
17013                );
17014            }
17015            self.step35_tp_qkv(e, fa, h, 1)?
17016        } else {
17017            None
17018        };
17019
17020        let (q0, k0, v0, gt) = match tp_qkv {
17021            Some(mut g3) => {
17022                let v = g3.pop().unwrap();
17023                let k = g3.pop().unwrap();
17024                let q = g3.pop().unwrap();
17025                let gt = e.matmul(gw, h, 1)?;
17026                (q, k, v, gt)
17027            }
17028            None => match pre_q {
17029                Some((hq, hdq)) => {
17030                    debug_assert!(
17031                        e.uses_q8_1_fast(gw),
17032                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
17033                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
17034                    );
17035                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
17036                        Some(t3) => t3,
17037                        None => (
17038                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
17039                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
17040                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
17041                        ),
17042                    };
17043                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
17044                    (a, b, c, gt)
17045                }
17046                None => {
17047                    if e.uses_q8_1_fast(&fa.wq)
17048                        && e.uses_q8_1_fast(&fa.wk)
17049                        && e.uses_q8_1_fast(&fa.wv)
17050                        && e.uses_q8_1_fast(gw)
17051                    {
17052                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
17053                        let (a, b, c) =
17054                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
17055                                Some(t3) => t3,
17056                                None => (
17057                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
17058                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
17059                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
17060                                ),
17061                            };
17062                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
17063                        (a, b, c, gt)
17064                    } else {
17065                        (
17066                            e.matmul(&fa.wq, h, 1)?,
17067                            e.matmul(&fa.wk, h, 1)?,
17068                            e.matmul(&fa.wv, h, 1)?,
17069                            e.matmul(gw, h, 1)?,
17070                        )
17071                    }
17072                }
17073            },
17074        };
17075
17076        let mut q = e.uninit(nh * hd)?;
17077        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
17078        let mut k = e.uninit(nkv * hd)?;
17079        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
17080        let ff = if swa {
17081            None
17082        } else {
17083            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
17084        };
17085        #[cfg(debug_assertions)]
17086        if let Some(ff) = ff {
17087            crate::debug_assert_tensor_stream_device(
17088                ff,
17089                &e.stream(),
17090                "step35_decode_attn.rope_freqs",
17091            );
17092        }
17093        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
17094
17095        if std::env::var("MEMRA_NOFA").is_ok() {
17096            return Err(
17097                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
17098                        cache; unset MEMRA_NOFA to use fa_decode"
17099                    .into(),
17100            );
17101        }
17102        let kvl = cache.kv[il].as_mut().unwrap();
17103        let next_len = kvl.len + 1;
17104        let (off, t_kv) = if swa && next_len > win {
17105            (next_len - win, win)
17106        } else {
17107            (0, next_len)
17108        };
17109        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
17110        e.append_kv_quantized(
17111            &k,
17112            &v0,
17113            &mut kvl.k,
17114            &mut kvl.v,
17115            write_row,
17116            kvl.kv_dim_k,
17117            kvl.kv_dim_v,
17118            kvl.k_tok_bytes,
17119            kvl.v_tok_bytes,
17120            crate::Engine::kv_fp8_on(),
17121        )?;
17122        kvl.len = next_len;
17123        let physical = kvl.physical_rows(off, off + t_kv)?;
17124        let k_view = e.view_u8_range(
17125            &kvl.k,
17126            physical.start * kvl.k_tok_bytes,
17127            physical.end * kvl.k_tok_bytes,
17128        );
17129        let v_view = e.view_u8_range(
17130            &kvl.v,
17131            physical.start * kvl.v_tok_bytes,
17132            physical.end * kvl.v_tok_bytes,
17133        );
17134        let mut attn = e.uninit(nh * hd)?;
17135        e.fa_decode_kvmod(
17136            &q,
17137            &k_view,
17138            &v_view,
17139            &mut attn,
17140            hd,
17141            nh,
17142            nkv,
17143            t_kv,
17144            scale,
17145            kvl.k_tok_bytes,
17146            kvl.v_tok_bytes,
17147            crate::Engine::kv_fp8_on(),
17148        )?;
17149
17150        let mut ag = e.uninit(nh * hd)?;
17151        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
17152        self.step35_o(e, fa, &ag, 1)
17153    }
17154}
17155
17156// ===================================================================================== //
17157//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
17158//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
17159//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
17160//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
17161//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
17162//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
17163// ===================================================================================== //
17164impl HybridModel {
17165    pub fn is_gemma4_e4b(&self) -> bool {
17166        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
17167    }
17168
17169    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
17170    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
17171    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
17172    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
17173        let g = self.cfg.gemma4.as_ref().unwrap();
17174        let swa = g.swa_pattern[il];
17175        let hd = if swa {
17176            g.key_length_swa
17177        } else {
17178            g.key_length_global
17179        } as usize;
17180        let Mixer::Full(fa) = &self.layers[il].mixer else {
17181            panic!("e4b layer {il} not full-attn")
17182        };
17183        let nh = fa.wq.out_features() / hd;
17184        let nkv = fa.wk.out_features() / hd;
17185        (
17186            hd,
17187            nkv,
17188            nh,
17189            if swa {
17190                g.rope_base_swa
17191            } else {
17192                g.rope_base_global
17193            },
17194            1.0,
17195            swa,
17196        )
17197    }
17198
17199    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
17200    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
17201        self.layers[il]
17202            .gemma4
17203            .as_ref()
17204            .and_then(|b| b.e4b.as_ref())
17205            .and_then(|e4| e4.kv_share.map(|t| t as usize))
17206    }
17207
17208    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
17209    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
17210    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
17211    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
17212    fn gemma4_e4b_inp_pl(
17213        &self,
17214        e: &Engine,
17215        tokens: &[u32],
17216        x_scaled: &CudaSlice<f32>,
17217        t: usize,
17218    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17219        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
17220        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
17221    }
17222
17223    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
17224    fn gemma4_e4b_inp_pl_dev(
17225        &self,
17226        e: &Engine,
17227        tok_d: &CudaSlice<u32>,
17228        x_scaled: &CudaSlice<f32>,
17229        t: usize,
17230    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17231        let aux = self.gemma4_aux.as_ref().unwrap();
17232        let m = aux.e4b.as_ref().unwrap();
17233        let n_embd = self.cfg.n_embd as usize;
17234        let n_layer = self.layers.len();
17235        let width = m.n_epl * n_layer;
17236        let tbl = m.tok_tbl_gpu.get_or_init(|| {
17237            e.upload_u8(&m.tok_embd_bytes)
17238                .expect("e4b per-layer token table upload")
17239        });
17240        let mut a =
17241            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
17242        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
17243        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
17244        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
17245        let mut pn = e.uninit(t * width)?;
17246        e.rms_norm(
17247            &p,
17248            m.proj_norm.float_data(),
17249            &mut pn,
17250            m.n_epl,
17251            t * n_layer,
17252            self.cfg.rms_eps,
17253        )?;
17254        let mut out = e.uninit(t * width)?;
17255        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
17256        Ok(out)
17257    }
17258
17259    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
17260    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
17261    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
17262    /// already holds this forward's rows — the target runs earlier in the stack).
17263    #[allow(clippy::too_many_arguments)]
17264    fn gemma4_e4b_attn(
17265        &self,
17266        e: &Engine,
17267        il: usize,
17268        hq: &CudaSlice<i8>,
17269        hdq: &CudaSlice<f32>,
17270        pos_d: &CudaSlice<i32>,
17271        t: usize,
17272        cache: &mut Cache,
17273        dc_bucket: Option<usize>,
17274    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17275        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
17276        let eps = self.cfg.rms_eps;
17277        let aux = self.gemma4_aux.as_ref().unwrap();
17278        let ones = aux.ones(e);
17279        #[cfg(debug_assertions)]
17280        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
17281        let Mixer::Full(fa) = &self.layers[il].mixer else {
17282            unreachable!()
17283        };
17284        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
17285        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
17286        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
17287        let h0 = e.zeros(0)?;
17288        let h = &h0;
17289
17290        let ff = if swa {
17291            None
17292        } else {
17293            Some(
17294                aux.rope_freqs(e)
17295                    .expect("e4b global rope needs rope_freqs.weight"),
17296            )
17297        };
17298        #[cfg(debug_assertions)]
17299        if let Some(ff) = ff {
17300            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
17301        }
17302        let share = self.gemma4_e4b_kv_target(il);
17303        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
17304        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
17305        let mut q;
17306        if let Some(_tgt) = share {
17307            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
17308            q = e.uninit(t * nh * hd)?;
17309            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
17310            // empty; q0 stands in for the unused k/v pointers).
17311            let mut kdummy = e.uninit(1)?;
17312            let mut vdummy = e.uninit(1)?;
17313            e.rms_norm_qkv_rope(
17314                &q0,
17315                &q0,
17316                &q0,
17317                fa.q_norm.float_data(),
17318                fa.q_norm.float_data(),
17319                ones,
17320                &mut q,
17321                &mut kdummy,
17322                &mut vdummy,
17323                hd,
17324                self.gemma4_rope_dims(il),
17325                nh * t,
17326                0,
17327                pos_d,
17328                nh,
17329                1,
17330                base,
17331                1.0,
17332                ff,
17333                eps,
17334            )?;
17335        } else {
17336            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
17337            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
17338            // q|k|v rows — the cat norm+rope twin consumes it directly.
17339            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
17340            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
17341            q = e.uninit(t * nh * hd)?;
17342            let mut k = e.uninit(t * nkv * hd)?;
17343            let mut v = e.uninit(t * nkv * hd)?;
17344            if t == 1 && cat.is_some() {
17345                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
17346                e.rms_norm_qkv_rope_cat(
17347                    &qkv0,
17348                    fa.q_norm.float_data(),
17349                    fa.k_norm.float_data(),
17350                    ones,
17351                    &mut q,
17352                    &mut k,
17353                    &mut v,
17354                    hd,
17355                    self.gemma4_rope_dims(il),
17356                    nh,
17357                    nkv,
17358                    pos_d,
17359                    nh,
17360                    nkv,
17361                    base,
17362                    1.0,
17363                    ff,
17364                    eps,
17365                )?;
17366            } else {
17367                let (q0, k0, v0) = match if t == 1 {
17368                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
17369                } else {
17370                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
17371                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
17372                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17373                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
17374                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
17375                    } else {
17376                        None
17377                    }
17378                } {
17379                    Some(triple) => triple,
17380                    None => (
17381                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
17382                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
17383                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
17384                    ), // E4B: real v (K != V)
17385                };
17386                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
17387                // the normed rows; V ones-rms, never roped).
17388                e.rms_norm_qkv_rope(
17389                    &q0,
17390                    &k0,
17391                    &v0,
17392                    fa.q_norm.float_data(),
17393                    fa.k_norm.float_data(),
17394                    ones,
17395                    &mut q,
17396                    &mut k,
17397                    &mut v,
17398                    hd,
17399                    self.gemma4_rope_dims(il),
17400                    nh * t,
17401                    nkv * t,
17402                    pos_d,
17403                    nh,
17404                    nkv,
17405                    base,
17406                    1.0,
17407                    ff,
17408                    eps,
17409                )?;
17410            }
17411            let kvl = cache.kv[il].as_mut().unwrap();
17412            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
17413            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
17414            // degenerate tok-0 stream, 2026-07-12).
17415            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17416            if dc_bucket.is_some() {
17417                // DC arm (graph serving): append at the len_d slot, advance the counter
17418                // in-stream — replay-correct, no host len in the launch args. Host mirrors
17419                // are NOT touched here (the replay loop owns them; a bump at capture-record
17420                // time would double-count the capture iteration).
17421                debug_assert!(t == 1);
17422                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
17423                e.append_kv_quantized_row_dc_inc(
17424                    &k,
17425                    &v,
17426                    &mut kvl.k,
17427                    &mut kvl.v,
17428                    &mut kvl.len_d,
17429                    kvl.kv_dim_k,
17430                    kvl.kv_dim_v,
17431                    kvl.k_tok_bytes,
17432                    kvl.v_tok_bytes,
17433                    cls,
17434                )?;
17435            } else {
17436                e.append_kv_quantized_rows(
17437                    &k,
17438                    &v,
17439                    &mut kvl.k,
17440                    &mut kvl.v,
17441                    kvl.len,
17442                    t,
17443                    kvl.kv_dim_k,
17444                    kvl.kv_dim_v,
17445                    kvl.k_tok_bytes,
17446                    kvl.v_tok_bytes,
17447                    cls,
17448                )?;
17449                kvl.len += t;
17450            }
17451            kv_f32 = Some((k, v));
17452        }
17453        // attention: per-row causal fa over the (own or target) quantized cache. The cache
17454        // already contains this forward's rows in both arms; row i attends [.., base+i].
17455        let kvl_idx = share.unwrap_or(il);
17456        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
17457        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
17458        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
17459        let mut attn = e.uninit(t * nh * hd)?;
17460        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
17461        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
17462        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
17463        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
17464        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
17465        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
17466        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
17467        //     rows (the T=K verify kernel; the target appended this forward's rows already).
17468        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
17469        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
17470        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
17471        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
17472            if let Some((kf, vf)) = &kv_f32 {
17473                if hd == 256 && t <= win {
17474                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17475                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17476                }
17477                if hd == 256 && swa && t > win {
17478                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
17479                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17480                }
17481                if hd == 512 && !swa {
17482                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17483                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17484                }
17485            } else if share.is_some() {
17486                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17487                let k_view = e.view_u8(&kvl.k, kvl.k.len());
17488                let v_view = e.view_u8(&kvl.v, kvl.v.len());
17489                if hd == 256 && (!swa || t <= win) {
17490                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
17491                    e.fa_prefill_view(
17492                        &q,
17493                        &k_view,
17494                        &v_view,
17495                        &mut attn,
17496                        hd,
17497                        nh,
17498                        nkv,
17499                        t,
17500                        t,
17501                        scale,
17502                        true,
17503                        kvl.k_tok_bytes,
17504                        kvl.v_tok_bytes,
17505                        g,
17506                    )?;
17507                    return Ok(e.matmul(&fa.wo, &attn, t)?);
17508                }
17509                // remaining shared classes (swa above the window; hd512 globals): dequant
17510                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
17511                let kv_dim = nkv * hd;
17512                let mut kf = e.uninit(t * kv_dim)?;
17513                let mut vf = e.uninit(t * kv_dim)?;
17514                e.fa_dequant_kv_view_f32(
17515                    &k_view,
17516                    &v_view,
17517                    &mut kf,
17518                    &mut vf,
17519                    kv_dim,
17520                    kv_dim,
17521                    t,
17522                    kvl.k_tok_bytes,
17523                    kvl.v_tok_bytes,
17524                    g,
17525                )?;
17526                if hd == 512 {
17527                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
17528                } else {
17529                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
17530                }
17531                return Ok(e.matmul(&fa.wo, &attn, t)?);
17532            }
17533        }
17534        if let Some(bucket) = dc_bucket {
17535            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
17536            // fa_decode_dc over the live counter. len_d already advanced past this token
17537            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
17538            // counter (advanced when the target ran earlier in the stack).
17539            assert!(t == 1);
17540            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
17541            // and under the window every live t_kv sits below it — cap the capture bucket
17542            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
17543            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
17544            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
17545            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
17546                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
17547            } else {
17548                bucket
17549            };
17550            let k_view = e.view_u8(&kvl.k, kvl.k.len());
17551            let v_view = e.view_u8(&kvl.v, kvl.v.len());
17552            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
17553            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
17554            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
17555            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
17556            // captured into the dc graph like any other launch. Extending the cascade to
17557            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
17558            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
17559            // MEMRA_WPF=0 rollback seam.
17560            if crate::Engine::wpf_level() >= 1 {
17561                e.prefetch_weight_l2(&fa.wo)?;
17562            }
17563            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
17564            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
17565            if e.uses_q8_1_fast(&fa.wo) {
17566                let mut oq = e.alloc_i8_uninit(nh * hd)?;
17567                let mut od = e.zeros(nh * hd / 32)?;
17568                e.fa_decode_dc_q8(
17569                    &q,
17570                    &k_view,
17571                    &v_view,
17572                    &mut attn,
17573                    hd,
17574                    nh,
17575                    nkv,
17576                    &kvl.len_d,
17577                    bucket,
17578                    scale,
17579                    kvl.k_tok_bytes,
17580                    kvl.v_tok_bytes,
17581                    g,
17582                    Some((&mut oq, &mut od)),
17583                )?;
17584                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
17585            }
17586            e.fa_decode_dc(
17587                &q,
17588                &k_view,
17589                &v_view,
17590                &mut attn,
17591                hd,
17592                nh,
17593                nkv,
17594                &kvl.len_d,
17595                bucket,
17596                scale,
17597                kvl.k_tok_bytes,
17598                kvl.v_tok_bytes,
17599                g,
17600            )?;
17601            return Ok(e.matmul(&fa.wo, &attn, t)?);
17602        }
17603        for i in 0..t {
17604            let avail = base_len + i + 1;
17605            let (off_tok, t_kv) = if swa && avail > win {
17606                (avail - win, win)
17607            } else {
17608                (0, avail)
17609            };
17610            let k_view = e.view_u8_range(
17611                &kvl.k,
17612                off_tok * kvl.k_tok_bytes,
17613                (off_tok + t_kv) * kvl.k_tok_bytes,
17614            );
17615            let v_view = e.view_u8_range(
17616                &kvl.v,
17617                off_tok * kvl.v_tok_bytes,
17618                (off_tok + t_kv) * kvl.v_tok_bytes,
17619            );
17620            let qv = e.view(&q, t * nh * hd);
17621            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
17622            let mut q_one = e.uninit(nh * hd)?;
17623            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
17624            let mut a_one = e.uninit(nh * hd)?;
17625            // read class MUST match the append class (globals are e4m3 under gkv): the
17626            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
17627            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
17628            e.fa_decode_kvmod(
17629                &q_one,
17630                &k_view,
17631                &v_view,
17632                &mut a_one,
17633                hd,
17634                nh,
17635                nkv,
17636                t_kv,
17637                scale,
17638                kvl.k_tok_bytes,
17639                kvl.v_tok_bytes,
17640                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
17641            )?;
17642            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
17643        }
17644        Ok(e.matmul(&fa.wo, &attn, t)?)
17645    }
17646
17647    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
17648    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
17649    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
17650    /// layer; does NOT advance cache.pos (caller owns pos).
17651    fn gemma4_e4b_trunk(
17652        &self,
17653        e: &Engine,
17654        tokens: &[u32],
17655        pos0: usize,
17656        cache: &mut Cache,
17657        head_last: bool,
17658    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17659        let n_embd = self.cfg.n_embd as usize;
17660        let t = tokens.len();
17661        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
17662        let pos_d = e.htod_i32(&pos)?;
17663        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
17664        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
17665        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
17666        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
17667    }
17668
17669    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
17670    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
17671    /// eager chain by construction: SAME functions, not twins).
17672    fn gemma4_e4b_trunk_core(
17673        &self,
17674        e: &Engine,
17675        x_in: CudaSlice<f32>,
17676        inp_pl: CudaSlice<f32>,
17677        pos_d: &CudaSlice<i32>,
17678        t: usize,
17679        cache: &mut Cache,
17680        dc_bucket: Option<usize>,
17681        cap_logits: bool,
17682        head_last: bool,
17683    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17684        let n_embd = self.cfg.n_embd as usize;
17685        let eps = self.cfg.rms_eps;
17686        let n_layer = self.layers.len();
17687        let mut x = x_in;
17688        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
17689        let n_epl = aux_e4b.n_epl;
17690
17691        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
17692        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
17693        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
17694        // head rides matmul_pre too. First layer's pair comes from a standalone fused
17695        // norm+quant.
17696        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
17697        for il in 0..n_layer {
17698            let layer = &self.layers[il];
17699            let (hq, hdq) = match h_carry.take() {
17700                Some(p) => p,
17701                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
17702            };
17703            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
17704            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
17705            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
17706            let bits = layer.gemma4.as_ref().unwrap();
17707            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
17708            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
17709            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
17710            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
17711            // the fused single-phase reduction is NOT FP-order-identical to the unfused
17712            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
17713            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
17714            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
17715            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
17716            // gate dropped, decode AND verify ride the same fused chain — parity by
17717            // construction, VERIFY-GATE 0.000e0.
17718            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
17719            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
17720                e,
17721                layer,
17722                &o,
17723                &x,
17724                t,
17725                Some(layer.post_attn_norm.float_data()),
17726                fuse_exit,
17727            )?;
17728            let mut resid = e.uninit(t * n_embd)?;
17729            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
17730            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
17731            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
17732            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
17733            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
17734            let g = if fuse_exit {
17735                // sn here = RAW f0 (post_ffw deferred).
17736                let (rq, rd) = e.rms_pre_add_q8_1(
17737                    &sn,
17738                    bits.post_ffw_norm.float_data(),
17739                    &attn_out,
17740                    &mut resid,
17741                    n_embd,
17742                    t,
17743                    self.cfg.rms_eps,
17744                )?;
17745                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
17746            } else {
17747                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
17748                e.matmul(&e4b.inp_gate, &resid, t)?
17749            };
17750            let mut act = e.uninit(t * n_epl)?;
17751            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
17752                let ipv = e.view(&inp_pl, n_epl * n_layer);
17753                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
17754                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
17755                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
17756            } else {
17757                let mut inp_this = e.uninit(t * n_epl)?;
17758                e.copy_rows_strided(
17759                    &inp_pl,
17760                    &mut inp_this,
17761                    n_epl,
17762                    t,
17763                    n_epl * n_layer,
17764                    il * n_epl,
17765                )?;
17766                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
17767                e.matmul(&e4b.proj, &act, t)?
17768            };
17769            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
17770            // ONE launch (glue-fusion lane; last layer emits through output_norm).
17771            let next_norm = if il + 1 < n_layer {
17772                self.layers[il + 1].attn_norm.float_data()
17773            } else {
17774                self.output_norm.float_data()
17775            };
17776            let mut xn = e.uninit(t * n_embd)?;
17777            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
17778                &y,
17779                e4b.post_norm.float_data(),
17780                &resid,
17781                bits.layer_scale,
17782                next_norm,
17783                &mut xn,
17784                n_embd,
17785                t,
17786                eps,
17787            )?;
17788            h_carry = Some(pair);
17789            x = xn;
17790        }
17791        // the head consumes the last layer's fused (output_norm) emit. head_last callers
17792        // (prime, last_only forward) need only the final row's logits — the all-T head is
17793        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
17794        let (oq, odq) = h_carry.take().unwrap();
17795        let h0 = e.zeros(0)?;
17796        let hm = if head_last { 1 } else { t };
17797        let (hq, hd) = if head_last && t > 1 {
17798            let mut q1 = e.uninit_i8(n_embd)?;
17799            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
17800            let nb = n_embd / 32;
17801            let mut d1 = e.uninit(nb)?;
17802            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
17803            (q1, d1)
17804        } else {
17805            (oq, odq)
17806        };
17807        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
17808        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
17809        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
17810        // Logit-returning callers (host logits / spec prime) keep the capped emit.
17811        if cap_logits {
17812            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
17813            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
17814        }
17815        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
17816        Ok((ld, x))
17817    }
17818
17819    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
17820    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
17821    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
17822    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
17823    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
17824    /// covers exactly the layers that appended).
17825    pub fn gemma4_e4b_decode_step_t_am_dev(
17826        &self,
17827        e: &Engine,
17828        tok_d: &CudaSlice<u32>,
17829        t: usize,
17830        pos0: usize,
17831        cache: &mut Cache,
17832    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17833        let n_embd = self.cfg.n_embd as usize;
17834        let eps = self.cfg.rms_eps;
17835        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
17836        let pos_d = e.htod_i32(&pos)?;
17837        let embd_gpu = self
17838            .embd_gpu
17839            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
17840        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
17841        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
17842        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
17843        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
17844        let (ld, xp) =
17845            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
17846        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
17847        // emit is already capped, matching the eager chain bit-for-bit).
17848        let n_vocab = self.output.out_features();
17849        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
17850        for i in 0..t {
17851            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
17852        }
17853        let mut hn = e.uninit(t * n_embd)?;
17854        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
17855        cache.pos += t;
17856        Ok((vam, hn))
17857    }
17858
17859    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
17860    /// prime path — mirror of `gemma4_decode_step_t_h`).
17861    pub(crate) fn gemma4_e4b_decode_step_t_h(
17862        &self,
17863        e: &Engine,
17864        tokens: &[u32],
17865        pos0: usize,
17866        cache: &mut Cache,
17867    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17868        let n_embd = self.cfg.n_embd as usize;
17869        let eps = self.cfg.rms_eps;
17870        let t = tokens.len();
17871        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
17872        let mut hn = e.uninit(t * n_embd)?;
17873        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
17874        cache.pos += t;
17875        Ok((e.dtoh(&ld)?, hn))
17876    }
17877
17878    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
17879    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
17880    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
17881    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
17882    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
17883    pub fn gemma4_e4b_decode_step_dcg(
17884        &self,
17885        e: &Engine,
17886        token_d: &mut CudaSlice<u32>,
17887        pos_d: &mut CudaSlice<i32>,
17888        embd_gpu: &CudaSlice<u8>,
17889        embd_qt: i32,
17890        embd_rb: usize,
17891        cache: &mut Cache,
17892        n_vocab: usize,
17893        bucket: usize,
17894    ) -> Result<(), Box<dyn std::error::Error>> {
17895        let n_embd = self.cfg.n_embd as usize;
17896        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
17897        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
17898        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
17899        let (ld, _x) =
17900            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
17901        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
17902        e.inc_seqlen(pos_d)?;
17903        Ok(())
17904    }
17905
17906    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
17907    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
17908    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
17909    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
17910    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
17911    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
17912    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
17913    #[allow(clippy::too_many_arguments)]
17914    pub fn gemma4_e4b_decode_step_dc(
17915        &self,
17916        e: &Engine,
17917        token_d: &CudaSlice<u32>,
17918        pos_d: &mut CudaSlice<i32>,
17919        embd_gpu: &CudaSlice<u8>,
17920        embd_qt: i32,
17921        embd_rb: usize,
17922        cache: &mut Cache,
17923        n_vocab: usize,
17924    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
17925        let n_embd = self.cfg.n_embd as usize;
17926        let eps = self.cfg.rms_eps;
17927        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
17928        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
17929        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
17930        let (ld, _x) =
17931            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
17932        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
17933        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
17934        e.inc_seqlen(pos_d)?;
17935        cache.pos += 1;
17936        let _ = eps;
17937        Ok(tok_out)
17938    }
17939
17940    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
17941    /// pre-output_norm hidden). Advances cache.pos.
17942    pub(crate) fn gemma4_e4b_decode_step_h(
17943        &self,
17944        e: &Engine,
17945        token: u32,
17946        cache: &mut Cache,
17947    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17948        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
17949        let logits = e.dtoh(&ld)?;
17950        cache.pos += 1;
17951        Ok((logits, x))
17952    }
17953
17954    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
17955    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
17956    /// fast; the prefill fa arms come later.
17957    pub(crate) fn gemma4_e4b_prime(
17958        &self,
17959        e: &Engine,
17960        tokens: &[u32],
17961        cache: &mut Cache,
17962    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
17963        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
17964        // process-kill as gemma4_prime — refuse per-request.
17965        if cache.pos != 0 {
17966            return Err(
17967                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
17968                        call or decode tokenwise"
17969                    .into(),
17970            );
17971        }
17972        let n_embd = self.cfg.n_embd as usize;
17973        let t = tokens.len();
17974        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
17975        cache.pos += t;
17976        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
17977        let xv = e.view(&x, t * n_embd);
17978        let row = xv.slice((t - 1) * n_embd..t * n_embd);
17979        let mut h_seed = e.uninit(n_embd)?;
17980        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
17981        Ok((last, h_seed, x))
17982    }
17983
17984    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
17985    pub(crate) fn gemma4_e4b_forward(
17986        &self,
17987        e: &Engine,
17988        tokens: &[u32],
17989        last_only: bool,
17990    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
17991        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
17992        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
17993        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
17994    }
17995}
17996
17997#[cfg(test)]
17998mod prime_chunk_schedule_tests {
17999    use super::{
18000        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, dynamic_prime_chunk_ranges,
18001        fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring, parse_step_ep_grouped_prefill,
18002        parse_step_tp_prefill, step_grouped_decode_shape, step_grouped_prefill_shape,
18003        step_tp_prefill_shape, validate_step_prime_batch_modes,
18004    };
18005
18006    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
18007        ranges.iter().map(|(start, end)| end - start).collect()
18008    }
18009
18010    fn auto_chunk(t: usize) -> usize {
18011        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
18012    }
18013
18014    #[test]
18015    fn active_matrix_prefix_scopes_reused_prime_slabs() {
18016        assert_eq!(
18017            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
18018            29 * 4096
18019        );
18020        assert_eq!(
18021            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
18022            29 * 4096
18023        );
18024        assert_eq!(
18025            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
18026            24 * 4096
18027        );
18028        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
18029        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
18030    }
18031
18032    #[test]
18033    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
18034        assert!(validate_step_prime_batch_modes(false, false).is_ok());
18035
18036        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
18037        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
18038
18039        for grouped in [false, true] {
18040            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
18041            assert!(err.contains("did not clear the live-server performance gate"));
18042            assert!(err.contains("per-session grouped prefill"));
18043        }
18044    }
18045
18046    #[test]
18047    fn step_grouped_path_is_eager_single_token_only() {
18048        assert!(step_grouped_decode_shape(false, 1));
18049        assert!(!step_grouped_decode_shape(true, 1));
18050        assert!(!step_grouped_decode_shape(false, 2));
18051        assert!(!step_grouped_decode_shape(true, 2));
18052    }
18053
18054    #[test]
18055    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
18056        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
18057        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
18058        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
18059        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
18060        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
18061        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
18062
18063        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
18064        assert!(step_grouped_prefill_shape(
18065            true,
18066            true,
18067            crate::cache::PRIME_CHUNK_MAX_TOKENS,
18068        ));
18069        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
18070        assert!(!step_grouped_prefill_shape(
18071            true,
18072            true,
18073            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
18074        ));
18075        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
18076        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
18077    }
18078
18079    #[test]
18080    fn step_tp_prefill_door_is_strict_and_default_off() {
18081        assert!(!parse_step_tp_prefill(None).unwrap());
18082        assert!(!parse_step_tp_prefill(Some("")).unwrap());
18083        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
18084        assert!(parse_step_tp_prefill(Some("1")).unwrap());
18085        assert!(parse_step_tp_prefill(Some("true")).is_err());
18086        assert!(parse_step_tp_prefill(Some("2")).is_err());
18087    }
18088
18089    #[test]
18090    fn step_tp_prefill_requires_the_qualified_tp4_shape() {
18091        assert!(step_tp_prefill_shape(
18092            true,
18093            PRIME_MIN_T,
18094            4,
18095            true,
18096            true,
18097            false,
18098        ));
18099        assert!(!step_tp_prefill_shape(
18100            false,
18101            PRIME_MIN_T,
18102            4,
18103            true,
18104            true,
18105            false,
18106        ));
18107        assert!(!step_tp_prefill_shape(
18108            true,
18109            PRIME_MIN_T - 1,
18110            4,
18111            true,
18112            true,
18113            false,
18114        ));
18115        assert!(!step_tp_prefill_shape(
18116            true,
18117            PRIME_MIN_T,
18118            2,
18119            true,
18120            true,
18121            false,
18122        ));
18123        assert!(!step_tp_prefill_shape(
18124            true,
18125            PRIME_MIN_T,
18126            4,
18127            false,
18128            true,
18129            false,
18130        ));
18131        assert!(!step_tp_prefill_shape(
18132            true,
18133            PRIME_MIN_T,
18134            4,
18135            true,
18136            false,
18137            false,
18138        ));
18139        assert!(!step_tp_prefill_shape(
18140            true,
18141            PRIME_MIN_T,
18142            4,
18143            true,
18144            true,
18145            true,
18146        ));
18147    }
18148
18149    #[test]
18150    fn fixed_schedule_retains_measured_geometry() {
18151        assert_eq!(
18152            sizes(&fixed_prime_chunk_ranges(461, 128)),
18153            vec![128, 128, 128, 77]
18154        );
18155        assert_eq!(
18156            sizes(&fixed_prime_chunk_ranges(1833, 230)),
18157            vec![230, 230, 230, 230, 230, 230, 230, 223]
18158        );
18159        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
18160        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
18161        assert_eq!(capped, vec![4096, 4088, 16]);
18162        assert!(capped.iter().all(|&rows| rows <= 4096));
18163        assert_eq!(
18164            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
18165            vec![4100],
18166            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
18167        );
18168    }
18169
18170    #[test]
18171    fn dynamic_schedule_matches_registered_shapes() {
18172        let cases = [
18173            (461, vec![64, 141, 132, 124]),
18174            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
18175            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
18176        ];
18177        for (t, expected) in cases {
18178            let chunk = auto_chunk(t);
18179            let fixed = fixed_prime_chunk_ranges(t, chunk);
18180            assert_eq!(
18181                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
18182                expected
18183            );
18184        }
18185    }
18186
18187    #[test]
18188    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
18189        for t in 256..=8192 {
18190            let chunk = auto_chunk(t);
18191            let fixed = fixed_prime_chunk_ranges(t, chunk);
18192            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
18193            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
18194            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
18195            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
18196            for pair in dynamic.windows(2) {
18197                assert_eq!(pair[0].1, pair[1].0, "T={t}");
18198            }
18199            assert!(
18200                dynamic
18201                    .iter()
18202                    .all(|(start, end)| end - start >= PRIME_MIN_T),
18203                "T={t} sizes={:?}",
18204                sizes(&dynamic)
18205            );
18206            if dynamic.len() >= 3 {
18207                let chunk_sizes = sizes(&dynamic);
18208                assert!(
18209                    chunk_sizes[0] < chunk_sizes[1],
18210                    "T={t} sizes={chunk_sizes:?}"
18211                );
18212                assert!(
18213                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
18214                    "T={t} sizes={chunk_sizes:?}"
18215                );
18216            }
18217        }
18218    }
18219}
18220
18221#[cfg(test)]
18222mod page_prefetch_tests {
18223    use super::{
18224        grouped_worker_prefetch_position, page_prefetch_positions,
18225        page_prefetch_window_from_values, worker_prefetch_positions,
18226    };
18227
18228    #[test]
18229    fn page_prefetch_window_keeps_existing_opt_in_default() {
18230        assert_eq!(page_prefetch_window_from_values(false, None), 0);
18231        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
18232        assert_eq!(page_prefetch_window_from_values(true, None), 1);
18233        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
18234        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
18235        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
18236    }
18237
18238    #[test]
18239    fn rolling_page_prefetch_advises_each_future_expert_once() {
18240        let advised: Vec<_> = (0..7)
18241            .flat_map(|position| page_prefetch_positions(position, 7, 3))
18242            .collect();
18243        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
18244
18245        let one_ahead: Vec<_> = (0..4)
18246            .flat_map(|position| page_prefetch_positions(position, 4, 1))
18247            .collect();
18248        assert_eq!(one_ahead, vec![1, 2, 3]);
18249        assert!(page_prefetch_positions(0, 4, 0).is_empty());
18250    }
18251
18252    #[test]
18253    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
18254        assert_eq!(grouped_worker_prefetch_position(0, None), None);
18255        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
18256            .chain(
18257                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
18258            )
18259            .collect();
18260        assert_eq!(positions, vec![0, 1, 2, 3]);
18261        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
18262    }
18263
18264    #[test]
18265    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
18266        let queued: Vec<_> = (0..8)
18267            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
18268            .collect();
18269        assert_eq!(queued, (0..8).collect::<Vec<_>>());
18270
18271        let one_at_a_time: Vec<_> = (0..4)
18272            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
18273            .collect();
18274        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
18275        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
18276    }
18277}
18278
18279pub struct G4DcSlots {
18280    x: CudaSlice<f32>,
18281    xn: CudaSlice<f32>,
18282    cur: CudaSlice<f32>,
18283    hq: CudaSlice<i8>,
18284    hd_: CudaSlice<f32>,
18285    q0: CudaSlice<f32>,
18286    k0: CudaSlice<f32>,
18287    v0: CudaSlice<f32>,
18288    q: CudaSlice<f32>,
18289    k: CudaSlice<f32>,
18290    v: CudaSlice<f32>,
18291    attn: CudaSlice<f32>,
18292    o: CudaSlice<f32>,
18293    attn_out: CudaSlice<f32>,
18294    zsh: CudaSlice<f32>,
18295    zq: CudaSlice<i8>,
18296    zd: CudaSlice<f32>,
18297    gate: CudaSlice<f32>,
18298    up: CudaSlice<f32>,
18299    act: CudaSlice<f32>,
18300    actq: CudaSlice<i8>,
18301    actd: CudaSlice<f32>,
18302    f0: CudaSlice<f32>,
18303    sn: CudaSlice<f32>,
18304    hn: CudaSlice<f32>,
18305    logits: CudaSlice<f32>,
18306}
18307
18308/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
18309/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
18310/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
18311/// fixed logits stage the head writes.
18312pub struct Step35TokenGraphState {
18313    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
18314    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
18315    pub token_d: cudarc::driver::CudaSlice<u32>,
18316    pub pos_d: cudarc::driver::CudaSlice<i32>,
18317    pub logits_stage: cudarc::driver::CudaSlice<f32>,
18318    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
18319    /// launch, so an alloc made inside one captured child is not referable from another):
18320    /// the running residual, the post-attention pair, the shared-expert row, and the
18321    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
18322    pub x: cudarc::driver::CudaSlice<f32>,
18323    pub x1: cudarc::driver::CudaSlice<f32>,
18324    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
18325    pub sh_stage: cudarc::driver::CudaSlice<f32>,
18326    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
18327    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
18328    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
18329    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
18330    pub router_logits: cudarc::driver::CudaSlice<f32>,
18331    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
18332    pub shexp_up: cudarc::driver::CudaSlice<f32>,
18333    pub shexp_act: cudarc::driver::CudaSlice<f32>,
18334    pub gate_sig: cudarc::driver::CudaSlice<f32>,
18335    pub dense_z: cudarc::driver::CudaSlice<f32>,
18336    pub dense_gate: cudarc::driver::CudaSlice<f32>,
18337    pub dense_up: cudarc::driver::CudaSlice<f32>,
18338    pub dense_act: cudarc::driver::CudaSlice<f32>,
18339    pub hn: cudarc::driver::CudaSlice<f32>,
18340    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
18341    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
18342    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
18343    pub probe_x: cudarc::driver::CudaSlice<f32>,
18344    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
18345    /// the in-graph tail argmax chain; host reads the ring once per chunk.
18346    pub token_hist: cudarc::driver::CudaSlice<u32>,
18347    pub hist_idx: cudarc::driver::CudaSlice<i32>,
18348}
18349
18350impl HybridModel {
18351    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
18352    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
18353    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
18354    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
18355    /// needs a rebuild this token).
18356    ///
18357    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
18358    /// but not their contents under this door (the TP rank caches are fully maintained
18359    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
18360    /// must not run with the door on until the local-dcw twin lands.
18361    pub(crate) fn step35_token_graph_step(
18362        &self,
18363        e: &Engine,
18364        token: u32,
18365        cache: &mut Cache,
18366    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
18367        if !self.uses_sliding_gated_moe_program()
18368            || !crate::tp::step_tp_graph_enabled()?
18369            || !crate::tp::step_tp_dcw_enabled()?
18370            || !crate::tp::step_tp_qkv_fused_enabled()?
18371            || !crate::tp::step_tp_dev_router_enabled()?
18372            || !crate::tp::step_nvfp4_dev_routes_enabled()?
18373        {
18374            return Ok(None);
18375        }
18376        let n_embd = self.cfg.n_embd as usize;
18377        let n_vocab = self.cfg.n_vocab as usize;
18378        let eps = self.cfg.rms_eps;
18379        let n_layers = self.layers.len();
18380        let pos = cache.pos;
18381        let staged_next = pos + 1;
18382        if staged_next < 96 {
18383            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
18384        }
18385
18386        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
18387        // eager fallback for the whole token; the host path also updates base_d there).
18388        for il in 0..n_layers {
18389            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
18390                return Ok(None); // caches not hydrated yet — eager warms them
18391            };
18392            if tp_kv.peek_append_ring(1)?.1 {
18393                return Ok(None);
18394            }
18395        }
18396
18397        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
18398        // their window and share one bucket forever after ctx > window).
18399        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
18400        if !fa_vec {
18401            return Ok(None);
18402        }
18403        let sp = crate::fa_split_keys(staged_next, 8);
18404        let bucket_max = (n_splits * sp).max(staged_next);
18405
18406        let mut state_guard = self
18407            .step35_token_graph
18408            .lock()
18409            .map_err(|_| "step35 token graph lock is poisoned")?;
18410        if state_guard.is_none() {
18411            let _main = e.gpu.enter_main()?;
18412            let n_expert = self
18413                .cfg
18414                .moe
18415                .as_ref()
18416                .map(|m| m.expert_count as usize)
18417                .unwrap_or(0);
18418            let n_ff_sh = self
18419                .layers
18420                .iter()
18421                .find_map(|l| match &l.ffn {
18422                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
18423                    _ => None,
18424                })
18425                .unwrap_or(0);
18426            let n_ff_dense = self
18427                .layers
18428                .iter()
18429                .find_map(|l| match &l.ffn {
18430                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
18431                    _ => None,
18432                })
18433                .unwrap_or(0);
18434            *state_guard = Some(Step35TokenGraphState {
18435                graphs: Vec::new(),
18436                token_d: e.stream().clone_htod(&[0u32])?,
18437                pos_d: e.htod_i32(&[pos as i32])?,
18438                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
18439                x: e.htod(&vec![0.0f32; n_embd])?,
18440                x1: e.htod(&vec![0.0f32; n_embd])?,
18441                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
18442                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
18443                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
18444                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
18445                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
18446                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18447                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18448                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
18449                gate_sig: e.htod(&vec![1.0f32; 1])?,
18450                dense_z: e.htod(&vec![0.0f32; n_embd])?,
18451                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18452                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18453                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
18454                hn: e.htod(&vec![0.0f32; n_embd])?,
18455                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
18456                probe_x: e.htod(&vec![0.0f32; n_embd])?,
18457                token_hist: e.stream().clone_htod(&[0u32; 16])?,
18458                hist_idx: e.htod_i32(&[0])?,
18459            });
18460        }
18461        let state = state_guard.as_mut().expect("state armed above");
18462        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
18463        // first use, and an alloc inside a captured section is a mem node (child graphs
18464        // reject those — the tail argmax chain needs them already resident).
18465        {
18466            let _main = e.gpu.enter_main()?;
18467            let Step35TokenGraphState {
18468                logits_stage,
18469                token_d,
18470                ..
18471            } = &mut *state;
18472            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
18473        }
18474
18475        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
18476        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
18477        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
18478        // ceiling at build so the baked pointers never move.
18479        if state.graphs.is_empty() {
18480            // Build the parent at this bucket. Capture executes nothing; correctness is
18481            // pinned at replay by the token-identity gate.
18482            self.step35_token_graph_build(e, cache, state, bucket_max)?;
18483        }
18484        {
18485            let (b, g) = state.graphs.first_mut().expect("graph built above");
18486            if *b != bucket_max {
18487                g.retarget_bucket(bucket_max)?;
18488                *b = bucket_max;
18489            }
18490        }
18491        let graph = state
18492            .graphs
18493            .first()
18494            .map(|(_, g)| g)
18495            .expect("graph built above");
18496
18497        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
18498        let t_fence = tg_timing.then(std::time::Instant::now);
18499        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
18500        // queued on the rank streams, and graph children carry no ordering edge to those
18501        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
18502        // sync is a no-op between consecutive replays.
18503        {
18504            let fa0 = match &self.layers[0].mixer {
18505                Mixer::Full(fa) => fa,
18506                _ => return Err("step35 token graph expects full-attention layers".into()),
18507            };
18508            let tp0 = fa0
18509                .step_tp_qkv
18510                .as_ref()
18511                .ok_or("step35 token graph lost its TP state")?;
18512            for rank in 0..tp0.runtime.devices().len() {
18513                let engine = tp0
18514                    .runtime
18515                    .rank_engine(rank)
18516                    .ok_or("step35 token graph lost a rank engine")?;
18517                let _main = engine.gpu.enter_main()?;
18518                engine.stream().synchronize()?;
18519            }
18520        }
18521
18522        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
18523        {
18524            let _main = e.gpu.enter_main()?;
18525            e.set_u32_one(&mut state.token_d, token)?;
18526            e.set_i32_one(&mut state.pos_d, pos as i32)?;
18527        }
18528        let t_launch = tg_timing.then(std::time::Instant::now);
18529        graph.launch(e)?;
18530        let t_book = tg_timing.then(std::time::Instant::now);
18531        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
18532        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
18533        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
18534        // replay error the counters are already advanced — acceptable: the decode aborts.
18535        for il in 0..n_layers {
18536            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
18537            let transaction = tp_kv.begin_transaction()?;
18538            let fa = match &self.layers[il].mixer {
18539                Mixer::Full(fa) => fa,
18540                _ => return Err("step35 token graph expects full-attention layers".into()),
18541            };
18542            let tp = fa
18543                .step_tp_qkv
18544                .as_ref()
18545                .ok_or("step35 token graph lost its TP state")?;
18546            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
18547            // incs own the counters). Shards unused.
18548            let empty: [CudaSlice<f32>; 0] = [];
18549            tp.runtime.append_tp_kv_transaction_inner(
18550                tp_kv,
18551                transaction,
18552                &empty,
18553                &empty,
18554                1,
18555                true,
18556            )?;
18557            tp.runtime
18558                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
18559            // Local shadow: lengths advance (v1 keeps contents stale under the door).
18560            if let Some(local) = cache.kv[il].as_mut() {
18561                local.len = pos + 1;
18562                let _main = e.gpu.enter_main()?;
18563                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
18564            }
18565        }
18566        cache.pos = pos + 1;
18567        let t_sync = tg_timing.then(std::time::Instant::now);
18568        let (logits, h_seed) = {
18569            let _main = e.gpu.enter_main()?;
18570            e.stream().synchronize()?;
18571            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
18572        };
18573        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
18574            use std::sync::atomic::{AtomicU64, Ordering};
18575            static NS: [AtomicU64; 5] = [
18576                AtomicU64::new(0),
18577                AtomicU64::new(0),
18578                AtomicU64::new(0),
18579                AtomicU64::new(0),
18580                AtomicU64::new(0),
18581            ];
18582            static CALLS: AtomicU64 = AtomicU64::new(0);
18583            let now = std::time::Instant::now();
18584            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
18585            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
18586            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
18587            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
18588            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
18589            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
18590            if calls % 100 == 0 {
18591                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
18592                eprintln!(
18593                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
18594                     syncdtoh_us={:.0} total_us={:.0}",
18595                    avg(0),
18596                    avg(1),
18597                    avg(2),
18598                    avg(3),
18599                    avg(4)
18600                );
18601            }
18602        }
18603        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
18604        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
18605            use std::io::Write;
18606            let (pm, px) = {
18607                let _main = e.gpu.enter_main()?;
18608                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
18609            };
18610            for (path, data) in [
18611                ("/root/tg-probe-mixed.bin", &pm),
18612                ("/root/tg-probe-x.bin", &px),
18613            ] {
18614                let mut fo = std::fs::OpenOptions::new()
18615                    .create(true)
18616                    .append(true)
18617                    .open(path)?;
18618                for v in data {
18619                    fo.write_all(&v.to_le_bytes())?;
18620                }
18621            }
18622        }
18623        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
18624        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
18625        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
18626            let hh = {
18627                let _main = e.gpu.enter_main()?;
18628                e.dtoh(&state.hn)?
18629            };
18630            use std::io::Write;
18631            let mut fo = std::fs::OpenOptions::new()
18632                .create(true)
18633                .append(true)
18634                .open(path)?;
18635            for v in &hh {
18636                fo.write_all(&v.to_le_bytes())?;
18637            }
18638        }
18639        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
18640        // per rank per token; diagnostics only.
18641        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
18642            for il in [0usize, 1, 44] {
18643                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
18644                let host_len = tp_kv.staged_len();
18645                let fa = match &self.layers[il].mixer {
18646                    Mixer::Full(fa) => fa,
18647                    _ => continue,
18648                };
18649                let tp = fa
18650                    .step_tp_qkv
18651                    .as_ref()
18652                    .ok_or("step35 token graph lost its TP state")?;
18653                for rank in 0..tp.runtime.devices().len() {
18654                    let engine = tp
18655                        .runtime
18656                        .rank_engine(rank)
18657                        .ok_or("step35 token graph lost a rank engine")?;
18658                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
18659                    let _main = engine.gpu.enter_main()?;
18660                    engine.stream().synchronize()?;
18661                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
18662                    let base_d = match rank_cache.base_d() {
18663                        Some(b) => engine.dtoh_i32_one(b)?,
18664                        None => -1,
18665                    };
18666                    eprintln!(
18667                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
18668                         len_d={len_d} base_d={base_d}"
18669                    );
18670                }
18671            }
18672        }
18673        Ok(Some((logits, h_seed)))
18674    }
18675
18676    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
18677    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
18678    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
18679    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
18680    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
18681    pub(crate) fn head_split_matvec(
18682        &self,
18683        e: &Engine,
18684        hn: &CudaSlice<f32>,
18685    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
18686        if self.head_split_fill_device(e, hn)?.is_none() {
18687            return Ok(None);
18688        }
18689        let guard = HEAD_SPLIT_WS
18690            .lock()
18691            .map_err(|_| "head split lock is poisoned")?;
18692        let ws = guard.as_ref().expect("filled above");
18693        let _main = e.gpu.enter_main()?;
18694        Ok(Some(e.dtoh(&ws.logits_e)?))
18695    }
18696
18697    /// Compute body of the split head: arms the replica + staging on first use, then fills
18698    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
18699    /// push) and orders e's stream behind it. None = ineligible.
18700    fn head_split_fill_device(
18701        &self,
18702        e: &Engine,
18703        hn: &CudaSlice<f32>,
18704    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
18705        use cudarc::driver::DevicePtr;
18706        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
18707            return Ok(None);
18708        };
18709        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
18710            Mixer::Full(fa) => fa
18711                .step_tp_qkv
18712                .as_ref()
18713                .and_then(|tp| tp.runtime.rank_engine(1)),
18714            _ => None,
18715        }) else {
18716            return Ok(None);
18717        };
18718        let n_embd = self.cfg.n_embd as usize;
18719        let n_vocab = self.cfg.n_vocab as usize;
18720        let half = n_vocab / 2;
18721        let mut guard = HEAD_SPLIT_WS
18722            .lock()
18723            .map_err(|_| "head split lock is poisoned")?;
18724        let pin = {
18725            let _main = e.gpu.enter_main()?;
18726            let stream = e.stream();
18727            let (ptr, _g) = head.device_ptr(&stream);
18728            ptr as u64
18729        };
18730        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
18731            // One-time: upload rank1's row half + persistent staging.
18732            let hi_rows = n_vocab - half;
18733            let (w1, hn1, y1, ev_done) = {
18734                let _r1 = rank1.gpu.enter_main()?;
18735                (
18736                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
18737                    rank1.htod(&vec![0.0f32; n_embd])?,
18738                    rank1.htod(&vec![0.0f32; hi_rows])?,
18739                    rank1.ctx().new_event(None)?,
18740                )
18741            };
18742            {
18743                use cudarc::driver::sys;
18744                let src = pin + (half * n_embd * 2) as u64;
18745                let dst = {
18746                    let _r1 = rank1.gpu.enter_main()?;
18747                    let rstream = rank1.stream();
18748                    let (d, _g) = w1.device_ptr(&rstream);
18749                    d as u64
18750                };
18751                let _r1 = rank1.gpu.enter_main()?;
18752                let r = unsafe {
18753                    sys::cuMemcpyAsync(
18754                        dst as sys::CUdeviceptr,
18755                        src as sys::CUdeviceptr,
18756                        hi_rows * n_embd * 2,
18757                        rank1.stream().cu_stream() as sys::CUstream,
18758                    )
18759                };
18760                if r != sys::CUresult::CUDA_SUCCESS {
18761                    return Err(format!("head split replica upload: {r:?}").into());
18762                }
18763                rank1.stream().synchronize()?;
18764            }
18765            let (logits_e, ev_hn) = {
18766                let _main = e.gpu.enter_main()?;
18767                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
18768            };
18769            let (raw_hn1, raw_y1) = {
18770                let _r1 = rank1.gpu.enter_main()?;
18771                let rstream = rank1.stream();
18772                let (a, _g0) = hn1.device_ptr(&rstream);
18773                let (b, _g1) = y1.device_ptr(&rstream);
18774                (a as u64, b as u64)
18775            };
18776            let raw_logits_hi = {
18777                let _main = e.gpu.enter_main()?;
18778                let stream = e.stream();
18779                let (l, _g) = logits_e.device_ptr(&stream);
18780                l as u64 + (half * 4) as u64
18781            };
18782            *guard = Some(HeadSplit {
18783                pin,
18784                w1,
18785                hn1,
18786                y1,
18787                logits_e,
18788                ev_hn,
18789                ev_done,
18790                raw_hn1,
18791                raw_y1,
18792                raw_logits_hi,
18793            });
18794        }
18795        let ws = guard.as_mut().expect("armed above");
18796        let hi_rows = n_vocab - half;
18797        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
18798        let raw_hn = {
18799            let _main = e.gpu.enter_main()?;
18800            let stream = e.stream();
18801            let (h, _g) = hn.device_ptr(&stream);
18802            ws.ev_hn.record(&stream)?;
18803            h as u64
18804        };
18805        {
18806            let _r1 = rank1.gpu.enter_main()?;
18807            rank1.stream().wait(&ws.ev_hn)?;
18808            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
18809            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
18810            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
18811            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
18812            ws.ev_done.record(&rank1.stream())?;
18813        }
18814        {
18815            let _main = e.gpu.enter_main()?;
18816            let head_lo = head.slice(0..half * n_embd * 2);
18817            let HeadSplit { logits_e, .. } = &mut *ws;
18818            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
18819            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
18820            e.stream().wait(&ws.ev_done)?;
18821            Ok(Some(()))
18822        }
18823    }
18824
18825    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
18826    /// row exactly like the host variant (identical halves, identical concat) and runs the
18827    /// device argmax into `token_d` — NO host readback. Returns false when the split is
18828    /// ineligible (caller falls back to the plain matmul head).
18829    pub(crate) fn head_split_argmax_device(
18830        &self,
18831        e: &Engine,
18832        hn: &CudaSlice<f32>,
18833        token_d: &mut CudaSlice<u32>,
18834    ) -> Result<bool, Box<dyn std::error::Error>> {
18835        if self.head_split_fill_device(e, hn)?.is_none() {
18836            return Ok(false);
18837        }
18838        let n_vocab = self.cfg.n_vocab as usize;
18839        let guard = HEAD_SPLIT_WS
18840            .lock()
18841            .map_err(|_| "head split lock is poisoned")?;
18842        let ws = guard.as_ref().expect("filled above");
18843        let _main = e.gpu.enter_main()?;
18844        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
18845        Ok(true)
18846    }
18847
18848    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
18849    /// token's row).
18850    pub(crate) fn head_split_logits_dtoh(
18851        &self,
18852        e: &Engine,
18853    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
18854        let guard = HEAD_SPLIT_WS
18855            .lock()
18856            .map_err(|_| "head split lock is poisoned")?;
18857        let ws = guard.as_ref().ok_or("head split logits not armed")?;
18858        let _main = e.gpu.enter_main()?;
18859        Ok(e.dtoh(&ws.logits_e)?)
18860    }
18861
18862    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
18863    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
18864    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
18865    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
18866    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
18867    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
18868    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
18869    /// own loop re-derive hist[k-1] from the returned row.
18870    pub fn step35_token_graph_chunk(
18871        &self,
18872        e: &Engine,
18873        token: u32,
18874        k_target: usize,
18875        cache: &mut Cache,
18876    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
18877        if !self.uses_sliding_gated_moe_program()
18878            || !crate::tp::step_tp_graph_enabled()?
18879            || !crate::tp::step_tp_dcw_enabled()?
18880            || !crate::tp::step_tp_qkv_fused_enabled()?
18881            || !crate::tp::step_tp_dev_router_enabled()?
18882            || !crate::tp::step_nvfp4_dev_routes_enabled()?
18883        {
18884            return Ok(None);
18885        }
18886        let n_layers = self.layers.len();
18887        let pos = cache.pos;
18888        let staged_next = pos + 1;
18889        if staged_next < 96 {
18890            return Ok(None);
18891        }
18892        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
18893        // exec's n_splits ladder must match eager per depth).
18894        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
18895        if !fa_vec {
18896            return Ok(None);
18897        }
18898        let sp = crate::fa_split_keys(staged_next, 8);
18899        let bucket_max = (n_splits * sp).max(staged_next);
18900        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
18901        let mut k = k_target.min(to_boundary).min(16);
18902        if k < 2 {
18903            return Ok(None);
18904        }
18905        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
18906        for il in 0..n_layers {
18907            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
18908                return Ok(None);
18909            };
18910            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
18911                k -= 1;
18912            }
18913            if k < 2 {
18914                return Ok(None);
18915            }
18916        }
18917
18918        let mut state_guard = self
18919            .step35_token_graph
18920            .lock()
18921            .map_err(|_| "step35 token graph lock is poisoned")?;
18922        let Some(state) = state_guard.as_mut() else {
18923            return Ok(None); // per-token path arms the state + stages first
18924        };
18925        if state.graphs.is_empty() {
18926            return Ok(None);
18927        }
18928        {
18929            let (b, g) = state.graphs.first_mut().expect("checked above");
18930            if *b != bucket_max {
18931                g.retarget_bucket(bucket_max)?;
18932                *b = bucket_max;
18933            }
18934        }
18935        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
18936
18937        // Rank-stream fence (eager stragglers; see the per-token path).
18938        {
18939            let fa0 = match &self.layers[0].mixer {
18940                Mixer::Full(fa) => fa,
18941                _ => return Err("step35 token graph expects full-attention layers".into()),
18942            };
18943            let tp0 = fa0
18944                .step_tp_qkv
18945                .as_ref()
18946                .ok_or("step35 token graph lost its TP state")?;
18947            for rank in 0..tp0.runtime.devices().len() {
18948                let engine = tp0
18949                    .runtime
18950                    .rank_engine(rank)
18951                    .ok_or("step35 token graph lost a rank engine")?;
18952                let _main = engine.gpu.enter_main()?;
18953                engine.stream().synchronize()?;
18954            }
18955        }
18956
18957        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
18958        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
18959        {
18960            let _main = e.gpu.enter_main()?;
18961            e.set_u32_one(&mut state.token_d, token)?;
18962            e.set_i32_one(&mut state.pos_d, pos as i32)?;
18963            e.set_i32_one(&mut state.hist_idx, 0)?;
18964        }
18965        for _ in 0..k {
18966            graph.launch(e)?;
18967        }
18968        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
18969        for il in 0..n_layers {
18970            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
18971            let transaction = tp_kv.begin_transaction()?;
18972            let fa = match &self.layers[il].mixer {
18973                Mixer::Full(fa) => fa,
18974                _ => return Err("step35 token graph expects full-attention layers".into()),
18975            };
18976            let tp = fa
18977                .step_tp_qkv
18978                .as_ref()
18979                .ok_or("step35 token graph lost its TP state")?;
18980            let empty: [CudaSlice<f32>; 0] = [];
18981            tp.runtime.append_tp_kv_transaction_inner(
18982                tp_kv,
18983                transaction,
18984                &empty,
18985                &empty,
18986                k,
18987                true,
18988            )?;
18989            tp.runtime
18990                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
18991            if let Some(local) = cache.kv[il].as_mut() {
18992                local.len = pos + k;
18993                let _main = e.gpu.enter_main()?;
18994                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
18995            }
18996        }
18997        cache.pos = pos + k;
18998        let (hist, logits) = {
18999            let _main = e.gpu.enter_main()?;
19000            e.stream().synchronize()?;
19001            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
19002        };
19003        Ok(Some((hist[..k].to_vec(), logits)))
19004    }
19005}
19006
19007impl HybridModel {
19008    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
19009    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
19010    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
19011    /// of each phase fork in parallel and merge into the following root section.
19012    #[allow(clippy::too_many_arguments)]
19013    fn step35_token_graph_build(
19014        &self,
19015        e: &Engine,
19016        cache: &mut Cache,
19017        state: &mut Step35TokenGraphState,
19018        bucket_max: usize,
19019    ) -> Result<(), Box<dyn std::error::Error>> {
19020        use cudarc::driver::DevicePtr;
19021        let n_embd = self.cfg.n_embd as usize;
19022        let eps = self.cfg.rms_eps;
19023        let n_layers = self.layers.len();
19024        let started = std::time::Instant::now();
19025        if !crate::router_kernel_on() {
19026            return Err(
19027                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
19028            );
19029        }
19030        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
19031            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
19032        }
19033
19034        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
19035        let embd_gpu = self
19036            .embd_gpu_try(e)
19037            .ok_or("step35 token graph could not upload the device embed table")?;
19038        let embd_qtype = match self.embd.ggml_type {
19039            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
19040            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
19041            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
19042        };
19043        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
19044
19045        // Fixed-stage pointers the sections reference.
19046        let (p_mixed, p_kshadow, p_vshadow) = {
19047            let _main = e.gpu.enter_main()?;
19048            let stream = e.stream();
19049            let (a, _g) = state.mixed_stage.device_ptr(&stream);
19050            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
19051            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
19052            (a as u64, b as u64, c as u64)
19053        };
19054
19055        crate::tp::token_graph_build_begin()?;
19056        let mut group_id: u32 = 0;
19057        for il in 0..n_layers {
19058            let layer = &self.layers[il];
19059            let fa = match &layer.mixer {
19060                Mixer::Full(fa) => fa,
19061                _ => return Err("step35 token graph expects full-attention layers".into()),
19062            };
19063            let tp = fa
19064                .step_tp_qkv
19065                .as_ref()
19066                .ok_or("step35 token graph lost its TP state")?;
19067            let attention = tp
19068                .attention
19069                .as_ref()
19070                .ok_or("step35 token graph lost its attention aux")?;
19071            let geometry = self.step35_geom(il);
19072            let window = geometry.window.map(|w| w as usize);
19073            let head_dim = geometry.head_dim_k as usize;
19074            let heads = geometry.n_head as usize;
19075            let kv_heads = geometry.n_head_kv as usize;
19076            let ranks = tp.runtime.devices().len();
19077            let local_heads = heads / ranks;
19078            let local_kv_heads = kv_heads / ranks;
19079            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
19080            let use_gate_shards =
19081                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
19082            if !use_gate_shards {
19083                return Err("step35 token graph requires the fused gate shards".into());
19084            }
19085
19086            let ws_index = tp
19087                .runtime
19088                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
19089            let ws_mutex = tp.runtime.decode_v2_workspace();
19090            let mut ws_guard = ws_mutex
19091                .lock()
19092                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
19093            let ws = ws_guard
19094                .get_mut(ws_index)
19095                .ok_or("step TP decode v2 workspace missing after ensure")?;
19096            tp.runtime
19097                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
19098            let mut rope_freqs = Vec::with_capacity(ranks);
19099            for rank in 0..ranks {
19100                let engine = tp
19101                    .runtime
19102                    .rank_engine(rank)
19103                    .ok_or("step35 token graph lost a rank engine")?;
19104                rope_freqs.push(if geometry.rope_factors {
19105                    self.step35_aux
19106                        .as_ref()
19107                        .and_then(|aux| aux.rope_freqs(engine))
19108                } else {
19109                    None
19110                });
19111            }
19112            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
19113                Some(crate::tp::StepTpGateShards::F32(shards))
19114            } else {
19115                attention
19116                    .gate_shards_bf16
19117                    .as_deref()
19118                    .map(crate::tp::StepTpGateShards::Bf16)
19119            };
19120
19121            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
19122            let decode_input = attention
19123                .decode_input
19124                .as_ref()
19125                .ok_or("step35 token graph requires the replicated decode input")?;
19126            let mut decode_input = decode_input
19127                .lock()
19128                .map_err(|_| "replicated decode input lock is poisoned")?;
19129            // Stage arming happens through the eager stage flow once; require it here.
19130            if ws.h_stage.is_none() {
19131                return Err(
19132                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
19133                );
19134            }
19135            {
19136                let state_x = &mut state.x;
19137                let token_d = &state.token_d;
19138                let pos_d = &state.pos_d;
19139                crate::tp::graph_section(e, None, || {
19140                    let _main = e.gpu.enter_main()?;
19141                    if il == 0 {
19142                        e.embed_gather_device_into(
19143                            embd_gpu,
19144                            token_d,
19145                            state_x,
19146                            n_embd,
19147                            embd_qtype,
19148                            embd_row_bytes,
19149                        )?;
19150                    }
19151                    {
19152                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
19153                        e.rms_norm(
19154                            state_x,
19155                            layer.attn_norm.float_data(),
19156                            h_stage,
19157                            n_embd,
19158                            1,
19159                            eps,
19160                        )?;
19161                    }
19162                    {
19163                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
19164                        let mut dst = pos_stage.slice_mut(0..1);
19165                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
19166                    }
19167                    Ok(())
19168                })?;
19169            }
19170
19171            // ---- R0/R1 (parallel): projections + dcw attention interior ----
19172            group_id += 1;
19173            for rank in 0..ranks {
19174                let engine = tp
19175                    .runtime
19176                    .rank_engine(rank)
19177                    .ok_or("step35 token graph lost a rank engine")?;
19178                {
19179                    // fa partial pool must reach the RUN CEILING before capture — an
19180                    // in-capture grow is a mem node (child graphs reject those), and the
19181                    // retarget path (increment C) widens the baked memsets up to the ceiling
19182                    // without moving the pool pointers. Two ensures cover both sp rungs.
19183                    let ceiling = window
19184                        .map(|w| cache.max_ctx.min(w))
19185                        .unwrap_or(cache.max_ctx);
19186                    let _main = engine.gpu.enter_main()?;
19187                    engine.fa_dcw_pool_ensure(
19188                        head_dim,
19189                        local_heads,
19190                        local_kv_heads,
19191                        ceiling.min(2048),
19192                    )?;
19193                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
19194                    engine.fa_dcw_pool_ensure(
19195                        head_dim,
19196                        local_heads,
19197                        local_kv_heads,
19198                        layer_bucket,
19199                    )?;
19200                }
19201                let runtime = &tp.runtime;
19202                let q_norm = &attention.q_norm;
19203                let k_norm = &attention.k_norm;
19204                let gate_ref = gate_shards_arg.as_ref();
19205                crate::tp::graph_section(engine, Some(group_id), || {
19206                    runtime.decode_v2_input_qkv_rank(
19207                        ws,
19208                        &state.pos_d,
19209                        &mut decode_input,
19210                        &tp.q,
19211                        &tp.k,
19212                        &tp.v,
19213                        q_norm,
19214                        k_norm,
19215                        head_dim,
19216                        geometry.n_rot as usize,
19217                        geometry.rope_base,
19218                        &rope_freqs,
19219                        eps,
19220                        gate_ref,
19221                        true,
19222                        false,
19223                        rank,
19224                        None,
19225                    )?;
19226                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
19227                    // replayed values track the live counters).
19228                    let distributed = cache.tp_kv[il]
19229                        .as_mut()
19230                        .ok_or("step35 token graph lost a TP cache")?;
19231                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
19232                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
19233                    let capacity = distributed.physical_capacity();
19234                    {
19235                        let rank_cache = distributed
19236                            .rank_mut(rank)
19237                            .ok_or("step35 token graph lost a rank cache")?;
19238                        let (k_plane, v_plane, len_d, base_d) =
19239                            rank_cache.planes_and_counters_mut();
19240                        engine.append_kv_quantized_dcw(
19241                            &ws.k[rank],
19242                            &ws.v_raw[rank],
19243                            k_plane,
19244                            v_plane,
19245                            len_d,
19246                            base_d,
19247                            kv_dim_k,
19248                            kv_dim_v,
19249                            ktb,
19250                            vtb,
19251                        )?;
19252                    }
19253                    {
19254                        let rank_cache = distributed
19255                            .rank_mut(rank)
19256                            .ok_or("step35 token graph lost a rank cache")?;
19257                        engine.inc_i32(rank_cache.len_d_mut())?;
19258                    }
19259                    let rank_cache = distributed
19260                        .rank(rank)
19261                        .ok_or("step35 token graph lost a rank cache")?;
19262                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
19263                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
19264                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
19265                    // retarget addresses combine's nsp at arg slot 6, and the fused
19266                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
19267                    // only the eager arm takes FUSION #2d.
19268                    engine.fa_decode_dcw(
19269                        &ws.q[rank],
19270                        &k_ring,
19271                        &v_ring,
19272                        &mut ws.attn_out[rank],
19273                        head_dim,
19274                        local_heads,
19275                        local_kv_heads,
19276                        rank_cache.len_d(),
19277                        rank_cache.base_d(),
19278                        window.unwrap_or(0),
19279                        layer_bucket,
19280                        geometry.attention_scale(),
19281                        ktb,
19282                        vtb,
19283                        None,
19284                    )?;
19285                    engine.attn_head_gate(
19286                        &ws.attn_out[rank],
19287                        &ws.gate[rank],
19288                        &mut ws.gated[rank],
19289                        None,
19290                        head_dim,
19291                        local_heads,
19292                        1,
19293                    )?;
19294                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
19295                    Ok(())
19296                })?;
19297            }
19298
19299            // ---- ROOT: combine + shadows + e-mirrors ----
19300            {
19301                let root = tp
19302                    .runtime
19303                    .rank_engine(0)
19304                    .ok_or("step35 token graph lost the root engine")?;
19305                let runtime = &tp.runtime;
19306                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
19307            }
19308            drop(ws_guard);
19309            drop(decode_input);
19310
19311            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
19312                .ok()
19313                .and_then(|v| v.parse().ok());
19314            if probe_layer == Some(il) {
19315                let Step35TokenGraphState {
19316                    mixed_stage,
19317                    probe_mixed,
19318                    ..
19319                } = &mut *state;
19320                crate::tp::graph_section(e, None, || {
19321                    let _main = e.gpu.enter_main()?;
19322                    let mut dst = probe_mixed.slice_mut(0..n_embd);
19323                    e.stream()
19324                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
19325                    Ok(())
19326                })?;
19327            }
19328
19329            // ---- FFN half ----
19330            match &layer.ffn {
19331                crate::hybrid::Ffn::Dense {
19332                    ffn_gate,
19333                    ffn_up,
19334                    ffn_down,
19335                } => {
19336                    let n_ff = ffn_gate.out_features();
19337                    let lim = self.cfg.clamp_shexp_at(il as u32);
19338                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
19339                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
19340                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
19341                    if lim.is_some() {
19342                        return Err("step35 token graph dense FFN with clamp unsupported".into());
19343                    }
19344                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
19345                        (
19346                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
19347                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
19348                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
19349                        ) => (wg, wu, wd),
19350                        _ => {
19351                            return Err(
19352                                "step35 token graph dense FFN requires bf16-resident weights"
19353                                    .into(),
19354                            );
19355                        }
19356                    };
19357                    crate::tp::graph_section(e, None, || {
19358                        let _main = e.gpu.enter_main()?;
19359                        let Step35TokenGraphState {
19360                            x,
19361                            x1,
19362                            mixed_stage,
19363                            dense_z,
19364                            dense_gate,
19365                            dense_up,
19366                            dense_act,
19367                            sh_stage,
19368                            ..
19369                        } = &mut *state;
19370                        e.add_rms_norm(
19371                            x,
19372                            mixed_stage,
19373                            layer.post_attn_norm.float_data(),
19374                            x1,
19375                            dense_z,
19376                            n_embd,
19377                            1,
19378                            eps,
19379                        )?;
19380                        // TWO SINGLE matvecs, not the dual: eager dense rides two
19381                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
19382                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
19383                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
19384                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
19385                        Self::ffn_act_lim(
19386                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
19387                        )?;
19388                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
19389                        e.add(x1, sh_stage, x, n_embd)?;
19390                        Ok(())
19391                    })?;
19392                }
19393                crate::hybrid::Ffn::Moe(m) => {
19394                    let moe = self
19395                        .cfg
19396                        .moe
19397                        .as_ref()
19398                        .ok_or("step35 token graph needs moe cfg")?;
19399                    let n_expert = moe.expert_count as usize;
19400                    let n_used = moe.expert_used_count as usize;
19401                    let sigmoid = self
19402                        .cfg
19403                        .sigmoid_router()
19404                        .ok_or("step35 token graph needs the sigmoid router")?;
19405                    let step_tp = m
19406                        .step_tp
19407                        .as_ref()
19408                        .ok_or("step35 token graph needs TP experts")?;
19409                    let bank = match &step_tp.experts {
19410                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
19411                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
19412                    };
19413                    let routes_ws_mutex = bank.device_workspace_handle();
19414                    let mut routes_guard = routes_ws_mutex
19415                        .lock()
19416                        .map_err(|_| "routes workspace lock is poisoned")?;
19417                    let routes_ws = routes_guard
19418                        .as_mut()
19419                        .ok_or("step35 token graph requires the routes workspace warmed")?;
19420                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
19421                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
19422                    let p_z = {
19423                        let root = step_tp
19424                            .runtime
19425                            .rank_engine(0)
19426                            .ok_or("routes root engine missing")?;
19427                        let _main = root.gpu.enter_main()?;
19428                        let stream = root.stream();
19429                        let in_stage = routes_ws
19430                            .in_stage_handle()
19431                            .ok_or("routes in stage not armed")?;
19432                        let (a, _g) = in_stage.device_ptr(&stream);
19433                        a as u64
19434                    };
19435                    let local_out = bank.expert_width / ranks;
19436
19437                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
19438                    crate::tp::graph_section(e, None, || {
19439                        let _main = e.gpu.enter_main()?;
19440                        {
19441                            let in_stage = routes_ws
19442                                .in_stage_mut()
19443                                .ok_or("routes in stage not armed")?;
19444                            let Step35TokenGraphState {
19445                                x, x1, mixed_stage, ..
19446                            } = &mut *state;
19447                            e.add_rms_norm(
19448                                x,
19449                                mixed_stage,
19450                                layer.post_attn_norm.float_data(),
19451                                x1,
19452                                in_stage,
19453                                n_embd,
19454                                1,
19455                                eps,
19456                            )?;
19457                        }
19458                        {
19459                            let z_ref = routes_ws
19460                                .in_stage_handle()
19461                                .ok_or("routes in stage not armed")?;
19462                            e.router_gemv_into(
19463                                m.gate_inp.float_data(),
19464                                z_ref,
19465                                &mut state.router_logits,
19466                                n_embd,
19467                                n_expert,
19468                                1,
19469                            )?;
19470                        }
19471                        let (sel_e, w_e) = routes_ws
19472                            .dev_route_e_mut()
19473                            .ok_or("routes staging not armed")?;
19474                        e.moe_router_sigmoid_topk_into(
19475                            &state.router_logits,
19476                            1,
19477                            n_expert,
19478                            n_used,
19479                            m.active_count(),
19480                            &m.exp_probs_b_dev,
19481                            &m.active_experts_dev,
19482                            sigmoid.0,
19483                            sigmoid.1,
19484                            sel_e,
19485                            w_e,
19486                        )?;
19487                        Ok(())
19488                    })?;
19489
19490                    // ---- R0r/R1r (parallel): routes sweeps ----
19491                    group_id += 1;
19492                    for rank in 0..ranks {
19493                        let engine = step_tp
19494                            .runtime
19495                            .rank_engine(rank)
19496                            .ok_or("routes rank engine missing")?;
19497                        let runtime = &step_tp.runtime;
19498                        crate::tp::graph_section(engine, Some(group_id), || {
19499                            runtime.routes_rank_section(
19500                                bank,
19501                                routes_ws,
19502                                p_z,
19503                                local_out,
19504                                n_used,
19505                                step_tp.activation_limit,
19506                                rank,
19507                            )
19508                        })?;
19509                    }
19510
19511                    // ---- ROOTr: combine into the out stage ----
19512                    {
19513                        let root = step_tp
19514                            .runtime
19515                            .rank_engine(0)
19516                            .ok_or("routes root engine missing")?;
19517                        let runtime = &step_tp.runtime;
19518                        crate::tp::graph_section(root, None, || {
19519                            runtime.routes_root_section(bank, routes_ws)
19520                        })?;
19521                    }
19522
19523                    // ---- E3: shexp + add_shared onto the out stage + residual ----
19524                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
19525                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
19526                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
19527                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
19528                        (
19529                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
19530                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
19531                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
19532                        ) => (wg, wu, wd),
19533                        _ => {
19534                            return Err(
19535                                "step35 token graph shexp requires bf16-resident weights".into()
19536                            );
19537                        }
19538                    };
19539                    let n_ff_sh = m
19540                        .gate_shexp
19541                        .as_ref()
19542                        .expect("matched Some above")
19543                        .out_features();
19544                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
19545                    // init, reproducing eager's ones vector without a launch.
19546                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
19547                    crate::tp::graph_section(e, None, || {
19548                        let _main = e.gpu.enter_main()?;
19549                        let (z_ref, out_stage) = routes_ws
19550                            .in_and_out_stages_mut()
19551                            .ok_or("routes stages not armed")?;
19552                        let Step35TokenGraphState {
19553                            x,
19554                            x1,
19555                            sh_stage,
19556                            shexp_gate,
19557                            shexp_up,
19558                            shexp_act,
19559                            gate_sig,
19560                            ..
19561                        } = &mut *state;
19562                        e.matvec_bf16_dual_into(
19563                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
19564                        )?;
19565                        Self::ffn_act_lim(
19566                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
19567                            n_ff_sh,
19568                        )?;
19569                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
19570                        if let Some(gate_w) = gate_inp_shexp {
19571                            e.sigmoid_dot_rows_into(
19572                                z_ref,
19573                                gate_w.float_data(),
19574                                gate_sig,
19575                                n_embd,
19576                                1,
19577                            )?;
19578                        }
19579                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
19580                        e.add(x1, out_stage, x, n_embd)?;
19581                        Ok(())
19582                    })?;
19583                }
19584            }
19585            if probe_layer == Some(il) {
19586                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
19587                crate::tp::graph_section(e, None, || {
19588                    let _main = e.gpu.enter_main()?;
19589                    let mut dst = probe_x.slice_mut(0..n_embd);
19590                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
19591                    Ok(())
19592                })?;
19593            }
19594        }
19595
19596        // ---- Tail: output norm + head into the logits stage ----
19597        let head = match &self.output {
19598            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
19599            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
19600        };
19601        crate::tp::graph_section(e, None, || {
19602            let _main = e.gpu.enter_main()?;
19603            let Step35TokenGraphState {
19604                x,
19605                hn,
19606                logits_stage,
19607                token_d,
19608                pos_d,
19609                token_hist,
19610                hist_idx,
19611                ..
19612            } = &mut *state;
19613            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
19614            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
19615            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
19616            // argmax_gate-validated), the id lands in the history ring, and pos advances on
19617            // device — consecutive launches chain with NO host sync. Single-token mode
19618            // overwrites token_d/pos_d from the host before each launch, so these nodes are
19619            // harmless there.
19620            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
19621            e.u32_hist_append(token_d, token_hist, hist_idx)?;
19622            e.inc_i32(pos_d)?;
19623            Ok(())
19624        })?;
19625
19626        let graph = crate::tp::token_graph_build_finish()?;
19627        state.graphs.push((bucket_max, graph));
19628        eprintln!(
19629            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
19630             build_ms={:.0} performance_claim=false",
19631            started.elapsed().as_secs_f64() * 1e3
19632        );
19633        Ok(())
19634    }
19635}