Skip to main content

memra_engine/
hybrid_forward.rs

1//! Hybrid forward pass (Stage-1, f32, prefill, single sequence). Per layer dispatches to a
2//! linear-attention (Gated DeltaNet) or full-attention mixer, then SwiGLU FFN. Matches
3//! llama.cpp src/models/qwen35.cpp node-for-node.
4
5use crate::Engine;
6use crate::cache::Cache;
7use cudarc::driver::CudaSlice;
8use memra_gguf::config::ModelConfig;
9
10/// Resident trunk transients for the eager prime (piecewise-graph foundation; see
11/// HybridModel::prime_slabs). Every live buffer prefix is fully overwritten before use per prime;
12/// capacity beyond the current token count must never cross a shape-sensitive boundary.
13pub struct PrimeSlabs {
14    pub t_cap: usize,
15    pub h: CudaSlice<f32>,
16    pub x1: CudaSlice<f32>,
17    pub z: CudaSlice<f32>,
18    pub act: CudaSlice<f32>,
19    pub xa: CudaSlice<f32>,
20    pub xb: CudaSlice<f32>,
21    pub h16: CudaSlice<u8>,
22    pub z16: CudaSlice<u8>,
23    /// piecewise boundary slabs (increment 2): GEMM outputs land here so the
24    /// downstream captured segments see fixed addresses.
25    pub gate: CudaSlice<f32>, // t * n_ff_max
26    pub up: CudaSlice<f32>,      // t * n_ff_max
27    pub ffn_out: CudaSlice<f32>, // t * n_embd
28    /// piecewise increment 3: per-layer S-glue segment graphs (down-add + next
29    /// attn-norm, ALL-slab IO, zero in-graph allocations -> keeperless capture is
30    /// clean). Baked at this t_cap; replay only when t == t_cap. seg_glue[il] fires
31    /// between layer il and il+1 (ping-pong parity is deterministic per il).
32    pub seg_glue: Vec<Option<cudarc::driver::CudaGraph>>,
33    /// increment 5 (core-split edition): the mixer out-GEMM writes _into_ `mixed`
34    /// directly (no staging copy — the increment-4 copy route was refuted), making
35    /// S-mid [add + post-norm] all-slab and capturable.
36    pub mixed: CudaSlice<f32>,
37    pub seg_mid: Vec<Option<cudarc::driver::CudaGraph>>,
38    pub seg_t: usize,
39}
40
41// Split prime ranges cannot enter the full-range segment-graph arm, and every slab access
42// is serialized by its device mutex after binding that device's CUDA context on the thread.
43unsafe impl Send for PrimeSlabs {}
44
45/// Shared-expert gate+up at t==1: NVFP4 fused2 (the ornith15/qwen35moe NVFP4 mints keep
46/// gate/up_shexp uniformly NVFP4, so the Q8-only fused2 never fired there and the pair fell
47/// to two mr2 singles + two re-quantizes of the same z — 2 of the 8 unfused launches/layer
48/// the orndecode B=1 census ranked at 17.1%), else the Q8_0 fused2 (the Q8 35B mint), else
49/// two singles. ONE helper for all three shexp dispatch sites — the MEMRA_GDN_MMA
50/// three-read-sites defect is the precedent for not inlining this thrice. Fusion law
51/// everywhere: per (tensor,row) the fused seg body is verbatim, so fused == singles
52/// bit-identically, and the shared (zq, zd) is the same quantize each single recomputes.
53fn shexp_gate_up_t1(
54    e: &Engine,
55    gate_shexp: &crate::model::GpuTensor,
56    up_shexp: &crate::model::GpuTensor,
57    z: &CudaSlice<f32>,
58    zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
59) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
60    let is_nvfp4 = |w: &crate::model::GpuTensor| matches!(w, crate::model::GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4);
61    if is_nvfp4(gate_shexp) && is_nvfp4(up_shexp) {
62        // Reuse the caller's t==1 z-quantize when one exists (the zq8 seam the dev arm
63        // already consumes) — the helper's own quantize is the identical kernel on the
64        // identical input, so this drops one launch per MoE layer without moving a byte.
65        let pair = match zq8 {
66            Some((zq, zd)) => e.matmul_nvfp4_fused2(gate_shexp, up_shexp, zq, zd, 1)?,
67            None => {
68                let (zq, zd) = e.quantize_q8_1(z, 1, gate_shexp.in_features())?;
69                e.matmul_nvfp4_fused2(gate_shexp, up_shexp, &zq, &zd, 1)?
70            }
71        };
72        if let Some(pair) = pair {
73            return Ok(pair);
74        }
75    }
76    match e.matmul_q8_fused2_x(gate_shexp, up_shexp, z)? {
77        Some(pair) => Ok(pair),
78        None => Ok((e.matmul(gate_shexp, z, 1)?, e.matmul(up_shexp, z, 1)?)),
79    }
80}
81
82fn active_matrix_values(
83    available: usize,
84    rows: usize,
85    columns: usize,
86    label: &str,
87) -> Result<usize, String> {
88    let required = rows
89        .checked_mul(columns)
90        .ok_or_else(|| format!("{label} shape overflows: {rows}x{columns}"))?;
91    if available < required {
92        return Err(format!(
93            "{label} has {available} values, fewer than the active {rows}x{columns} ({required})"
94        ));
95    }
96    Ok(required)
97}
98
99fn step_grouped_decode_shape(prefill: bool, tokens: usize) -> bool {
100    !prefill && tokens == 1
101}
102
103fn parse_step_ep_grouped_prefill(value: Option<&str>) -> Result<bool, String> {
104    match value {
105        None | Some("") | Some("0") => Ok(false),
106        Some("1") => Ok(true),
107        Some(value) => Err(format!(
108            "MEMRA_STEP_EP_GROUPED_PREFILL={value:?} is invalid; expected 0 or 1"
109        )),
110    }
111}
112
113fn step_ep_grouped_prefill_enabled() -> Result<bool, String> {
114    parse_step_ep_grouped_prefill(
115        std::env::var("MEMRA_STEP_EP_GROUPED_PREFILL")
116            .ok()
117            .as_deref(),
118    )
119}
120
121fn step_grouped_prefill_shape(enabled: bool, prefill: bool, tokens: usize) -> bool {
122    enabled && prefill && (PRIME_MIN_T..=crate::cache::PRIME_CHUNK_MAX_TOKENS).contains(&tokens)
123}
124
125fn parse_step_tp_prefill(value: Option<&str>) -> Result<bool, String> {
126    match value {
127        None | Some("") | Some("0") => Ok(false),
128        Some("1") => Ok(true),
129        Some(value) => Err(format!(
130            "MEMRA_STEP_TP_PREFILL={value:?} is invalid; expected 0 or 1"
131        )),
132    }
133}
134
135fn step_tp_prefill_enabled() -> Result<bool, String> {
136    parse_step_tp_prefill(std::env::var("MEMRA_STEP_TP_PREFILL").ok().as_deref())
137}
138
139fn validate_step_prime_batch_modes(tp_prefill: bool, grouped_prefill: bool) -> Result<(), String> {
140    if grouped_prefill && !tp_prefill {
141        return Err("MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into());
142    }
143    if tp_prefill {
144        return Err(
145            "Step TP4 cross-request prime batching did not clear the live-server performance \
146             gate; use per-session grouped prefill"
147                .into(),
148        );
149    }
150    Ok(())
151}
152
153fn step_tp_prefill_shape(
154    enabled: bool,
155    tokens: usize,
156    ranks: usize,
157    native_p2p: bool,
158    has_rank_local_attention: bool,
159    fp8_kv: bool,
160) -> bool {
161    // TP2 admitted 2026-08-25 behind the same off-by-default door. The prefill body is
162    // rank-count-generic (every geometry check divides by `ranks`); only this shape ever
163    // named 4. TP4's high-context NO-GO was a TRANSPORT verdict — token-row column
164    // gathers plus remote O blocks issue ~61,440 peer copies per 4K attention layer on
165    // that placement — which is not evidence about a 2-card native-P2P placement that
166    // reduces O rank-locally. TP2 is UNQUALIFIED until its own prefill argmax + TTFT
167    // receipts land; the door stays off by default.
168    enabled
169        && tokens >= PRIME_MIN_T
170        && matches!(ranks, 2 | 4)
171        && native_p2p
172        && has_rank_local_attention
173        && !fp8_kv
174}
175
176fn empty_cache_layers<T>(n: usize) -> Vec<Option<T>> {
177    std::iter::repeat_with(|| None).take(n).collect()
178}
179
180/// Temporarily move a PP-2 cache's layer state into two independently-owned cache shells.
181/// The stage walkers then receive disjoint `&mut Cache` values and can run on separate host
182/// threads without aliasing. GPU buffers are moved, not copied; Drop restores every layer
183/// and publishes the last position completed by both stages.
184struct PrimeCacheStages<'a> {
185    parent: &'a mut Cache,
186    cut: usize,
187    stage0: Cache,
188    stage1: Cache,
189}
190
191impl<'a> PrimeCacheStages<'a> {
192    fn new(parent: &'a mut Cache, cut: usize) -> Self {
193        let n = parent.kv.len();
194        assert_eq!(parent.recur.len(), n, "cache layer vectors disagree");
195        assert!(cut <= n, "PP-2 cache cut {cut} exceeds {n} layers");
196        let mut kv0 = empty_cache_layers(n);
197        let mut kv1 = empty_cache_layers(n);
198        let mut tp_kv0 = empty_cache_layers(n);
199        let mut tp_kv1 = empty_cache_layers(n);
200        let mut recur0 = empty_cache_layers(n);
201        let mut recur1 = empty_cache_layers(n);
202        for i in 0..cut {
203            kv0[i] = parent.kv[i].take();
204            tp_kv0[i] = parent.tp_kv[i].take();
205            recur0[i] = parent.recur[i].take();
206        }
207        for i in cut..n {
208            kv1[i] = parent.kv[i].take();
209            tp_kv1[i] = parent.tp_kv[i].take();
210            recur1[i] = parent.recur[i].take();
211        }
212        let pos = parent.pos;
213        let max_ctx = parent.max_ctx;
214        Self {
215            parent,
216            cut,
217            stage0: Cache {
218                kv: kv0,
219                tp_kv: tp_kv0,
220                recur: recur0,
221                pos,
222                max_ctx,
223                last_logits_dev: None,
224                dflash_taps: None,
225            },
226            stage1: Cache {
227                kv: kv1,
228                tp_kv: tp_kv1,
229                recur: recur1,
230                pos,
231                max_ctx,
232                last_logits_dev: None,
233                dflash_taps: None,
234            },
235        }
236    }
237
238    fn parts(&mut self) -> (&mut Cache, &mut Cache) {
239        (&mut self.stage0, &mut self.stage1)
240    }
241}
242
243impl Drop for PrimeCacheStages<'_> {
244    fn drop(&mut self) {
245        let n = self.parent.kv.len();
246        for i in 0..n {
247            let source = if i < self.cut {
248                &mut self.stage0
249            } else {
250                &mut self.stage1
251            };
252            debug_assert!(self.parent.kv[i].is_none());
253            debug_assert!(self.parent.tp_kv[i].is_none());
254            debug_assert!(self.parent.recur[i].is_none());
255            self.parent.kv[i] = source.kv[i].take();
256            self.parent.tp_kv[i] = source.tp_kv[i].take();
257            self.parent.recur[i] = source.recur[i].take();
258        }
259        self.parent.pos = self.stage0.pos.min(self.stage1.pos);
260    }
261}
262
263/// task #18 (attn side): one sequence's pre-attention outputs (post-rope q/k, v, out-gate).
264pub(crate) struct AttnPre {
265    pub q: cudarc::driver::CudaSlice<f32>,
266    pub k: cudarc::driver::CudaSlice<f32>,
267    pub v: cudarc::driver::CudaSlice<f32>,
268    pub gate: Option<cudarc::driver::CudaSlice<f32>>,
269}
270
271/// task #18: one sequence's GDN prep outputs (the scan inputs).
272pub(crate) struct GdnPrep {
273    pub hk: usize,
274    pub q_l2: cudarc::driver::CudaSlice<f32>,
275    pub k_l2: cudarc::driver::CudaSlice<f32>,
276    pub v_g: cudarc::driver::CudaSlice<f32>,
277    pub beta: cudarc::driver::CudaSlice<f32>,
278    pub g_log: cudarc::driver::CudaSlice<f32>,
279    pub kb16: Option<cudarc::driver::CudaSlice<u8>>,
280    pub qb16: Option<cudarc::driver::CudaSlice<u8>>,
281}
282
283/// Device scratch for the burst verify stream (see `verify_stream_scratch`).
284pub(crate) struct VerifyStreamScratch {
285    pub pos_d: CudaSlice<i32>,
286    pub row_ctrs: Vec<CudaSlice<i32>>,
287}
288use crate::hybrid::{FullAttnLayer, HybridModel, LinearAttnLayer, Mixer, MoeWeights};
289
290struct MoeInputTraceWriter {
291    dir: std::path::PathBuf,
292    index: std::fs::File,
293    payloads: std::collections::HashMap<u16, (std::fs::File, u64)>,
294}
295
296static MOE_INPUT_TRACE_WRITER: std::sync::OnceLock<std::sync::Mutex<Option<MoeInputTraceWriter>>> =
297    std::sync::OnceLock::new();
298
299/// STAGE-2 GROUPED DECODE gate (MEMRA_MOE_GDEC, default ON; `=0` restores the sequential
300/// per-expert launch chain). See `moe_gdec_token`.
301fn gdec_enabled() -> bool {
302    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
303    *E.get_or_init(|| {
304        std::env::var("MEMRA_MOE_GDEC")
305            .map(|v| v != "0")
306            .unwrap_or(true)
307    })
308}
309
310/// SLAB-LOCAL RESIDENT ARM gate (lane/pp-leverb 2026-08-08, MEMRA_MOE_SLAB, default ON;
311/// `=0` restores the SLRU dispatch even when resident slabs exist). Read PER CALL, never
312/// memoized — probes A/B the two provenances in one process (the MEMRA_PRIME_PP pattern).
313/// See `moe_ffn_sequential_zq8`'s slab_local arm: the sigmoid-router archs (step35/M3/Hy3)
314/// are denied every `dev_exps` consumer (pairs/dev route softmax), so before this arm the
315/// fits-VRAM resident slabs were UPLOADED for them but never READ — the SLRU kept staging
316/// the same bytes beside a dead copy (37 GB H2D per pp4096 prime on the Step SKU, anatomy
317/// receipt). The arm reads the SAME bytes through the SAME kernels; only the pointer
318/// PROVENANCE changes (slab base + ex*stride vs SLRU slot address) — the bit-identity class
319/// `moe_ffn_dev`'s resident arm already documents against its SLRU arm.
320fn moe_slab_enabled() -> bool {
321    std::env::var("MEMRA_MOE_SLAB").as_deref() != Ok("0")
322}
323
324/// Expert-grouped dispatch remains opt-in after the local 5090 transfer gate rejected the
325/// default flip. `=0` selects the established path, while any other explicit value enables the
326/// grouped research arm for the current call.
327fn moe_grouped_enabled(_cfg: &ModelConfig, _prefill: bool) -> bool {
328    std::env::var("MEMRA_MOE_GROUPED")
329        .map(|value| value != "0")
330        .unwrap_or(false)
331}
332
333/// Deterministic in-token expert prefetch. `MEMRA_MOE_PREFETCH=1` overlaps memory-source H2D on the
334/// copy stream; selecting the opt-in worker spill backend enables the same known-next hook for disk.
335fn moe_prefetch_enabled() -> bool {
336    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
337    *E.get_or_init(|| {
338        std::env::var("MEMRA_MOE_PREFETCH").as_deref() == Ok("1")
339            || crate::spill_pread::worker_enabled()
340    })
341}
342
343/// Best-effort OS page-cache prefetch distance for mmap-backed expert ranges. Independent of the
344/// H2D copy-stream experiment so storage->RAM and RAM->HBM overlap can be measured separately.
345/// The opt-in default stays one expert to preserve the original experiment; spill rigs can widen
346/// it with `MEMRA_MOE_PAGE_PREFETCH_WINDOW` to cover NVMe latency.
347fn moe_page_prefetch_window() -> usize {
348    static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
349    *W.get_or_init(|| {
350        page_prefetch_window_from_values(
351            std::env::var("MEMRA_MOE_PAGE_PREFETCH").as_deref() == Ok("1"),
352            std::env::var("MEMRA_MOE_PAGE_PREFETCH_WINDOW")
353                .ok()
354                .as_deref(),
355        )
356    })
357}
358
359fn page_prefetch_window_from_values(enabled: bool, raw_window: Option<&str>) -> usize {
360    if !enabled {
361        return 0;
362    }
363    raw_window.and_then(|value| value.parse().ok()).unwrap_or(1)
364}
365
366/// Return only the newly exposed positions in a rolling lookahead window. Position zero seeds the
367/// full window; each later position adds one expert at the far edge. Thus widening the window does
368/// not repeatedly issue `MADV_WILLNEED` for the same range.
369fn page_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
370    if window == 0 || position >= len {
371        return len..len;
372    }
373    let (start, count) = if position == 0 {
374        (1, window)
375    } else {
376        (position.saturating_add(window), 1)
377    };
378    let start = start.min(len);
379    start..start.saturating_add(count).min(len)
380}
381
382/// Grouped worker-I/O schedule: prime the first active expert before the loop, then queue exactly
383/// one known-next expert at each iteration. Returning positions keeps expert ordering authoritative.
384fn grouped_worker_prefetch_position(order_len: usize, current: Option<usize>) -> Option<usize> {
385    let position = current.map_or(0, |position| position.saturating_add(1));
386    (position < order_len).then_some(position)
387}
388
389/// Fill the worker ring with complete experts, retaining one pinned buffer for an unexpected
390/// demand miss. Each expert has gate/up/down extents, so depth 16 admits a rolling five-expert
391/// window. Position zero primes the current expert too: its three independent reads can run in
392/// parallel instead of demand-serializing gate, up, and down before any useful GPU work exists.
393fn worker_prefetch_window() -> usize {
394    static WINDOW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
395    *WINDOW.get_or_init(|| {
396        let automatic = crate::spill_pread::configured_depth().saturating_sub(1) / 3;
397        std::env::var("MEMRA_SPILL_WORKER_EXPERT_WINDOW")
398            .ok()
399            .and_then(|value| value.parse::<usize>().ok())
400            .unwrap_or(automatic.max(1))
401    })
402}
403
404/// Return only positions newly exposed by a rolling worker-I/O window. Unlike mmap page advice,
405/// this includes the current expert when the window is seeded so all three current projections
406/// enter the CPU pool together.
407fn worker_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
408    if window == 0 || position >= len {
409        return len..len;
410    }
411    let (start, count) = if position == 0 {
412        (0, window)
413    } else {
414        (position.saturating_add(window).saturating_sub(1), 1)
415    };
416    let start = start.min(len);
417    start..start.saturating_add(count).min(len)
418}
419
420/// LAUNCH-STRUCTURE STAGE 3 gate (MEMRA_MOE_DEV, default ON; `=0` restores host routing). The
421/// zero-DtoH device-dispatch path for fully-resident layers: router top-k output stays on device,
422/// expert weight pointers come from the per-layer device table. Requires the fused router (the
423/// dev path consumes the device sel/w directly), so MEMRA_FUSED_ROUTER=0 also disables it.
424fn moe_dev_enabled() -> bool {
425    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
426    *E.get_or_init(|| {
427        std::env::var("MEMRA_MOE_DEV")
428            .map(|v| v != "0")
429            .unwrap_or(true)
430            && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0"))
431    })
432}
433
434/// Device sigmoid top-k is the default for Step-3.7 / M3 / Hy3 / GLM-DSA. `MEMRA_SIG_ROUTER=0` restores
435/// the full-logit DtoH plus `moe_route_sigmoid_host` oracle without changing expert dispatch.
436fn sigmoid_router_enabled() -> bool {
437    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
438    *E.get_or_init(|| {
439        std::env::var("MEMRA_SIG_ROUTER")
440            .map(|v| v != "0")
441            .unwrap_or(true)
442    })
443}
444
445/// MoE EXPERT dp4a gate (MEMRA_MOE_Q8, default ON; `=0` restores the Stage-A f32-dequant expert
446/// kernels). Applies when gate/up/down expert qtypes are all in the dp4a body set (IQ3_S/IQ4_XS).
447/// FP-order differs from Stage-A (int dp4a + warp tree) — argmax/run-gen/stream-identity gates
448/// arbitrate; the sequential and fused q8 paths ship as a matched pair (MEMRA_MOE_GATE contract).
449fn moe_q8_enabled() -> bool {
450    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
451    *E.get_or_init(|| {
452        std::env::var("MEMRA_MOE_Q8")
453            .map(|v| v != "0")
454            .unwrap_or(true)
455    })
456}
457
458/// gemma4 fast-arm gate: qtypes with an `expert_dot_g` dp4a body (superset used by the gelu
459/// dev arm; the qwen q8 arms keep their own battery-gated q8_expert_supported policy).
460fn expert_dp4a_supported(qt: i32) -> bool {
461    qt == crate::QT_Q4_0
462        || qt == crate::QT_IQ3_S
463        || qt == crate::QT_IQ4_XS
464        || qt == crate::QT_Q3_K
465        || qt == crate::QT_Q4_K
466        || qt == crate::QT_Q6_K
467}
468
469fn q8_expert_supported(qt: i32) -> bool {
470    // k-quant arms added 2026-07-06 (Q3_K/Q4_K/Q6_K bodies for the UD tail layers). Briefly
471    // default-excluded the same day when they appeared to break 35B real-prompt spec — the
472    // ACTUAL culprit was the MoE router's cuBLASLt n-dependence (d994271); with the router
473    // decode-exact at verify t, the k-quant arms pass the full spec battery (p1/p2/p3 + raw
474    // K=1..8) and are DEFAULT ON again (+9 tok/s: 148.9 -> 157.9). MEMRA_MOE_Q8_KQ=0 excludes.
475    static KQ: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
476    let kq = *KQ.get_or_init(|| {
477        std::env::var("MEMRA_MOE_Q8_KQ")
478            .map(|v| v != "0")
479            .unwrap_or(true)
480    });
481    // NVFP4 experts: DEFAULT ON (2026-07-17). The M3-era "decode-vs-verify MISMATCH 3.4e1"
482    // that had this excluded was the missing per-expert macro-scale fold, fixed in the
483    // dev-kernel epilogues + moe_w_scale_by_expert; the 35B ct-NVFP4 artifact now runs the
484    // q8 arm at parity with the IQ4_XS daily (174-178 tok/s, spec K=1..8 exact). M3/Hy3
485    // never reach the q8 arms regardless (sigmoid-router cfg gates on pairs/dev/gdec).
486    // MEMRA_MOE_Q8_NVFP4=0 restores the f32 arm.
487    let nvfp4_q8 = std::env::var("MEMRA_MOE_Q8_NVFP4")
488        .map(|v| v != "0")
489        .unwrap_or(true);
490    qt == crate::QT_IQ3_S
491        || qt == crate::QT_IQ4_XS
492        || (nvfp4_q8 && qt == crate::QT_NVFP4)
493        || (kq && (qt == crate::QT_Q3_K || qt == crate::QT_Q4_K || qt == crate::QT_Q6_K))
494}
495
496/// The decode-once (_dec) and IQ-MMA expert kernels dequant via IQ-specific extractors —
497/// k-quant tensors must fall to the _em dot path instead.
498fn q8_expert_dec_supported(qt: i32) -> bool {
499    qt == crate::QT_IQ3_S || qt == crate::QT_IQ4_XS || qt == crate::QT_Q4_0
500}
501
502/// Grouped-f16 door (MEMRA_MOE_F16G) per-projection admission: the qtype has a dequant-to-f16
503/// kernel in cu/moe_f16_grouped.cu AND the projection's k dimension tiles its block size.
504/// Round 49 widened coverage to q35's UD mix (gate/up IQ3_S x39 + Q3_K x1 + IQ4_XS x1; down
505/// IQ4_XS x37 + Q6_K x3 + Q4_K x1) — the round-47 IQ4_XS/Q4_0-only table admitted ~1 of 41
506/// q35 layers, which is why that cell measured FLAT.
507fn f16g_proj_ok(qt: i32, in_f: usize) -> bool {
508    match qt {
509        crate::QT_Q4_0 => in_f % 32 == 0,
510        crate::QT_IQ4_XS | crate::QT_IQ3_S | crate::QT_Q3_K | crate::QT_Q4_K | crate::QT_Q6_K => {
511            in_f % 256 == 0
512        }
513        // NVFP4 (block 64) added lane/moebatch-q35moe 2026-08-21: the ornith15 expert bank is
514        // uniform NVFP4, which passed the pairs q8 gate but missed BOTH batched doors
515        // (use_mma's dec set and this table), so 14.7k-token prefill rode the per-pair _em
516        // fallback — 88.6% of the prime wall (prime-anatomy receipt).
517        crate::QT_NVFP4 => in_f % 64 == 0,
518        _ => false,
519    }
520}
521
522/// STAGE 3 prewarm gate (MEMRA_MOE_PREWARM, default ON; `=0` leaves residency organic). One-shot
523/// per layer: force-admit every block while FREE slots cover the whole layer (never evicts).
524fn moe_prewarm_enabled() -> bool {
525    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
526    *E.get_or_init(|| {
527        std::env::var("MEMRA_MOE_PREWARM")
528            .map(|v| v != "0")
529            .unwrap_or(true)
530    })
531}
532
533/// During a discarded fixed-residency profile, admit CPU-routed misses after their current-token
534/// CPU result is complete. The current result and numeric path are unchanged; later warmup tokens
535/// can then vote for and exercise those experts on GPU before the cache is frozen.
536fn cpu_expert_profile_admit_enabled() -> bool {
537    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
538    *E.get_or_init(|| std::env::var("MEMRA_CPU_EXPERT_FREEZE_PROFILE_ADMIT").as_deref() == Ok("1"))
539}
540
541/// Minimum prompt length for the BATCHED cache prime (`prime_cache`). Below this the tokenwise
542/// decode loop wins anyway (the batched path's GEMM dispatch needs m>=16, and the stateful conv
543/// kernel needs T >= d_conv-1). Callers: generate / generate_spec.
544pub const PRIME_MIN_T: usize = 16;
545
546/// MEMRA_STEP_GEMM_PRIME_SUFFIX: does a CONTINUATION prime (`cache.pos > 0` — a rewound
547/// session's suffix, or a prompt remainder split across scheduler ticks) ride the batched
548/// GEMM prime, like a fresh prompt does?
549///
550/// DEFAULT ON since 2026-08-29, by decision, under the flip bar the OFF-era FLAGS row
551/// wrote down (never byte identity — a prime-decomposition m-dependence that EVERY
552/// measured prime path shares, walk included, bars that gate for all of them):
553///  1. vendor-default sampled rows: the blind, rubric-pre-registered 8-turn quality A/B
554///     (research/step37-sampled-quality-20260828, 72/72 valid rows, engagement receipts
555///     per row) — WARM-GEMM sits inside COLD's own self-spread at t4 and t8 (t8 carried
556///     at n=16; the round-1 walk-over-gemm signal collapsed at p~0.91).
557///  2. the 8-turn cache-on twin: warm TTFT 0.58 s (door) vs 7.15 s (walk) on the real
558///     warm serving shape, zero faults.
559///  3. the batched prime's own standard: acceptance 0.80-0.86 across all arms with the
560///     door arm highest at t8; interleaved arms; zero ILLEGAL/#87/panics in 19 boots.
561/// Precondition shipped first: the SWA-ring checkpoint restore fix (c9a617ca99) — real
562/// session reuse crosses the grow path before any door question matters.
563/// Why it is worth it, measured: the walk continuation costs 5.5978 ms/suffix-token
564/// against this path's 0.99 ms/token (five-point sweep, R^2 0.9976), a 7.97x suffix
565/// slope collapse.
566///
567/// The `seq_end` fix beneath is NOT gated on this door — it is unconditional, because
568/// the chunk-local `seq_end` it replaced is wrong for a fresh prompt of 4096+k tokens
569/// (k in [PRIME_MIN_T, 512)) with no continuation anywhere in sight.
570///
571/// `=0` is the kill switch (continuations back on the walk, fresh primes keep the fast
572/// path); `=1` forces; `MEMRA_STEP_GEMM_PRIME=0` remains the whole-path seam. Read per
573/// call, not cached — probes flip it in process.
574fn step_gemm_prime_suffix_on() -> bool {
575    std::env::var("MEMRA_STEP_GEMM_PRIME_SUFFIX").as_deref() != Ok("0")
576}
577
578/// Widest tick the MoE DEV per-token program serves (lane/orndecode-20260822). PRIME_MIN_T
579/// doubled as the dev-arm's upper bound on the assumption that t==16 only ever meant real
580/// prefill; the exact-16 decode tier broke that assumption — at B=16 the MoE stage crossed
581/// onto the t>=MMA_T grouped/kq GEMM program (m_e ~1.6 rows/expert: 52.6% of the tick at
582/// ~104 us/launch) or the `_em` per-pair fallback (67.7 us), both catastrophically slower
583/// than the dev q8 kernels that serve B<=8 (8.8 us gate_up covering a token's whole expert
584/// set). Decode widths 2..=16 now ride dev; the grouped/pairs prefill programs start at 17.
585/// gate2/gate3 byte batteries at B=12/16 are the qualification (bit-checked vs isolated).
586const MOE_DEV_MAX_T: usize = 16;
587const PRIME_PIPE_MICROBATCHES: usize = 8;
588const PRIME_PIPE_MIN_CHUNK: usize = 128;
589const PRIME_PIPE_EDGE_MIN_CHUNK: usize = 64;
590const PRIME_PIPE_LINEAR_WORK: usize = 8;
591
592fn prime_pp2_auto_geometry(n_layers: usize) -> bool {
593    crate::pp::prime_pp_on()
594        && !crate::pp::pp2_streams_off()
595        && crate::pp::pp_cuts(n_layers).is_some_and(|cuts| cuts.len() == 3)
596}
597
598/// Effective internal prime chunk. An explicit MEMRA_PRIME_CHUNK is authoritative.
599/// Naked PP-2 primes use the measured pipeline geometry: up to eight microchunks, never
600/// below 128 tokens, while the legacy 4096-token cap remains the long-context bound.
601pub fn prime_chunk_tokens(t: usize, n_layers: usize) -> usize {
602    if let Ok(value) = std::env::var("MEMRA_PRIME_CHUNK") {
603        let parsed = value
604            .parse::<usize>()
605            .unwrap_or(crate::cache::PRIME_CHUNK_MAX_TOKENS);
606        return if crate::cache::swa_ring_on() {
607            if parsed == 0 {
608                crate::cache::PRIME_CHUNK_MAX_TOKENS
609            } else {
610                parsed.min(crate::cache::PRIME_CHUNK_MAX_TOKENS)
611            }
612        } else {
613            parsed
614        };
615    }
616    let chunk = crate::cache::PRIME_CHUNK_MAX_TOKENS;
617    if prime_pp2_auto_geometry(n_layers) && t >= 2 * PRIME_PIPE_MIN_CHUNK {
618        chunk.min(
619            t.div_ceil(PRIME_PIPE_MICROBATCHES)
620                .max(PRIME_PIPE_MIN_CHUNK),
621        )
622    } else {
623        chunk
624    }
625}
626
627fn fixed_prime_chunk_ranges(t: usize, chunk: usize) -> Vec<(usize, usize)> {
628    fixed_prime_chunk_ranges_for_ring(t, chunk, crate::cache::swa_ring_on())
629}
630
631fn fixed_prime_chunk_ranges_for_ring(t: usize, chunk: usize, ring_on: bool) -> Vec<(usize, usize)> {
632    if chunk == 0 || t <= chunk {
633        return vec![(0, t)];
634    }
635    let mut ranges = Vec::with_capacity(t.div_ceil(chunk));
636    let mut start = 0usize;
637    while start < t {
638        let mut end = (start + chunk).min(t);
639        if t - end > 0 && t - end < PRIME_MIN_T {
640            if ring_on {
641                let shifted = t - PRIME_MIN_T;
642                end = if shifted > start { shifted } else { t };
643            } else {
644                end = t;
645            }
646        }
647        ranges.push((start, end));
648        start = end;
649    }
650    ranges
651}
652
653fn prime_chunk_work(prefix: usize, total: usize) -> u128 {
654    let prefix = prefix as u128;
655    prefix * (prefix + (PRIME_PIPE_LINEAR_WORK as u128) * (total as u128))
656}
657
658fn dynamic_prime_chunk_ranges(
659    t: usize,
660    fixed_chunk: usize,
661    fixed: &[(usize, usize)],
662) -> Vec<(usize, usize)> {
663    let n = fixed.len();
664    if n < 3 {
665        return fixed.to_vec();
666    }
667
668    let max_first = t - (n - 1) * PRIME_MIN_T;
669    let first = fixed_chunk
670        .div_ceil(2)
671        .max(PRIME_PIPE_EDGE_MIN_CHUNK)
672        .min(max_first);
673    let mut ranges = Vec::with_capacity(n);
674    ranges.push((0, first));
675
676    let first_work = prime_chunk_work(first, t);
677    let work_span = prime_chunk_work(t, t) - first_work;
678    let denominator = (n - 1) as u128;
679    let mut previous = first;
680    for boundary in 1..n - 1 {
681        let target = first_work * denominator + work_span * (boundary as u128);
682        let remaining = n - 1 - boundary;
683        let mut low = previous + PRIME_MIN_T;
684        let mut high = t - remaining * PRIME_MIN_T;
685        while low < high {
686            let mid = low + (high - low) / 2;
687            if prime_chunk_work(mid, t) * denominator >= target {
688                high = mid;
689            } else {
690                low = mid + 1;
691            }
692        }
693        ranges.push((previous, low));
694        previous = low;
695    }
696    ranges.push((previous, t));
697    ranges
698}
699
700/// Internal prime ranges. The naked PP-2 pipeline defaults to a short-fill,
701/// equal-modeled-time schedule; MEMRA_PRIME_CHUNK_SCHED=fixed restores the measured
702/// equal-token ranges. An explicit MEMRA_PRIME_CHUNK always retains fixed semantics.
703///
704/// `gdn_grid`: the model runs the chunked GDN WY scan (`HybridModel::gdn_prime_grid_on`) —
705/// AUTO-scheduled internal boundaries are then snapped down to the WY-chunk grid
706/// (`align_prime_ranges_to_gdn`; the spec-longctx grid law, extended from serve splits to
707/// the PP prime microchunks). Explicit MEMRA_PRIME_CHUNK keeps its operator-authoritative
708/// (fixed, unaligned) semantics — the FLAGS caveat documents that identity contract.
709pub fn prime_chunk_ranges(t: usize, n_layers: usize, gdn_grid: bool) -> Vec<(usize, usize)> {
710    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
711    let chunk = prime_chunk_tokens(t, n_layers);
712    let fixed = fixed_prime_chunk_ranges(t, chunk);
713    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
714        Ok(value) => value == "dynamic",
715        Err(_) => true,
716    };
717    if explicit_chunk {
718        return fixed;
719    }
720    let ranges = if !dynamic || !prime_pp2_auto_geometry(n_layers) {
721        fixed
722    } else {
723        dynamic_prime_chunk_ranges(t, chunk, &fixed)
724    };
725    // MEMRA_PRIME_GRID_ALIGN=0 is the shared rollback seam of the grid law (same env the
726    // worker's serve-boundary alignment honors, read per call so gates can flip it
727    // in-process): the legacy off-grid auto schedule — the toothed cell's broken arm.
728    if gdn_grid && std::env::var("MEMRA_PRIME_GRID_ALIGN").as_deref() != Ok("0") {
729        align_prime_ranges_to_gdn(&ranges, t, Engine::gdn_chunk_size())
730    } else {
731        ranges
732    }
733}
734
735/// Snap AUTO prime-range internal boundaries DOWN to the GDN WY-chunk grid (lane/
736/// hermes-perf-fixes, 2026-08-23 — the missing helper the PP-auto-ranges finding names).
737///
738/// THE LAW THIS EXTENDS (measured, research/multiturn-cache-20260821/
739/// LONGCTX-EXACTNESS-20260821.md; the serve-split half already ships as the worker's
740/// `grid_align_boundary`): under the chunked WY scan a prompt primed as two calls split at
741/// L is bit-identical to the monolithic prime iff `L % gdn_chunk_size() == 0` — an off-grid
742/// call start shifts the fold grid and materializes recurrent state at a point the
743/// monolithic program never computes. The prime loop walks these ranges as separate
744/// `prime_layers` calls, so INTERNAL microchunk boundaries are the same seam: the PP-2
745/// auto geometry (`t.div_ceil(8).max(128)` fills, and every dynamic short-fill boundary)
746/// lands off the 32-token grid for most prompt lengths, which is exactly the
747/// chunk-value bit-identity the GDN lane falsified (FLAGS PRIME_CHUNK/SCHED caveat).
748///
749/// Boundaries only move DOWN (earlier is always semantically safe — same argument as the
750/// worker's alignment); a boundary that collapses onto its predecessor is dropped (ranges
751/// merge). The final range always ends at `t`. Aligning down only GROWS the tail
752/// remainder, so the fixed-schedule tail-merge rule is never re-violated. Cost bound: at
753/// most `c-1` tokens shift per boundary.
754pub fn align_prime_ranges_to_gdn(
755    ranges: &[(usize, usize)],
756    t: usize,
757    c: usize,
758) -> Vec<(usize, usize)> {
759    if c == 0 || ranges.len() < 2 {
760        return ranges.to_vec();
761    }
762    let mut out: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
763    let mut start = 0usize;
764    for (i, &(_, end)) in ranges.iter().enumerate() {
765        let e = if i + 1 == ranges.len() {
766            t
767        } else {
768            end / c * c
769        };
770        if e > start {
771            out.push((start, e));
772            start = e;
773        } // else: boundary collapsed onto its predecessor — merge into the next range
774    }
775    debug_assert_eq!(out.last().map(|&(_, e)| e), Some(t));
776    out
777}
778
779struct HeadSplit {
780    pin: u64,
781    w1: CudaSlice<u8>,
782    hn1: CudaSlice<f32>,
783    y1: CudaSlice<f32>,
784    logits_e: CudaSlice<f32>,
785    ev_hn: cudarc::driver::CudaEvent,
786    ev_done: cudarc::driver::CudaEvent,
787    raw_hn1: u64,
788    raw_y1: u64,
789    raw_logits_hi: u64,
790    /// SAMPLED-TAIL scratch (perturbed row + the filter's threshold/z/max slots + the row
791    /// index). Allocating these per token cost more than the split head saved: the first
792    /// sampled-split measurement came in at 78.25 tok/s against 78.96 for the unsplit head,
793    /// which is five allocations per token, not arithmetic.
794    samp: Option<SampScratch>,
795}
796
797struct SampScratch {
798    pb: CudaSlice<f32>,
799    th: CudaSlice<f32>,
800    z: CudaSlice<f32>,
801    mx: CudaSlice<f32>,
802    rows: CudaSlice<i32>,
803}
804/// HEAD-SPLIT workspace (host + device twins share it).
805static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
806
807/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
808/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
809/// input bits — rank1's local selection is bit-equal to the root's.
810#[allow(clippy::type_complexity)]
811static DEV1_ROUTER_REPS: std::sync::Mutex<
812    Option<(
813        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
814        Option<CudaSlice<f32>>,
815    )>,
816> = std::sync::Mutex::new(None);
817
818/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
819/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
820/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
821#[allow(clippy::type_complexity)]
822static SHEXP_D1_REPS: std::sync::Mutex<
823    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
824> = std::sync::Mutex::new(None);
825#[allow(clippy::type_complexity)]
826static SHEXP_D1_WS: std::sync::Mutex<
827    Option<(
828        (usize, usize),
829        CudaSlice<f32>,
830        CudaSlice<f32>,
831        CudaSlice<f32>,
832        cudarc::driver::CudaEvent,
833        cudarc::driver::CudaEvent,
834    )>,
835> = std::sync::Mutex::new(None);
836
837/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
838static SHEXP_OV_WS: std::sync::Mutex<
839    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
840> = std::sync::Mutex::new(None);
841
842impl HybridModel {
843    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
844    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
845    /// an off-grid prime-call boundary shifts the WY fold grid (see
846    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
847    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
848    pub fn gdn_prime_grid_on(&self) -> bool {
849        Engine::gdn_chunked_enabled()
850            && self
851                .layers
852                .iter()
853                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
854    }
855
856    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
857    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
858    /// (the device buffers must be addressable on both sides — the TP registry builds its
859    /// own Engine per rank, so this is a real seam, not a formality).
860    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
861        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
862    }
863
864    fn step35_tp_qkv(
865        &self,
866        e: &Engine,
867        fa: &FullAttnLayer,
868        h: &CudaSlice<f32>,
869        t: usize,
870    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
871        let Some(tp) = fa.step_tp_qkv.as_ref() else {
872            return Ok(None);
873        };
874        let values = active_matrix_values(
875            h.len(),
876            t,
877            self.cfg.n_embd as usize,
878            "Step TP QKV activation",
879        )?;
880        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
881        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
882        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
883        // round-trip on every execute that the peer transport exists to remove. The
884        // device twins are byte-identical by construction (the same bytes travel dtod
885        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
886        // The host arm below remains the transport for !native_p2p (host staging IS that
887        // transport) and for a root context this engine cannot address.
888        if Self::step35_tp_device_resident(e, tp) {
889            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
890            // on theirs (same context, different streams).
891            e.stream().synchronize()?;
892            let q = tp
893                .runtime
894                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
895            let k = tp
896                .runtime
897                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
898            let v = tp
899                .runtime
900                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
901            Self::step35_tp_log_once(tp, "qkv", "device-resident");
902            return Ok(Some(vec![q, k, v]));
903        }
904        let host = e.dtoh_view(&h.slice(0..values))?;
905        let q = if tp.runtime.native_p2p() {
906            tp.runtime
907                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
908        } else {
909            tp.runtime
910                .bf16_column_parallel_resident(&tp.q, &host, t)?
911                .gathered
912        };
913        let k = if tp.runtime.native_p2p() {
914            tp.runtime
915                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
916        } else {
917            tp.runtime
918                .bf16_column_parallel_resident(&tp.k, &host, t)?
919                .gathered
920        };
921        let v = if tp.runtime.native_p2p() {
922            tp.runtime
923                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
924        } else {
925            tp.runtime
926                .bf16_column_parallel_resident(&tp.v, &host, t)?
927                .gathered
928        };
929        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
930        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
931    }
932
933    /// One transport banner per (projection, transport) — the old per-call eprintln fired
934    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
935    /// unbouncing (the sibling grouped-EP path already learned this).
936    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
937        use std::sync::atomic::{AtomicBool, Ordering};
938        static LOGGED: [AtomicBool; 4] = [
939            AtomicBool::new(false),
940            AtomicBool::new(false),
941            AtomicBool::new(false),
942            AtomicBool::new(false),
943        ];
944        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
945        if LOGGED[idx].swap(true, Ordering::Relaxed) {
946            return;
947        }
948        eprintln!(
949            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
950             tensor_parallel=true attention_local=true kv_local=true transport={} \
951             native_p2p={} bulk_p2p={} activation={activation} \
952             output={} performance_claim=false (logged once per transport)",
953            tp.layer,
954            tp.devices,
955            tp.runtime.transport_label(),
956            tp.runtime.native_p2p(),
957            tp.runtime.bulk_p2p(),
958            if activation == "device-resident" {
959                "root-resident"
960            } else {
961                "root-readback"
962            },
963        );
964    }
965
966    fn step35_tp_o(
967        &self,
968        e: &Engine,
969        fa: &FullAttnLayer,
970        activation: &CudaSlice<f32>,
971        tokens: usize,
972    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
973        let Some(tp) = fa.step_tp_qkv.as_ref() else {
974            return Ok(None);
975        };
976        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
977        // of the attention output, no host O staging, root-resident reduction consumed in
978        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
979        if Self::step35_tp_device_resident(e, tp) {
980            e.stream().synchronize()?; // producer fence, as the QKV half
981            let output = tp
982                .runtime
983                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
984            Self::step35_tp_log_once(tp, "o", "device-resident");
985            return Ok(Some(output));
986        }
987        let host = e.dtoh(activation)?;
988        let output = if tp.runtime.native_p2p() {
989            tp.runtime
990                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
991        } else {
992            tp.runtime
993                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
994        };
995        Self::step35_tp_log_once(tp, "o", "host-canonical");
996        Ok(Some(e.htod(&output)?))
997    }
998
999    fn step35_o(
1000        &self,
1001        e: &Engine,
1002        fa: &FullAttnLayer,
1003        activation: &CudaSlice<f32>,
1004        tokens: usize,
1005    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1006        match self.step35_tp_o(e, fa, activation, tokens)? {
1007            Some(output) => Ok(output),
1008            None => e.matmul(&fa.wo, activation, tokens),
1009        }
1010    }
1011
1012    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
1013    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
1014    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
1015    /// (it forces a dtoh + host hash per layer).
1016    fn prime_trace_path() -> Option<&'static str> {
1017        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1018        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
1019            .as_deref()
1020    }
1021
1022    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
1023    /// each prime_layers stage and accumulates wall time per stage class, printed after
1024    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
1025    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
1026    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
1027    fn prime_anatomy_on() -> bool {
1028        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1029        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
1030    }
1031
1032    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
1033        static S: [std::sync::atomic::AtomicU64; 5] = [
1034            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
1035            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
1036            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
1037            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
1038            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
1039        ];
1040        &S
1041    }
1042
1043    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
1044    pub fn forward(
1045        &self,
1046        e: &Engine,
1047        tokens: &[u32],
1048    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1049        if self.is_gemma4_e4b() {
1050            return self.gemma4_e4b_forward(e, tokens, false);
1051        }
1052        if self.uses_gemma_program() {
1053            return self.gemma4_forward(e, tokens, false);
1054        }
1055        let cfg = &self.cfg;
1056        let n_embd = cfg.n_embd as usize;
1057        let t = tokens.len();
1058        let eps = cfg.rms_eps;
1059        let pos: Vec<i32> = (0..t as i32).collect();
1060        let pos_d = e.htod_i32(&pos)?;
1061
1062        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1063
1064        for (il, layer) in self.layers.iter().enumerate() {
1065            // attn_norm
1066            let mut h = e.uninit(t * n_embd)?;
1067            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1068
1069            let mixed = match &layer.mixer {
1070                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1071                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1072                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1073            };
1074
1075            // residual 1
1076            let mut x1 = e.uninit(t * n_embd)?;
1077            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1078
1079            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1080            let mut z = e.uninit(t * n_embd)?;
1081            e.rms_norm(
1082                &x1,
1083                layer.post_attn_norm.float_data(),
1084                &mut z,
1085                n_embd,
1086                t,
1087                eps,
1088            )?;
1089            let ffn_out = match &layer.ffn {
1090                crate::hybrid::Ffn::Dense {
1091                    ffn_gate,
1092                    ffn_up,
1093                    ffn_down,
1094                } => {
1095                    let n_ff = ffn_gate.out_features();
1096                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1097                    let up = g2.pop().unwrap();
1098                    let gate = g2.pop().unwrap();
1099                    let mut act = e.uninit(t * n_ff)?;
1100                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1101                    // both the dense MLP and the shared expert, and its limit is
1102                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1103                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1104                    Self::ffn_act_lim(
1105                        e,
1106                        &self.cfg,
1107                        &gate,
1108                        &up,
1109                        1.0,
1110                        1.0,
1111                        self.cfg.clamp_shexp_at(il as u32),
1112                        &mut act,
1113                        t * n_ff,
1114                    )?;
1115                    e.matmul(ffn_down, &act, t)?
1116                }
1117                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1118            };
1119            let mut x2 = e.uninit(t * n_embd)?;
1120            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1121            x = x2;
1122        }
1123
1124        let mut hn = e.uninit(t * n_embd)?;
1125        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1126        let logits = e.matmul(&self.output, &hn, t)?;
1127        Ok(e.dtoh(&logits)?)
1128    }
1129
1130    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1131    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1132    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1133    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1134    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1135    pub fn forward_last(
1136        &self,
1137        e: &Engine,
1138        tokens: &[u32],
1139    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1140        if self.uses_gemma_program() {
1141            return self.gemma4_forward(e, tokens, true);
1142        }
1143        let cfg = &self.cfg;
1144        let n_embd = cfg.n_embd as usize;
1145        let t = tokens.len();
1146        let eps = cfg.rms_eps;
1147        let pos: Vec<i32> = (0..t as i32).collect();
1148        let pos_d = e.htod_i32(&pos)?;
1149
1150        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1151        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1152        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1153        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1154        let anat = Self::prime_anatomy_on();
1155        let mut anat_last = if anat {
1156            e.stream().synchronize()?;
1157            Some(std::time::Instant::now())
1158        } else {
1159            None
1160        };
1161        macro_rules! anat_mark {
1162            ($slot:expr) => {
1163                if let Some(ts) = anat_last.as_mut() {
1164                    e.stream().synchronize()?;
1165                    Self::prime_anatomy_slots()[$slot].fetch_add(
1166                        ts.elapsed().as_nanos() as u64,
1167                        std::sync::atomic::Ordering::Relaxed,
1168                    );
1169                    *ts = std::time::Instant::now();
1170                }
1171            };
1172        }
1173        for (il, layer) in self.layers.iter().enumerate() {
1174            let mut h = e.uninit(t * n_embd)?;
1175            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1176            if probe {
1177                e.stream().synchronize()?;
1178                eprintln!("[probe] L{il} norm ok");
1179            }
1180            anat_mark!(4);
1181            let mixed = match &layer.mixer {
1182                Mixer::Full(fa) => {
1183                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1184                    anat_mark!(0);
1185                    y
1186                }
1187                Mixer::Linear(la) => {
1188                    let y = self.linear_attn(e, la, &h, t)?;
1189                    anat_mark!(1);
1190                    y
1191                }
1192                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1193            };
1194            if probe {
1195                e.stream().synchronize()?;
1196                eprintln!("[probe] L{il} mixer ok");
1197            }
1198            let mut x1 = e.uninit(t * n_embd)?;
1199            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1200            let mut z = e.uninit(t * n_embd)?;
1201            e.rms_norm(
1202                &x1,
1203                layer.post_attn_norm.float_data(),
1204                &mut z,
1205                n_embd,
1206                t,
1207                eps,
1208            )?;
1209            anat_mark!(4);
1210            let ffn_out = match &layer.ffn {
1211                crate::hybrid::Ffn::Dense {
1212                    ffn_gate,
1213                    ffn_up,
1214                    ffn_down,
1215                } => {
1216                    let n_ff = ffn_gate.out_features();
1217                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1218                    let up = g2.pop().unwrap();
1219                    let gate = g2.pop().unwrap();
1220                    let mut act = e.uninit(t * n_ff)?;
1221                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1222                    Self::ffn_act_lim(
1223                        e,
1224                        &self.cfg,
1225                        &gate,
1226                        &up,
1227                        1.0,
1228                        1.0,
1229                        self.cfg.clamp_shexp_at(il as u32),
1230                        &mut act,
1231                        t * n_ff,
1232                    )?;
1233                    let y = e.matmul(ffn_down, &act, t)?;
1234                    anat_mark!(3);
1235                    y
1236                }
1237                crate::hybrid::Ffn::Moe(m) => {
1238                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1239                    anat_mark!(2);
1240                    y
1241                }
1242            };
1243            if probe {
1244                e.stream().synchronize()?;
1245                eprintln!("[probe] L{il} ffn ok");
1246            }
1247            let mut x2 = e.uninit(t * n_embd)?;
1248            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1249            x = x2;
1250        }
1251        if anat {
1252            let s = Self::prime_anatomy_slots();
1253            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1254            eprintln!(
1255                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1256                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1257                ms(0),
1258                ms(1),
1259                ms(2),
1260                ms(3),
1261                ms(4)
1262            );
1263        }
1264        // norm over all T, then slice the LAST row and run lm_head on that single row.
1265        let mut hn = e.uninit(t * n_embd)?;
1266        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1267        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1268        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1269        let mut hlast = e.uninit(n_embd)?;
1270        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1271        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1272        Ok(e.dtoh(&logits)?)
1273    }
1274
1275    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1276    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1277    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1278    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1279    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1280    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1281    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1282    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1283    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1284    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1285    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1286    ///   (c) `cache.pos`/KV len/len_d advance by T.
1287    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1288    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1289    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1290    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1291    ///
1292    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1293    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1294    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1295    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1296    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1297    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1298    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1299    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1300    /// differently under load — research/tick-seg-20260807, receipt in
1301    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1302    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1303    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1304    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1305    /// caller that SPLITS one request across calls passes the remainder.
1306    pub fn prime_cache(
1307        &self,
1308        e: &Engine,
1309        tokens: &[u32],
1310        cache: &mut Cache,
1311        queued_after: usize,
1312    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1313        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1314    }
1315
1316    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1317    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1318    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1319    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1320    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1321    pub fn prime_cache_overlaid(
1322        &self,
1323        e: &Engine,
1324        tokens: &[u32],
1325        cache: &mut Cache,
1326        queued_after: usize,
1327        overlay: Option<&crate::vision::EmbedOverlay>,
1328    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1329        let n_embd = self.cfg.n_embd as usize;
1330        let t = tokens.len();
1331        // MEMRA_PRIME_TROWS=1: prefill through the same-session t-row walk (per-row t=1
1332        // program = the tokenwise-prime ORACLE class) — replaces the host-canonical
1333        // per-token step-TP prime. Text-only fresh primes; anything else falls through.
1334        // MEMRA_STEP_GEMM_PRIME: prime the prompt through the batched GEMM path, CHUNKED.
1335        // The batch entry supplies both halves of the fast prime — the GEMM trunk at m = chunk
1336        // and the grouped NVFP4 MoE — which is why routing only the MoE through the ordinary
1337        // chunk loop measured 26.9 s against 3.7 s here. Chunking keeps the transients bounded:
1338        // a whole 32k prompt in one call would build a 262144-pair CSR and ~4.3 GB of partials
1339        // per rank, the blow-up the chunked prime exists to prevent.
1340        //
1341        // CONTINUATION (lane/gemm-suffix, 2026-08-28): the entry NO LONGER requires
1342        // `cache.pos == 0`. The batch core has been continuation-capable since 7700e0b6
1343        // (positions carry each sequence's base; the fresh-prompt guard narrowed to B > 1),
1344        // and d99b2ea3 named this outer guard as the remaining blocker in its own message.
1345        // Every multi-turn suffix and every tick remainder was paying the walk's measured
1346        // ~7.2 ms/token against this path's ~1.0 ms/token, which is why session-affinity
1347        // reuse measured a 1.012x wash on a growing conversation.
1348        // ONE DEFECT HAD TO BE FIXED FIRST, and it was LIVE before this lift:
1349        // `step35_prime_batch_layers` passed `ts[s]` — the CHUNK's length — as `seq_end`.
1350        // `seq_end` is the REQUEST's absolute end position and it steers step35's SWA arm
1351        // (`seq_end > win`, win = 512 on step37). A chunk SHORTER than the window at a
1352        // NONZERO base therefore selected the UNWINDOWED FA arm over a view that the `off`
1353        // trim leaves at ~win-1+t rows: it attended OUTSIDE the sliding window. That was
1354        // already reachable with no continuation at all — a fresh prompt of 4096+k for k in
1355        // [PRIME_MIN_T, 512) ends in a trailing chunk of exactly that shape. `seq_end` is now
1356        // threaded from here (request-absolute, `+ queued_after`, computed ONCE before the
1357        // chunk loop, chunk-size-invariant exactly as on the walk), which is what makes the
1358        // suffix arm expressible at all rather than merely reachable.
1359        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1360        let seq_end = if legacy_calllocal {
1361            cache.pos + t
1362        } else {
1363            cache.pos + t + queued_after
1364        };
1365        // MEMRA_STEP_GEMM_PRIME_SUFFIX is the SUFFIX-ONLY seam: off (its default in this
1366        // commit) leaves continuations on the walk while fresh primes stay on the fast path;
1367        // MEMRA_STEP_GEMM_PRIME=0 is the whole-path seam. The `seq_end` threading above is
1368        // deliberately NOT behind either door — it is a correctness fix for the fresh path too.
1369        if overlay.is_none()
1370            && (cache.pos == 0 || step_gemm_prime_suffix_on())
1371            && t >= PRIME_MIN_T
1372            && crate::step_gemm_prime_on()
1373            && self.uses_sliding_gated_moe_program()
1374        {
1375            let n_embd = self.cfg.n_embd as usize;
1376            let base = cache.pos;
1377            let width = crate::cache::PRIME_CHUNK_MAX_TOKENS;
1378            let mut hiddens = e.uninit(t * n_embd)?;
1379            let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1380            let mut start = 0usize;
1381            while start < t {
1382                // A trailing chunk below the walk floor folds into the previous one; every chunk
1383                // this entry sees must clear PRIME_MIN_T on its own.
1384                let mut end = (start + width).min(t);
1385                if t - end > 0 && t - end < PRIME_MIN_T {
1386                    end = t;
1387                }
1388                let mut out = self.step35_prime_cache_batch(
1389                    e,
1390                    &[&tokens[start..end]],
1391                    &mut [cache],
1392                    &[seq_end],
1393                )?;
1394                if out.len() != 1 {
1395                    return Err("B=1 batched prime returned a non-singleton".into());
1396                }
1397                let (logits, h_seed, hidden) = out.remove(0);
1398                e.copy_into(
1399                    &mut hiddens,
1400                    start * n_embd,
1401                    &hidden,
1402                    (end - start) * n_embd,
1403                )?;
1404                last = Some((logits, h_seed));
1405                start = end;
1406            }
1407            let (logits, h_seed) = last.expect("prime produced no chunk");
1408            // ENGAGEMENT RECEIPT, both directions. `base` is the discriminator: base=0 is a
1409            // fresh prime (this line existed before the lift), base>0 is a SUFFIX riding the
1410            // GEMM trunk — the arm this lane added. The declining twin below counts the other
1411            // direction, so a log that shows neither line is an instrument fault, not a pass.
1412            eprintln!(
1413                "[gemm-prime] ENGAGED t={t} base={base} seq_end={seq_end} chunks<={width} (GEMM trunk + grouped MoE)"
1414            );
1415            return Ok((logits, h_seed, hiddens));
1416        }
1417        if self.uses_sliding_gated_moe_program() {
1418            eprintln!(
1419                "[gemm-prime] WALK t={t} base={} seq_end={seq_end} (batched prime declined)",
1420                cache.pos
1421            );
1422        }
1423        if overlay.is_none() {
1424            if let Some(out) = self.step35_prime_trows(e, tokens, cache)? {
1425                return Ok(out);
1426            }
1427        }
1428        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1429        // session cache — every chunk (including the first) takes the continuation arm
1430        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1431        assert!(
1432            t >= PRIME_MIN_T,
1433            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1434        );
1435        assert!(
1436            cache.pos + t <= cache.max_ctx,
1437            "prime_cache: prompt exceeds cache max_ctx"
1438        );
1439
1440        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1441        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1442        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1443        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1444        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1445        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1446        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1447        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1448        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1449        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1450        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1451        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1452            if self.is_gemma4_e4b() {
1453                if overlay.is_some() {
1454                    return Err(
1455                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1456                    );
1457                }
1458                return self.gemma4_e4b_prime(e, tokens, cache);
1459            }
1460            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1461            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1462            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1463            // spans become bidirectional attention islands (lane/gemma-vision).
1464            return self.gemma4_prime(e, tokens, cache, overlay);
1465        }
1466        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1467        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1468        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1469        // the prefill's ARITHMETIC, so two rigs with different values produced different
1470        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1471        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1472        // (VERDICT.md) — and it is NOT what docs originally said:
1473        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1474        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1475        //     output head), so growing a chunk cannot move an existing row's value.
1476        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1477        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1478        //     not describe our leak.
1479        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1480        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1481        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1482        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1483        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1484        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1485        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1486        // the source — every row is in one numeric class, so the chunk size no longer steers
1487        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1488        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1489        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1490        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1491        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1492        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1493        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1494        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1495        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1496        // across calls, the request still ends at the same absolute position, whatever the tick
1497        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1498        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1499        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1500        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1501        // on in a measured default run.
1502        // `seq_end` (and its MEMRA_PRIME_CALLLOCAL seam) is computed ONCE above the batched
1503        // entry so both prime arms read the identical request-absolute value.
1504        if ranges.len() == 1 {
1505            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1506        }
1507        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1508        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1509        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1510        // this lane owns the balanced two-stage schedule only.
1511        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1512            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1513                if overlay.is_some() {
1514                    return Err(
1515                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1516                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1517                            .into(),
1518                    );
1519                }
1520                if crate::pp::pp_multi_stream_same_device() {
1521                    return Err(
1522                        "prime chunk pipeline refused with 2 stage streams on one device — \
1523                         that concurrent-stream placement remains quarantined by the deferred \
1524                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1525                         the serial split."
1526                            .into(),
1527                    );
1528                }
1529                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1530            }
1531        }
1532        let mut hiddens = e.uninit(t * n_embd)?;
1533        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1534        for &(start, end) in &ranges {
1535            // chunked prime writes tap rows at the chunk's absolute offset
1536            if let Some(taps) = cache.dflash_taps.as_mut() {
1537                taps.base = start;
1538            }
1539            let (l, hs, x) =
1540                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1541            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1542            last = Some((l, hs));
1543        }
1544        let (logits, h_seed) = last.unwrap();
1545        Ok((logits, h_seed, hiddens))
1546    }
1547
1548    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1549    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1550    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1551    /// norm, lm head, and caller hidden-stack copy as the serial split.
1552    fn prime_cache_pp2_pipelined(
1553        &self,
1554        e: &Engine,
1555        tokens: &[u32],
1556        cache: &mut Cache,
1557        seq_end: usize,
1558        ranges: &[(usize, usize)],
1559        fence: &[usize],
1560    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1561        debug_assert_eq!(fence.len(), 3);
1562        debug_assert!(ranges.len() >= 2);
1563        let rt = crate::pp::PpNRt::get(e)?;
1564        assert_eq!(
1565            rt.n_stages(),
1566            2,
1567            "prime pipeline requires exactly two PP stages"
1568        );
1569        let n_embd = self.cfg.n_embd as usize;
1570        let t = tokens.len();
1571        let initial_base = cache.pos;
1572        let caller_stream = e.stream();
1573
1574        // #87 reverse publication before any new stage allocation, then prewarm both
1575        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1576        // after stage 1(N) is queued would synchronize that stream and erase the first
1577        // overlap on a two-chunk prompt.
1578        rt.fence_stages_behind(&caller_stream)?;
1579        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1580        rt.prepare_overlap_slots(0, max_payload)?;
1581
1582        let mut hiddens = e.uninit(t * n_embd)?;
1583        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1584        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1585        let (cache0, cache1) = stage_caches.parts();
1586        let (first_start, first_end) = ranges[0];
1587        let mut slot = self.prime_pp2_stage0_enqueue(
1588            e,
1589            rt,
1590            &tokens[first_start..first_end],
1591            cache0,
1592            seq_end,
1593            fence,
1594            initial_base + first_start,
1595            true,
1596        )?;
1597        cache0.pos = initial_base + first_end;
1598
1599        for (i, &(start, end)) in ranges.iter().enumerate() {
1600            let base = initial_base + start;
1601            debug_assert_eq!(
1602                cache1.pos, base,
1603                "stage 1 must drain chunks in original position order"
1604            );
1605            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1606                let next_base = initial_base + next_start;
1607                debug_assert_eq!(
1608                    cache0.pos, next_base,
1609                    "stage 0 must issue chunks in original position order"
1610                );
1611                let cache0_stage = &mut *cache0;
1612                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1613                // on one host thread therefore serialize even if the calls are ordered as
1614                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1615                // stage 1 consumes slot N while stage 0 produces slot N+1.
1616                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1617                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1618                        let next = self
1619                            .prime_pp2_stage0_enqueue(
1620                                e,
1621                                rt,
1622                                &tokens[next_start..next_end],
1623                                cache0_stage,
1624                                seq_end,
1625                                fence,
1626                                next_base,
1627                                true,
1628                            )
1629                            .map_err(|err| err.to_string())?;
1630                        cache0_stage.pos = initial_base + next_end;
1631                        Ok(next)
1632                    });
1633                    let x = self.prime_pp2_stage1_enqueue(
1634                        e,
1635                        rt,
1636                        slot,
1637                        end - start,
1638                        cache1,
1639                        seq_end,
1640                        fence,
1641                        base,
1642                        true,
1643                    )?;
1644                    let out = {
1645                        rt.bind_stage(1)?;
1646                        let _st1 = rt.enter(1);
1647                        let e1 = rt.engine(1, e);
1648                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1649                    };
1650                    let next = stage0
1651                        .join()
1652                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1653                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1654                    Ok((out, Some(next)))
1655                })?
1656            } else {
1657                let x = self.prime_pp2_stage1_enqueue(
1658                    e,
1659                    rt,
1660                    slot,
1661                    end - start,
1662                    cache1,
1663                    seq_end,
1664                    fence,
1665                    base,
1666                    true,
1667                )?;
1668                let out = {
1669                    rt.bind_stage(1)?;
1670                    let _st1 = rt.enter(1);
1671                    let e1 = rt.engine(1, e);
1672                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1673                };
1674                (out, None)
1675            };
1676
1677            rt.publish_to(1, &caller_stream)?;
1678            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1679            last = Some((out.0, out.1));
1680            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1681
1682            if let Some(next) = next_slot {
1683                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1684                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1685                // Stage 0(N+1) is already queued before this wait is appended, so its
1686                // overlap with stage 1(N) is preserved.
1687                rt.fence_stages_behind(&caller_stream)?;
1688                slot = next;
1689            }
1690        }
1691
1692        debug_assert_eq!(cache0.pos, initial_base + t);
1693        debug_assert_eq!(cache1.pos, initial_base + t);
1694        let (logits, h_seed) = last.unwrap();
1695        Ok((logits, h_seed, hiddens))
1696    }
1697
1698    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1699    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1700    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1701    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1702    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1703    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1704    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1705        if Engine::gdn_db_on()
1706            && Engine::gdn_chunked_enabled()
1707            && t >= 16
1708            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1709            && num_k * 2 == num_v
1710        {
1711            num_k
1712        } else {
1713            num_v
1714        }
1715    }
1716
1717    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1718    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1719    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1720    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1721    fn f16out_on(e: &Engine, t: usize) -> bool {
1722        crate::f16_ffi::pp_f16_enabled()
1723            && t >= 16
1724            && !e.verify_exact_on()
1725            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1726    }
1727
1728    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1729    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1730    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1731    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1732    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1733    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1734    /// see one entry, byte-identical behavior.
1735    pub fn prime_slabs_get(
1736        &self,
1737        e: &Engine,
1738        t: usize,
1739        n_embd: usize,
1740        n_ff_max: usize,
1741    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1742        let mut slabs = self.prime_slabs.lock().unwrap();
1743        let dev = e.ctx().ordinal();
1744        let need_new = match slabs.get(&dev) {
1745            None => true,
1746            Some(sl) => sl.lock().unwrap().t_cap < t,
1747        };
1748        if need_new {
1749            slabs.insert(
1750                dev,
1751                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1752                    t_cap: t,
1753                    h: e.uninit(t * n_embd)?,
1754                    x1: e.uninit(t * n_embd)?,
1755                    z: e.uninit(t * n_embd)?,
1756                    act: e.uninit(t * n_ff_max)?,
1757                    xa: e.uninit(t * n_embd)?,
1758                    xb: e.uninit(t * n_embd)?,
1759                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1760                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1761                    gate: e.uninit(t * n_ff_max)?,
1762                    up: e.uninit(t * n_ff_max)?,
1763                    ffn_out: e.uninit(t * n_embd)?,
1764                    seg_glue: Vec::new(),
1765                    mixed: e.uninit(t * n_embd)?,
1766                    seg_mid: Vec::new(),
1767                    seg_t: 0,
1768                })),
1769            );
1770        }
1771        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1772    }
1773
1774    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1775    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1776    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1777    fn prime_chunk(
1778        &self,
1779        e: &Engine,
1780        tokens: &[u32],
1781        cache: &mut Cache,
1782        seq_end: usize,
1783        chunk_off: usize,
1784        overlay: Option<&crate::vision::EmbedOverlay>,
1785    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1786        if crate::pp::pp_host_bounce_active()
1787            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1788        {
1789            return Err(
1790                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1791                 has no active prime stage split and would peer-read remote weights; keep \
1792                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1793                    .into(),
1794            );
1795        }
1796        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1797        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1798        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1799        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1800        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1801        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1802        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1803        // loader is off and there is nothing remote to split for.
1804        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1805            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1806                if overlay.is_some() {
1807                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1808                         run single-device or MEMRA_PRIME_PP=0"
1809                        .into());
1810                }
1811                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1812            }
1813        }
1814        if crate::pp::pp_host_bounce_active() {
1815            return Err(
1816                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1817                 refusing an unsplit remote-weight walk"
1818                    .into(),
1819            );
1820        }
1821        let t = tokens.len();
1822        let base = cache.pos;
1823        debug_assert!(
1824            seq_end >= base + t,
1825            "prime_chunk: seq_end must cover this chunk"
1826        );
1827        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1828        let pos_d = e.htod_i32(&pos)?;
1829
1830        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1831        if let Some(ov) = overlay {
1832            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1833            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1834            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1835            let n_embd = self.cfg.n_embd as usize;
1836            for &(pos, row_off, n_rows) in &ov.spans {
1837                let lo = pos.max(chunk_off);
1838                let hi = (pos + n_rows).min(chunk_off + t);
1839                if lo < hi {
1840                    let src_row = row_off + (lo - pos);
1841                    let view = ov
1842                        .rows
1843                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1844                    e.copy_view_into(
1845                        &mut x_embed,
1846                        (lo - chunk_off) * n_embd,
1847                        &view,
1848                        (hi - lo) * n_embd,
1849                    )?;
1850                }
1851            }
1852        }
1853        let x = self.prime_layers(
1854            e,
1855            x_embed,
1856            0,
1857            self.layers.len(),
1858            &pos_d,
1859            t,
1860            base,
1861            cache,
1862            seq_end,
1863        )?;
1864        self.prime_chunk_epilogue(e, x, t, cache)
1865    }
1866
1867    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1868    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1869    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1870    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1871    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1872    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1873    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1874    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1875    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1876    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1877    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1878    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1879    ///     each stage walks through its own resident transients;
1880    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1881    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1882    #[allow(clippy::too_many_arguments)]
1883    fn prime_layers(
1884        &self,
1885        e: &Engine,
1886        x_in: CudaSlice<f32>,
1887        lo: usize,
1888        hi: usize,
1889        pos_d: &CudaSlice<i32>,
1890        t: usize,
1891        base: usize,
1892        cache: &mut Cache,
1893        seq_end: usize,
1894    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1895        let cfg = &self.cfg;
1896        let n_embd = cfg.n_embd as usize;
1897        let eps = cfg.rms_eps;
1898        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1899        // standalone convert launches). Only when the f16 lane serves and T reaches the
1900        // GEMM tier; bit-identical either way.
1901        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1902        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1903        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1904        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1905        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1906        // capacity tail must stay behind checked views. The hidden-stack return clones the
1907        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1908        let n_ff_max = self
1909            .layers
1910            .iter()
1911            .map(|l| match &l.ffn {
1912                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1913                _ => n_embd,
1914            })
1915            .max()
1916            .unwrap_or(n_embd)
1917            .max(n_embd);
1918        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1919        let slab = if use_slabs {
1920            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1921        } else {
1922            None
1923        };
1924        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1925        let mut x_own; // fallback storage when slabs are off
1926        type SlabRefs<'a> = (
1927            &'a mut CudaSlice<f32>,
1928            &'a mut CudaSlice<f32>,
1929            &'a mut CudaSlice<f32>,
1930            &'a mut CudaSlice<f32>,
1931            &'a mut CudaSlice<u8>,
1932            &'a mut CudaSlice<u8>,
1933            &'a mut CudaSlice<f32>,
1934            &'a mut CudaSlice<f32>,
1935            &'a mut CudaSlice<f32>,
1936        );
1937        let (mut x_cur, mut x_nxt, sl): (
1938            &mut CudaSlice<f32>,
1939            &mut CudaSlice<f32>,
1940            Option<SlabRefs>,
1941        );
1942        let mut seg: Option<(
1943            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1944            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1945            &mut CudaSlice<f32>,
1946            &mut usize,
1947        )> = None;
1948        let mut x_own2;
1949        match slab_guard.as_mut() {
1950            Some(g) => {
1951                let slabs = &mut **g;
1952                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1953                let PrimeSlabs {
1954                    xa,
1955                    xb,
1956                    h,
1957                    x1,
1958                    z,
1959                    act,
1960                    h16,
1961                    z16,
1962                    gate,
1963                    up,
1964                    ffn_out,
1965                    seg_glue,
1966                    mixed,
1967                    seg_mid,
1968                    seg_t,
1969                    ..
1970                } = slabs;
1971                x_cur = xa;
1972                x_nxt = xb;
1973                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1974                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1975            }
1976            None => {
1977                x_own = x_in;
1978                x_own2 = e.uninit(t * n_embd)?;
1979                x_cur = &mut x_own;
1980                x_nxt = &mut x_own2;
1981                sl = None;
1982            }
1983        }
1984        let mut alloc_h;
1985        let mut alloc_x1;
1986        let mut alloc_z;
1987        let mut alloc_act;
1988        let mut alloc_h16;
1989        let mut alloc_z16;
1990        let mut alloc_gate;
1991        let mut alloc_up;
1992        let mut alloc_fo;
1993        let (h, x1, z, act): (
1994            &mut CudaSlice<f32>,
1995            &mut CudaSlice<f32>,
1996            &mut CudaSlice<f32>,
1997            &mut CudaSlice<f32>,
1998        );
1999        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
2000        let (sl_gate, sl_up, sl_fo): (
2001            &mut CudaSlice<f32>,
2002            &mut CudaSlice<f32>,
2003            &mut CudaSlice<f32>,
2004        );
2005        match sl {
2006            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
2007                h = a;
2008                x1 = b;
2009                z = c;
2010                act = d;
2011                h16 = e16;
2012                z16 = f16b;
2013                sl_gate = g;
2014                sl_up = u;
2015                sl_fo = fo;
2016            }
2017            None => {
2018                alloc_h = e.uninit(t * n_embd)?;
2019                alloc_x1 = e.uninit(t * n_embd)?;
2020                alloc_z = e.uninit(t * n_embd)?;
2021                alloc_act = e.uninit(t * n_ff_max)?;
2022                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2023                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2024                alloc_gate = e.uninit(t * n_ff_max)?;
2025                alloc_up = e.uninit(t * n_ff_max)?;
2026                alloc_fo = e.uninit(t * n_embd)?;
2027                h = &mut alloc_h;
2028                x1 = &mut alloc_x1;
2029                z = &mut alloc_z;
2030                act = &mut alloc_act;
2031                h16 = &mut alloc_h16;
2032                z16 = &mut alloc_z16;
2033                sl_gate = &mut alloc_gate;
2034                sl_up = &mut alloc_up;
2035                sl_fo = &mut alloc_fo;
2036            }
2037        }
2038        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
2039        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
2040        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
2041        // first prime at this t (capture does not execute -> launch right after).
2042        let n_layers = self.layers.len();
2043        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
2044        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
2045        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
2046        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
2047        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
2048        // machinery stays (byte-identical) as their foundation.
2049        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
2050        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
2051        // step35 rides its own mixer through the normal per-layer arm below.
2052        let use_seg = f16fuse
2053            && seg.is_some()
2054            && !self.uses_sliding_gated_moe_program()
2055            && lo == 0
2056            && hi == n_layers
2057            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1")
2058            // GRAPH-LAUNCH HEADROOM GUARD (see spec::GRAPH_LAUNCH_MIN_FREE): below the
2059            // driver-free floor this prime call takes the eager fused else-arm (the
2060            // byte-identical twin the opt-in was gated against) instead of replaying
2061            // the S-mid/S-glue segment graphs into an exhausted card. Probe runs only
2062            // when the opt-in flag is armed (short-circuit order).
2063            && {
2064                let ok = crate::spec::graph_launch_headroom_ok(e);
2065                if !ok {
2066                    static NOTED: std::sync::Once = std::sync::Once::new();
2067                    NOTED.call_once(|| crate::spec::graph_replay_suspended_note("prime-seg"));
2068                }
2069                ok
2070            };
2071        if let Some((sg, sm, _, st)) = seg.as_mut() {
2072            if **st != t {
2073                sg.clear();
2074                sg.extend((0..n_layers).map(|_| None));
2075                sm.clear();
2076                sm.extend((0..n_layers).map(|_| None));
2077                **st = t;
2078            }
2079        }
2080        {
2081            let layer_lo = &self.layers[lo];
2082            if f16fuse {
2083                e.rms_norm_f16out(
2084                    x_cur,
2085                    layer_lo.attn_norm.float_data(),
2086                    h,
2087                    h16,
2088                    n_embd,
2089                    t,
2090                    eps,
2091                )?;
2092            } else {
2093                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
2094            }
2095        }
2096        let anat = Self::prime_anatomy_on();
2097        let mut anat_last = if anat {
2098            e.stream().synchronize()?;
2099            Some(std::time::Instant::now())
2100        } else {
2101            None
2102        };
2103        // Closes the region that just ENDED into `slot`, restarting the clock.
2104        macro_rules! anat_mark {
2105            ($slot:expr) => {
2106                if let Some(ts) = anat_last.as_mut() {
2107                    e.stream().synchronize()?;
2108                    Self::prime_anatomy_slots()[$slot].fetch_add(
2109                        ts.elapsed().as_nanos() as u64,
2110                        std::sync::atomic::Ordering::Relaxed,
2111                    );
2112                    *ts = std::time::Instant::now();
2113                }
2114            };
2115        }
2116        for il in lo..hi {
2117            let layer = &self.layers[il];
2118            let hx16 = if f16fuse { Some(&*h16) } else { None };
2119            if use_seg {
2120                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
2121                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
2122                let (pre, pre16, w_out) = match &layer.mixer {
2123                    Mixer::Full(fa) => {
2124                        let g3 = match hx16 {
2125                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
2126                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
2127                        };
2128                        let (pre, pre16) =
2129                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
2130                        (pre, pre16, &fa.wo)
2131                    }
2132                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2133                    Mixer::Linear(la) => {
2134                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2135                        let g4 = match hx16 {
2136                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
2137                            None => e.matmul_group(&ws, h, t)?,
2138                        };
2139                        let (pre, pre16) =
2140                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
2141                        (pre, pre16, &la.ssm_out)
2142                    }
2143                };
2144                {
2145                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
2146                    let pre_n = pre.len() / t;
2147                    let xh_pre = match pre16 {
2148                        Some(x) => x,
2149                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
2150                    };
2151                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
2152                        let y = e.matmul(w_out, &pre, t)?;
2153                        e.copy_into(mslab, 0, &y, t * n_embd)?;
2154                    }
2155                    if sm[il].is_none() {
2156                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2157                        let w_post = layer.post_attn_norm.float_data();
2158                        e.stream().synchronize()?;
2159                        e.stream()
2160                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2161                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2162                            e.add(x_cur, mslab, x1, t * n_embd)?;
2163                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2164                            Ok(())
2165                        })();
2166                        let g = e.stream().end_capture(
2167                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2168                        r?;
2169                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2170                    }
2171                    sm[il].as_ref().unwrap().launch()?;
2172                }
2173            } else {
2174                let mixed = match &layer.mixer {
2175                    Mixer::Full(fa) => {
2176                        let y =
2177                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2178                        anat_mark!(0);
2179                        y
2180                    }
2181                    Mixer::Linear(la) => {
2182                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2183                        anat_mark!(1);
2184                        y
2185                    }
2186                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2187                };
2188                if f16fuse {
2189                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2190                    // bit-identical) — the standalone add pass disappears.
2191                    e.add_rms_norm_f16out(
2192                        x_cur,
2193                        &mixed,
2194                        layer.post_attn_norm.float_data(),
2195                        x1,
2196                        z,
2197                        z16,
2198                        n_embd,
2199                        t,
2200                        eps,
2201                    )?;
2202                } else {
2203                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2204                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2205                }
2206                anat_mark!(4);
2207            }
2208            let zx16 = if f16fuse { Some(&*z16) } else { None };
2209            match &layer.ffn {
2210                crate::hybrid::Ffn::Dense {
2211                    ffn_gate,
2212                    ffn_up,
2213                    ffn_down,
2214                } => {
2215                    let n_ff = ffn_gate.out_features();
2216                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2217                    // the allocating group + copy when a mirror is missing.
2218                    let mut into_ok = false;
2219                    if let Some(xh) = zx16 {
2220                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2221                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2222                    }
2223                    if !into_ok {
2224                        let mut g2 = match zx16 {
2225                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2226                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2227                        };
2228                        let up_y = g2.pop().unwrap();
2229                        let gate_y = g2.pop().unwrap();
2230                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2231                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2232                    }
2233                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2234                    // operand in-epilogue; non-silu activations keep the standalone convert.
2235                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2236                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2237                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2238                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2239                    {
2240                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2241                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2242                        Some(a16)
2243                    } else {
2244                        Self::ffn_act_lim(
2245                            e,
2246                            &self.cfg,
2247                            sl_gate,
2248                            sl_up,
2249                            1.0,
2250                            1.0,
2251                            d_lim,
2252                            act,
2253                            t * n_ff,
2254                        )?;
2255                        None
2256                    };
2257                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2258                    let xh_act = match act16 {
2259                        Some(x) => x,
2260                        None => e.f16_act(act, t * n_ff, n_ff)?,
2261                    };
2262                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2263                        let y = e.matmul(ffn_down, &*act, t)?;
2264                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2265                    }
2266                }
2267                crate::hybrid::Ffn::Moe(m) => {
2268                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2269                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2270                    anat_mark!(2);
2271                }
2272            }
2273            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2274                anat_mark!(3);
2275            }
2276            if use_seg && il + 1 < hi {
2277                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2278                let w_next = self.layers[il + 1].attn_norm.float_data();
2279                let (sg, _, _, _) = seg.as_mut().unwrap();
2280                if sg[il].is_none() {
2281                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2282                    e.stream().synchronize()?;
2283                    e.stream()
2284                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2285                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2286                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2287                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2288                        Ok(())
2289                    })();
2290                    let g = e.stream().end_capture(
2291                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2292                    );
2293                    r?;
2294                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2295                }
2296                sg[il].as_ref().unwrap().launch()?;
2297            } else {
2298                if il + 1 < hi {
2299                    let w_next = self.layers[il + 1].attn_norm.float_data();
2300                    if f16fuse {
2301                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2302                    } else {
2303                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2304                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2305                    }
2306                } else {
2307                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2308                }
2309            }
2310            anat_mark!(4);
2311            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2312            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2313            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2314            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2315            // unset (the default) costs one OnceLock read per layer.
2316            if let Some(path) = Self::prime_trace_path() {
2317                let row = (base + t - 1) as usize;
2318                let host = e.dtoh(x_nxt)?;
2319                let last = &host[(t - 1) * n_embd..t * n_embd];
2320                use std::io::Write as _;
2321                let mut f = std::fs::OpenOptions::new()
2322                    .create(true)
2323                    .append(true)
2324                    .open(path)?;
2325                let mut h64: u64 = 0xcbf29ce484222325;
2326                for v in last {
2327                    h64 ^= v.to_bits() as u64;
2328                    h64 = h64.wrapping_mul(0x100000001b3);
2329                }
2330                writeln!(
2331                    f,
2332                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2333                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2334                    last[0], last[1], last[2]
2335                )?;
2336            }
2337            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2338            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2339            self.dflash_tap(e, cache, il, x_nxt, t)?;
2340            std::mem::swap(&mut x_cur, &mut x_nxt);
2341        }
2342        if anat {
2343            let s = Self::prime_anatomy_slots();
2344            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2345            eprintln!(
2346                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2347                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2348                ms(0),
2349                ms(1),
2350                ms(2),
2351                ms(3),
2352                ms(4)
2353            );
2354        }
2355        // hidden-stack return: clone the final x out of the slab
2356        let mut x = e.uninit(t * n_embd)?;
2357        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2358        drop(slab_guard);
2359        Ok(x)
2360    }
2361
2362    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2363    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2364    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2365    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2366    fn prime_chunk_epilogue(
2367        &self,
2368        e: &Engine,
2369        x: CudaSlice<f32>,
2370        t: usize,
2371        cache: &mut Cache,
2372    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2373        let n_embd = self.cfg.n_embd as usize;
2374        let eps = self.cfg.rms_eps;
2375        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2376        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2377        // the post-norm copy happens after hn exists).
2378        let mut h_seed = e.uninit(n_embd)?;
2379        if !crate::spec::spec_hpost() {
2380            e.copy_view_into(
2381                &mut h_seed,
2382                0,
2383                &x.slice((t - 1) * n_embd..t * n_embd),
2384                n_embd,
2385            )?;
2386        }
2387        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2388        let mut hn = e.uninit(t * n_embd)?;
2389        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2390        if crate::spec::spec_hpost() {
2391            e.copy_view_into(
2392                &mut h_seed,
2393                0,
2394                &hn.slice((t - 1) * n_embd..t * n_embd),
2395                n_embd,
2396            )?;
2397        }
2398        let last = e.view(&hn, t * n_embd);
2399        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2400        let mut hlast = e.uninit(n_embd)?;
2401        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2402        let logits = e.matmul(&self.output, &hlast, 1)?;
2403        cache.pos += t;
2404        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2405        // post-norm stack hn (MEMRA_SPEC_HPOST).
2406        Ok((
2407            e.dtoh(&logits)?,
2408            h_seed,
2409            if crate::spec::spec_hpost() { hn } else { x },
2410        ))
2411    }
2412
2413    /// Post-final-norm hidden state of one row of a prime-returned hidden stack — the
2414    /// embedding-pooling read (lane/embed-serve). `hiddens` is `prime_cache`'s third
2415    /// return: the pre-norm stack by default, but ALREADY post-norm under
2416    /// MEMRA_SPEC_HPOST (see `prime_chunk_epilogue`), so the norm is applied only in
2417    /// the default shape. Returns the host f32 row (`n_embd` wide).
2418    pub fn hidden_postnorm_row(
2419        &self,
2420        e: &Engine,
2421        hiddens: &CudaSlice<f32>,
2422        row: usize,
2423    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2424        let n_embd = self.cfg.n_embd as usize;
2425        let mut x1 = e.uninit(n_embd)?;
2426        e.copy_view_into(
2427            &mut x1,
2428            0,
2429            &hiddens.slice(row * n_embd..(row + 1) * n_embd),
2430            n_embd,
2431        )?;
2432        if crate::spec::spec_hpost() {
2433            return Ok(e.dtoh(&x1)?);
2434        }
2435        let mut hn = e.uninit(n_embd)?;
2436        e.rms_norm(
2437            &x1,
2438            self.output_norm.float_data(),
2439            &mut hn,
2440            n_embd,
2441            1,
2442            self.cfg.rms_eps,
2443        )?;
2444        Ok(e.dtoh(&hn)?)
2445    }
2446
2447    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2448    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2449    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2450    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2451    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2452    /// prefill kernels. Structure mirrors the verify split exactly:
2453    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2454    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2455    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2456    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2457    ///                  there via the sharded loader) → `publish_to`
2458    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2459    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2460    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2461    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2462    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2463    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2464    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2465    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2466    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2467    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2468    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2469    /// and its liveness counter is bumped here — the gate goes green with this function.
2470    fn prime_chunk_ppn(
2471        &self,
2472        e: &Engine,
2473        tokens: &[u32],
2474        cache: &mut Cache,
2475        seq_end: usize,
2476        fence: &[usize],
2477    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2478        let rt = crate::pp::PpNRt::get(e)?;
2479        let n_st = fence.len() - 1;
2480        assert_eq!(
2481            rt.n_stages(),
2482            n_st,
2483            "PpNRt stage count {} != fence stages {n_st}",
2484            rt.n_stages()
2485        );
2486        let n_embd = self.cfg.n_embd as usize;
2487        let t = tokens.len();
2488        let base = cache.pos;
2489        debug_assert!(
2490            seq_end >= base + t,
2491            "prime_chunk_ppn: seq_end must cover this chunk"
2492        );
2493        let payload = t * n_embd;
2494        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2495        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2496        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2497        let caller_stream = e.stream();
2498        rt.fence_stages_behind(&caller_stream)?;
2499
2500        if n_st == 2 {
2501            let slot =
2502                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2503            let x =
2504                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2505            let out = {
2506                rt.bind_stage(1)?;
2507                let _st1 = rt.enter(1);
2508                let e1 = rt.engine(1, e);
2509                self.prime_chunk_epilogue(e1, x, t, cache)?
2510            };
2511            rt.publish_to(1, &caller_stream)?;
2512            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2513            return Ok(out);
2514        }
2515
2516        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2517
2518        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2519        let mut slot = {
2520            let _st0 = rt.enter(0);
2521            let e0 = rt.engine(0, e);
2522            let pos_d = e0.htod_i32(&pos)?;
2523            let x = self.embed(e0, tokens)?;
2524            let x =
2525                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2526            rt.tx(0, &x, payload)?
2527            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2528        };
2529
2530        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2531        for s in 1..n_st - 1 {
2532            let _st = rt.enter(s);
2533            let es = rt.engine(s, e);
2534            let pos_d = es.htod_i32(&pos)?;
2535            let x = rt.rx(s - 1, slot, payload)?;
2536            let x = self.prime_layers(
2537                es,
2538                x,
2539                fence[s],
2540                fence[s + 1],
2541                &pos_d,
2542                t,
2543                base,
2544                cache,
2545                seq_end,
2546            )?;
2547            slot = rt.tx(s, &x, payload)?;
2548        }
2549
2550        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2551        let _stl = rt.enter(n_st - 1);
2552        let el = rt.engine(n_st - 1, e);
2553        let pos_d = el.htod_i32(&pos)?;
2554        let x = rt.rx(n_st - 2, slot, payload)?;
2555        let x = self.prime_layers(
2556            el,
2557            x,
2558            fence[n_st - 1],
2559            fence[n_st],
2560            &pos_d,
2561            t,
2562            base,
2563            cache,
2564            seq_end,
2565        )?;
2566        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2567        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2568        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2569        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2570        // stage stream host-side, but the law is stated in events, not in a dtoh side
2571        // effect a later deferred form would remove.
2572        rt.publish_to(n_st - 1, &caller_stream)?;
2573        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2574        Ok(out)
2575    }
2576
2577    fn prime_pp2_stage0_enqueue(
2578        &self,
2579        e: &Engine,
2580        rt: &crate::pp::PpNRt,
2581        tokens: &[u32],
2582        cache: &mut Cache,
2583        seq_end: usize,
2584        fence: &[usize],
2585        base: usize,
2586        pipelined: bool,
2587    ) -> Result<usize, Box<dyn std::error::Error>> {
2588        let t = tokens.len();
2589        let n_embd = self.cfg.n_embd as usize;
2590        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2591        rt.bind_stage(0)?;
2592        let _st0 = rt.enter(0);
2593        let e0 = rt.engine(0, e);
2594        let pos_d = e0.htod_i32(&pos)?;
2595        let x = self.embed(e0, tokens)?;
2596        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2597        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2598        if pipelined {
2599            rt.tx_pipelined(0, &x, t * n_embd)
2600        } else {
2601            rt.tx(0, &x, t * n_embd)
2602        }
2603    }
2604
2605    fn prime_pp2_stage1_enqueue(
2606        &self,
2607        e: &Engine,
2608        rt: &crate::pp::PpNRt,
2609        slot: usize,
2610        t: usize,
2611        cache: &mut Cache,
2612        seq_end: usize,
2613        fence: &[usize],
2614        base: usize,
2615        pipelined: bool,
2616    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2617        let n_embd = self.cfg.n_embd as usize;
2618        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2619        rt.bind_stage(1)?;
2620        let _st1 = rt.enter(1);
2621        let e1 = rt.engine(1, e);
2622        let pos_d = e1.htod_i32(&pos)?;
2623        let x = rt.rx(0, slot, t * n_embd)?;
2624        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2625        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2626    }
2627
2628    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2629    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2630    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2631    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2632    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2633    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2634    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2635    /// bookkeeping still runs on the host per call — the real replay path moves the write
2636    /// slot to the len_d device counter (increment 3).
2637    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2638    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2639    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2640    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2641    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2642    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2643    pub fn prime_chunk_captured(
2644        &self,
2645        e: &Engine,
2646        x_in: &CudaSlice<f32>,
2647        pos_d: &CudaSlice<i32>,
2648        t: usize,
2649        cache: &mut Cache,
2650        len_d: &CudaSlice<i32>,
2651        logits_out: &mut CudaSlice<f32>,
2652        h_seed_out: &mut CudaSlice<f32>,
2653    ) -> Result<(), Box<dyn std::error::Error>> {
2654        let cfg = &self.cfg;
2655        let n_embd = cfg.n_embd as usize;
2656        let eps = cfg.rms_eps;
2657        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2658        let mut x = e.uninit(t * n_embd)?;
2659        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2660        for (il, layer) in self.layers.iter().enumerate() {
2661            let mut h = e.uninit(t * n_embd)?;
2662            let mut hx16: Option<CudaSlice<u8>> = None;
2663            if f16fuse {
2664                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2665                e.rms_norm_f16out(
2666                    &x,
2667                    layer.attn_norm.float_data(),
2668                    &mut h,
2669                    &mut b16,
2670                    n_embd,
2671                    t,
2672                    eps,
2673                )?;
2674                hx16 = Some(b16);
2675            } else {
2676                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2677            }
2678            let mixed = match &layer.mixer {
2679                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2680                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2681                // come from the caller (see step35_attn_pre_wo's doc note).
2682                Mixer::Full(fa) => {
2683                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2684                }
2685                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2686                Mixer::Linear(la) => {
2687                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2688                    let g4 = match hx16.as_ref() {
2689                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2690                        None => e.matmul_group(&ws, &h, t)?,
2691                    };
2692                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2693                }
2694            };
2695            let mut x1 = e.uninit(t * n_embd)?;
2696            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2697            let mut z = e.uninit(t * n_embd)?;
2698            let mut zx16: Option<CudaSlice<u8>> = None;
2699            if f16fuse {
2700                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2701                e.rms_norm_f16out(
2702                    &x1,
2703                    layer.post_attn_norm.float_data(),
2704                    &mut z,
2705                    &mut b16,
2706                    n_embd,
2707                    t,
2708                    eps,
2709                )?;
2710                zx16 = Some(b16);
2711            } else {
2712                e.rms_norm(
2713                    &x1,
2714                    layer.post_attn_norm.float_data(),
2715                    &mut z,
2716                    n_embd,
2717                    t,
2718                    eps,
2719                )?;
2720            }
2721            let ffn_out = match &layer.ffn {
2722                crate::hybrid::Ffn::Dense {
2723                    ffn_gate,
2724                    ffn_up,
2725                    ffn_down,
2726                } => {
2727                    let n_ff = ffn_gate.out_features();
2728                    let mut g2 = match &zx16 {
2729                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2730                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2731                    };
2732                    let up = g2.pop().unwrap();
2733                    let gate = g2.pop().unwrap();
2734                    let mut act = e.uninit(t * n_ff)?;
2735                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2736                    Self::ffn_act_lim(
2737                        e,
2738                        &self.cfg,
2739                        &gate,
2740                        &up,
2741                        1.0,
2742                        1.0,
2743                        self.cfg.clamp_shexp_at(il as u32),
2744                        &mut act,
2745                        t * n_ff,
2746                    )?;
2747                    e.matmul(ffn_down, &act, t)?
2748                }
2749                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2750            };
2751            let mut x2 = e.uninit(t * n_embd)?;
2752            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2753            x = x2;
2754        }
2755        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2756        if !crate::spec::spec_hpost() {
2757            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2758        }
2759        let mut hn = e.uninit(t * n_embd)?;
2760        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2761        if crate::spec::spec_hpost() {
2762            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2763        }
2764        let mut hlast = e.uninit(n_embd)?;
2765        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2766        let logits = e.matmul(&self.output, &hlast, 1)?;
2767        let nv = logits.len();
2768        e.copy_into(logits_out, 0, &logits, nv)?;
2769        Ok(())
2770    }
2771
2772    fn step35_prime_batch_on() -> bool {
2773        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2774    }
2775
2776    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2777    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2778    #[allow(clippy::too_many_arguments)]
2779    /// `seq_ends[s]`: sequence s's REQUEST-absolute end position — NOT `ts[s]`. It is the
2780    /// only thing step35's SWA arm keys on, so a chunk-local value here decides the attention
2781    /// kernel from the chunk size (and, below the 512-row window at a nonzero base, drops the
2782    /// window mask entirely). See the batched entry's note in `prime_cache_overlaid`.
2783    #[allow(clippy::too_many_arguments)]
2784    fn step35_prime_batch_layers(
2785        &self,
2786        e: &Engine,
2787        mut x: CudaSlice<f32>,
2788        lo: usize,
2789        hi: usize,
2790        ts: &[usize],
2791        offs: &[usize],
2792        seq_ends: &[usize],
2793        pos_ds: &[CudaSlice<i32>],
2794        caches: &mut [&mut Cache],
2795    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2796        let cfg = &self.cfg;
2797        let n_embd = cfg.n_embd as usize;
2798        let eps = cfg.rms_eps;
2799        let b = ts.len();
2800        let total: usize = ts.iter().sum();
2801        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2802
2803        let split = |e: &Engine,
2804                     y: &CudaSlice<f32>,
2805                     dim: usize|
2806         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2807            let mut out = Vec::with_capacity(b);
2808            for s in 0..b {
2809                let mut ys = e.uninit(ts[s] * dim)?;
2810                e.copy_view_into(
2811                    &mut ys,
2812                    0,
2813                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2814                    ts[s] * dim,
2815                )?;
2816                out.push(ys);
2817            }
2818            Ok(out)
2819        };
2820
2821        // MEMRA_PRIME_PROF=1: per-phase wall inside the prime, sync-bounded (absolute time
2822        // inflates; the SPLIT is the signal). Two inspection passes failed to find where a
2823        // 3.8 s/4096-token chunk goes against a ~0.55 s compute budget, and nsys cannot capture
2824        // through the server's worker, so the walk measures itself.
2825        let prof = std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1");
2826        let mut ph = [0f64; 4]; // 0 norm+qkv, 1 attn, 2 o_proj+norm, 3 moe
2827        let mut mark = |e: &Engine, acc: usize, t0: &mut std::time::Instant, ph: &mut [f64; 4]| {
2828            if prof {
2829                let _ = e.stream().synchronize();
2830                ph[acc] += t0.elapsed().as_secs_f64() * 1e3;
2831                *t0 = std::time::Instant::now();
2832            }
2833        };
2834        let mut pt = std::time::Instant::now();
2835        for il in lo..hi {
2836            let layer = &self.layers[il];
2837            let Mixer::Full(fa) = &layer.mixer else {
2838                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2839            };
2840
2841            let mut h = e.uninit(total * n_embd)?;
2842            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2843            if f16fuse {
2844                e.rms_norm_f16out(
2845                    &x,
2846                    layer.attn_norm.float_data(),
2847                    &mut h,
2848                    &mut hx16,
2849                    n_embd,
2850                    total,
2851                    eps,
2852                )?;
2853            } else {
2854                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2855            }
2856
2857            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2858            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2859            // application stay verbatim.
2860            let gate_w = fa
2861                .attn_gate
2862                .as_ref()
2863                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2864            let mut g4 = if f16fuse {
2865                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2866            } else {
2867                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2868            };
2869            let gate = g4.pop().unwrap();
2870            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2871                (0..b).map(|_| Vec::with_capacity(3)).collect();
2872            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2873                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2874                    parts[s].push(ys);
2875                }
2876            }
2877            let gates = split(e, &gate, gate_w.out_features())?;
2878            let geometry = self.step35_geom(il);
2879            let hd = geometry.head_dim_k as usize;
2880            let nh = geometry.n_head as usize;
2881            let mut ag_cat = e.uninit(total * nh * hd)?;
2882            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2883                mark(e, 0, &mut pt, &mut ph);
2884                let ag = self.step35_attn_pre_wo(
2885                    e,
2886                    fa,
2887                    g3s,
2888                    None,
2889                    Some(&gate),
2890                    &pos_ds[s],
2891                    ts[s],
2892                    Some(&mut *caches[s]),
2893                    il,
2894                    seq_ends[s],
2895                )?;
2896                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2897            }
2898            mark(e, 1, &mut pt, &mut ph);
2899            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2900
2901            let mut x1 = e.uninit(total * n_embd)?;
2902            let mut z = e.uninit(total * n_embd)?;
2903            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2904            if f16fuse {
2905                e.add_rms_norm_f16out(
2906                    &x,
2907                    &mixed,
2908                    layer.post_attn_norm.float_data(),
2909                    &mut x1,
2910                    &mut z,
2911                    &mut zx16,
2912                    n_embd,
2913                    total,
2914                    eps,
2915                )?;
2916            } else {
2917                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2918                e.rms_norm(
2919                    &x1,
2920                    layer.post_attn_norm.float_data(),
2921                    &mut z,
2922                    n_embd,
2923                    total,
2924                    eps,
2925                )?;
2926            }
2927
2928            mark(e, 2, &mut pt, &mut ph);
2929            let ffn_out = match &layer.ffn {
2930                crate::hybrid::Ffn::Dense {
2931                    ffn_gate,
2932                    ffn_up,
2933                    ffn_down,
2934                } => {
2935                    let n_ff = ffn_gate.out_features();
2936                    let mut g2 = if f16fuse {
2937                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2938                    } else {
2939                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2940                    };
2941                    let up = g2.pop().unwrap();
2942                    let gate = g2.pop().unwrap();
2943                    let mut act = e.uninit(total * n_ff)?;
2944                    let d_lim = cfg.clamp_shexp_at(il as u32);
2945                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2946                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2947                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2948                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2949                            Some(y) => y,
2950                            None => e.matmul(ffn_down, &act, total)?,
2951                        }
2952                    } else {
2953                        Self::ffn_act_lim(
2954                            e,
2955                            cfg,
2956                            &gate,
2957                            &up,
2958                            1.0,
2959                            1.0,
2960                            d_lim,
2961                            &mut act,
2962                            total * n_ff,
2963                        )?;
2964                        e.matmul(ffn_down, &act, total)?
2965                    }
2966                }
2967                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2968            };
2969            let mut x2 = e.uninit(total * n_embd)?;
2970            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2971            x = x2;
2972            mark(e, 3, &mut pt, &mut ph);
2973        }
2974        if prof {
2975            eprintln!(
2976                "[prime-prof] t={total} layers={} norm+qkv={:.0}ms attn={:.0}ms o_proj={:.0}ms moe={:.0}ms",
2977                hi - lo,
2978                ph[0],
2979                ph[1],
2980                ph[2],
2981                ph[3]
2982            );
2983        }
2984        Ok(x)
2985    }
2986
2987    fn step35_prime_batch_epilogue(
2988        &self,
2989        e: &Engine,
2990        x: CudaSlice<f32>,
2991        ts: &[usize],
2992        offs: &[usize],
2993        caches: &mut [&mut Cache],
2994    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2995        let n_embd = self.cfg.n_embd as usize;
2996        let total: usize = ts.iter().sum();
2997        let mut hn = e.uninit(total * n_embd)?;
2998        e.rms_norm(
2999            &x,
3000            self.output_norm.float_data(),
3001            &mut hn,
3002            n_embd,
3003            total,
3004            self.cfg.rms_eps,
3005        )?;
3006
3007        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
3008        let mut out = Vec::with_capacity(ts.len());
3009        for s in 0..ts.len() {
3010            let mut hidden = e.uninit(ts[s] * n_embd)?;
3011            e.copy_view_into(
3012                &mut hidden,
3013                0,
3014                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
3015                ts[s] * n_embd,
3016            )?;
3017            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3018            let mut h_seed = e.uninit(n_embd)?;
3019            e.copy_view_into(
3020                &mut h_seed,
3021                0,
3022                &hidden_src.slice(last0..last0 + n_embd),
3023                n_embd,
3024            )?;
3025            // Exactness-first: the serial reference runs the output head at m=1.
3026            let mut hlast = e.uninit(n_embd)?;
3027            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3028            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
3029            caches[s].pos += ts[s];
3030            out.push((logits, h_seed, hidden));
3031        }
3032        Ok(out)
3033    }
3034
3035    /// `seq_ends[s]` = sequence s's REQUEST-absolute end position (`cache.pos + prompt_len
3036    /// + queued_after`, computed once before any chunk loop). Only step35's SWA arm reads it,
3037    /// and it must NOT be this chunk's own length: see the note on the batched entry in
3038    /// `prime_cache_overlaid` for the window the chunk-local value opened.
3039    fn step35_prime_cache_batch(
3040        &self,
3041        e: &Engine,
3042        prompts: &[&[u32]],
3043        caches: &mut [&mut Cache],
3044        seq_ends: &[usize],
3045    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3046        assert_eq!(
3047            seq_ends.len(),
3048            prompts.len(),
3049            "step35 batched prime: one seq_end per sequence"
3050        );
3051        validate_step_prime_batch_modes(
3052            step_tp_prefill_enabled()?,
3053            step_ep_grouped_prefill_enabled()?,
3054        )?;
3055        if crate::pp::pp_host_bounce_active()
3056            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
3057        {
3058            return Err(
3059                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
3060                 stage split; refusing an unsplit remote-weight walk"
3061                    .into(),
3062            );
3063        }
3064        if !Self::step35_prime_batch_on() {
3065            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
3066        }
3067        // Continuation chunks are admitted (positions above carry each sequence's base). The
3068        // remaining restriction is genuine: a CROSS-REQUEST batch mixing sequences at different
3069        // positions still needs per-request queued_after to place its KV, so B > 1 keeps the
3070        // fresh-prompt rule.
3071        if prompts.len() > 1 && caches.iter().any(|c| c.pos != 0) {
3072            return Err(
3073                "step35 batched prime supports continuation only at B=1; a cross-request batch \
3074                 at mixed positions requires per-request queued_after"
3075                    .into(),
3076            );
3077        }
3078
3079        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3080        for &t in &ts {
3081            assert!(
3082                t >= PRIME_MIN_T,
3083                "step35 batched prime needs T >= {PRIME_MIN_T}"
3084            );
3085        }
3086        for (s, c) in caches.iter().enumerate() {
3087            // POS-INCLUSIVE, like the walk's assert: a continuation chunk's rows land at
3088            // c.pos.., so the fresh-only `ts[s] <= max_ctx` form under-checked it.
3089            assert!(
3090                c.pos + ts[s] <= c.max_ctx,
3091                "step35 batched prime exceeds cache max_ctx"
3092            );
3093            assert!(
3094                seq_ends[s] >= c.pos + ts[s],
3095                "step35 batched prime: seq_end must cover this chunk"
3096            );
3097        }
3098        // MEMRA_STEP35_PRIME_BATCH_TSEND=1: CANARY SEAM restoring the pre-fix chunk-local
3099        // `seq_end` (this chunk's own length, which `ts[s]` used to supply here). It is suffix-
3100        // and chunk-VARIANT by construction, so the suffix byte-identity gate MUST break under
3101        // it. That is how the defect is DEMONSTRATED rather than argued: one binary, one seam,
3102        // the legacy arm fails cold-vs-rewound identity and the default arm passes. Read per
3103        // call; never on in a measured default run.
3104        let legacy_tsend = std::env::var("MEMRA_STEP35_PRIME_BATCH_TSEND").as_deref() == Ok("1");
3105        let seq_ends_eff: Vec<usize> = if legacy_tsend {
3106            ts.clone()
3107        } else {
3108            seq_ends.to_vec()
3109        };
3110        let offs: Vec<usize> = ts
3111            .iter()
3112            .scan(0usize, |a, &t| {
3113                let o = *a;
3114                *a += t;
3115                Some(o)
3116            })
3117            .collect();
3118        let total: usize = ts.iter().sum();
3119        let payload = total * self.cfg.n_embd as usize;
3120        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3121        // Positions start at each sequence's CURRENT cache position, not 0, so this entry can
3122        // prime a continuation chunk. The attention core already supports it: step35_attn_pre_wo
3123        // with Some(cache) is PRIME mode — it appends this chunk's post-rope K / raw V and
3124        // attends THROUGH the cache view — so only the hardcoded 0..t and the guard below ever
3125        // restricted it to fresh prompts.
3126        let positions: Vec<Vec<i32>> = ts
3127            .iter()
3128            .zip(caches.iter())
3129            .map(|(&t, c)| {
3130                let base = c.pos as i32;
3131                (0..t as i32).map(|i| base + i).collect()
3132            })
3133            .collect();
3134        let upload_positions =
3135            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
3136                positions
3137                    .iter()
3138                    .map(|p| e.htod_i32(p))
3139                    .collect::<Result<_, _>>()
3140            };
3141
3142        static ONCE: std::sync::Once = std::sync::Once::new();
3143        ONCE.call_once(|| {
3144            eprintln!(
3145                "[step35-prime-batch] first concat prime: B={} tokens={total}",
3146                prompts.len()
3147            );
3148        });
3149
3150        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
3151            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
3152                let rt = crate::pp::PpNRt::get(e)?;
3153                let n_st = fence.len() - 1;
3154                assert_eq!(
3155                    rt.n_stages(),
3156                    n_st,
3157                    "step35 prime batch stage count mismatch"
3158                );
3159                let caller_stream = e.stream();
3160                rt.fence_stages_behind(&caller_stream)?;
3161
3162                let mut slot = {
3163                    let _st0 = rt.enter(0);
3164                    let e0 = rt.engine(0, e);
3165                    let pos_ds = upload_positions(e0)?;
3166                    let x = self.embed(e0, &cat_tokens)?;
3167                    let x = self.step35_prime_batch_layers(
3168                        e0,
3169                        x,
3170                        fence[0],
3171                        fence[1],
3172                        &ts,
3173                        &offs,
3174                        &seq_ends_eff,
3175                        &pos_ds,
3176                        caches,
3177                    )?;
3178                    rt.tx(0, &x, payload)?
3179                };
3180                for s in 1..n_st - 1 {
3181                    let _st = rt.enter(s);
3182                    let es = rt.engine(s, e);
3183                    let pos_ds = upload_positions(es)?;
3184                    let x = rt.rx(s - 1, slot, payload)?;
3185                    let x = self.step35_prime_batch_layers(
3186                        es,
3187                        x,
3188                        fence[s],
3189                        fence[s + 1],
3190                        &ts,
3191                        &offs,
3192                        &seq_ends_eff,
3193                        &pos_ds,
3194                        caches,
3195                    )?;
3196                    slot = rt.tx(s, &x, payload)?;
3197                }
3198
3199                let _stl = rt.enter(n_st - 1);
3200                let el = rt.engine(n_st - 1, e);
3201                let pos_ds = upload_positions(el)?;
3202                let x = rt.rx(n_st - 2, slot, payload)?;
3203                let x = self.step35_prime_batch_layers(
3204                    el,
3205                    x,
3206                    fence[n_st - 1],
3207                    fence[n_st],
3208                    &ts,
3209                    &offs,
3210                    &seq_ends_eff,
3211                    &pos_ds,
3212                    caches,
3213                )?;
3214                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
3215                rt.publish_to(n_st - 1, &caller_stream)?;
3216                crate::pp::STEP35_PRIME_BATCH_SPLITS
3217                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3218                out
3219            } else {
3220                let pos_ds = upload_positions(e)?;
3221                let x = self.embed(e, &cat_tokens)?;
3222                let x = self.step35_prime_batch_layers(
3223                    e,
3224                    x,
3225                    0,
3226                    self.layers.len(),
3227                    &ts,
3228                    &offs,
3229                    &seq_ends_eff,
3230                    &pos_ds,
3231                    caches,
3232                )?;
3233                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3234            }
3235        } else {
3236            let pos_ds = upload_positions(e)?;
3237            let x = self.embed(e, &cat_tokens)?;
3238            let x = self.step35_prime_batch_layers(
3239                e,
3240                x,
3241                0,
3242                self.layers.len(),
3243                &ts,
3244                &offs,
3245                &seq_ends_eff,
3246                &pos_ds,
3247                caches,
3248            )?;
3249            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3250        };
3251        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3252        Ok(out)
3253    }
3254
3255    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
3256    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
3257    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
3258    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
3259    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
3260    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
3261    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
3262    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
3263    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
3264    /// over the quantized past; Linear: the stateful pad_view twin — the same state
3265    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
3266    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
3267    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
3268    /// back to single-chunk serving).
3269    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
3270    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
3271    pub fn prime_cache_batch(
3272        &self,
3273        e: &Engine,
3274        prompts: &[&[u32]],
3275        caches: &mut [&mut Cache],
3276    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3277        if crate::pp::pp_cuts(self.layers.len()).is_some()
3278            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
3279        {
3280            return Err("pipeline rewrite is not qualified for batched prime".into());
3281        }
3282        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3283            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3284                return Err("neither batched-prime nor eager rewrite is qualified".into());
3285            }
3286            if prompts.len() != caches.len() {
3287                return Err("prime fallback prompt/cache shape mismatch".into());
3288            }
3289            static ONCE: std::sync::Once = std::sync::Once::new();
3290            ONCE.call_once(|| {
3291                eprintln!(
3292                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3293                );
3294            });
3295            return prompts
3296                .iter()
3297                .copied()
3298                .zip(caches.iter_mut())
3299                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3300                .collect();
3301        }
3302        let cfg = &self.cfg;
3303        let n_embd = cfg.n_embd as usize;
3304        let eps = cfg.rms_eps;
3305        let b = prompts.len();
3306        assert!(b >= 1 && b == caches.len());
3307        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3308        let carried = pos0s.iter().any(|&p| p > 0);
3309        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3310        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3311        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3312        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3313        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3314        if self.uses_gemma_program() {
3315            return Err(
3316                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3317                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3318                    .into(),
3319            );
3320        }
3321        // Step35 has a dedicated concat walk: the generic core below cannot express its
3322        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3323        if self.uses_sliding_gated_moe_program() {
3324            // The cross-request driver hands whole requests (no chunk loop of its own), so each
3325            // sequence's request-absolute end IS its base plus its prompt length — the value
3326            // `ts[s]` happened to equal for the fresh B>=1 batches this caller admits, which is
3327            // why this arm is bit-for-bit unchanged by the seq_end threading.
3328            let seq_ends: Vec<usize> = caches
3329                .iter()
3330                .zip(prompts.iter())
3331                .map(|(c, p)| c.pos + p.len())
3332                .collect();
3333            return self.step35_prime_cache_batch(e, prompts, caches, &seq_ends);
3334        }
3335        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3336        for &t in &ts {
3337            assert!(
3338                t >= PRIME_MIN_T,
3339                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3340            );
3341        }
3342        for (s, c) in caches.iter().enumerate() {
3343            assert!(
3344                c.pos + ts[s] <= c.max_ctx,
3345                "prime_cache_batch: prompt exceeds cache max_ctx"
3346            );
3347        }
3348        let total: usize = ts.iter().sum();
3349        let offs: Vec<usize> = ts
3350            .iter()
3351            .scan(0usize, |a, &t| {
3352                let o = *a;
3353                *a += t;
3354                Some(o)
3355            })
3356            .collect();
3357        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3358        let pos_ds: Vec<CudaSlice<i32>> = ts
3359            .iter()
3360            .zip(&pos0s)
3361            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3362            .collect::<Result<_, _>>()?;
3363        // split a concat [total, dim] buffer into per-seq copies
3364        let split = |e: &Engine,
3365                     y: &CudaSlice<f32>,
3366                     dim: usize|
3367         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3368            let mut out = Vec::with_capacity(b);
3369            for s in 0..b {
3370                let mut ys = e.uninit(ts[s] * dim)?;
3371                e.copy_view_into(
3372                    &mut ys,
3373                    0,
3374                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3375                    ts[s] * dim,
3376                )?;
3377                out.push(ys);
3378            }
3379            Ok(out)
3380        };
3381
3382        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3383        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3384        for (il, layer) in self.layers.iter().enumerate() {
3385            let mut h = e.uninit(total * n_embd)?;
3386            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3387            e.rms_norm_f16out(
3388                &x,
3389                layer.attn_norm.float_data(),
3390                &mut h,
3391                &mut hx16,
3392                n_embd,
3393                total,
3394                eps,
3395            )?;
3396            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3397            let mut mixed = e.uninit(total * n_embd)?;
3398            match &layer.mixer {
3399                Mixer::Full(fa) => {
3400                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3401                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3402                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3403                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3404                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3405                    // back to the per-seq dispatch.
3406                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3407                    let (n_head, n_head_kv, head_dim) = (
3408                        geometry.n_head as usize,
3409                        geometry.n_head_kv as usize,
3410                        geometry.head_dim_k as usize,
3411                    );
3412                    let fa_scale = geometry.attention_scale();
3413                    let use_favl = !carried
3414                        && (2..=8).contains(&b)
3415                        && (head_dim == 256 || head_dim == 128)
3416                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3417                        && std::env::var("MEMRA_NOFA").is_err()
3418                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3419                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3420                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3421                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3422                    if use_favl {
3423                        let (qf_w, kf_w, vf_w) = (
3424                            fa.wq.out_features(),
3425                            fa.wk.out_features(),
3426                            fa.wv.out_features(),
3427                        );
3428                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3429                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3430                        // cannot check its own extents; `qf_w` is the wq out-features that set
3431                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3432                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3433                        struct APre {
3434                            q: CudaSlice<f32>,
3435                            gate: Option<CudaSlice<f32>>,
3436                            qn: CudaSlice<f32>,
3437                            kn: CudaSlice<f32>,
3438                        }
3439                        let mut aps = Vec::with_capacity(b);
3440                        for &t in ts.iter().take(b) {
3441                            aps.push(APre {
3442                                q: e.uninit(t * n_head * head_dim)?,
3443                                gate: Some(e.uninit(t * n_head * head_dim)?),
3444                                qn: e.uninit(t * n_head * head_dim)?,
3445                                kn: e.uninit(t * n_head_kv * head_dim)?,
3446                            });
3447                        }
3448                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3449                            let kvl = caches[0].kv[il].as_ref().unwrap();
3450                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3451                        };
3452                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3453                            .map(|s| {
3454                                let (o, t) = (offs[s], ts[s]);
3455                                let kvl = caches[s].kv[il].as_ref().unwrap();
3456                                assert!(
3457                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3458                                    "prime_cache_batch attn vl: fresh + capacity"
3459                                );
3460                                crate::AttnPreVl {
3461                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3462                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3463                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3464                                    q: e.addr_f32(&aps[s].q),
3465                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3466                                    qn: e.addr_f32(&aps[s].qn),
3467                                    kn: e.addr_f32(&aps[s].kn),
3468                                    kc: e.addr_u8(&kvl.k),
3469                                    vc: e.addr_u8(&kvl.v),
3470                                    t: t as i32,
3471                                    pad: 0,
3472                                }
3473                            })
3474                            .collect();
3475                        e.attn_pre_vl8(
3476                            &pargs,
3477                            fa.q_norm.float_data(),
3478                            fa.k_norm.float_data(),
3479                            head_dim,
3480                            geometry.n_rot as usize,
3481                            n_head,
3482                            n_head_kv,
3483                            self.cfg.rms_eps,
3484                            geometry.rope_base,
3485                            1.0,
3486                            kv_dim_k,
3487                            kv_dim_v,
3488                            ktb,
3489                            vtb,
3490                        )?;
3491                        for s in 0..b {
3492                            let kvl = caches[s].kv[il].as_mut().unwrap();
3493                            kvl.len += ts[s];
3494                            let new_len = kvl.len as i32;
3495                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3496                        }
3497                        let mut attns = Vec::with_capacity(b);
3498                        let mut mirrors = Vec::with_capacity(b);
3499                        for &t in ts.iter().take(b) {
3500                            attns.push(e.uninit(t * n_head * head_dim)?);
3501                            let n = t * n_head_kv * head_dim;
3502                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3503                        }
3504                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3505                        // promoted single-seq config is on; else the mma favl.
3506                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3507                            Ok("0") => false,
3508                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3509                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3510                            // portable build.
3511                            Ok("1") => {
3512                                crate::refuse_portable_force(
3513                                    "MEMRA_FA3=1",
3514                                    "the sm_90a fa3/bf16 kernels",
3515                                );
3516                                true
3517                            }
3518                            _ => cfg!(memra_hopper_mma),
3519                        };
3520                        if fa3_on {
3521                            let mut q16s = Vec::with_capacity(b);
3522                            let mut v16s = Vec::with_capacity(b);
3523                            for s in 0..b {
3524                                let t = ts[s];
3525                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3526                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3527                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3528                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3529                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3530                                e.f32_to_bf16_v(
3531                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3532                                    &mut v16,
3533                                    t * n_head_kv * head_dim,
3534                                )?;
3535                                q16s.push(q16);
3536                                v16s.push((k16, v16));
3537                            }
3538                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3539                            let mut kp = qp;
3540                            let mut vp = qp;
3541                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3542                            let mut tsv = [0i32; 8];
3543                            for s in 0..b {
3544                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3545                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3546                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3547                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3548                                tsv[s] = ts[s] as i32;
3549                            }
3550                            let rc = unsafe {
3551                                crate::fa3_vl_raw(
3552                                    qp.as_ptr(),
3553                                    kp.as_ptr(),
3554                                    vp.as_ptr(),
3555                                    op.as_ptr(),
3556                                    tsv.as_ptr(),
3557                                    b as i32,
3558                                    n_head as i32,
3559                                    n_head_kv as i32,
3560                                    head_dim as i32,
3561                                    fa_scale,
3562                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3563                                )
3564                            };
3565                            if rc != 0 {
3566                                return Err(format!("memra_fa3_vl rc={rc}").into());
3567                            }
3568                        } else {
3569                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3570                                .map(|s| crate::FaSeqVl {
3571                                    q: e.addr_f32(&aps[s].qn),
3572                                    k16: e.addr_u8(&mirrors[s].0),
3573                                    v16: e.addr_u8(&mirrors[s].1),
3574                                    o: e.addr_f32(&attns[s]),
3575                                    kf: e.addr_f32(&aps[s].kn),
3576                                    vf: e.addr_f32v(
3577                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3578                                    ),
3579                                    t: ts[s] as i32,
3580                                    pad: 0,
3581                                })
3582                                .collect();
3583                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3584                        }
3585                        for (s, attn) in attns.into_iter().enumerate() {
3586                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3587                                e,
3588                                attn,
3589                                &aps[s].gate,
3590                                ts[s],
3591                                n_head,
3592                                head_dim,
3593                            )?;
3594                            let mut done = false;
3595                            if let Some(xh) = &ag16 {
3596                                done = e.try_f16_gemm_pre_into_off(
3597                                    &fa.wo,
3598                                    xh,
3599                                    ts[s],
3600                                    &mut mixed,
3601                                    offs[s] * n_embd,
3602                                )?;
3603                            }
3604                            if !done {
3605                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3606                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3607                            }
3608                        }
3609                    } else {
3610                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3611                            (0..b).map(|_| Vec::new()).collect();
3612                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3613                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3614                                parts[s].push(ys);
3615                            }
3616                        }
3617                        for (s, g3s) in parts.into_iter().enumerate() {
3618                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3619                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3620                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3621                            )?;
3622                            let mut done = false;
3623                            if let Some(xh) = &ag16 {
3624                                done = e.try_f16_gemm_pre_into_off(
3625                                    &fa.wo,
3626                                    xh,
3627                                    ts[s],
3628                                    &mut mixed,
3629                                    offs[s] * n_embd,
3630                                )?;
3631                            }
3632                            if !done {
3633                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3634                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3635                            }
3636                        }
3637                    }
3638                }
3639                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3640                Mixer::Linear(la) => {
3641                    // task #16: NO split copies (cores read row-offset views of the concat
3642                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3643                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3644                    // varlen K5 launch for all sequences.
3645                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3646                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3647                    let outs =
3648                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3649                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3650                        let (o, t) = (offs[s], ts[s]);
3651                        let mut done = false;
3652                        if let Some(xh) = &gn16 {
3653                            done = e.try_f16_gemm_pre_into_off(
3654                                &la.ssm_out,
3655                                xh,
3656                                t,
3657                                &mut mixed,
3658                                o * n_embd,
3659                            )?;
3660                        }
3661                        if !done {
3662                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3663                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3664                        }
3665                    }
3666                }
3667            }
3668            let mut x1 = e.uninit(total * n_embd)?;
3669            let mut z = e.uninit(total * n_embd)?;
3670            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3671            e.add_rms_norm_f16out(
3672                &x,
3673                &mixed,
3674                layer.post_attn_norm.float_data(),
3675                &mut x1,
3676                &mut z,
3677                &mut zx16,
3678                n_embd,
3679                total,
3680                eps,
3681            )?;
3682            let ffn_out = match &layer.ffn {
3683                crate::hybrid::Ffn::Dense {
3684                    ffn_gate,
3685                    ffn_up,
3686                    ffn_down,
3687                } => {
3688                    let n_ff = ffn_gate.out_features();
3689                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3690                    let up = g2.pop().unwrap();
3691                    let gate = g2.pop().unwrap();
3692                    let mut act = e.uninit(total * n_ff)?;
3693                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3694                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3695                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3696                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3697                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3698                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3699                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3700                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3701                            Some(y) => y,
3702                            None => e.matmul(ffn_down, &act, total)?,
3703                        }
3704                    } else {
3705                        Self::ffn_act_lim(
3706                            e,
3707                            &self.cfg,
3708                            &gate,
3709                            &up,
3710                            1.0,
3711                            1.0,
3712                            d_lim,
3713                            &mut act,
3714                            total * n_ff,
3715                        )?;
3716                        e.matmul(ffn_down, &act, total)?
3717                    }
3718                }
3719                crate::hybrid::Ffn::Moe(m) => {
3720                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3721                }
3722            };
3723            let mut x2 = e.uninit(total * n_embd)?;
3724            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3725            x = x2;
3726        }
3727        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3728        let mut hn = e.uninit(total * n_embd)?;
3729        e.rms_norm(
3730            &x,
3731            self.output_norm.float_data(),
3732            &mut hn,
3733            n_embd,
3734            total,
3735            eps,
3736        )?;
3737        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3738        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3739        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3740        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3741        // argmax battery arbitrates, same as every other prefill GEMM change.
3742        let mut hcat = e.uninit(b * n_embd)?;
3743        for s in 0..b {
3744            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3745            e.copy_view_into(
3746                &mut hcat,
3747                s * n_embd,
3748                &hn.slice(last0..last0 + n_embd),
3749                n_embd,
3750            )?;
3751        }
3752        let logits_cat = if b >= 2 {
3753            e.try_f16_gemm(&self.output, &hcat, b)?
3754        } else {
3755            None
3756        };
3757        let logits_host: Option<Vec<f32>> = match &logits_cat {
3758            Some(lc) => Some(e.dtoh(lc)?),
3759            None => None,
3760        };
3761        let n_vocab = self.output.out_features();
3762        let mut hidden_all = if crate::spec::spec_hpost() {
3763            split(e, &hn, n_embd)?
3764        } else {
3765            split(e, &x, n_embd)?
3766        };
3767        let mut out = Vec::with_capacity(b);
3768        for s in 0..b {
3769            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3770            let mut h_seed = e.uninit(n_embd)?;
3771            if !crate::spec::spec_hpost() {
3772                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3773            } else {
3774                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3775            }
3776            let logits = match &logits_host {
3777                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3778                None => {
3779                    let mut hlast = e.uninit(n_embd)?;
3780                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3781                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3782                }
3783            };
3784            caches[s].pos += ts[s];
3785            out.push((logits, h_seed, hidden_all.remove(0)));
3786        }
3787        Ok(out)
3788    }
3789
3790    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3791    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3792    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3793    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3794    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3795    ///
3796    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3797    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3798    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3799    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3800    #[allow(clippy::too_many_arguments)]
3801    fn full_attn_prime(
3802        &self,
3803        e: &Engine,
3804        fa: &FullAttnLayer,
3805        h: &CudaSlice<f32>,
3806        hx: Option<&CudaSlice<u8>>,
3807        pos_d: &CudaSlice<i32>,
3808        t: usize,
3809        cache: &mut Cache,
3810        il: usize,
3811        seq_end: usize,
3812    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3813        if self.uses_sliding_gated_moe_program() {
3814            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3815        }
3816        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3817        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3818        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3819        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3820        let g3 = match hx {
3821            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3822            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3823        };
3824        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3825    }
3826
3827    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3828    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3829    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3830    fn full_attn_prime_core(
3831        &self,
3832        e: &Engine,
3833        fa: &FullAttnLayer,
3834        g3: Vec<CudaSlice<f32>>,
3835        pos_d: &CudaSlice<i32>,
3836        t: usize,
3837        cache: &mut Cache,
3838        il: usize,
3839    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3840        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3841        if let Some(xh) = &ag16 {
3842            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3843                return Ok(y);
3844            }
3845        }
3846        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3847    }
3848
3849    fn full_attn_prime_core_inner(
3850        &self,
3851        e: &Engine,
3852        fa: &FullAttnLayer,
3853        g3: Vec<CudaSlice<f32>>,
3854        pos_d: &CudaSlice<i32>,
3855        t: usize,
3856        cache: &mut Cache,
3857        il: usize,
3858    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3859        let cfg = &self.cfg;
3860        let geometry = cfg.full_attention_geometry_at(il as u32);
3861        let n_head = geometry.n_head as usize;
3862        let n_head_kv = geometry.n_head_kv as usize;
3863        let head_dim = geometry.head_dim_k as usize;
3864        let scale = geometry.attention_scale();
3865        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3866        let AttnPre { q, k, v, gate } = pre;
3867        let mut attn = e.uninit(t * n_head * head_dim)?;
3868        self.full_attn_prime_fa_dispatch(
3869            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3870        )?;
3871        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3872    }
3873
3874    /// task #18 (attn side): projections tail through KV append — everything before the
3875    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3876    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3877    #[allow(clippy::type_complexity)]
3878    fn full_attn_prime_pre_fa(
3879        &self,
3880        e: &Engine,
3881        fa: &FullAttnLayer,
3882        mut g3: Vec<CudaSlice<f32>>,
3883        pos_d: &CudaSlice<i32>,
3884        t: usize,
3885        cache: &mut Cache,
3886        il: usize,
3887    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3888        let cfg = &self.cfg;
3889        let geometry = cfg.full_attention_geometry_at(il as u32);
3890        let n_head = geometry.n_head as usize;
3891        let n_head_kv = geometry.n_head_kv as usize;
3892        let head_dim = geometry.head_dim_k as usize;
3893        let eps = cfg.rms_eps;
3894
3895        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3896        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3897        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3898        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3899        let v = g3.pop().unwrap();
3900        let mut k = g3.pop().unwrap();
3901        let qf = g3.pop().unwrap();
3902        let (mut q, gate) = if gated {
3903            let mut q = e.uninit(t * n_head * head_dim)?;
3904            let mut gate = e.uninit(t * n_head * head_dim)?;
3905            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3906            (q, Some(gate))
3907        } else {
3908            (qf, None)
3909        };
3910
3911        let mut qn = e.uninit(t * n_head * head_dim)?;
3912        e.rms_norm(
3913            &q,
3914            fa.q_norm.float_data(),
3915            &mut qn,
3916            head_dim,
3917            n_head * t,
3918            eps,
3919        )?;
3920        q = qn;
3921        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3922        e.rms_norm(
3923            &k,
3924            fa.k_norm.float_data(),
3925            &mut kn,
3926            head_dim,
3927            n_head_kv * t,
3928            eps,
3929        )?;
3930        k = kn;
3931        let rope_dims = geometry.n_rot as usize;
3932        e.rope_neox(
3933            &mut q,
3934            pos_d,
3935            head_dim,
3936            rope_dims,
3937            n_head,
3938            t,
3939            geometry.rope_base,
3940            1.0,
3941        )?;
3942        e.rope_neox(
3943            &mut k,
3944            pos_d,
3945            head_dim,
3946            rope_dims,
3947            n_head_kv,
3948            t,
3949            geometry.rope_base,
3950            1.0,
3951        )?;
3952
3953        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3954        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3955        {
3956            let kvl = cache.kv[il].as_mut().unwrap();
3957            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3958            e.append_kv_quantized_rows(
3959                &k,
3960                &v,
3961                &mut kvl.k,
3962                &mut kvl.v,
3963                kvl.len,
3964                t,
3965                kvl.kv_dim_k,
3966                kvl.kv_dim_v,
3967                kvl.k_tok_bytes,
3968                kvl.v_tok_bytes,
3969                crate::Engine::kv_fp8_on(),
3970            )?;
3971            kvl.len += t;
3972            let new_len = kvl.len as i32;
3973            e.set_i32_one(&mut kvl.len_d, new_len)?;
3974        }
3975
3976        let base_len = {
3977            let kvl = cache.kv[il].as_ref().unwrap();
3978            kvl.len - t // KV rows present BEFORE this chunk's append above
3979        };
3980        Ok((AttnPre { q, k, v, gate }, base_len))
3981    }
3982
3983    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3984    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3985    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3986    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3987    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3988    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3989    #[allow(clippy::too_many_arguments)]
3990    fn full_attn_prime_fa_dispatch(
3991        &self,
3992        e: &Engine,
3993        q: &CudaSlice<f32>,
3994        k: &CudaSlice<f32>,
3995        v: &CudaSlice<f32>,
3996        attn: &mut CudaSlice<f32>,
3997        base_len: usize,
3998        t: usize,
3999        cache: &mut Cache,
4000        il: usize,
4001        head_dim: usize,
4002        n_head: usize,
4003        n_head_kv: usize,
4004        scale: f32,
4005    ) -> Result<(), Box<dyn std::error::Error>> {
4006        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
4007        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
4008        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
4009        // attend through the quantized cache exactly like every later chunk (quantize-then-
4010        // attend). One numeric class for every row => the chunk size cannot decide where a
4011        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
4012        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
4013        // pin-the-boundary approach).
4014        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
4015        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
4016        // with the fix unconditional, only re-introducing the class edge can prove the gate
4017        // still detects the mechanism. Never on in a measured default run.
4018        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
4019            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4020                e.sdpa_naive(
4021                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4022                )?;
4023            } else {
4024                e.fa_prefill(
4025                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4026                )?;
4027            }
4028            return Ok(());
4029        }
4030        let kvl = cache.kv[il].as_ref().unwrap();
4031        let t_kv = base_len + t;
4032        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
4033        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
4034        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
4035        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
4036        // same numeric class, so the uniform contract holds on the fallback too.
4037        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4038            e.sdpa_naive_quantized_view(
4039                q,
4040                &k_view,
4041                &v_view,
4042                attn,
4043                head_dim,
4044                n_head,
4045                n_head_kv,
4046                t,
4047                t_kv,
4048                scale,
4049                true,
4050                kvl.k_tok_bytes,
4051                kvl.v_tok_bytes,
4052            )?;
4053            return Ok(());
4054        }
4055        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
4056        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
4057        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
4058        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
4059        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
4060        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
4061        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
4062        let deqw = std::env::var("MEMRA_PRIME_DEQW")
4063            .map(|v| v != "0")
4064            .unwrap_or(true);
4065        if deqw {
4066            e.fa_prefill_view_ws(
4067                q,
4068                &k_view,
4069                &v_view,
4070                attn,
4071                head_dim,
4072                n_head,
4073                n_head_kv,
4074                t,
4075                t_kv,
4076                scale,
4077                true,
4078                kvl.k_tok_bytes,
4079                kvl.v_tok_bytes,
4080                crate::Engine::kv_fp8_on(),
4081            )?;
4082        } else {
4083            e.fa_prefill_view(
4084                q,
4085                &k_view,
4086                &v_view,
4087                attn,
4088                head_dim,
4089                n_head,
4090                n_head_kv,
4091                t,
4092                t_kv,
4093                scale,
4094                true,
4095                kvl.k_tok_bytes,
4096                kvl.v_tok_bytes,
4097                crate::Engine::kv_fp8_on(),
4098            )?;
4099        }
4100        Ok(())
4101    }
4102
4103    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
4104    /// (bit-identical composition) and hands wo its fp16 operand directly.
4105    fn full_attn_prime_post_fa(
4106        &self,
4107        e: &Engine,
4108        attn: CudaSlice<f32>,
4109        gate: &Option<CudaSlice<f32>>,
4110        t: usize,
4111        n_head: usize,
4112        head_dim: usize,
4113    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4114        let (attn_g, ag16) = match gate {
4115            Some(gate) => {
4116                let n = t * n_head * head_dim;
4117                let mut ag = e.uninit(n)?;
4118                if Self::f16out_on(e, t) {
4119                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
4120                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
4121                    (ag, Some(a16))
4122                } else {
4123                    let mut gsig = e.uninit(n)?;
4124                    e.sigmoid(gate, &mut gsig, n)?;
4125                    e.mul(&attn, &gsig, &mut ag, n)?;
4126                    (ag, None)
4127                }
4128            }
4129            None => (attn, None),
4130        };
4131        Ok((attn_g, ag16))
4132    }
4133
4134    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
4135    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
4136    /// carried THROUGH the cache like the spec verify does: carried-ring conv
4137    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
4138    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
4139    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
4140    fn linear_attn_prime(
4141        &self,
4142        e: &Engine,
4143        la: &LinearAttnLayer,
4144        h: &CudaSlice<f32>,
4145        hx: Option<&CudaSlice<u8>>,
4146        t: usize,
4147        cache: &mut Cache,
4148        il: usize,
4149    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4150        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
4151        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
4152        let g4 = match hx {
4153            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
4154            None => e.matmul_group(&ws, h, t)?,
4155        };
4156        self.linear_attn_prime_core(e, la, g4, t, cache, il)
4157    }
4158
4159    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
4160    fn linear_attn_prime_core(
4161        &self,
4162        e: &Engine,
4163        la: &LinearAttnLayer,
4164        mut g4: Vec<CudaSlice<f32>>,
4165        t: usize,
4166        cache: &mut Cache,
4167        il: usize,
4168    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4169        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
4170    }
4171
4172    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
4173    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
4174    /// conv ring writes back from the true tail. None = classic path, byte-identical.
4175    #[allow(clippy::too_many_arguments)]
4176    fn linear_attn_prime_core_pad_inner(
4177        &self,
4178        e: &Engine,
4179        la: &LinearAttnLayer,
4180        mut g4: Vec<CudaSlice<f32>>,
4181        t: usize,
4182        cache: &mut Cache,
4183        il: usize,
4184        pad_len: Option<&CudaSlice<i32>>,
4185    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4186        // shim over the view twin (task #16): full-range views of the owned buffers.
4187        let geometry = la.geometry;
4188        let d_state = geometry.key_head_dim as usize;
4189        let num_k = geometry.key_heads as usize;
4190        let num_v = geometry.value_heads as usize;
4191        let key_dim = d_state * num_k;
4192        let value_dim = geometry.value_head_dim as usize * num_v;
4193        let conv_dim = key_dim * 2 + value_dim;
4194        let alpha = g4.pop().unwrap(); // [T, num_v]
4195        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4196        let z = g4.pop().unwrap(); // [T, value_dim]
4197        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4198        self.linear_attn_prime_core_pad_view(
4199            e,
4200            la,
4201            &qkv_mixed.slice(0..t * conv_dim),
4202            &z.slice(0..t * value_dim),
4203            &beta_raw.slice(0..t * num_v),
4204            &alpha.slice(0..t * num_v),
4205            t,
4206            cache,
4207            il,
4208            pad_len,
4209        )
4210    }
4211
4212    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
4213    /// shared verbatim by the per-seq scan path and the varlen batched path.
4214    #[allow(clippy::too_many_arguments)]
4215    fn linear_attn_gdn_prep(
4216        &self,
4217        e: &Engine,
4218        la: &LinearAttnLayer,
4219        qkv_mixed: &cudarc::driver::CudaView<f32>,
4220        beta_raw: &cudarc::driver::CudaView<f32>,
4221        alpha: &cudarc::driver::CudaView<f32>,
4222        t: usize,
4223        cache: &mut Cache,
4224        il: usize,
4225        pad_len: Option<&CudaSlice<i32>>,
4226    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
4227        let cfg = &self.cfg;
4228        let geometry = la.geometry;
4229        let d_state = geometry.key_head_dim as usize;
4230        let num_k = geometry.key_heads as usize;
4231        let num_v = geometry.value_heads as usize;
4232        let d_conv = geometry.conv_kernel as usize;
4233        let key_dim = d_state * num_k; // 2048
4234        let value_dim = geometry.value_head_dim as usize * num_v;
4235        let conv_dim = key_dim * 2 + value_dim; // 8192
4236        let eps = cfg.rms_eps;
4237        debug_assert!(
4238            t >= d_conv - 1,
4239            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
4240        );
4241
4242        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
4243        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
4244        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
4245        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
4246        let rl = cache.recur[il].as_mut().unwrap();
4247        let hk = Self::gdn_hk(e, t, num_v, num_k);
4248        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
4249        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
4250        let mut q_g = e.uninit(d_state * hk * t)?;
4251        let mut k_g = e.uninit(d_state * hk * t)?;
4252        let mut v_g = e.uninit(d_state * num_v * t)?;
4253        if conv_fuse {
4254            e.ssm_conv1d_gdn_state_pad(
4255                qkv_mixed,
4256                &mut rl.conv_state,
4257                la.ssm_conv1d.float_data(),
4258                &mut q_g,
4259                &mut k_g,
4260                &mut v_g,
4261                conv_dim,
4262                t,
4263                d_conv,
4264                d_state,
4265                num_v,
4266                num_k,
4267                key_dim,
4268                hk,
4269                pad_len,
4270            )?;
4271        } else {
4272            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
4273            e.ssm_conv1d_tm_state_pad_v(
4274                qkv_mixed,
4275                &mut rl.conv_state,
4276                la.ssm_conv1d.float_data(),
4277                &mut conv_out,
4278                conv_dim,
4279                t,
4280                d_conv,
4281                pad_len,
4282            )?;
4283            e.qkv_to_gdn_repack(
4284                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
4285            )?;
4286        }
4287        let mut q_l2 = e.uninit(d_state * hk * t)?;
4288        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
4289        // Emitted only where a consumer exists (the wgmma config) — on other arches the
4290        // alloc + epilogue stores would be pure waste.
4291        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4292            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4293            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4294            Some(qb)
4295        } else {
4296            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4297            None
4298        };
4299        let mut k_l2 = e.uninit(d_state * hk * t)?;
4300        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4301        let kb16 = if Engine::l2_v2_on(d_state) {
4302            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4303            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4304            Some(kb)
4305        } else {
4306            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4307            None
4308        };
4309        let mut beta = e.uninit(t * num_v)?;
4310        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4311        let mut g_log = e.uninit(t * num_v)?;
4312        e.gdn_glog_v(
4313            alpha,
4314            la.ssm_dt.float_data(),
4315            la.ssm_a.float_data(),
4316            &mut g_log,
4317            num_v,
4318            t,
4319        )?;
4320        if let Some(len_d) = pad_len {
4321            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4322        }
4323        Ok(GdnPrep {
4324            hk,
4325            q_l2,
4326            k_l2,
4327            v_g,
4328            beta,
4329            g_log,
4330            kb16,
4331            qb16,
4332        })
4333    }
4334
4335    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4336    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4337    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4338    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4339    #[allow(clippy::too_many_arguments)]
4340    fn linear_attn_prime_core_batch(
4341        &self,
4342        e: &Engine,
4343        la: &LinearAttnLayer,
4344        g4: &[CudaSlice<f32>],
4345        offs: &[usize],
4346        ts: &[usize],
4347        caches: &mut [&mut Cache],
4348        il: usize,
4349    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4350        let geometry = la.geometry;
4351        let d_state = geometry.key_head_dim as usize;
4352        let num_k = geometry.key_heads as usize;
4353        let num_v = geometry.value_heads as usize;
4354        let d_conv = geometry.conv_kernel as usize;
4355        let key_dim = d_state * num_k;
4356        let value_dim = geometry.value_head_dim as usize * num_v;
4357        let conv_dim = key_dim * 2 + value_dim;
4358        let eps = self.cfg.rms_eps;
4359        let scale = 1.0 / (d_state as f32).sqrt();
4360        let b = ts.len();
4361        let c = Engine::gdn_chunk_size();
4362        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4363        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4364        let carried = caches.iter().any(|c| c.pos > 0);
4365        let use_vl = !carried
4366            && (2..=8).contains(&b)
4367            && Engine::gdn_chunked_enabled()
4368            && ts.iter().all(|&t| t >= 16)
4369            && e.gdn_mma_enabled(c)
4370            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4371        if !use_vl {
4372            return (0..b)
4373                .map(|s| {
4374                    let (o, t) = (offs[s], ts[s]);
4375                    self.linear_attn_prime_core_pad_view(
4376                        e,
4377                        la,
4378                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4379                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4380                        &g4[2].slice(o * num_v..(o + t) * num_v),
4381                        &g4[3].slice(o * num_v..(o + t) * num_v),
4382                        t,
4383                        caches[s],
4384                        il,
4385                        None,
4386                    )
4387                })
4388                .collect();
4389        }
4390        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4391        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4392        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4393        struct SeqBufs {
4394            conv_out: CudaSlice<f32>,
4395            q_g: CudaSlice<f32>,
4396            k_g: CudaSlice<f32>,
4397            v_g: CudaSlice<f32>,
4398            q_l2: CudaSlice<f32>,
4399            k_l2: CudaSlice<f32>,
4400            beta: CudaSlice<f32>,
4401            g_log: CudaSlice<f32>,
4402            gn: CudaSlice<f32>,
4403            gn16: CudaSlice<u8>,
4404        }
4405        let f16o = Self::f16out_on(e, 16);
4406        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4407        let mut sb = Vec::with_capacity(b);
4408        let mut pres = Vec::with_capacity(b);
4409        for &t in ts.iter().take(b) {
4410            sb.push(SeqBufs {
4411                conv_out: e.uninit(conv_dim * t)?,
4412                q_g: e.uninit(d_state * hk * t)?,
4413                k_g: e.uninit(d_state * hk * t)?,
4414                v_g: e.uninit(d_state * num_v * t)?,
4415                q_l2: e.uninit(d_state * hk * t)?,
4416                k_l2: e.uninit(d_state * hk * t)?,
4417                beta: e.uninit(t * num_v)?,
4418                g_log: e.uninit(t * num_v)?,
4419                gn: e.uninit(d_state * num_v * t)?,
4420                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4421            });
4422            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4423        }
4424        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4425            .map(|s| {
4426                let (o, t) = (offs[s], ts[s]);
4427                let rl = caches[s].recur[il].as_ref().unwrap();
4428                crate::GdnPrepVl {
4429                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4430                    conv_state: e.addr_f32(&rl.conv_state),
4431                    conv_out: e.addr_f32(&sb[s].conv_out),
4432                    q_g: e.addr_f32(&sb[s].q_g),
4433                    k_g: e.addr_f32(&sb[s].k_g),
4434                    v_g: e.addr_f32(&sb[s].v_g),
4435                    q_l2: e.addr_f32(&sb[s].q_l2),
4436                    k_l2: e.addr_f32(&sb[s].k_l2),
4437                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4438                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4439                    beta: e.addr_f32(&sb[s].beta),
4440                    g_log: e.addr_f32(&sb[s].g_log),
4441                    o: e.addr_f32(&pres[s].o),
4442                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4443                    gn: e.addr_f32(&sb[s].gn),
4444                    gn16: e.addr_u8(&sb[s].gn16),
4445                    kb16: if Engine::l2_v2_on(d_state) {
4446                        e.addr_u8(&pres[s].kb16)
4447                    } else {
4448                        0
4449                    },
4450                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4451                        e.addr_u8(&pres[s].qb16)
4452                    } else {
4453                        0
4454                    },
4455                    t: t as i32,
4456                    pad: 0,
4457                }
4458            })
4459            .collect();
4460        let args: Vec<crate::GdnSeqVl> = (0..b)
4461            .map(|s| {
4462                let rl = caches[s].recur[il].as_ref().unwrap();
4463                crate::GdnSeqVl {
4464                    kb16: e.addr_u8(&pres[s].kb16),
4465                    gcum: e.addr_f32(&pres[s].gcum),
4466                    beta: e.addr_f32(&sb[s].beta),
4467                    u: e.addr_f32(&pres[s].u),
4468                    wb16: e.addr_u8(&pres[s].wb16),
4469                    y: e.addr_u8(&pres[s].y16),
4470                    ssnap: e.addr_u8(&pres[s].ssnap16),
4471                    state_in: e.addr_f32(&rl.ssm_state),
4472                    state_out: e.addr_f32(&rl.ssm_state_alt),
4473                    q: e.addr_f32(&sb[s].q_l2),
4474                    p: e.addr_f32(&pres[s].p),
4475                    o: e.addr_f32(&pres[s].o),
4476                    k: e.addr_f32(&sb[s].k_l2),
4477                    v: e.addr_f32(&sb[s].v_g),
4478                    g: e.addr_f32(&sb[s].g_log),
4479                    a: e.addr_f32(&pres[s].a),
4480                    w: e.addr_f32(&pres[s].w),
4481                    t: ts[s] as i32,
4482                    nc: pres[s].nc as i32,
4483                }
4484            })
4485            .collect();
4486        e.gdn_prep_vl8(
4487            &prep_args,
4488            la.ssm_conv1d.float_data(),
4489            la.ssm_dt.float_data(),
4490            la.ssm_a.float_data(),
4491            conv_dim,
4492            d_conv,
4493            d_state,
4494            num_v,
4495            num_k,
4496            key_dim,
4497            hk,
4498            eps,
4499        )?;
4500        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4501        // both standalone mirror launches vanish on the default config.
4502        if !Engine::l2_v2_on(d_state) {
4503            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4504        }
4505        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4506        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4507            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4508            if !Engine::l2_v2_on(d_state) {
4509                for s in 0..b {
4510                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4511                }
4512            }
4513            let mut wa = [crate::GdnWVl::default(); 8];
4514            for s in 0..b {
4515                wa[s] = crate::GdnWVl {
4516                    qb16: e.addr_u8(&pres[s].qb16),
4517                    pb16: e.addr_u8(&pres[s].pb16),
4518                };
4519            }
4520            Some(crate::GdnWVl8(wa))
4521        } else {
4522            None
4523        };
4524        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4525        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4526        if f16o {
4527            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4528        }
4529        // per-seq state swap (+ non-f16out tail fallback)
4530        let mut out = Vec::with_capacity(b);
4531        for (s, bufs) in sb.into_iter().enumerate() {
4532            let rl = caches[s].recur[il].as_mut().unwrap();
4533            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4534            let (o, t) = (offs[s], ts[s]);
4535            let SeqBufs { mut gn, gn16, .. } = bufs;
4536            if f16o {
4537                out.push((gn, Some(gn16)));
4538            } else {
4539                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4540                e.gated_rmsnorm_zv(
4541                    &pres[s].o,
4542                    la.ssm_norm.float_data(),
4543                    &z_v,
4544                    &mut gn,
4545                    d_state,
4546                    num_v * t,
4547                    eps,
4548                )?;
4549                out.push((gn, None));
4550            }
4551        }
4552        Ok(out)
4553    }
4554
4555    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4556    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4557    /// Same kernels, same values, byte-identical to the Vec shim above.
4558    #[allow(clippy::too_many_arguments)]
4559    fn linear_attn_prime_core_pad_view(
4560        &self,
4561        e: &Engine,
4562        la: &LinearAttnLayer,
4563        qkv_mixed: &cudarc::driver::CudaView<f32>,
4564        z: &cudarc::driver::CudaView<f32>,
4565        beta_raw: &cudarc::driver::CudaView<f32>,
4566        alpha: &cudarc::driver::CudaView<f32>,
4567        t: usize,
4568        cache: &mut Cache,
4569        il: usize,
4570        pad_len: Option<&CudaSlice<i32>>,
4571    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4572        let cfg = &self.cfg;
4573        let geometry = la.geometry;
4574        let d_state = geometry.key_head_dim as usize;
4575        let num_v = geometry.value_heads as usize;
4576        let eps = cfg.rms_eps;
4577        let scale = 1.0 / (d_state as f32).sqrt();
4578
4579        let prep =
4580            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4581
4582        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4583        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4584        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4585        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4586        // verify keep the sequential kernel).
4587        let mut o = e.uninit(d_state * num_v * t)?;
4588        let rl = cache.recur[il].as_mut().unwrap();
4589        {
4590            let crate::cache::RecurLayer {
4591                ssm_state,
4592                ssm_state_alt,
4593                ..
4594            } = rl;
4595            e.gdn_scan_prefill(
4596                &prep.q_l2,
4597                &prep.k_l2,
4598                &prep.v_g,
4599                &prep.g_log,
4600                &prep.beta,
4601                prep.kb16.as_ref(),
4602                prep.qb16.as_ref(),
4603                ssm_state,
4604                ssm_state_alt,
4605                &mut o,
4606                num_v,
4607                t,
4608                scale,
4609                prep.hk,
4610            )?;
4611        }
4612        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4613
4614        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4615        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4616        let mut gn = e.uninit(d_state * num_v * t)?;
4617        let gn16 = if Self::f16out_on(e, t) {
4618            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4619            e.gated_rmsnorm_f16out_zv(
4620                &o,
4621                la.ssm_norm.float_data(),
4622                z,
4623                &mut gn,
4624                &mut g16,
4625                d_state,
4626                num_v * t,
4627                eps,
4628            )?;
4629            Some(g16)
4630        } else {
4631            e.gated_rmsnorm_zv(
4632                &o,
4633                la.ssm_norm.float_data(),
4634                z,
4635                &mut gn,
4636                d_state,
4637                num_v * t,
4638                eps,
4639            )?;
4640            None
4641        };
4642        Ok((gn, gn16))
4643    }
4644
4645    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4646    #[allow(clippy::too_many_arguments)]
4647    fn linear_attn_prime_core_pad(
4648        &self,
4649        e: &Engine,
4650        la: &LinearAttnLayer,
4651        g4: Vec<CudaSlice<f32>>,
4652        t: usize,
4653        cache: &mut Cache,
4654        il: usize,
4655        pad_len: Option<&CudaSlice<i32>>,
4656    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4657        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4658        if let Some(xh) = &gn16 {
4659            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4660                return Ok(y);
4661            }
4662        }
4663        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4664    }
4665
4666    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4667    ///
4668    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4669    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4670    pub fn full_attn(
4671        &self,
4672        e: &Engine,
4673        fa: &FullAttnLayer,
4674        h: &CudaSlice<f32>,
4675        pos_d: &CudaSlice<i32>,
4676        t: usize,
4677        il: usize,
4678    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4679        if self.uses_sliding_gated_moe_program() {
4680            return self.step35_attn(e, fa, h, pos_d, t, il);
4681        }
4682        let cfg = &self.cfg;
4683        let _n_embd = cfg.n_embd as usize;
4684        let geometry = cfg.full_attention_geometry_at(il as u32);
4685        let n_head = geometry.n_head as usize;
4686        let n_head_kv = geometry.n_head_kv as usize;
4687        let head_dim = geometry.head_dim_k as usize;
4688        let eps = cfg.rms_eps;
4689        let scale = geometry.attention_scale();
4690
4691        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4692        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4693        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4694        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4695        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4696        let v = g3.pop().unwrap();
4697        let mut k = g3.pop().unwrap();
4698        let qf = g3.pop().unwrap();
4699        let (mut q, gate) = if gated {
4700            let mut q = e.uninit(t * n_head * head_dim)?;
4701            let mut gate = e.uninit(t * n_head * head_dim)?;
4702            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4703            (q, Some(gate))
4704        } else {
4705            (qf, None)
4706        };
4707
4708        // QK-norm (per head_dim row), then partial RoPE.
4709        let mut qn = e.uninit(t * n_head * head_dim)?;
4710        e.rms_norm(
4711            &q,
4712            fa.q_norm.float_data(),
4713            &mut qn,
4714            head_dim,
4715            n_head * t,
4716            eps,
4717        )?;
4718        q = qn;
4719        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4720        e.rms_norm(
4721            &k,
4722            fa.k_norm.float_data(),
4723            &mut kn,
4724            head_dim,
4725            n_head_kv * t,
4726            eps,
4727        )?;
4728        k = kn;
4729        let rope_dims = geometry.n_rot as usize;
4730        e.rope_neox(
4731            &mut q,
4732            pos_d,
4733            head_dim,
4734            rope_dims,
4735            n_head,
4736            t,
4737            geometry.rope_base,
4738            1.0,
4739        )?;
4740        e.rope_neox(
4741            &mut k,
4742            pos_d,
4743            head_dim,
4744            rope_dims,
4745            n_head_kv,
4746            t,
4747            geometry.rope_base,
4748            1.0,
4749        )?;
4750
4751        // SDPA
4752        let mut attn = e.uninit(t * n_head * head_dim)?;
4753        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4754        // falls back to naive sdpa.
4755        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4756            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4757            e.sdpa_naive(
4758                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4759            )?;
4760        } else {
4761            e.fa_prefill(
4762                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4763            )?;
4764        }
4765
4766        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4767        let attn_g = match &gate {
4768            Some(gate) => {
4769                let mut gsig = e.uninit(t * n_head * head_dim)?;
4770                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4771                let mut ag = e.uninit(t * n_head * head_dim)?;
4772                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4773                ag
4774            }
4775            None => attn,
4776        };
4777
4778        // o projection
4779        let o = e.matmul(&fa.wo, &attn_g, t)?;
4780        Ok(o)
4781    }
4782
4783    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4784    pub fn linear_attn(
4785        &self,
4786        e: &Engine,
4787        la: &LinearAttnLayer,
4788        h: &CudaSlice<f32>,
4789        t: usize,
4790    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4791        let cfg = &self.cfg;
4792        let _n_embd = cfg.n_embd as usize;
4793        let geometry = la.geometry;
4794        let d_state = geometry.key_head_dim as usize;
4795        let num_k = geometry.key_heads as usize;
4796        let num_v = geometry.value_heads as usize;
4797        let d_conv = geometry.conv_kernel as usize;
4798        let head_k = d_state;
4799        let head_v = geometry.value_head_dim as usize;
4800        let key_dim = head_k * num_k; // 2048
4801        let value_dim = head_v * num_v; // 4096
4802        let conv_dim = key_dim * 2 + value_dim; // 8192
4803        let eps = cfg.rms_eps;
4804        let scale = 1.0 / (d_state as f32).sqrt();
4805
4806        // projections
4807        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4808        let mut g4 = e.matmul_group(
4809            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4810            h,
4811            t,
4812        )?;
4813        let alpha = g4.pop().unwrap(); // [T, num_v]
4814        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4815        let z = g4.pop().unwrap(); // [T, value_dim]
4816        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4817
4818        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4819        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4820        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4821        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4822        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4823        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4824        let _ = (head_k, head_v);
4825        let mut q_g = e.uninit(d_state * num_v * t)?;
4826        let mut k_g = e.uninit(d_state * num_v * t)?;
4827        let mut v_g = e.uninit(d_state * num_v * t)?;
4828        e.ssm_conv1d_gdn(
4829            &qkv_mixed,
4830            la.ssm_conv1d.float_data(),
4831            &mut q_g,
4832            &mut k_g,
4833            &mut v_g,
4834            conv_dim,
4835            t,
4836            d_conv,
4837            d_state,
4838            num_v,
4839            num_k,
4840            key_dim,
4841        )?;
4842        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4843        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4844        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4845        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4846        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4847        let v_gd = v_g;
4848
4849        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4850        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4851        let mut beta = e.uninit(t * num_v)?;
4852        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4853        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4854        let mut g_log = e.uninit(t * num_v)?;
4855        e.gdn_glog(
4856            &alpha,
4857            la.ssm_dt.float_data(),
4858            la.ssm_a.float_data(),
4859            &mut g_log,
4860            num_v,
4861            t,
4862        )?;
4863
4864        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4865        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4866        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4867        let mut o = e.uninit(d_state * num_v * t)?;
4868        e.gdn_scan_prefill(
4869            &q_l2,
4870            &k_l2,
4871            &v_gd,
4872            &g_log,
4873            &beta,
4874            None,
4875            None,
4876            &state_in,
4877            &mut state_out,
4878            &mut o,
4879            num_v,
4880            t,
4881            scale,
4882            num_v,
4883        )?;
4884
4885        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4886        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4887        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4888        // o rows are (t*num_v+vh) too. Good.
4889        let mut gn = e.uninit(d_state * num_v * t)?;
4890        e.gated_rmsnorm(
4891            &o,
4892            la.ssm_norm.float_data(),
4893            &z,
4894            &mut gn,
4895            d_state,
4896            num_v * t,
4897            eps,
4898        )?;
4899
4900        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4901        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4902        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4903        let out = e.matmul(&la.ssm_out, &gn, t)?;
4904        Ok(out)
4905    }
4906}
4907
4908impl HybridModel {
4909    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4910    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4911    ///
4912    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4913    /// different 860160-byte block than the same expert of layer 7).
4914    ///
4915    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4916    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4917    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4918    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4919    pub fn moe_ffn_il(
4920        &self,
4921        e: &Engine,
4922        m: &MoeWeights,
4923        z: &CudaSlice<f32>,
4924        t: usize,
4925        il: u16,
4926    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4927        Self::moe_ffn_inner(
4928            e,
4929            m,
4930            z,
4931            None,
4932            t,
4933            &self.cfg,
4934            il,
4935            self.max_moe_block(),
4936            false,
4937            None,
4938            self.uses_sliding_gated_moe_program(),
4939        )
4940    }
4941
4942    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4943    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4944    pub fn moe_ffn_il_prefill(
4945        &self,
4946        e: &Engine,
4947        m: &MoeWeights,
4948        z: &CudaSlice<f32>,
4949        t: usize,
4950        il: u16,
4951    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4952        Self::moe_ffn_inner(
4953            e,
4954            m,
4955            z,
4956            None,
4957            t,
4958            &self.cfg,
4959            il,
4960            self.max_moe_block(),
4961            true,
4962            Some(&self.step_grouped_prefill),
4963            self.uses_sliding_gated_moe_program(),
4964        )
4965    }
4966
4967    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4968    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4969    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4970    pub fn moe_ffn_il_zq8(
4971        &self,
4972        e: &Engine,
4973        m: &MoeWeights,
4974        z: &CudaSlice<f32>,
4975        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4976        t: usize,
4977        il: u16,
4978    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4979        Self::moe_ffn_inner(
4980            e,
4981            m,
4982            z,
4983            zq8,
4984            t,
4985            &self.cfg,
4986            il,
4987            self.max_moe_block(),
4988            false,
4989            None,
4990            self.uses_sliding_gated_moe_program(),
4991        )
4992    }
4993
4994    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4995    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4996    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4997    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4998    ///
4999    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
5000    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
5001    pub(crate) fn moe_ffn(
5002        e: &Engine,
5003        m: &MoeWeights,
5004        z: &CudaSlice<f32>,
5005        t: usize,
5006        cfg: &ModelConfig,
5007        il: u16,
5008        max_block: usize,
5009    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5010        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
5011    }
5012
5013    #[allow(clippy::too_many_arguments)]
5014    pub(crate) fn moe_ffn_inner(
5015        e: &Engine,
5016        m: &MoeWeights,
5017        z: &CudaSlice<f32>,
5018        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5019        t: usize,
5020        cfg: &ModelConfig,
5021        il: u16,
5022        max_block: usize,
5023        prefill: bool,
5024        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
5025        sliding_gated_moe: bool,
5026    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5027        let worker_io = crate::spill_pread::worker_enabled();
5028        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
5029        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
5030            e.with_moe_cache(max_block, |cache, _| {
5031                cache.begin_forward_epoch(il, t);
5032                if worker_io {
5033                    cache.begin_worker_scope();
5034                }
5035                Ok(())
5036            })?;
5037        }
5038        if m.step_ep.is_some() || m.step_tp.is_some() {
5039            let moe = cfg
5040                .moe
5041                .as_ref()
5042                .ok_or("Step distributed execution requires MoE model metadata")?;
5043            let n_embd = cfg.n_embd as usize;
5044            let n_expert = moe.expert_count as usize;
5045            let n_used = moe.expert_used_count as usize;
5046            let sigmoid = cfg
5047                .sigmoid_router()
5048                .ok_or("Step distributed execution requires the Step sigmoid router")?;
5049            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5050            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5051            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
5052            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
5053                return Err(
5054                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
5055                );
5056            }
5057            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
5058                return Err(format!(
5059                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
5060                    PRIME_MIN_T,
5061                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
5062                )
5063                .into());
5064            }
5065            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
5066            let grouped_prefill_shape =
5067                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
5068            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
5069                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
5070            }) {
5071                let (selected, route_weights) =
5072                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
5073                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5074                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5075                Self::trace_moe_input(e, il, t, n_embd, z)?;
5076                let selected = selected
5077                    .iter()
5078                    .map(|&expert| expert as usize)
5079                    .collect::<Vec<_>>();
5080
5081                // The narrow route readback above orders the owning-stage producer. The grouped
5082                // runtime then copies the resident root activation into its persistent rank inputs.
5083                e.stream().synchronize()?;
5084                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
5085                    state.projection.set_activation_limit(ep.activation_limit)?;
5086                    ep.runtime
5087                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
5088                            ep.experts.e4m3()?,
5089                            &mut state.projection,
5090                            z,
5091                            t,
5092                            &selected,
5093                        )?;
5094                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
5095                        &state.projection,
5096                        &mut state.combine,
5097                        &route_weights,
5098                    )?;
5099                    ep.runtime.execute_step_grouped_expert_parallel_gate(
5100                        ep.experts.e4m3()?,
5101                        &mut state.projection,
5102                    )?;
5103                    ep.runtime.execute_step_grouped_expert_parallel_combine(
5104                        &state.projection,
5105                        &mut state.combine,
5106                    )?;
5107                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
5108                        &state.projection,
5109                        &state.combine,
5110                        e,
5111                    )?;
5112                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5113                    if prefill {
5114                        // A shared plan may be reused by the next layer on a different runtime
5115                        // stream. Complete the owning-stage copy before its source is overwritten.
5116                        e.stream().synchronize()?;
5117                    }
5118                    eprintln!(
5119                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
5120                         attention_layout=tensor-parallel expert_layout=expert-parallel \
5121                         expert_transport={} native_p2p=true route_control=host-narrow \
5122                         input=root-device projection_workspaces=persistent \
5123                         combine=root-device output=owning-stage-device \
5124                         prefill={prefill} batched_decode=false capacity={} \
5125                         performance_claim=false",
5126                        ep.devices,
5127                        ep.runtime.transport_label(),
5128                        state.projection.max_tokens(),
5129                    );
5130                    Ok::<_, Box<dyn std::error::Error>>(output)
5131                };
5132
5133                if grouped_prefill_shape {
5134                    let grouped_prefill = grouped_prefill
5135                        .ok_or("Step grouped prefill has no model-scoped executor")?;
5136                    let mut shared = grouped_prefill
5137                        .lock()
5138                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
5139                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
5140                        state.devices != ep.devices
5141                            || state.grouped.projection.max_tokens() < t
5142                            || state.grouped.projection.input_width() != n_embd
5143                            || state.grouped.projection.expert_width()
5144                                != moe.expert_ff_length as usize
5145                    });
5146                    if needs_prepare {
5147                        let seed_input = vec![0.0f32; n_embd];
5148                        let seed_selected = &selected[..n_used];
5149                        let seed_weights = &route_weights[..n_used];
5150                        let projection = ep
5151                            .runtime
5152                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
5153                                ep.experts.e4m3()?,
5154                                &seed_input,
5155                                1,
5156                                seed_selected,
5157                                ep.activation_limit,
5158                                t,
5159                            )?;
5160                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
5161                            &projection,
5162                            seed_weights,
5163                        )?;
5164                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
5165                            devices: ep.devices.clone(),
5166                            grouped: crate::hybrid::StepEpGroupedDecode {
5167                                projection,
5168                                combine,
5169                            },
5170                        });
5171                        eprintln!(
5172                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
5173                             shared_across_layers=true performance_claim=false",
5174                            ep.devices,
5175                        );
5176                    }
5177                    return execute(
5178                        &mut shared
5179                            .state
5180                            .as_mut()
5181                            .expect("Step grouped prefill state prepared above")
5182                            .grouped,
5183                    );
5184                }
5185
5186                let mut grouped = ep
5187                    .grouped_decode
5188                    .as_ref()
5189                    .expect("grouped decode presence checked above")
5190                    .lock()
5191                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
5192                return execute(&mut grouped);
5193            }
5194            if grouped_prefill_shape {
5195                return Err(
5196                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
5197                        .into(),
5198                );
5199            }
5200            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
5201            // expert program — the per-layer host logits readback (the last per-layer host
5202            // sync) disappears. Selection tie-breaking may differ from the host router:
5203            // numeric-class door, run-gen argmax gate + boot battery.
5204            // STEP TP2 GEMM PRIME (2026-08-27, TTFT lane): a prime chunk's routed MoE goes
5205            // through ONE grouped f16 GEMM per projection over the resident NVFP4 banks —
5206            // the per-token device routes below cost 240 s at m=4092 (measured), the grouped
5207            // lane's sizing rows run 170-270 TFLOP/s. Router selections come from the same
5208            // sigmoid host oracle the EP arm uses; shexp rides the canonical grouped add.
5209            // t>=16 alone keys the branch: the batch prime reaches here through moe_ffn_il,
5210            // whose `prefill` is FALSE (only the _prefill twin sets it), and no other step37
5211            // route runs t>=16 — verify walks t<=8, decode t=1. Requiring `prefill` made the
5212            // first gate arm skip this branch entirely and wake the generic f16g arm instead
5213            // (48 s + kq_gemm_sk rc=1001, 2026-08-27).
5214            if t >= 16 && crate::step_gemm_prime_on() {
5215                if let Some(tp) = &m.step_tp {
5216                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5217                        // MEMRA_PRIME_PROF=1 sub-split of the moe bucket. The phase timer put
5218                        // 1788 ms of a 3093 ms chunk here, but forcing the 32-row tile form (4x
5219                        // more weight dequant) moved it only 5% — so the grouped GEMM is not
5220                        // obviously what dominates. The router below is a HOST oracle: sigmoid +
5221                        // top-8 over 288 experts for every one of 4096 tokens, per layer, which
5222                        // is a D2H copy and a full pipeline drain 42 times per chunk. Attribute
5223                        // it before optimizing the kernel it sits in front of.
5224                        let mprof =
5225                            std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1") && t >= 16;
5226                        let mut mt = std::time::Instant::now();
5227                        let (selected, route_weights) = Self::moe_route_sigmoid_cfg(
5228                            e, &logits, t, n_expert, n_used, m, sigmoid,
5229                        )?;
5230                        let sel_i32: Vec<i32> = selected.iter().map(|&x| x as i32).collect();
5231                        let d_router = if mprof {
5232                            let _ = e.stream().synchronize();
5233                            let v = mt.elapsed().as_secs_f64() * 1e3;
5234                            mt = std::time::Instant::now();
5235                            v
5236                        } else {
5237                            0.0
5238                        };
5239                        // MEMRA_MOE_DETERM=1: run the WHOLE grouped routine twice on identical
5240                        // inputs and diff. The standalone harness cleared the grouped GEMM kernels
5241                        // (8 invocations, both lanes, maxdiff 0.0 over 20.9M elements) but it does
5242                        // not model the cross-device join/scatter or the o_proj-style reduction,
5243                        // and the loader refuses both topologies (TP1, same-device) that would
5244                        // isolate those by env. This tests the un-excluded region directly, in
5245                        // the place it actually runs.
5246                        //
5247                        // The prime is nondeterministic: same prompt, one forward, temperature=0,
5248                        // max_tokens=1, and the first token varies across reps. That blocks
5249                        // MEMRA_PP_BF16's correctness receipt and invalidates every byte-identity
5250                        // gate taken through the server. This probe also yields the jitter
5251                        // MAGNITUDE, which any tolerance band needs.
5252                        let mdet = std::env::var("MEMRA_MOE_DETERM").as_deref() == Ok("1")
5253                            && t >= 16
5254                            && il < 4;
5255                        if mdet {
5256                            let a = tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5257                                bank,
5258                                e,
5259                                z,
5260                                t,
5261                                &sel_i32,
5262                                &route_weights,
5263                                n_used,
5264                                tp.activation_limit,
5265                            )?;
5266                            let b = tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5267                                bank,
5268                                e,
5269                                z,
5270                                t,
5271                                &sel_i32,
5272                                &route_weights,
5273                                n_used,
5274                                tp.activation_limit,
5275                            )?;
5276                            let (ha, hb) = (e.dtoh(&a)?, e.dtoh(&b)?);
5277                            let mut md = 0.0f32;
5278                            let mut ndiff = 0usize;
5279                            for (x, y) in ha.iter().zip(hb.iter()) {
5280                                let d = (x - y).abs();
5281                                if d > 0.0 {
5282                                    ndiff += 1;
5283                                }
5284                                if d > md {
5285                                    md = d;
5286                                }
5287                            }
5288                            eprintln!(
5289                                "[moe-determ] il={il} t={t} maxdiff={md:.3e} \
5290                                 differing={ndiff}/{} -> {}",
5291                                ha.len(),
5292                                if ndiff == 0 {
5293                                    "IDENTICAL"
5294                                } else {
5295                                    "NONDETERMINISTIC"
5296                                }
5297                            );
5298                        }
5299                        let mut output =
5300                            tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5301                                bank,
5302                                e,
5303                                z,
5304                                t,
5305                                &sel_i32,
5306                                &route_weights,
5307                                n_used,
5308                                tp.activation_limit,
5309                            )?;
5310                        let d_gemm = if mprof {
5311                            let _ = e.stream().synchronize();
5312                            let v = mt.elapsed().as_secs_f64() * 1e3;
5313                            mt = std::time::Instant::now();
5314                            v
5315                        } else {
5316                            0.0
5317                        };
5318                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5319                        if mprof {
5320                            let _ = e.stream().synchronize();
5321                            let d_shared = mt.elapsed().as_secs_f64() * 1e3;
5322                            // Per LAYER, not accumulated: the four trunk phases already carry the
5323                            // per-chunk totals, and one line per layer is what shows whether the
5324                            // cost is flat across layers or concentrated in a few.
5325                            eprintln!(
5326                                "[moe-prof] il={il} t={t} router={d_router:.1}ms \
5327                                 gemm={d_gemm:.1}ms shared={d_shared:.1}ms"
5328                            );
5329                        }
5330                        return Ok(output);
5331                    }
5332                }
5333            }
5334            if t == 1
5335                && crate::tp::step_nvfp4_dev_routes_enabled()?
5336                && crate::tp::step_tp_dev_router_enabled()?
5337            {
5338                if let Some(tp) = &m.step_tp {
5339                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5340                        let (sf, route_norm) = sigmoid;
5341                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
5342                        // before the router — the rank streams overlap the gemv+topk.
5343                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
5344                        // from its own z copy (replicated deterministic router — identical
5345                        // bits in, identical sel/w out) and starts its sweep without
5346                        // waiting the root's sel broadcast.
5347                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5348                        let d1_router = *D1_ROUTER.get_or_init(|| {
5349                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
5350                        });
5351                        if d1_router {
5352                            let (sf_h, rn_h) = sigmoid;
5353                            let n_ex = m.gate_exps.n_expert;
5354                            let act_ct = m.active_count();
5355                            let _ = tp.runtime.nvfp4_routes_prestage_with(
5356                                bank,
5357                                e,
5358                                z,
5359                                |rank1, in1, sel1, w1| {
5360                                    let mut guard = DEV1_ROUTER_REPS
5361                                        .lock()
5362                                        .map_err(|_| "dev1 router replica lock")?;
5363                                    let (reps, scratch) =
5364                                        guard.get_or_insert_with(|| (Default::default(), None));
5365                                    if !reps.contains_key(&il) {
5366                                        use cudarc::driver::DevicePtr;
5367                                        let (g1, p1, a1) = (
5368                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
5369                                            rank1.htod(&vec![0.0f32; n_ex])?,
5370                                            rank1.alloc_u8_uninit(n_ex)?,
5371                                        );
5372                                        for (src, dst_len, dst) in [
5373                                            (
5374                                                {
5375                                                    let s = e.stream();
5376                                                    let (p, _g) =
5377                                                        m.gate_inp.float_data().device_ptr(&s);
5378                                                    p as u64
5379                                                },
5380                                                n_ex * n_embd * 4,
5381                                                {
5382                                                    let s = rank1.stream();
5383                                                    let (p, _g) = g1.device_ptr(&s);
5384                                                    p as u64
5385                                                },
5386                                            ),
5387                                            (
5388                                                {
5389                                                    let s = e.stream();
5390                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
5391                                                    p as u64
5392                                                },
5393                                                n_ex * 4,
5394                                                {
5395                                                    let s = rank1.stream();
5396                                                    let (p, _g) = p1.device_ptr(&s);
5397                                                    p as u64
5398                                                },
5399                                            ),
5400                                            (
5401                                                {
5402                                                    let s = e.stream();
5403                                                    let (p, _g) =
5404                                                        m.active_experts_dev.device_ptr(&s);
5405                                                    p as u64
5406                                                },
5407                                                n_ex,
5408                                                {
5409                                                    let s = rank1.stream();
5410                                                    let (p, _g) = a1.device_ptr(&s);
5411                                                    p as u64
5412                                                },
5413                                            ),
5414                                        ] {
5415                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
5416                                        }
5417                                        rank1.stream().synchronize()?;
5418                                        reps.insert(il, (g1, p1, a1));
5419                                    }
5420                                    if scratch.is_none() {
5421                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5422                                    }
5423                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5424                                    let logits1 = scratch.as_mut().expect("armed above");
5425                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5426                                    rank1.moe_router_sigmoid_topk_into(
5427                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5428                                        w1,
5429                                    )?;
5430                                    Ok(true)
5431                                },
5432                            )?;
5433                        } else {
5434                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5435                        }
5436                        // Persistent selection buffers: the allocating topk built two fresh
5437                        // slices per layer; sel/w land in process-static rows instead
5438                        // (host-op diet — same kernel, same bytes).
5439                        static SELW: std::sync::Mutex<
5440                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5441                        > = std::sync::Mutex::new(None);
5442                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5443                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5444                            *selw = Some((
5445                                e.ctx().ordinal(),
5446                                e.htod_i32(&vec![0i32; n_used])?,
5447                                e.htod(&vec![0.0f32; n_used])?,
5448                            ));
5449                        }
5450                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5451                        e.moe_router_sigmoid_topk_into(
5452                            &logits,
5453                            t,
5454                            n_expert,
5455                            n_used,
5456                            m.active_count(),
5457                            &m.exp_probs_b_dev,
5458                            &m.active_experts_dev,
5459                            sf,
5460                            route_norm,
5461                            sel_d,
5462                            w_d,
5463                        )?;
5464                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5465                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5466                        // PREJOIN hook so it executes while the peer rank drains its sweep
5467                        // (fills dev0's join wait); apply adds the identical values after.
5468                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5469                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5470                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5471                        });
5472                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5473                        // expert runs on rank1 — the idle device — same kernels, same
5474                        // split program, down row root-resident: bit-identical.
5475                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5476                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5477                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5478                        }) && tp.runtime.rank_engine(1).is_some();
5479                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5480                        // overlap ws + ones row and hand their RAW pointers to the routed
5481                        // run — the join add folds the shexp apply into one launch.
5482                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5483                        let tail3 = *TAIL3
5484                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5485                        let mut ov_issued = false;
5486                        let mut d1_issued = false;
5487                        let mut tail_folded = false;
5488                        let mut output = if shexp_d1 {
5489                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5490                            tp.runtime
5491                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5492                                    bank,
5493                                    e,
5494                                    z,
5495                                    &sel_d,
5496                                    &w_d,
5497                                    n_used,
5498                                    tp.activation_limit,
5499                                    || {
5500                                        d1_issued = Self::shexp_dev1_issue(
5501                                            e, rank1, m, z, cfg, il, n_embd,
5502                                        )?;
5503                                        Ok(())
5504                                    },
5505                                )?
5506                        } else if shexp_ov {
5507                            // Raw sh/ones pointers for the fused tail (persistent statics;
5508                            // pointers stable, no lock held across the routed call). The
5509                            // sh CONTENT is written by the prejoin-issued kernels earlier
5510                            // on e's stream — stream order covers the fused add.
5511                            let post_add = if tail3 {
5512                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5513                            } else {
5514                                None
5515                            };
5516                            let used_post = post_add.is_some();
5517                            let out = tp
5518                                .runtime
5519                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5520                                    bank,
5521                                    e,
5522                                    z,
5523                                    &sel_d,
5524                                    &w_d,
5525                                    n_used,
5526                                    tp.activation_limit,
5527                                    || {
5528                                        ov_issued =
5529                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5530                                        Ok(())
5531                                    },
5532                                    post_add,
5533                                )?;
5534                            // ov_issued false with post_add armed = an early-return arm
5535                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5536                            // fall through to the normal shexp add (battery v22 receipt:
5537                            // the strict error here failed every graph-door boot).
5538                            if used_post && ov_issued {
5539                                tail_folded = true; // apply folded into the join add
5540                            }
5541                            out
5542                        } else {
5543                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5544                                bank,
5545                                e,
5546                                z,
5547                                &sel_d,
5548                                &w_d,
5549                                n_used,
5550                                tp.activation_limit,
5551                            )?
5552                        };
5553                        if output.len() != t * n_embd {
5554                            return Err(format!(
5555                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5556                                output.len()
5557                            )
5558                            .into());
5559                        }
5560                        if tail_folded {
5561                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5562                        } else if d1_issued {
5563                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5564                        } else if ov_issued {
5565                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5566                        } else {
5567                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5568                        }
5569                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5570                            std::sync::atomic::AtomicU64::new(0);
5571                        let layer_bit = 1u64 << (il as u64 % 64);
5572                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5573                            & layer_bit
5574                            == 0
5575                        {
5576                            eprintln!(
5577                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5578                                 expert_transport={} native_p2p={} router=device \
5579                                 activation=host-canonical accumulation=host-canonical \
5580                                 output=e-device io=device performance_claim=false \
5581                                 (logged once per layer)",
5582                                tp.devices,
5583                                tp.runtime.transport_label(),
5584                                tp.runtime.native_p2p(),
5585                            );
5586                        }
5587                        return Ok(output);
5588                    }
5589                }
5590            }
5591            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5592            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5593            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5594            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5595            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5596            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5597            let route_started = route_timing.then(std::time::Instant::now);
5598            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5599                e,
5600                &logits,
5601                z,
5602                t,
5603                n_embd,
5604                n_expert,
5605                n_used,
5606                m.exp_probs_b.as_deref(),
5607                sigmoid,
5608                m.active_experts.as_deref(),
5609            )?;
5610            if let Some(started) = route_started {
5611                use std::sync::atomic::Ordering;
5612                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5613                    + started.elapsed().as_nanos() as u64;
5614                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5615                if calls % 430 == 0 {
5616                    eprintln!(
5617                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5618                        ns as f64 / 1.0e6,
5619                        ns as f64 / calls as f64 / 1.0e3,
5620                    );
5621                }
5622            }
5623            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5624            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5625            Self::trace_moe_input(e, il, t, n_embd, z)?;
5626            let selected = selected
5627                .iter()
5628                .map(|&expert| expert as usize)
5629                .collect::<Vec<_>>();
5630            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5631            // combined output comes back as an e-context row — no host round-trip, no host
5632            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5633            // both preserve f32 bits), gated by greedy token identity.
5634            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5635                if let Some(tp) = &m.step_tp {
5636                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5637                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5638                            bank,
5639                            e,
5640                            z,
5641                            &selected,
5642                            &route_weights,
5643                            n_used,
5644                            tp.activation_limit,
5645                        )?;
5646                        if output.len() != t * n_embd {
5647                            return Err(format!(
5648                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5649                                output.len()
5650                            )
5651                            .into());
5652                        }
5653                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5654                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5655                            std::sync::atomic::AtomicU64::new(0);
5656                        let layer_bit = 1u64 << (il as u64 % 64);
5657                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5658                            & layer_bit
5659                            == 0
5660                        {
5661                            eprintln!(
5662                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5663                                 expert_transport={} native_p2p={} activation=host-canonical \
5664                                 accumulation=host-canonical output=e-device io=device \
5665                                 performance_claim=false (logged once per layer)",
5666                                tp.devices,
5667                                tp.runtime.transport_label(),
5668                                tp.runtime.native_p2p(),
5669                            );
5670                        }
5671                        return Ok(output);
5672                    }
5673                }
5674            }
5675            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5676                (
5677                    match &tp.experts {
5678                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5679                            tp.runtime.run_tensor_parallel_routes(
5680                                bank,
5681                                &input,
5682                                t,
5683                                &selected,
5684                                &route_weights,
5685                                n_used,
5686                            )?
5687                        }
5688                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5689                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5690                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5691                                    bank,
5692                                    &input,
5693                                    &selected,
5694                                    &route_weights,
5695                                    n_used,
5696                                    tp.activation_limit,
5697                                )?
5698                            } else {
5699                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5700                                    bank,
5701                                    &input,
5702                                    t,
5703                                    &selected,
5704                                    &route_weights,
5705                                    n_used,
5706                                    tp.activation_limit,
5707                                )?
5708                            }
5709                        }
5710                    },
5711                    "tp",
5712                    &tp.devices,
5713                    tp.runtime.transport_label(),
5714                    tp.runtime.native_p2p(),
5715                )
5716            } else {
5717                let ep = m
5718                    .step_ep
5719                    .as_ref()
5720                    .ok_or("Step distributed runtime has no EP or TP state")?;
5721                (
5722                    match &ep.experts {
5723                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5724                            ep.runtime.run_routed_experts(
5725                                bank,
5726                                &input,
5727                                t,
5728                                &selected,
5729                                &route_weights,
5730                                n_used,
5731                                ep.activation_limit,
5732                            )?
5733                        }
5734                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5735                            ep.runtime.run_routed_experts_nvfp4(
5736                                bank,
5737                                &input,
5738                                t,
5739                                &selected,
5740                                &route_weights,
5741                                n_used,
5742                                ep.activation_limit,
5743                            )?
5744                        }
5745                    },
5746                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5747                    &ep.devices,
5748                    ep.runtime.transport_label(),
5749                    ep.runtime.native_p2p(),
5750                )
5751            };
5752            if routed.len() != t * n_embd {
5753                return Err(format!(
5754                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5755                    routed.len()
5756                )
5757                .into());
5758            }
5759            let mut output = e.htod(&routed)?;
5760            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5761            // Once per layer per process: the topology contract line is a boot receipt, not a
5762            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5763            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5764            let layer_bit = 1u64 << (il as u64 % 64);
5765            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5766                == 0
5767            {
5768                eprintln!(
5769                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5770                     expert_transport={transport} native_p2p={native_p2p} \
5771                     activation={} accumulation={} output={} \
5772                     performance_claim=false (logged once per layer)",
5773                    if let Some(ep) = &m.step_ep {
5774                        ep.runtime.expert_activation_label()
5775                    } else {
5776                        "host-canonical"
5777                    },
5778                    if let Some(ep) = &m.step_ep {
5779                        ep.runtime.expert_accumulation_label()
5780                    } else {
5781                        "host-canonical"
5782                    },
5783                    if let Some(ep) = &m.step_ep {
5784                        ep.runtime.expert_output_label()
5785                    } else {
5786                        "host-accumulated"
5787                    },
5788                );
5789                if let Some(ep) = &m.step_ep {
5790                    if let Some(limit) = ep.activation_limit {
5791                        eprintln!(
5792                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5793                             formula=min-silu-times-clamped-up performance_claim=false"
5794                        );
5795                    }
5796                }
5797            }
5798            return Ok(output);
5799        }
5800        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5801            let moe = cfg.moe.as_ref().unwrap();
5802            let n_expert = moe.expert_count as usize;
5803            let n_used = moe.expert_used_count as usize;
5804            let sigmoid = cfg.sigmoid_router().unwrap();
5805            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5806            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5807            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5808        }
5809        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5810        // current caller into this research arm; the naked default stays on the established path.
5811        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5812            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5813            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5814            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5815            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5816            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5817            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5818                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5819                let g_host = e.dtoh(&grouped_out)?;
5820                let s_host = e.dtoh(&seq_out)?;
5821                let g_bytes: &[u8] = unsafe {
5822                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5823                };
5824                let s_bytes: &[u8] = unsafe {
5825                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5826                };
5827                if g_bytes == s_bytes {
5828                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5829                } else {
5830                    let diffs = g_host
5831                        .iter()
5832                        .zip(s_host.iter())
5833                        .enumerate()
5834                        .filter(|(_, (a, b))| a != b)
5835                        .count();
5836                    let maxdiff = g_host
5837                        .iter()
5838                        .zip(s_host.iter())
5839                        .map(|(a, b)| (a - b).abs())
5840                        .fold(0.0f32, f32::max);
5841                    panic!(
5842                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5843                        g_host.len()
5844                    );
5845                }
5846            }
5847            return Ok(grouped_out);
5848        }
5849        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5850    }
5851
5852    fn sigmoid_resident_dev_eligible(
5853        e: &Engine,
5854        m: &MoeWeights,
5855        cfg: &ModelConfig,
5856        sliding_gated_moe: bool,
5857    ) -> bool {
5858        let Some(moe) = cfg.moe.as_ref() else {
5859            return false;
5860        };
5861        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5862        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5863        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5864        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5865            std::env::var("MEMRA_MOE_STATS").is_ok()
5866                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5867                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5868                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5869                || std::env::var("MEMRA_MOE_GATE").is_ok()
5870        });
5871        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5872            if dev.dev != e.ctx().ordinal() {
5873                return false;
5874            }
5875            let q8 = moe_q8_enabled()
5876                && q8_expert_supported(m.gate_exps.qtype)
5877                && q8_expert_supported(m.up_exps.qtype)
5878                && q8_expert_supported(m.down_exps.qtype);
5879            let fp8 = dev.fp8_blk.is_some()
5880                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5881                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5882                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5883            q8 || fp8
5884        });
5885        sliding_gated_moe
5886            && sigmoid_router_enabled()
5887            && moe_dev_enabled()
5888            && moe_slab_enabled()
5889            && !observation_mode
5890            && moe.expert_used_count <= 8
5891            && m.has_uniform_expert_layout()
5892            && m.gate_exps.macros.is_none()
5893            && m.up_exps.macros.is_none()
5894            && m.down_exps.macros.is_none()
5895            && !m.has_macros
5896            && resident_layout_supported
5897    }
5898
5899    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5900    pub(crate) fn moe_ffn_sequential(
5901        e: &Engine,
5902        m: &MoeWeights,
5903        z: &CudaSlice<f32>,
5904        t: usize,
5905        cfg: &ModelConfig,
5906        il: u16,
5907        max_block: usize,
5908    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5909        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5910    }
5911
5912    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5913    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5914    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5915    fn moe_router_logits(
5916        e: &Engine,
5917        m: &MoeWeights,
5918        z: &CudaSlice<f32>,
5919        t: usize,
5920        cfg: &ModelConfig,
5921    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5922        if t < PRIME_MIN_T {
5923            // Decode and speculative verify use one fixed per-row reduction program.
5924            if crate::router_kernel_on() {
5925                e.router_gemv(
5926                    m.gate_inp.float_data(),
5927                    z,
5928                    cfg.n_embd as usize,
5929                    m.gate_exps.n_expert,
5930                    t,
5931                )
5932            } else {
5933                e.matmul_decode_exact(&m.gate_inp, z, t)
5934            }
5935        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5936            e.router_gemv(
5937                m.gate_inp.float_data(),
5938                z,
5939                cfg.n_embd as usize,
5940                m.gate_exps.n_expert,
5941                t,
5942            )
5943        } else {
5944            e.matmul(&m.gate_inp, z, t)
5945        }
5946    }
5947
5948    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5949    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5950    /// trace is independent of the dispatch optimization selected for the forward.
5951    fn trace_moe_routes(
5952        il: u16,
5953        t: usize,
5954        sel_all: &[u32],
5955        weights: &[f32],
5956    ) -> Result<(), Box<dyn std::error::Error>> {
5957        use std::io::Write as _;
5958        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5959            let mut f = std::fs::OpenOptions::new()
5960                .create(true)
5961                .append(true)
5962                .open(path)?;
5963            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5964            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5965        }
5966        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5967            let mut f = std::fs::OpenOptions::new()
5968                .create(true)
5969                .append(true)
5970                .open(path)?;
5971            let pairs: Vec<String> = sel_all
5972                .iter()
5973                .zip(weights)
5974                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5975                .collect();
5976            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5977        }
5978        Ok(())
5979    }
5980
5981    #[allow(clippy::too_many_arguments)]
5982    fn trace_sigmoid_router_logits(
5983        e: &Engine,
5984        il: u16,
5985        t: usize,
5986        n_expert: usize,
5987        n_used: usize,
5988        logits: &CudaSlice<f32>,
5989        m: &MoeWeights,
5990        (scaling_factor, route_norm): (f32, bool),
5991    ) -> Result<(), Box<dyn std::error::Error>> {
5992        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5993            return Ok(());
5994        }
5995        let logits = e.dtoh(logits)?;
5996        let active: Vec<u8> = m
5997            .active_experts
5998            .as_ref()
5999            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
6000            .unwrap_or_else(|| vec![1; n_expert]);
6001        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
6002        crate::sigrouter_contract::capture_served_logits(
6003            il as u32,
6004            t,
6005            n_expert,
6006            n_used,
6007            scaling_factor,
6008            route_norm,
6009            &active,
6010            &bias,
6011            &logits,
6012        )?;
6013        Ok(())
6014    }
6015
6016    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
6017    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
6018    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
6019    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
6020    fn trace_moe_input(
6021        e: &Engine,
6022        il: u16,
6023        t: usize,
6024        n_embd: usize,
6025        z: &CudaSlice<f32>,
6026    ) -> Result<(), Box<dyn std::error::Error>> {
6027        use std::io::Write as _;
6028        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
6029            return Ok(());
6030        };
6031        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
6032        let host = e.dtoh_view(&z.slice(0..values))?;
6033        let bytes = unsafe {
6034            std::slice::from_raw_parts(
6035                host.as_ptr().cast::<u8>(),
6036                host.len() * std::mem::size_of::<f32>(),
6037            )
6038        };
6039        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
6040        let mut state = state
6041            .lock()
6042            .map_err(|_| "MoE input trace writer lock is poisoned")?;
6043        if state.is_none() {
6044            let dir = std::path::PathBuf::from(&dir);
6045            std::fs::create_dir_all(&dir)?;
6046            let index = std::fs::OpenOptions::new()
6047                .create(true)
6048                .append(true)
6049                .open(dir.join("index.jsonl"))?;
6050            *state = Some(MoeInputTraceWriter {
6051                dir,
6052                index,
6053                payloads: std::collections::HashMap::new(),
6054            });
6055        }
6056        let writer = state.as_mut().unwrap();
6057        if writer.dir != std::path::Path::new(&dir) {
6058            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
6059        }
6060        let file_name = format!("layer-{il:03}.f32");
6061        if !writer.payloads.contains_key(&il) {
6062            let payload = std::fs::OpenOptions::new()
6063                .create(true)
6064                .append(true)
6065                .open(writer.dir.join(&file_name))?;
6066            let offset = payload.metadata()?.len();
6067            writer.payloads.insert(il, (payload, offset));
6068        }
6069        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
6070        let row_offset = *offset;
6071        payload.write_all(bytes)?;
6072        *offset += bytes.len() as u64;
6073        writeln!(
6074            writer.index,
6075            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
6076             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
6077             \"payload_bytes\":{}}}",
6078            bytes.len()
6079        )?;
6080        Ok(())
6081    }
6082
6083    #[allow(clippy::too_many_arguments)]
6084    pub(crate) fn moe_ffn_sequential_zq8(
6085        e: &Engine,
6086        m: &MoeWeights,
6087        z: &CudaSlice<f32>,
6088        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
6089        t: usize,
6090        cfg: &ModelConfig,
6091        il: u16,
6092        max_block: usize,
6093    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6094        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
6095        let moe = cfg.moe.as_ref().unwrap();
6096        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
6097        let n_expert = moe.expert_count as usize; // 256
6098        let n_used = moe.expert_used_count as usize; // 8
6099        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
6100
6101        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
6102        debug_assert_eq!(m.gate_exps.in_f, n_embd);
6103        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
6104        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
6105        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
6106        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
6107
6108        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
6109        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
6110        let lim_exp = cfg.clamp_exp_at(il as u32);
6111        let lim_shexp = cfg.clamp_shexp_at(il as u32);
6112        let use_cache = Engine::moe_cache_enabled();
6113        let uniform_experts = m.has_uniform_expert_layout();
6114        let moe_q8 = uniform_experts
6115            && moe_q8_enabled()
6116            && q8_expert_supported(m.gate_exps.qtype)
6117            && q8_expert_supported(m.up_exps.qtype)
6118            && q8_expert_supported(m.down_exps.qtype);
6119        // Experimental secondary backend: complete experts already resident in the SLRU stay on
6120        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
6121        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
6122        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
6123        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
6124        // commands and CI have no llama.cpp or OpenMP dependency.
6125        let cpu_expert_requested = crate::cpu_experts::configured();
6126        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
6127            return Err(std::io::Error::other(
6128                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
6129            )
6130            .into());
6131        }
6132        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
6133        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
6134        // Those backends are each deterministic but are different numeric configurations, so a
6135        // later prefill eviction can change greedy output. Freeze after the first real prefill;
6136        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
6137        // staging below and cannot change backend assignment.
6138        let freeze_cpu_residency = cpu_expert_requested
6139            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
6140        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
6141            .ok()
6142            .and_then(|value| value.parse::<usize>().ok())
6143            .is_some_and(|tokens| tokens > 0);
6144        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
6145            e.freeze_moe_cache();
6146        }
6147        let cache_frozen = use_cache && e.moe_cache_frozen();
6148        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
6149
6150        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
6151        // cannot change logits, selected expert ids, or routing weights.
6152        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
6153        if let Some(sig) = cfg.sigmoid_router() {
6154            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
6155        }
6156
6157        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
6158        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
6159        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
6160        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
6161        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
6162        // per-token host stall that dominated the 35B decode wall after stages 1+2.
6163        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
6164        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
6165        // only difference is where sel/w/pointers are READ from (device instead of params).
6166        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
6167        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
6168        // Any non-resident layer falls through to host routing + the gdec/sequential path.
6169        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
6170        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
6171        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
6172        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
6173        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
6174        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
6175        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
6176        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
6177        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
6178        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
6179        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
6180        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
6181        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
6182        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
6183        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
6184        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
6185        // now rides the dev loop below (same kernels per token as decode); pairs serves real
6186        // prefill (t >= 16, where spec never verifies).
6187        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
6188        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
6189        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
6190        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
6191        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
6192        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
6193        // ride the macro-aware sequential/staged paths below or every expert output is off by
6194        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
6195        let no_exp_macros = m.gate_exps.macros.is_none()
6196            && m.up_exps.macros.is_none()
6197            && m.down_exps.macros.is_none();
6198        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
6199        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
6200        // so it cannot even see the per-layer limit.
6201        if cfg.sigmoid_router().is_none()
6202            && cfg.m3.is_none()
6203            && cfg.hy3.is_none()
6204            && !cfg.swiglu_clamped_at(il as u32)
6205            && no_exp_macros
6206            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
6207            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
6208            // pairs serves real prefill from 17 up.
6209            && t > MOE_DEV_MAX_T
6210            && m.dev_exps.is_some()
6211            && moe_q8_enabled()
6212            && q8_expert_supported(m.gate_exps.qtype)
6213            && q8_expert_supported(m.up_exps.qtype)
6214            && q8_expert_supported(m.down_exps.qtype)
6215            && std::env::var("MEMRA_MOE_PAIRS")
6216                .map(|v| v != "0")
6217                .unwrap_or(true)
6218            && std::env::var("MEMRA_MOE_STATS").is_err()
6219        {
6220            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
6221        }
6222
6223        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
6224        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
6225        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
6226        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
6227        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
6228        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
6229        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
6230        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
6231        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
6232        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
6233        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
6234        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
6235        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
6236        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
6237        // Keyed off sigmoid_router() so arch #4 is denied by construction.
6238        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
6239        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
6240        let dev_ok = uniform_experts
6241            && cfg.sigmoid_router().is_none()
6242            && cfg.m3.is_none()
6243            && cfg.hy3.is_none()
6244            && !cfg.swiglu_clamped_at(il as u32);
6245        // Observation modes must route through the host-visible selection below. Otherwise a fully
6246        // resident layer returns through device dispatch before its trace/stats row is recorded,
6247        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
6248        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
6249            || std::env::var("MEMRA_MOE_TRACE").is_ok()
6250            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
6251            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
6252        if dev_ok
6253            && t <= MOE_DEV_MAX_T
6254            && m.dev_exps.is_some()
6255            && n_used <= 8
6256            && moe_dev_enabled()
6257            && !observe_routes
6258        {
6259            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
6260        }
6261        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
6262            let row_ok = e.with_moe_cache(max_block, |c, eng| {
6263                if moe_prewarm_enabled() {
6264                    c.prewarm_layer(il, m, eng)?;
6265                }
6266                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
6267            })?;
6268            if row_ok {
6269                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
6270            }
6271        }
6272
6273        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
6274        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
6275            if cpu_hybrid {
6276                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
6277                    e,
6278                    &logits,
6279                    z,
6280                    t,
6281                    n_embd,
6282                    n_expert,
6283                    n_used,
6284                    m.exp_probs_b.as_deref(),
6285                    sig,
6286                    m.active_experts.as_deref(),
6287                )?;
6288                (sel, w, Some(input))
6289            } else {
6290                let (sel, w) =
6291                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
6292                (sel, w, None)
6293            }
6294        } else {
6295            let (sel, w) =
6296                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
6297            (sel, w, None)
6298        };
6299        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
6300
6301        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
6302        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
6303        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
6304        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
6305        Self::trace_moe_input(e, il, t, n_embd, z)?;
6306
6307        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
6308        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
6309        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
6310        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
6311        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
6312        // wait for each pending block, so later copies can overlap the earlier expert kernels while
6313        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
6314        // T=1; batched forwards can have token-local consumers still in flight between selections.
6315        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
6316        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
6317        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
6318        let worker_disk_prefetch =
6319            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
6320        let promote_worker_h2d =
6321            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
6322        if promote_worker_h2d {
6323            let mut selected_blocks = Vec::with_capacity(n_used * 3);
6324            for &ex in sel_all.iter().take(n_used) {
6325                let ex = ex as u16;
6326                selected_blocks.extend([
6327                    BlockId::new(il, PROJ_GATE, ex),
6328                    BlockId::new(il, PROJ_UP, ex),
6329                    BlockId::new(il, PROJ_DOWN, ex),
6330                ]);
6331            }
6332            for &ex in sel_all.iter().take(n_used) {
6333                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
6334            }
6335            e.with_moe_cache(max_block, |cache, eng| {
6336                cache.promote_worker_reads_at_safe_boundary(
6337                    &selected_blocks,
6338                    &selected_blocks,
6339                    eng,
6340                )?;
6341                Ok(())
6342            })?;
6343        }
6344
6345        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
6346        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
6347        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
6348            let mut cnt = vec![0u32; n_expert];
6349            for &s in sel_all.iter() {
6350                cnt[s as usize] += 1;
6351            }
6352            let total = sel_all.len() as f64;
6353            let mut h = 0.0f64;
6354            let mut active = 0usize;
6355            for &c in &cnt {
6356                if c > 0 {
6357                    active += 1;
6358                    let p = c as f64 / total;
6359                    h -= p * p.log2();
6360                }
6361            }
6362            let maxc = cnt.iter().copied().max().unwrap_or(0);
6363            println!(
6364                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
6365                il,
6366                t,
6367                sel_all.len(),
6368                active,
6369                n_expert,
6370                h,
6371                (n_expert as f64).log2(),
6372                total / active.max(1) as f64,
6373                maxc
6374            );
6375        }
6376
6377        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
6378        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
6379        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
6380        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
6381        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
6382        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
6383        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
6384        // zeroed-then-accumulated exactly as before (fallback).
6385        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
6386        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
6387        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
6388        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
6389        let gdec_may_fire = uniform_experts
6390            && use_cache
6391            && n_used <= 8
6392            && gdec_enabled()
6393            && !cfg.swiglu_clamped_at(il as u32);
6394        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
6395        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
6396        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
6397        // archs the slabs were uploaded but never read, and every expert went through the
6398        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
6399        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
6400        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
6401        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
6402        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
6403        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
6404        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
6405        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
6406        // strictly worse than staging); under PP-2 without the prime walker this admits
6407        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
6408        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
6409        let slab_local = m
6410            .dev_exps
6411            .as_ref()
6412            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
6413        let slab_bases = slab_local.map(|d| {
6414            use cudarc::driver::DevicePtr;
6415            let s = e.stream();
6416            let (pg, _g0) = d.gate.device_ptr(&s);
6417            let (pu, _g1) = d.up.device_ptr(&s);
6418            let (pd, _g2) = d.down.device_ptr(&s);
6419            (pg as u64, pu as u64, pd as u64)
6420        });
6421        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6422        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6423        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6424        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6425        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6426        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6427        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6428        // all-resident tokens, staged loop for misses), which is a dispatch-class
6429        // comparison, not a provenance one.
6430        let slab_fused_may_fire = slab_bases.is_some()
6431            && n_used <= 8
6432            && gdec_enabled()
6433            && !cfg.swiglu_clamped_at(il as u32)
6434            && cfg.m3.is_none()
6435            && no_exp_macros
6436            && moe_q8;
6437        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6438        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6439        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6440            e.uninit(t * n_embd)?
6441        } else {
6442            e.zeros(t * n_embd)?
6443        };
6444        // The router readback above already established a host boundary. Copy each small-t hidden
6445        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6446        let cpu_input = if cpu_hybrid {
6447            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6448        } else {
6449            None
6450        };
6451
6452        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6453        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6454        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6455        // measured ~123 memsets/token of the decode wall).
6456        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6457        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6458        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6459        let mut scratch_g: Option<CudaSlice<u8>> = None;
6460        let mut scratch_u: Option<CudaSlice<u8>> = None;
6461        let mut scratch_d: Option<CudaSlice<u8>> = None;
6462        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6463        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6464
6465        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6466        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6467        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6468        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6469        let page_window = moe_page_prefetch_window();
6470
6471        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6472        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6473        for tok in 0..t {
6474            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6475            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6476            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6477            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6478
6479            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6480            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6481            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6482            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6483            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6484            // miss falls through to the sequential loop below, which admits as before. In steady
6485            // state on a fully-resident rig every token-layer takes the grouped path.
6486            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6487            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6488            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6489            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6490            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6491            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6492            let no_macros = m.gate_exps.macros.is_none()
6493                && m.up_exps.macros.is_none()
6494                && m.down_exps.macros.is_none();
6495            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6496            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6497            // with pointers computed from the resident slab base + ex*stride instead of
6498            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6499            // slab holds every expert by construction, so this arm never falls through
6500            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6501            // staging both die). Bit-identity class: pointer provenance only, the same
6502            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6503            // slab exists it is strictly better (no lock, no miss).
6504            if slab_fused_may_fire {
6505                let (pg, pu, pd) = slab_bases.unwrap();
6506                let mut gp = [0u64; 8];
6507                let mut up = [0u64; 8];
6508                let mut dp = [0u64; 8];
6509                for (j, &ex) in sel.iter().enumerate() {
6510                    let ex = ex as usize;
6511                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6512                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6513                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6514                }
6515                let mut wv = [0f32; 8];
6516                wv[..n_used].copy_from_slice(w);
6517                if tok_q8.is_none() {
6518                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6519                }
6520                let (zq, zd) = tok_q8.as_ref().unwrap();
6521                let act = e.moe_gate_up_silu8_q8(
6522                    crate::WPtr8(gp),
6523                    crate::WPtr8(up),
6524                    zq,
6525                    zd,
6526                    n_embd,
6527                    n_ff_exp,
6528                    n_used,
6529                    m.gate_exps.qtype,
6530                    m.up_exps.qtype,
6531                    m.gate_exps.row_bytes,
6532                    m.up_exps.row_bytes,
6533                )?;
6534                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6535                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6536                e.moe_down8_fma_q8(
6537                    crate::WPtr8(dp),
6538                    crate::F32x8(wv),
6539                    &aq2,
6540                    &ad2,
6541                    &mut dst,
6542                    n_ff_exp,
6543                    n_embd,
6544                    n_used,
6545                    m.down_exps.qtype,
6546                    m.down_exps.row_bytes,
6547                )?;
6548                continue;
6549            }
6550            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6551                if tok_q8.is_none() {
6552                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6553                }
6554                let (zq, zd) = tok_q8.as_ref().unwrap();
6555                if Self::moe_gdec_token_q8(
6556                    e,
6557                    m,
6558                    il,
6559                    max_block,
6560                    zq,
6561                    zd,
6562                    sel,
6563                    w,
6564                    &mut moe_out,
6565                    tok,
6566                    n_embd,
6567                    n_ff_exp,
6568                    n_used,
6569                )? {
6570                    continue;
6571                }
6572            } else if gdec_may_fire
6573                && cfg.m3.is_none()
6574                && no_macros
6575                && Self::moe_gdec_token(
6576                    e,
6577                    m,
6578                    il,
6579                    max_block,
6580                    &zt,
6581                    sel,
6582                    w,
6583                    &mut moe_out,
6584                    tok,
6585                    n_embd,
6586                    n_ff_exp,
6587                    n_used,
6588                )?
6589            {
6590                continue;
6591            }
6592
6593            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6594            // slab pair could fire. This token fell through to a sequential axpy loop, which
6595            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6596            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6597            // has no fallible predicate), included for the allocation invariant's symmetry.
6598            if gdec_may_fire || slab_fused_may_fire {
6599                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6600                e.memset_zeros_view(&mut row)?;
6601            }
6602
6603            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6604            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6605            // stall this path exists to remove, while mixing projections would require another
6606            // activation round-trip. Weight addresses remain valid until this worker is joined at
6607            // the bottom of the token scope.
6608            let mut cpu_mask = vec![false; sel.len()];
6609            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6610                let gpu_resident = if use_cache {
6611                    e.with_moe_cache(max_block, |cache, _| {
6612                        Ok(sel
6613                            .iter()
6614                            .map(|&expert| {
6615                                let expert = expert as u16;
6616                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6617                                    .into_iter()
6618                                    .filter(|&projection| {
6619                                        cache
6620                                            .resident(BlockId::new(il, projection, expert))
6621                                            .is_some()
6622                                    })
6623                                    .count()
6624                            })
6625                            .collect::<Vec<_>>())
6626                    })?
6627                } else {
6628                    vec![0; sel.len()]
6629                };
6630                let mut cpu_selected = Vec::new();
6631                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6632                    if gpu_resident[index] != 3 {
6633                        cpu_mask[index] = true;
6634                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6635                        let expert = expert as usize;
6636                        cpu_selected.push((expert, route_weight));
6637                    }
6638                }
6639                if crate::cpu_experts::predictor_enabled() {
6640                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6641                    // from this layer's MoE input and prefetches predicted-and-missing
6642                    // experts into the companion RAM cache. Never blocks this thread.
6643                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6644                    crate::cpu_experts::predictor_submit(il, row);
6645                }
6646                if cpu_selected.is_empty() {
6647                    None
6648                } else {
6649                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6650                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6651                        .map_err(std::io::Error::other)?;
6652                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6653                }
6654            } else {
6655                None
6656            };
6657
6658            let worker_window = worker_disk_prefetch
6659                .then(worker_prefetch_window)
6660                .unwrap_or(0);
6661            for (j, &ex) in sel.iter().enumerate() {
6662                if cpu_mask[j] {
6663                    continue;
6664                }
6665                let ex = ex as usize;
6666                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6667                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6668                // fused form) and macro-carrying artifacts — still have their bytes in the
6669                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6670                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6671                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6672                if let Some(d) = slab_local {
6673                    let gl = m.gate_exps.expert_layout(ex);
6674                    let ul = m.up_exps.expert_layout(ex);
6675                    let dl = m.down_exps.expert_layout(ex);
6676                    let (g0, u0, d0) = (
6677                        ex * m.gate_exps.expert_stride,
6678                        ex * m.up_exps.expert_stride,
6679                        ex * m.down_exps.expert_stride,
6680                    );
6681                    let (gate, up) = if moe_q8 {
6682                        if tok_q8.is_none() {
6683                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6684                        }
6685                        let (zq, zd) = tok_q8.as_ref().unwrap();
6686                        (
6687                            e.qmatvec_expert_q8(
6688                                &d.gate,
6689                                g0..g0 + gl.len,
6690                                zq,
6691                                zd,
6692                                1,
6693                                m.gate_exps.in_f,
6694                                m.gate_exps.out_f,
6695                                gl.qtype,
6696                                gl.row_bytes,
6697                            )?,
6698                            e.qmatvec_expert_q8(
6699                                &d.up,
6700                                u0..u0 + ul.len,
6701                                zq,
6702                                zd,
6703                                1,
6704                                m.up_exps.in_f,
6705                                m.up_exps.out_f,
6706                                ul.qtype,
6707                                ul.row_bytes,
6708                            )?,
6709                        )
6710                    } else {
6711                        (
6712                            e.qmatvec_view(
6713                                &d.gate,
6714                                g0..g0 + gl.len,
6715                                &zt,
6716                                1,
6717                                m.gate_exps.in_f,
6718                                m.gate_exps.out_f,
6719                                gl.qtype,
6720                                gl.row_bytes,
6721                            )?,
6722                            e.qmatvec_view(
6723                                &d.up,
6724                                u0..u0 + ul.len,
6725                                &zt,
6726                                1,
6727                                m.up_exps.in_f,
6728                                m.up_exps.out_f,
6729                                ul.qtype,
6730                                ul.row_bytes,
6731                            )?,
6732                        )
6733                    };
6734                    let mut act = e.uninit(n_ff_exp)?;
6735                    Self::ffn_act_lim(
6736                        e,
6737                        cfg,
6738                        &gate,
6739                        &up,
6740                        m.gate_exps.macro_scale(ex),
6741                        m.up_exps.macro_scale(ex),
6742                        lim_exp,
6743                        &mut act,
6744                        n_ff_exp,
6745                    )?;
6746                    let y = if moe_q8 {
6747                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6748                        e.qmatvec_expert_q8(
6749                            &d.down,
6750                            d0..d0 + dl.len,
6751                            &aq2,
6752                            &ad2,
6753                            1,
6754                            m.down_exps.in_f,
6755                            m.down_exps.out_f,
6756                            dl.qtype,
6757                            dl.row_bytes,
6758                        )?
6759                    } else {
6760                        let actv = act.slice(0..n_ff_exp);
6761                        e.qmatvec_view(
6762                            &d.down,
6763                            d0..d0 + dl.len,
6764                            &actv,
6765                            1,
6766                            m.down_exps.in_f,
6767                            m.down_exps.out_f,
6768                            dl.qtype,
6769                            dl.row_bytes,
6770                        )?
6771                    };
6772                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6773                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6774                    continue;
6775                }
6776                for next in page_prefetch_positions(j, sel.len(), page_window) {
6777                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6778                }
6779                let keep = [
6780                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6781                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6782                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6783                ];
6784                if worker_disk_prefetch && worker_window > 0 {
6785                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6786                        Self::moe_prefetch_disk_expert(
6787                            e,
6788                            il,
6789                            sel[next] as usize,
6790                            m,
6791                            max_block,
6792                            &keep,
6793                        )?;
6794                    }
6795                } else if cache_dispatch
6796                    && !cpu_hybrid
6797                    && moe_prefetch_enabled()
6798                    && j + 1 < sel.len()
6799                {
6800                    let next = sel[j + 1] as usize;
6801                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6802                }
6803                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6804                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6805                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6806                    // layouts stay on the metadata-aware f32 path.
6807                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6808                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6809                    }
6810                    let gate = if gate_q8 {
6811                        let (zq, zd) = tok_q8.as_ref().unwrap();
6812                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6813                    } else {
6814                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6815                    };
6816                    let up = if up_q8 {
6817                        let (zq, zd) = tok_q8.as_ref().unwrap();
6818                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6819                    } else {
6820                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6821                    };
6822                    let mut act = e.uninit(n_ff_exp)?;
6823                    Self::ffn_act_lim(
6824                        e,
6825                        cfg,
6826                        &gate,
6827                        &up,
6828                        m.gate_exps.macro_scale(ex),
6829                        m.up_exps.macro_scale(ex),
6830                        lim_exp,
6831                        &mut act,
6832                        n_ff_exp,
6833                    )?;
6834                    let y = if down_q8 {
6835                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6836                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6837                    } else {
6838                        let actv = act.slice(0..n_ff_exp);
6839                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6840                    };
6841                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6842                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6843                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6844                } else if cache_dispatch {
6845                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6846                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6847                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6848                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6849                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6850                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6851                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6852                    Self::ffn_act_lim(
6853                        e,
6854                        cfg,
6855                        &gate,
6856                        &up,
6857                        m.gate_exps.macro_scale(ex),
6858                        m.up_exps.macro_scale(ex),
6859                        lim_exp,
6860                        &mut act,
6861                        n_ff_exp,
6862                    )?;
6863                    let actv = act.slice(0..n_ff_exp);
6864                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6865                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6866                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6867                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6868                } else if cache_frozen {
6869                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6870                    // first prime. Reuse every fixed resident projection directly and stage only a
6871                    // true miss through the ordinary scratch slot. This preserves the established
6872                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6873                    let gate = Self::moe_frozen_gemm(
6874                        e,
6875                        il,
6876                        PROJ_GATE,
6877                        ex,
6878                        m,
6879                        max_block,
6880                        &zt,
6881                        &mut scratch_g,
6882                        g_len,
6883                    )?;
6884                    let up = Self::moe_frozen_gemm(
6885                        e,
6886                        il,
6887                        PROJ_UP,
6888                        ex,
6889                        m,
6890                        max_block,
6891                        &zt,
6892                        &mut scratch_u,
6893                        u_len,
6894                    )?;
6895                    let mut act = e.uninit(n_ff_exp)?;
6896                    Self::ffn_act_lim(
6897                        e,
6898                        cfg,
6899                        &gate,
6900                        &up,
6901                        m.gate_exps.macro_scale(ex),
6902                        m.up_exps.macro_scale(ex),
6903                        lim_exp,
6904                        &mut act,
6905                        n_ff_exp,
6906                    )?;
6907                    let actv = act.slice(0..n_ff_exp);
6908                    let y = Self::moe_frozen_gemm(
6909                        e,
6910                        il,
6911                        PROJ_DOWN,
6912                        ex,
6913                        m,
6914                        max_block,
6915                        &actv,
6916                        &mut scratch_d,
6917                        d_len,
6918                    )?;
6919                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6920                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6921                } else {
6922                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6923                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6924                    // fully overwrites the byte range the GEMM reads).
6925                    if scratch_g.is_none() {
6926                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6927                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6928                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6929                    }
6930                    let (sg, su, sd) = (
6931                        scratch_g.as_mut().unwrap(),
6932                        scratch_u.as_mut().unwrap(),
6933                        scratch_d.as_mut().unwrap(),
6934                    );
6935                    let gl = m.gate_exps.expert_layout(ex);
6936                    let ul = m.up_exps.expert_layout(ex);
6937                    let dl = m.down_exps.expert_layout(ex);
6938                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6939                    let gate = e.qmatvec_view(
6940                        sg,
6941                        0..gl.len,
6942                        &zt,
6943                        1,
6944                        m.gate_exps.in_f,
6945                        m.gate_exps.out_f,
6946                        gl.qtype,
6947                        gl.row_bytes,
6948                    )?;
6949
6950                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6951                    let up = e.qmatvec_view(
6952                        su,
6953                        0..ul.len,
6954                        &zt,
6955                        1,
6956                        m.up_exps.in_f,
6957                        m.up_exps.out_f,
6958                        ul.qtype,
6959                        ul.row_bytes,
6960                    )?;
6961
6962                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6963                    Self::ffn_act_lim(
6964                        e,
6965                        cfg,
6966                        &gate,
6967                        &up,
6968                        m.gate_exps.macro_scale(ex),
6969                        m.up_exps.macro_scale(ex),
6970                        lim_exp,
6971                        &mut act,
6972                        n_ff_exp,
6973                    )?;
6974
6975                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6976                    let actv = act.slice(0..n_ff_exp);
6977                    let y = e.qmatvec_view(
6978                        sd,
6979                        0..dl.len,
6980                        &actv,
6981                        1,
6982                        m.down_exps.in_f,
6983                        m.down_exps.out_f,
6984                        dl.qtype,
6985                        dl.row_bytes,
6986                    )?;
6987
6988                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6989                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6990                }
6991            }
6992            if let Some(worker) = cpu_worker {
6993                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6994                let cpu_output = e.htod(&cpu_output)?;
6995                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6996                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6997            }
6998            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6999                for (j, &ex) in sel.iter().enumerate() {
7000                    if cpu_mask[j] {
7001                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
7002                    }
7003                }
7004            }
7005        }
7006
7007        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
7008        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
7009        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7010        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7011        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7012            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7013        {
7014            let n_ff_sh = gate_shexp.out_features(); // 512
7015            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
7016            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
7017            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
7018            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
7019            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
7020            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
7021            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
7022            let verify_t = t > 1 && t < PRIME_MIN_T;
7023            let (sg_gate, sg_up) = if t == 1 {
7024                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
7025            } else if verify_t {
7026                (
7027                    e.matmul_decode_exact(gate_shexp, z, t)?,
7028                    e.matmul_decode_exact(up_shexp, z, t)?,
7029                )
7030            } else {
7031                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
7032            };
7033            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
7034            Self::ffn_act_lim(
7035                e,
7036                cfg,
7037                &sg_gate,
7038                &sg_up,
7039                1.0,
7040                1.0,
7041                lim_shexp,
7042                &mut sa,
7043                t * n_ff_sh,
7044            )?;
7045            let sh = if verify_t {
7046                e.matmul_decode_exact(down_shexp, &sa, t)?
7047            } else {
7048                e.matmul(down_shexp, &sa, t)?
7049            }; // [T, n_embd]
7050
7051            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
7052            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
7053            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
7054            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
7055            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
7056            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
7057            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
7058            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
7059            // expert's contribution into every token's residual, so under cross-request
7060            // concat prefill a session's hidden state depended on its co-arrivals' token
7061            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
7062            let g = match &m.gate_inp_shexp {
7063                Some(gate_inp_shexp) => {
7064                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
7065                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7066                    } else {
7067                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7068                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
7069                        e.sigmoid(&gs, &mut g, t)?;
7070                        g
7071                    }
7072                }
7073                None => e.htod(&vec![1.0f32; t])?,
7074            };
7075            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
7076            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7077        }
7078
7079        Ok(moe_out)
7080    }
7081
7082    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
7083    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
7084    pub fn stage1_h2d_per_token(&self) -> u64 {
7085        use crate::hybrid::Ffn;
7086        let n_used = self
7087            .cfg
7088            .moe
7089            .as_ref()
7090            .map(|m| m.expert_used_count as u64)
7091            .unwrap_or(0);
7092        let mut bytes = 0u64;
7093        for l in self.layers.iter() {
7094            if let Ffn::Moe(m) = &l.ffn {
7095                bytes += n_used
7096                    * (m.gate_exps.max_expert_bytes()
7097                        + m.up_exps.max_expert_bytes()
7098                        + m.down_exps.max_expert_bytes()) as u64;
7099            }
7100        }
7101        bytes
7102    }
7103
7104    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
7105    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
7106    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
7107    pub(crate) fn max_moe_block(&self) -> usize {
7108        use crate::hybrid::Ffn;
7109        let mut mx = 0usize;
7110        let mut scan = |ffn: &Ffn| {
7111            if let Ffn::Moe(m) = ffn {
7112                mx = mx
7113                    .max(m.gate_exps.max_expert_bytes())
7114                    .max(m.up_exps.max_expert_bytes())
7115                    .max(m.down_exps.max_expert_bytes());
7116            }
7117        };
7118        for l in self.layers.iter() {
7119            scan(&l.ffn);
7120        }
7121        if let Some(mtp) = self.mtp.as_ref() {
7122            scan(&mtp.ffn);
7123        }
7124        mx
7125    }
7126
7127    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
7128    /// but have no bytes and therefore consume no residency slot.
7129    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
7130        use crate::hybrid::Ffn;
7131        let mut sizes = Vec::new();
7132        let mut scan = |ffn: &Ffn| {
7133            let Ffn::Moe(m) = ffn else { return };
7134            for ex in 0..m.gate_exps.n_expert {
7135                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
7136                    continue;
7137                }
7138                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
7139                    let len = exps.expert_layout(ex).len;
7140                    if len > 0 {
7141                        sizes.push(len);
7142                    }
7143                }
7144            }
7145        };
7146        for layer in &self.layers {
7147            scan(&layer.ffn);
7148        }
7149        if let Some(mtp) = &self.mtp {
7150            scan(&mtp.ffn);
7151        }
7152        sizes
7153    }
7154
7155    /// Persist the frozen residency set so a later process can restage it directly and skip
7156    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
7157    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
7158    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
7159    /// post-freeze argmax gate still validates the serving assignment.
7160    pub fn save_cpu_expert_residency_profile(
7161        &self,
7162        e: &Engine,
7163        path: &std::path::Path,
7164    ) -> Result<(), Box<dyn std::error::Error>> {
7165        let Some(ids) = e.export_moe_residency() else {
7166            return Err("no MoE residency cache to persist".into());
7167        };
7168        let mut body = format!(
7169            "memra-freeze-profile v1 max_block={} blocks={}\n",
7170            self.max_moe_block(),
7171            ids.len()
7172        );
7173        for (layer, proj, ex) in &ids {
7174            body.push_str(&format!("{layer} {proj} {ex}\n"));
7175        }
7176        let tmp = path.with_extension("tmp");
7177        std::fs::write(&tmp, body)?;
7178        std::fs::rename(&tmp, path)?;
7179        println!(
7180            "[moe-cache] freeze profile saved: {} blocks -> {}",
7181            ids.len(),
7182            path.display()
7183        );
7184        Ok(())
7185    }
7186
7187    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
7188    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
7189    /// missing or its header does not match this model's slot geometry.
7190    pub fn restore_cpu_expert_residency_profile(
7191        &self,
7192        e: &Engine,
7193        path: &std::path::Path,
7194    ) -> Result<bool, Box<dyn std::error::Error>> {
7195        use crate::hybrid::Ffn;
7196        use crate::moe_cache::BlockId;
7197        let Ok(content) = std::fs::read_to_string(path) else {
7198            return Ok(false);
7199        };
7200        let mut lines = content.lines();
7201        let Some(header) = lines.next() else {
7202            return Ok(false);
7203        };
7204        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
7205        if !header.starts_with(&expected) {
7206            println!(
7207                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
7208                path.display()
7209            );
7210            return Ok(false);
7211        }
7212        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
7213            std::collections::HashMap::new();
7214        for line in lines {
7215            let mut fields = line.split_whitespace();
7216            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
7217            else {
7218                continue;
7219            };
7220            let (Ok(layer), Ok(proj), Ok(ex)) =
7221                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
7222            else {
7223                continue;
7224            };
7225            by_layer
7226                .entry(layer)
7227                .or_default()
7228                .push(BlockId::new(layer, proj, ex));
7229        }
7230        let requested: usize = by_layer.values().map(Vec::len).sum();
7231        if requested == 0 {
7232            return Ok(false);
7233        }
7234        let max_block = self.max_moe_block();
7235        let mut restaged = 0usize;
7236        let mut stage_layer =
7237            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
7238                let Ffn::Moe(m) = ffn else { return Ok(()) };
7239                let Some(ids) = by_layer.get(&layer_index) else {
7240                    return Ok(());
7241                };
7242                e.with_moe_cache(max_block, |cache, eng| {
7243                    for id in ids {
7244                        if cache.restage_block(*id, m, eng)? {
7245                            restaged += 1;
7246                        }
7247                    }
7248                    Ok(())
7249                })
7250            };
7251        for (index, layer) in self.layers.iter().enumerate() {
7252            stage_layer(index as u16, &layer.ffn)?;
7253        }
7254        if let Some(mtp) = self.mtp.as_ref() {
7255            stage_layer(u16::MAX, &mtp.ffn)?;
7256        }
7257        e.freeze_moe_cache();
7258        println!(
7259            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
7260            path.display()
7261        );
7262        Ok(true)
7263    }
7264
7265    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
7266    pub fn freeze_cpu_expert_residency(
7267        &self,
7268        e: &Engine,
7269    ) -> Result<(), Box<dyn std::error::Error>> {
7270        e.freeze_moe_cache();
7271        Ok(())
7272    }
7273
7274    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
7275    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
7276    /// the model's activation exactly.
7277    ///
7278    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
7279    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
7280    /// form for anything that can land on a clamped layer.
7281    pub fn ffn_act(
7282        e: &Engine,
7283        cfg: &ModelConfig,
7284        gate: &CudaSlice<f32>,
7285        up: &CudaSlice<f32>,
7286        act: &mut CudaSlice<f32>,
7287        n: usize,
7288    ) -> Result<(), Box<dyn std::error::Error>> {
7289        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
7290    }
7291
7292    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
7293    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
7294    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
7295    #[allow(clippy::too_many_arguments)]
7296    pub(crate) fn ffn_act_scaled(
7297        e: &Engine,
7298        cfg: &ModelConfig,
7299        gate: &CudaSlice<f32>,
7300        up: &CudaSlice<f32>,
7301        gs: f32,
7302        us: f32,
7303        act: &mut CudaSlice<f32>,
7304        n: usize,
7305    ) -> Result<(), Box<dyn std::error::Error>> {
7306        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
7307    }
7308
7309    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
7310    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
7311    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
7312    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
7313    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
7314    ///                 arrays are SEPARATE and a layer can have one without the other.
7315    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
7316    /// already known live.
7317    #[allow(clippy::too_many_arguments)]
7318    pub(crate) fn ffn_act_lim(
7319        e: &Engine,
7320        cfg: &ModelConfig,
7321        gate: &CudaSlice<f32>,
7322        up: &CudaSlice<f32>,
7323        gs: f32,
7324        us: f32,
7325        limit: Option<f32>,
7326        act: &mut CudaSlice<f32>,
7327        n: usize,
7328    ) -> Result<(), Box<dyn std::error::Error>> {
7329        if let Some(m3) = cfg.m3.as_ref() {
7330            debug_assert!(
7331                limit.is_none(),
7332                "m3 swigluoai and step35 clamp are different archs"
7333            );
7334            return e.swigluoai_mul_scaled(
7335                gate,
7336                up,
7337                gs,
7338                us,
7339                m3.swiglu_alpha,
7340                m3.swiglu_limit,
7341                act,
7342                n,
7343            );
7344        }
7345        if let Some(l) = limit {
7346            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
7347        }
7348        if gs == 1.0 && us == 1.0 {
7349            return e.silu_mul(gate, up, act, n);
7350        }
7351        e.silu_mul_scaled(gate, up, gs, us, act, n)
7352    }
7353
7354    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
7355    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
7356    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
7357    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
7358    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
7359    fn moe_route(
7360        e: &Engine,
7361        logits: &CudaSlice<f32>,
7362        t: usize,
7363        n_expert: usize,
7364        n_used: usize,
7365    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7366        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
7367    }
7368
7369    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
7370    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
7371    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
7372    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
7373    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
7374    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
7375    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
7376    #[allow(clippy::too_many_arguments)]
7377    fn moe_route_sigmoid_cfg(
7378        e: &Engine,
7379        logits: &CudaSlice<f32>,
7380        t: usize,
7381        n_expert: usize,
7382        n_used: usize,
7383        m: &MoeWeights,
7384        (sf, route_norm): (f32, bool),
7385    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7386        if sigmoid_router_enabled() {
7387            return e.moe_router_sigmoid_topk_host(
7388                logits,
7389                t,
7390                n_expert,
7391                n_used,
7392                m.active_count(),
7393                &m.exp_probs_b_dev,
7394                &m.active_experts_dev,
7395                sf,
7396                route_norm,
7397            );
7398        }
7399        let lg = e.dtoh(logits)?;
7400        Self::moe_route_sigmoid_host(
7401            &lg,
7402            t,
7403            n_expert,
7404            n_used,
7405            m.exp_probs_b.as_deref(),
7406            sf,
7407            route_norm,
7408            m.active_experts.as_deref(),
7409        )
7410    }
7411
7412    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
7413    /// the existing softmax device kernel has no mask input.
7414    fn moe_route_cfg(
7415        e: &Engine,
7416        logits: &CudaSlice<f32>,
7417        t: usize,
7418        n_expert: usize,
7419        n_used: usize,
7420        active: Option<&[bool]>,
7421    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7422        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7423        // rollback) via the single-sync pinned readback — softmax arch only.
7424        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7425            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7426        }
7427        // Host oracle (the §D bit-identity reference).
7428        let lg = e.dtoh(logits)?; // [T*n_expert] host
7429        let mut sel = vec![0u32; t * n_used];
7430        let mut w_out = vec![0f32; t * n_used];
7431        for tok in 0..t {
7432            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7433            // softmax over ALL n_expert (stable: subtract max)
7434            let maxl = row
7435                .iter()
7436                .enumerate()
7437                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7438                .map(|(_, &x)| x)
7439                .fold(f32::NEG_INFINITY, f32::max);
7440            let mut probs = vec![0f32; n_expert];
7441            let mut den = 0f32;
7442            for i in 0..n_expert {
7443                if active.is_some_and(|mask| !mask[i]) {
7444                    continue;
7445                }
7446                let x = (row[i] - maxl).exp();
7447                probs[i] = x;
7448                den += x;
7449            }
7450            for p in probs.iter_mut() {
7451                *p /= den;
7452            }
7453            // stable DESC sort: prob DESC, ascending-index tiebreak.
7454            let mut idx: Vec<usize> = (0..n_expert)
7455                .filter(|&i| active.is_none_or(|mask| mask[i]))
7456                .collect();
7457            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7458            let sl = &idx[..n_used];
7459            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7460            let mut ws: f32 = wv.iter().sum();
7461            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7462            for x in wv.iter_mut() {
7463                *x /= ws;
7464            }
7465            for j in 0..n_used {
7466                sel[tok * n_used + j] = sl[j] as u32;
7467                w_out[tok * n_used + j] = wv[j];
7468            }
7469        }
7470        Ok((sel, w_out))
7471    }
7472
7473    #[allow(clippy::too_many_arguments)]
7474    fn moe_route_sigmoid_with_input(
7475        e: &Engine,
7476        logits: &CudaSlice<f32>,
7477        input: &CudaSlice<f32>,
7478        t: usize,
7479        in_features: usize,
7480        n_expert: usize,
7481        n_used: usize,
7482        bias: Option<&[f32]>,
7483        (sf, route_norm): (f32, bool),
7484        active: Option<&[bool]>,
7485    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7486        let logit_values =
7487            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7488        let input_values =
7489            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7490        let (lg, input) = e.dtoh_pair_views(
7491            &logits.slice(0..logit_values),
7492            &input.slice(0..input_values),
7493        )?;
7494        let (sel, w) =
7495            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7496        Ok((sel, w, input))
7497    }
7498
7499    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7500    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7501    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7502    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7503    pub fn start_moe_prefetch_predictor(
7504        &self,
7505        e: &Engine,
7506        cfg: &ModelConfig,
7507    ) -> Result<(), Box<dyn std::error::Error>> {
7508        use crate::hybrid::Ffn;
7509        let Some(sig) = cfg.sigmoid_router() else {
7510            return Err("prefetch predictor requires a sigmoid-router arch".into());
7511        };
7512        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7513            .export_moe_residency()
7514            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7515            .into_iter()
7516            .collect();
7517        let mut layers = Vec::new();
7518        for (index, layer) in self.layers.iter().enumerate() {
7519            let Ffn::Moe(m) = &layer.ffn else { continue };
7520            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7521                continue;
7522            };
7523            let router = e.dtoh(data)?;
7524            let n_expert = m.gate_exps.n_expert;
7525            let n_embd = m.gate_exps.in_f;
7526            if router.len() != n_embd * n_expert {
7527                continue;
7528            }
7529            let build = |exps: &crate::model::HostExps| {
7530                (0..n_expert)
7531                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7532                    .collect::<Vec<_>>()
7533            };
7534            layers.push((
7535                index as u16,
7536                crate::cpu_experts::PredictLayerInit {
7537                    router,
7538                    bias: m.exp_probs_b.clone(),
7539                    active: m.active_experts.clone(),
7540                    n_embd,
7541                    n_used: cfg
7542                        .moe
7543                        .as_ref()
7544                        .map(|moe| moe.expert_used_count as usize)
7545                        .ok_or("prefetch predictor requires MoE config")?,
7546                    sig,
7547                    weights_n_expert: n_expert,
7548                    gate: build(&m.gate_exps),
7549                    up: build(&m.up_exps),
7550                    down: build(&m.down_exps),
7551                },
7552            ));
7553        }
7554        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7555    }
7556
7557    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7558    /// selection math to the rollback runtime, applied to host-computed logits.
7559    #[allow(clippy::too_many_arguments)]
7560    pub fn moe_route_sigmoid_host_public(
7561        logits: &[f32],
7562        t: usize,
7563        n_expert: usize,
7564        n_used: usize,
7565        bias: Option<&[f32]>,
7566        sf: f32,
7567        route_norm: bool,
7568        active: Option<&[bool]>,
7569    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7570        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7571    }
7572
7573    #[allow(clippy::too_many_arguments)]
7574    fn moe_route_sigmoid_host(
7575        lg: &[f32],
7576        t: usize,
7577        n_expert: usize,
7578        n_used: usize,
7579        bias: Option<&[f32]>,
7580        sf: f32,
7581        route_norm: bool,
7582        active: Option<&[bool]>,
7583    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7584        let active_count = active
7585            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7586            .unwrap_or(n_expert);
7587        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7588        if lg.len() != t * n_expert {
7589            return Err(format!(
7590                "sigmoid router logits length mismatch: got {}, expected {}",
7591                lg.len(),
7592                t * n_expert,
7593            )
7594            .into());
7595        }
7596        let mut sel = vec![0u32; t * n_used];
7597        let mut w_out = vec![0f32; t * n_used];
7598        for tok in 0..t {
7599            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7600            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7601            // selection score = sigmoid + bias; weight = plain sigmoid.
7602            let selsc: Vec<f32> = match bias {
7603                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7604                None => scores.clone(),
7605            };
7606            let mut idx: Vec<usize> = (0..n_expert)
7607                .filter(|&i| active.is_none_or(|mask| mask[i]))
7608                .collect();
7609            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7610            let sl = &idx[..n_used];
7611            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7612            if route_norm {
7613                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7614                for x in wv.iter_mut() {
7615                    *x = *x / ws * sf;
7616                }
7617            } else {
7618                for x in wv.iter_mut() {
7619                    *x *= sf;
7620                }
7621            }
7622            for j in 0..n_used {
7623                sel[tok * n_used + j] = sl[j] as u32;
7624                w_out[tok * n_used + j] = wv[j];
7625            }
7626        }
7627        Ok((sel, w_out))
7628    }
7629
7630    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7631    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7632    /// macro-scaled experts, and observation modes are denied by the caller.
7633    #[allow(clippy::too_many_arguments)]
7634    fn moe_ffn_sigmoid_dev(
7635        e: &Engine,
7636        m: &MoeWeights,
7637        z: &CudaSlice<f32>,
7638        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7639        logits: &CudaSlice<f32>,
7640        t: usize,
7641        cfg: &ModelConfig,
7642        il: u16,
7643        (scaling_factor, route_norm): (f32, bool),
7644    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7645        let moe = cfg.moe.as_ref().unwrap();
7646        let n_embd = cfg.n_embd as usize;
7647        let n_expert = moe.expert_count as usize;
7648        let n_used = moe.expert_used_count as usize;
7649        let n_ff_exp = moe.expert_ff_length as usize;
7650        let dev = m.dev_exps.as_ref().unwrap();
7651        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7652        debug_assert!(m.has_uniform_expert_layout());
7653        debug_assert!(!m.has_macros);
7654
7655        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7656            logits,
7657            t,
7658            n_expert,
7659            n_used,
7660            m.active_count(),
7661            &m.exp_probs_b_dev,
7662            &m.active_experts_dev,
7663            scaling_factor,
7664            route_norm,
7665        )?;
7666        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7667        if let Some(fp8) = dev.fp8_blk.as_ref() {
7668            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7669            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7670            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7671            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7672            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7673            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7674
7675            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7676            // activations with block-128 E4M3 weights. This deliberately
7677            // simple resident reference is the correctness oracle for later
7678            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7679            // load-time Q8 diagnostic representation, so one process never
7680            // crosses between numerical programs.
7681            let selected = e.dtoh_i32(&sel_d)?;
7682            let route_weights = e.dtoh(&w_d)?;
7683            let mut moe_out = e.zeros(t * n_embd)?;
7684            for tok in 0..t {
7685                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7686                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7687                for j in 0..n_used {
7688                    let pair = tok * n_used + j;
7689                    let expert = selected[pair] as usize;
7690                    let gate = Self::moe_resident_fp8_e4m3(
7691                        e,
7692                        &m.gate_exps,
7693                        &dev.gate,
7694                        &fp8.gate,
7695                        expert,
7696                        &zt,
7697                        1,
7698                    )?;
7699                    let up = Self::moe_resident_fp8_e4m3(
7700                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7701                    )?;
7702                    let mut act = e.uninit(n_ff_exp)?;
7703                    Self::ffn_act_lim(
7704                        e,
7705                        cfg,
7706                        &gate,
7707                        &up,
7708                        1.0,
7709                        1.0,
7710                        cfg.clamp_exp_at(il as u32),
7711                        &mut act,
7712                        n_ff_exp,
7713                    )?;
7714                    let act = act.slice(0..n_ff_exp);
7715                    let down = Self::moe_resident_fp8_e4m3(
7716                        e,
7717                        &m.down_exps,
7718                        &dev.down,
7719                        &fp8.down,
7720                        expert,
7721                        &act,
7722                        1,
7723                    )?;
7724                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7725                }
7726            }
7727            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7728                eprintln!(
7729                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7730                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7731                    cfg.clamp_exp_at(il as u32).is_some(),
7732                );
7733            }
7734            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7735            return Ok(moe_out);
7736        }
7737        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7738            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7739            (combined, combined)
7740        } else {
7741            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7742        };
7743        let (zq, zd) = match (t, zq8) {
7744            (1, Some((q, d))) => (q.clone(), d.clone()),
7745            _ => e.quantize_q8_1(z, t, n_embd)?,
7746        };
7747        let n_pairs = t * n_used;
7748        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7749            // The final Step layers retain the established separate gate/up -> clamp -> down
7750            // arithmetic. Pair rows are derived from token position; selected expert ids and
7751            // routing weights remain the device router's buffers throughout.
7752            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7753            let pair_tok_d = e.htod_i32(&pair_tok)?;
7754            let gate = e.moe_pairs_matvec_q8(
7755                &dev.ptr_row,
7756                0,
7757                &pair_tok_d,
7758                &sel_d,
7759                &zq,
7760                &zd,
7761                n_embd,
7762                n_ff_exp,
7763                n_expert,
7764                n_pairs,
7765                m.gate_exps.qtype,
7766                gate_row_bytes,
7767            )?;
7768            let up = e.moe_pairs_matvec_q8(
7769                &dev.ptr_row,
7770                1,
7771                &pair_tok_d,
7772                &sel_d,
7773                &zq,
7774                &zd,
7775                n_embd,
7776                n_ff_exp,
7777                n_expert,
7778                n_pairs,
7779                m.up_exps.qtype,
7780                up_row_bytes,
7781            )?;
7782            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7783            Self::ffn_act_lim(
7784                e,
7785                cfg,
7786                &gate,
7787                &up,
7788                1.0,
7789                1.0,
7790                cfg.clamp_exp_at(il as u32),
7791                &mut act,
7792                n_pairs * n_ff_exp,
7793            )?;
7794            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7795            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7796            let pair_self_d = e.htod_i32(&pair_self)?;
7797            let down = e.moe_pairs_matvec_q8(
7798                &dev.ptr_row,
7799                2,
7800                &pair_self_d,
7801                &sel_d,
7802                &aq2,
7803                &ad2,
7804                n_ff_exp,
7805                n_embd,
7806                n_expert,
7807                n_pairs,
7808                m.down_exps.qtype,
7809                m.down_exps.row_bytes,
7810            )?;
7811            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7812            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7813            let tok_off_d = e.htod_i32(&tok_off)?;
7814            let tok_ids_d = e.htod_i32(&tok_ids)?;
7815            let mut output = e.uninit(t * n_embd)?;
7816            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7817            output
7818        } else {
7819            let act = e.moe_gate_up_silu8_dev_q8_rows(
7820                &dev.ptr_row,
7821                &sel_d,
7822                &zq,
7823                &zd,
7824                t,
7825                n_embd,
7826                n_ff_exp,
7827                n_used,
7828                n_expert,
7829                m.gate_exps.qtype,
7830                m.up_exps.qtype,
7831                gate_row_bytes,
7832                up_row_bytes,
7833                &m.dev_macros,
7834            )?;
7835            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7836            let mut output = e.uninit(t * n_embd)?;
7837            e.moe_down8_fma_dev_q8_rows_g(
7838                &dev.ptr_row,
7839                &sel_d,
7840                &w_d,
7841                &aq2,
7842                &ad2,
7843                &mut output,
7844                t,
7845                n_ff_exp,
7846                n_embd,
7847                n_used,
7848                n_expert,
7849                m.down_exps.qtype,
7850                m.down_exps.row_bytes,
7851            )?;
7852            output
7853        };
7854
7855        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7856            eprintln!(
7857                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7858                cfg.clamp_exp_at(il as u32).is_some(),
7859                dev.gu_il,
7860            );
7861        }
7862        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7863        Ok(moe_out)
7864    }
7865
7866    #[allow(clippy::too_many_arguments)]
7867    fn moe_resident_fp8_e4m3(
7868        e: &Engine,
7869        exps: &crate::model::HostExps,
7870        bytes: &CudaSlice<u8>,
7871        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7872        expert: usize,
7873        x: &cudarc::driver::CudaView<f32>,
7874        m: usize,
7875    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7876        let layout = exps.expert_layout(expert);
7877        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7878        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7879        let byte_start = expert * exps.expert_stride;
7880        let scale_start = expert * scales.expert_stride;
7881        let weight = bytes.slice(byte_start..byte_start + layout.len);
7882        let scale = scales
7883            .scales
7884            .slice(scale_start..scale_start + scales.expert_stride);
7885        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7886    }
7887
7888    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7889    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7890    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7891    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7892    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7893    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7894    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7895    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7896    fn moe_ffn_pairs(
7897        e: &Engine,
7898        m: &MoeWeights,
7899        z: &CudaSlice<f32>,
7900        logits: &CudaSlice<f32>,
7901        t: usize,
7902        cfg: &ModelConfig,
7903    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7904        let moe = cfg.moe.as_ref().unwrap();
7905        let n_embd = cfg.n_embd as usize;
7906        let n_expert = moe.expert_count as usize;
7907        let n_used = moe.expert_used_count as usize;
7908        let n_ff_exp = moe.expert_ff_length as usize;
7909        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7910        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7911        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7912        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7913        debug_assert!(
7914            !cfg.swiglu_clamped_anywhere(),
7915            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7916        );
7917        let dev = m.dev_exps.as_ref().unwrap();
7918        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7919        let (rbg_d, rbu_d) = if dev.gu_il {
7920            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7921            (sxx, sxx)
7922        } else {
7923            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7924        };
7925
7926        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7927        let n_pairs = t * n_used;
7928        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7929        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7930        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7931        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7932        let pair_w: Vec<f32> = w_all.clone();
7933        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7934        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7935        let pt = e.htod_i32(&pair_tok)?;
7936        let px = e.htod_i32(&pair_ex)?;
7937        let pw = e.htod(&pair_w)?;
7938        let toff = e.htod_i32(&tok_off)?;
7939        let tids = e.htod_i32(&tok_ids)?;
7940
7941        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7942        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7943        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7944        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7945        for p in 0..n_pairs {
7946            by_ex[pair_ex[p] as usize].push(p as i32);
7947        }
7948        let mut ex_ids: Vec<i32> = Vec::new();
7949        let mut ex_off: Vec<i32> = vec![0];
7950        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7951        for (ex, list) in by_ex.iter().enumerate() {
7952            if list.is_empty() {
7953                continue;
7954            }
7955            ex_ids.push(ex as i32);
7956            ex_pairs.extend_from_slice(list);
7957            ex_off.push(ex_pairs.len() as i32);
7958        }
7959        let n_active = ex_ids.len();
7960        let exi = e.htod_i32(&ex_ids)?;
7961        let exo = e.htod_i32(&ex_off)?;
7962        let exp_d = e.htod_i32(&ex_pairs)?;
7963        let _ = &px; // pair-major twin keeps it; em path uses CSR
7964
7965        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7966        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7967        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7968        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7969        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7970        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7971        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7972        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7973        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7974        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7975        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7976        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7977        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7978        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7979        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7980        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7981        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7982        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7983        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7984        let mma_t = *MMA_T.get_or_init(|| {
7985            std::env::var("MEMRA_MOE_MMA_T")
7986                .ok()
7987                .and_then(|v| v.parse().ok())
7988                .unwrap_or(16)
7989        });
7990        let use_mma = std::env::var("MEMRA_MOE_MMA")
7991            .map(|v| v != "0")
7992            .unwrap_or(true)
7993            && t >= mma_t
7994            && q8_expert_dec_supported(m.gate_exps.qtype)
7995            && q8_expert_dec_supported(m.up_exps.qtype)
7996            && q8_expert_dec_supported(m.down_exps.qtype)
7997            && n_embd % 256 == 0
7998            && n_ff_exp % 256 == 0;
7999        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
8000        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
8001        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
8002        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
8003        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
8004        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
8005        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
8006        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
8007        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
8008        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
8009        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
8010        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
8011        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
8012        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
8013        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
8014        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
8015            && q8_expert_dec_supported(m.up_exps.qtype)
8016            && q8_expert_dec_supported(m.down_exps.qtype)
8017            && n_embd % 256 == 0
8018            && n_ff_exp % 256 == 0;
8019        let f16g_mode = crate::moe_f16g_mode();
8020        let f16g = f16g_mode != 0
8021            && t >= mma_t
8022            && (f16g_mode != 3 || !mma_capable)
8023            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
8024            && f16g_proj_ok(m.up_exps.qtype, n_embd)
8025            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
8026        if use_mma || f16g {
8027            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
8028            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
8029            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
8030            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
8031            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
8032            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
8033            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
8034            let y_down = if f16g {
8035                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
8036                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
8037                // permute at the very end back to pair-id order for the scatter.
8038                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
8039                let csr_tok_d = e.htod_i32(&csr_tok)?;
8040                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
8041                let g_csr = e.moe_f16_grouped(
8042                    &dev.ptr_row,
8043                    0,
8044                    n_expert,
8045                    &exi,
8046                    &ex_off,
8047                    &exo,
8048                    &z_f16,
8049                    &z_s,
8050                    n_embd,
8051                    n_ff_exp,
8052                    n_active,
8053                    n_pairs,
8054                    m.gate_exps.qtype,
8055                    rbg_d,
8056                )?;
8057                let u_csr = e.moe_f16_grouped(
8058                    &dev.ptr_row,
8059                    1,
8060                    n_expert,
8061                    &exi,
8062                    &ex_off,
8063                    &exo,
8064                    &z_f16,
8065                    &z_s,
8066                    n_embd,
8067                    n_ff_exp,
8068                    n_active,
8069                    n_pairs,
8070                    m.up_exps.qtype,
8071                    rbu_d,
8072                )?;
8073                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
8074                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
8075                let d_csr = e.moe_f16_grouped(
8076                    &dev.ptr_row,
8077                    2,
8078                    n_expert,
8079                    &exi,
8080                    &ex_off,
8081                    &exo,
8082                    &a_f16,
8083                    &a_s,
8084                    n_ff_exp,
8085                    n_embd,
8086                    n_active,
8087                    n_pairs,
8088                    m.down_exps.qtype,
8089                    m.down_exps.row_bytes,
8090                )?;
8091                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
8092            } else {
8093                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
8094                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
8095                let gate = e.mmq_iq_experts(
8096                    &dev.ptr_row,
8097                    0,
8098                    n_expert,
8099                    &exi,
8100                    &exo,
8101                    &exp_d,
8102                    &pt,
8103                    &z_scr,
8104                    n_embd,
8105                    n_ff_exp,
8106                    n_active,
8107                    n_pairs,
8108                    t,
8109                    m.gate_exps.qtype,
8110                    rbg_d,
8111                )?;
8112                let up = e.mmq_iq_experts(
8113                    &dev.ptr_row,
8114                    1,
8115                    n_expert,
8116                    &exi,
8117                    &exo,
8118                    &exp_d,
8119                    &pt,
8120                    &z_scr,
8121                    n_embd,
8122                    n_ff_exp,
8123                    n_active,
8124                    n_pairs,
8125                    t,
8126                    m.up_exps.qtype,
8127                    rbu_d,
8128                )?;
8129                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
8130                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
8131                // registers and writes ONLY the quantized scratch — the two-pass chain
8132                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
8133                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
8134                let a_scr = if crate::moe_fuse_actq_on() {
8135                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
8136                } else {
8137                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
8138                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
8139                };
8140                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
8141                let pself = e.htod_i32(&pair_self)?;
8142                e.mmq_iq_experts(
8143                    &dev.ptr_row,
8144                    2,
8145                    n_expert,
8146                    &exi,
8147                    &exo,
8148                    &exp_d,
8149                    &pself,
8150                    &a_scr,
8151                    n_ff_exp,
8152                    n_embd,
8153                    n_active,
8154                    n_pairs,
8155                    n_pairs,
8156                    m.down_exps.qtype,
8157                    m.down_exps.row_bytes,
8158                )?
8159            };
8160            let mut moe_out = e.uninit(t * n_embd)?;
8161            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
8162            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8163                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8164            {
8165                let n_ff_sh = gate_shexp.out_features();
8166                let sg_gate = e.matmul(gate_shexp, z, t)?;
8167                let sg_up = e.matmul(up_shexp, z, t)?;
8168                let mut sa = e.uninit(t * n_ff_sh)?;
8169                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8170                let sh = e.matmul(down_shexp, &sa, t)?;
8171                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8172                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
8173                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
8174                // i.e. the one real prefill actually takes on a resident-expert MoE model,
8175                // so the concat-prime isolation fix has to land here as well.
8176                let g = match &m.gate_inp_shexp {
8177                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
8178                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8179                    }
8180                    Some(gate_inp_shexp) => {
8181                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8182                        let mut g = e.uninit(t)?;
8183                        e.sigmoid(&gs, &mut g, t)?;
8184                        g
8185                    }
8186                    None => e.htod(&vec![1.0f32; t])?,
8187                };
8188                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8189            }
8190            return Ok(moe_out);
8191        }
8192
8193        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
8194        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
8195        let dec = std::env::var("MEMRA_MOE_DEC")
8196            .map(|v| v != "0")
8197            .unwrap_or(true);
8198        let matvec = |proj,
8199                      exi: &_,
8200                      exo: &_,
8201                      exp_d: &_,
8202                      pt: &_,
8203                      aq: &_,
8204                      ad: &_,
8205                      inf,
8206                      outf,
8207                      qtype,
8208                      rb|
8209         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8210            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
8211            let dec = dec && q8_expert_dec_supported(qtype);
8212            if dec {
8213                e.moe_pairs_matvec_q8_dec(
8214                    &dev.ptr_row,
8215                    proj,
8216                    exi,
8217                    exo,
8218                    exp_d,
8219                    pt,
8220                    aq,
8221                    ad,
8222                    inf,
8223                    outf,
8224                    n_expert,
8225                    n_active,
8226                    n_pairs,
8227                    qtype,
8228                    rb,
8229                )
8230            } else {
8231                e.moe_pairs_matvec_q8_em(
8232                    &dev.ptr_row,
8233                    proj,
8234                    exi,
8235                    exo,
8236                    exp_d,
8237                    pt,
8238                    aq,
8239                    ad,
8240                    inf,
8241                    outf,
8242                    n_expert,
8243                    n_active,
8244                    n_pairs,
8245                    qtype,
8246                    rb,
8247                )
8248            }
8249        };
8250        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8251        let gate = matvec(
8252            0,
8253            &exi,
8254            &exo,
8255            &exp_d,
8256            &pt,
8257            &zq,
8258            &zd,
8259            n_embd,
8260            n_ff_exp,
8261            m.gate_exps.qtype,
8262            rbg_d,
8263        )?;
8264        let up = matvec(
8265            1,
8266            &exi,
8267            &exo,
8268            &exp_d,
8269            &pt,
8270            &zq,
8271            &zd,
8272            n_embd,
8273            n_ff_exp,
8274            m.up_exps.qtype,
8275            rbu_d,
8276        )?;
8277        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
8278        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8279        // down consumes PAIR-major activation rows: pair_tok = identity.
8280        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
8281        let pself = e.htod_i32(&pair_self)?;
8282        let y_down = matvec(
8283            2,
8284            &exi,
8285            &exo,
8286            &exp_d,
8287            &pself,
8288            &aq2,
8289            &ad2,
8290            n_ff_exp,
8291            n_embd,
8292            m.down_exps.qtype,
8293            m.down_exps.row_bytes,
8294        )?;
8295        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
8296        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
8297
8298        // SHARED EXPERT epilogue — same as the other paths.
8299        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8300        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8301        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8302            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8303        {
8304            let n_ff_sh = gate_shexp.out_features();
8305            // These decode-exact forms are required by the new Step resident arm. Keep the
8306            // established grouped shared-expert program for every other architecture: widening
8307            // this to Gemma changed its speculative acceptance despite green argmax gates.
8308            let step_exact = true;
8309            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
8310            let (sg_gate, sg_up) = if step_exact && t == 1 {
8311                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
8312            } else if verify_t {
8313                let mut fused = None;
8314                if crate::spec::spec_fused_t()
8315                    && (2..=4).contains(&t)
8316                    && e.uses_q8_1_fast(gate_shexp)
8317                    && e.uses_q8_1_fast(up_shexp)
8318                {
8319                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8320                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8321                }
8322                match fused {
8323                    Some(pair) => pair,
8324                    None => (
8325                        e.matmul_decode_exact(gate_shexp, z, t)?,
8326                        e.matmul_decode_exact(up_shexp, z, t)?,
8327                    ),
8328                }
8329            } else {
8330                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8331            };
8332            let mut sa = e.uninit(t * n_ff_sh)?;
8333            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8334            let sh = if verify_t {
8335                e.matmul_decode_exact(down_shexp, &sa, t)?
8336            } else {
8337                e.matmul(down_shexp, &sa, t)?
8338            };
8339            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8340            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
8341            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
8342            // dispatch choice cannot change bits.
8343            let g = match &m.gate_inp_shexp {
8344                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
8345                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8346                }
8347                Some(gate_inp_shexp) => {
8348                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8349                    let mut g = e.uninit(t)?;
8350                    e.sigmoid(&gs, &mut g, t)?;
8351                    g
8352                }
8353                None => e.htod(&vec![1.0f32; t])?,
8354            };
8355            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8356        }
8357        Ok(moe_out)
8358    }
8359
8360    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
8361    #[allow(clippy::too_many_arguments)]
8362    #[allow(clippy::too_many_arguments)]
8363    fn moe_ffn_dev(
8364        e: &Engine,
8365        m: &MoeWeights,
8366        z: &CudaSlice<f32>,
8367        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
8368        logits: &CudaSlice<f32>,
8369        t: usize,
8370        cfg: &ModelConfig,
8371        il: u16,
8372        max_block: usize,
8373    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8374        let moe = cfg.moe.as_ref().unwrap();
8375        let n_embd = cfg.n_embd as usize;
8376        let n_expert = moe.expert_count as usize;
8377        let n_used = moe.expert_used_count as usize;
8378        let n_ff_exp = moe.expert_ff_length as usize;
8379        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
8380        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
8381        // clamped layers; assert both so a future caller that skips the gate fails loudly.
8382        debug_assert!(
8383            cfg.sigmoid_router().is_none(),
8384            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
8385        );
8386        debug_assert!(
8387            !cfg.swiglu_clamped_at(il as u32),
8388            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
8389        );
8390
8391        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
8392        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
8393        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
8394        // skipped entirely for macro-free experts (every k-quant GGUF).
8395        if m.has_macros {
8396            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
8397        }
8398
8399        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
8400        let mut moe_out = e.uninit(t * n_embd)?;
8401
8402        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
8403        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
8404        if let Some(dev) = m.dev_exps.as_ref() {
8405            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
8406            // the combined stride; up's base is offset in the ptr table. Down unchanged.
8407            let (rbg_d, rbu_d) = if dev.gu_il {
8408                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8409                (sxx, sxx)
8410            } else {
8411                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8412            };
8413            let q8 = moe_q8_enabled()
8414                && q8_expert_supported(m.gate_exps.qtype)
8415                && q8_expert_supported(m.up_exps.qtype)
8416                && q8_expert_supported(m.down_exps.qtype);
8417            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
8418            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
8419            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
8420            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8421            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8422            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8423            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8424            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8425            let rows_arm = q8
8426                && t > 1
8427                && crate::spec::spec_m2()
8428                && n_ff_exp == 512
8429                && n_used <= 8
8430                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8431                    .map(|v| v.is_empty() || v == "v")
8432                    .unwrap_or(true)
8433                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8434                    .map(|v| v.is_empty() || v == "w8h2v")
8435                    .unwrap_or(true);
8436            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8437            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8438            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8439            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8440            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8441            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8442            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8443            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8444            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8445                .ok()
8446                .and_then(|v| v.parse::<i32>().ok())
8447                .unwrap_or(1);
8448            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8449            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8450            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8451            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8452            // axis. Three chain-pinning attempts did not close it (receipts,
8453            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8454            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8455            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8456            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8457            // never decode-batch-gate at B=8 on the MoE model itself.
8458            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8459            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8460            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8461            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8462            // de-admission verdict above stands until those gates are GREEN on the MoE
8463            // artifact; this door must never default on.
8464            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8465            let csr_qt = |qt: i32| {
8466                qt == crate::QT_IQ4_XS
8467                    || qt == crate::QT_IQ3_S
8468                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8469            };
8470            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8471            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8472            let csr_arm = rows_arm
8473                && csr_mode > 0
8474                && t <= csr_t_max
8475                && csr_uniform
8476                && csr_qt(m.gate_exps.qtype)
8477                && csr_qt(m.up_exps.qtype)
8478                && csr_qt(m.down_exps.qtype);
8479            if csr_arm {
8480                if csr_mode == 2 {
8481                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8482                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8483                }
8484                let n_pairs = t * n_used;
8485                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8486                let act = e.moe_gate_up_silu8_dev_q8_csr(
8487                    &dev.ptr_row,
8488                    &sel_d,
8489                    &zq,
8490                    &zd,
8491                    n_pairs,
8492                    n_embd,
8493                    n_ff_exp,
8494                    n_used,
8495                    n_expert,
8496                    m.gate_exps.qtype,
8497                    m.up_exps.qtype,
8498                    rbg_d,
8499                    rbu_d,
8500                )?;
8501                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8502                // down stays on the _rows twin — BOTH CSR down variants measured negative
8503                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8504                // 16-group rows have too little decode to amortize any dedup structure.
8505                e.moe_down8_fma_dev_q8_rows(
8506                    &dev.ptr_row,
8507                    &sel_d,
8508                    &w_d,
8509                    &aq2,
8510                    &ad2,
8511                    &mut moe_out,
8512                    t,
8513                    n_ff_exp,
8514                    n_embd,
8515                    n_used,
8516                    n_expert,
8517                    m.down_exps.qtype,
8518                    m.down_exps.row_bytes,
8519                )?;
8520                if csr_mode == 2 {
8521                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8522                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8523                        &dev.ptr_row,
8524                        &sel_d,
8525                        &zq,
8526                        &zd,
8527                        t,
8528                        n_embd,
8529                        n_ff_exp,
8530                        n_used,
8531                        n_expert,
8532                        m.gate_exps.qtype,
8533                        m.up_exps.qtype,
8534                        rbg_d,
8535                        rbu_d,
8536                        &m.dev_macros,
8537                    )?;
8538                    let mut out_r = e.uninit(t * n_embd)?;
8539                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8540                    e.moe_down8_fma_dev_q8_rows(
8541                        &dev.ptr_row,
8542                        &sel_d,
8543                        &w_d,
8544                        &aq2r,
8545                        &ad2r,
8546                        &mut out_r,
8547                        t,
8548                        n_ff_exp,
8549                        n_embd,
8550                        n_used,
8551                        n_expert,
8552                        m.down_exps.qtype,
8553                        m.down_exps.row_bytes,
8554                    )?;
8555                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8556                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8557                    let ba = a1
8558                        .iter()
8559                        .zip(&a2)
8560                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8561                        .count();
8562                    let bo = o1
8563                        .iter()
8564                        .zip(&o2)
8565                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8566                        .count();
8567                    if ba + bo > 0 {
8568                        eprintln!(
8569                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8570                            a1.len(),
8571                            o1.len()
8572                        );
8573                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8574                        let sel_h = e.dtoh_i32(&sel_d)?;
8575                        let mut shown = 0;
8576                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8577                            if x.to_bits() != y.to_bits() && shown < 4 {
8578                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8579                                let ex = sel_h[p];
8580                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8581                                eprintln!(
8582                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8583                                );
8584                                shown += 1;
8585                            }
8586                        }
8587                        std::process::exit(3);
8588                    }
8589                }
8590            } else if rows_arm {
8591                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8592                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8593                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8594                    use std::sync::atomic::{AtomicU64, Ordering};
8595                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8596                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8597                    static CALLS: AtomicU64 = AtomicU64::new(0);
8598                    let sel_h = e.dtoh_i32(&sel_d)?;
8599                    let mut u: Vec<i32> = sel_h.clone();
8600                    u.sort_unstable();
8601                    u.dedup();
8602                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8603                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8604                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8605                    if c % 480 == 0 {
8606                        let p = PAIRS.load(Ordering::Relaxed);
8607                        let q = UNIQ.load(Ordering::Relaxed);
8608                        eprintln!(
8609                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8610                            q as f64 / p as f64
8611                        );
8612                    }
8613                }
8614                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8615                let act = e.moe_gate_up_silu8_dev_q8_rows(
8616                    &dev.ptr_row,
8617                    &sel_d,
8618                    &zq,
8619                    &zd,
8620                    t,
8621                    n_embd,
8622                    n_ff_exp,
8623                    n_used,
8624                    n_expert,
8625                    m.gate_exps.qtype,
8626                    m.up_exps.qtype,
8627                    rbg_d,
8628                    rbu_d,
8629                    &m.dev_macros,
8630                )?;
8631                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8632                e.moe_down8_fma_dev_q8_rows(
8633                    &dev.ptr_row,
8634                    &sel_d,
8635                    &w_d,
8636                    &aq2,
8637                    &ad2,
8638                    &mut moe_out,
8639                    t,
8640                    n_ff_exp,
8641                    n_embd,
8642                    n_used,
8643                    n_expert,
8644                    m.down_exps.qtype,
8645                    m.down_exps.row_bytes,
8646                )?;
8647            } else {
8648                for tok in 0..t {
8649                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8650                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8651                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8652                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8653                    if q8 {
8654                        let (zq, zd) = match (t, zq8) {
8655                            (1, Some((q, d))) => (q.clone(), d.clone()),
8656                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8657                        };
8658                        let act = e.moe_gate_up_silu8_dev_q8(
8659                            &dev.ptr_row,
8660                            &selt,
8661                            &zq,
8662                            &zd,
8663                            n_embd,
8664                            n_ff_exp,
8665                            n_used,
8666                            n_expert,
8667                            m.gate_exps.qtype,
8668                            m.up_exps.qtype,
8669                            rbg_d,
8670                            rbu_d,
8671                            &m.dev_macros,
8672                        )?;
8673                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8674                        e.moe_down8_fma_dev_q8(
8675                            &dev.ptr_row,
8676                            &selt,
8677                            &wt,
8678                            &aq2,
8679                            &ad2,
8680                            &mut dst,
8681                            n_ff_exp,
8682                            n_embd,
8683                            n_used,
8684                            n_expert,
8685                            m.down_exps.qtype,
8686                            m.down_exps.row_bytes,
8687                        )?;
8688                    } else {
8689                        let act = e.moe_gate_up_silu8_dev(
8690                            &dev.ptr_row,
8691                            &selt,
8692                            &zt,
8693                            n_embd,
8694                            n_ff_exp,
8695                            n_used,
8696                            n_expert,
8697                            m.gate_exps.qtype,
8698                            m.up_exps.qtype,
8699                            rbg_d,
8700                            rbu_d,
8701                            &m.dev_macros,
8702                        )?;
8703                        e.moe_down8_fma_dev(
8704                            &dev.ptr_row,
8705                            &selt,
8706                            &wt,
8707                            &act,
8708                            &mut dst,
8709                            n_ff_exp,
8710                            n_embd,
8711                            n_used,
8712                            n_expert,
8713                            m.down_exps.qtype,
8714                            m.down_exps.row_bytes,
8715                        )?;
8716                    }
8717                }
8718            }
8719        } else {
8720            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8721            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8722            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8723            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8724            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8725            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8726            let q8 = moe_q8_enabled()
8727                && q8_expert_supported(m.gate_exps.qtype)
8728                && q8_expert_supported(m.up_exps.qtype)
8729                && q8_expert_supported(m.down_exps.qtype);
8730            e.with_moe_cache(max_block, |c, eng| {
8731                let row = c
8732                    .layer_dev_row(il, n_expert, eng)?
8733                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8734                for tok in 0..t {
8735                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8736                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8737                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8738                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8739                    if q8 {
8740                        let (zq, zd) = match (t, zq8) {
8741                            (1, Some((q, d))) => (q.clone(), d.clone()),
8742                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8743                        };
8744                        let act = eng.moe_gate_up_silu8_dev_q8(
8745                            row,
8746                            &selt,
8747                            &zq,
8748                            &zd,
8749                            n_embd,
8750                            n_ff_exp,
8751                            n_used,
8752                            n_expert,
8753                            m.gate_exps.qtype,
8754                            m.up_exps.qtype,
8755                            m.gate_exps.row_bytes,
8756                            m.up_exps.row_bytes,
8757                            &m.dev_macros,
8758                        )?;
8759                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8760                        eng.moe_down8_fma_dev_q8(
8761                            row,
8762                            &selt,
8763                            &wt,
8764                            &aq2,
8765                            &ad2,
8766                            &mut dst,
8767                            n_ff_exp,
8768                            n_embd,
8769                            n_used,
8770                            n_expert,
8771                            m.down_exps.qtype,
8772                            m.down_exps.row_bytes,
8773                        )?;
8774                    } else {
8775                        let act = eng.moe_gate_up_silu8_dev(
8776                            row,
8777                            &selt,
8778                            &zt,
8779                            n_embd,
8780                            n_ff_exp,
8781                            n_used,
8782                            n_expert,
8783                            m.gate_exps.qtype,
8784                            m.up_exps.qtype,
8785                            m.gate_exps.row_bytes,
8786                            m.up_exps.row_bytes,
8787                            &m.dev_macros,
8788                        )?;
8789                        eng.moe_down8_fma_dev(
8790                            row,
8791                            &selt,
8792                            &wt,
8793                            &act,
8794                            &mut dst,
8795                            n_ff_exp,
8796                            n_embd,
8797                            n_used,
8798                            n_expert,
8799                            m.down_exps.qtype,
8800                            m.down_exps.row_bytes,
8801                        )?;
8802                    }
8803                }
8804                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8805                c.hits += (t * 3 * n_used) as u64;
8806                Ok(())
8807            })?;
8808        }
8809
8810        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8811        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8812        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8813        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8814        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8815            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8816        {
8817            let n_ff_sh = gate_shexp.out_features();
8818            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8819            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8820            let verify_t = t > 1 && t < PRIME_MIN_T;
8821            let (sg_gate, sg_up) = if t == 1 {
8822                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8823            } else if verify_t {
8824                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8825                // rides one shared quantize + one fused2 batched launch instead of two
8826                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8827                let mut fused = None;
8828                if crate::spec::spec_fused_t()
8829                    && (2..=4).contains(&t)
8830                    && e.uses_q8_1_fast(gate_shexp)
8831                    && e.uses_q8_1_fast(up_shexp)
8832                {
8833                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8834                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8835                }
8836                match fused {
8837                    Some(pair) => pair,
8838                    None => (
8839                        e.matmul_decode_exact(gate_shexp, z, t)?,
8840                        e.matmul_decode_exact(up_shexp, z, t)?,
8841                    ),
8842                }
8843            } else {
8844                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8845            };
8846            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8847            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8848            let sh = if verify_t {
8849                e.matmul_decode_exact(down_shexp, &sa, t)?
8850            } else {
8851                e.matmul(down_shexp, &sa, t)?
8852            };
8853            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8854            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8855            // between the two arms; prefill keeps the batched cuBLASLt linear).
8856            let g = match &m.gate_inp_shexp {
8857                Some(gate_inp_shexp) => {
8858                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8859                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8860                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8861                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8862                    } else {
8863                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8864                        let mut g = e.uninit(t)?;
8865                        e.sigmoid(&gs, &mut g, t)?;
8866                        g
8867                    }
8868                }
8869                None => e.htod(&vec![1.0f32; t])?,
8870            };
8871            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8872        }
8873
8874        Ok(moe_out)
8875    }
8876
8877    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8878    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8879    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8880    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8881    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8882    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8883    /// the collected raw pointers cannot move between collection and launch (single-threaded
8884    /// decode; the lock is held only for collection, launches are stream-ordered after any
8885    /// prior same-stream staging writes).
8886    #[allow(clippy::too_many_arguments)]
8887    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8888    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8889    #[allow(clippy::too_many_arguments)]
8890    fn moe_gdec_token_q8(
8891        e: &Engine,
8892        m: &MoeWeights,
8893        il: u16,
8894        max_block: usize,
8895        zq: &CudaSlice<i8>,
8896        zd: &CudaSlice<f32>,
8897        sel: &[u32],
8898        w: &[f32],
8899        moe_out: &mut CudaSlice<f32>,
8900        tok: usize,
8901        n_embd: usize,
8902        n_ff_exp: usize,
8903        n_used: usize,
8904    ) -> Result<bool, Box<dyn std::error::Error>> {
8905        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8906        use cudarc::driver::DevicePtr;
8907        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8908            let mut g = [0u64; 8];
8909            let mut u = [0u64; 8];
8910            let mut d = [0u64; 8];
8911            for (j, &ex) in sel.iter().enumerate() {
8912                let ex = ex as u16;
8913                let (Some(sg), Some(su), Some(sd)) = (
8914                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8915                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8916                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8917                ) else {
8918                    return Ok(None);
8919                };
8920                let __s = eng.stream();
8921                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8922                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8923                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8924                g[j] = pg as u64;
8925                u[j] = pu as u64;
8926                d[j] = pd as u64;
8927            }
8928            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8929                for &ex in sel {
8930                    let ex = ex as u16;
8931                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8932                        c.note_profile_hit(BlockId::new(il, proj, ex));
8933                    }
8934                }
8935            }
8936            c.hits += (3 * n_used) as u64;
8937            Ok(Some((g, u, d)))
8938        })?;
8939        let Some((g, u, d)) = ptrs else {
8940            return Ok(false);
8941        };
8942        let mut wv = [0f32; 8];
8943        wv[..n_used].copy_from_slice(w);
8944        let act = e.moe_gate_up_silu8_q8(
8945            crate::WPtr8(g),
8946            crate::WPtr8(u),
8947            zq,
8948            zd,
8949            n_embd,
8950            n_ff_exp,
8951            n_used,
8952            m.gate_exps.qtype,
8953            m.up_exps.qtype,
8954            m.gate_exps.row_bytes,
8955            m.up_exps.row_bytes,
8956        )?;
8957        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8958        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8959        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8960        e.moe_down8_fma_q8(
8961            crate::WPtr8(d),
8962            crate::F32x8(wv),
8963            &aq2,
8964            &ad2,
8965            &mut dst,
8966            n_ff_exp,
8967            n_embd,
8968            n_used,
8969            m.down_exps.qtype,
8970            m.down_exps.row_bytes,
8971        )?;
8972        Ok(true)
8973    }
8974
8975    fn moe_gdec_token(
8976        e: &Engine,
8977        m: &MoeWeights,
8978        il: u16,
8979        max_block: usize,
8980        zt: &cudarc::driver::CudaView<f32>,
8981        sel: &[u32],
8982        w: &[f32],
8983        moe_out: &mut CudaSlice<f32>,
8984        tok: usize,
8985        n_embd: usize,
8986        n_ff_exp: usize,
8987        n_used: usize,
8988    ) -> Result<bool, Box<dyn std::error::Error>> {
8989        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8990        use cudarc::driver::DevicePtr;
8991        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8992        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8993            let mut g = [0u64; 8];
8994            let mut u = [0u64; 8];
8995            let mut d = [0u64; 8];
8996            for (j, &ex) in sel.iter().enumerate() {
8997                let ex = ex as u16;
8998                let (Some(sg), Some(su), Some(sd)) = (
8999                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
9000                    c.resident(BlockId::new(il, PROJ_UP, ex)),
9001                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
9002                ) else {
9003                    return Ok(None);
9004                };
9005                let __s = eng.stream();
9006                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
9007                let (pu, _e1) = c.slot(su).device_ptr(&__s);
9008                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
9009                g[j] = pg as u64;
9010                u[j] = pu as u64;
9011                d[j] = pd as u64;
9012            }
9013            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
9014                for &ex in sel {
9015                    let ex = ex as u16;
9016                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
9017                        c.note_profile_hit(BlockId::new(il, proj, ex));
9018                    }
9019                }
9020            }
9021            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
9022            Ok(Some((g, u, d)))
9023        })?;
9024        let Some((g, u, d)) = ptrs else {
9025            return Ok(false);
9026        };
9027        let mut wv = [0f32; 8];
9028        wv[..n_used].copy_from_slice(w);
9029        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
9030        let act = e.moe_gate_up_silu8(
9031            crate::WPtr8(g),
9032            crate::WPtr8(u),
9033            zt,
9034            n_embd,
9035            n_ff_exp,
9036            n_used,
9037            m.gate_exps.qtype,
9038            m.up_exps.qtype,
9039            m.gate_exps.row_bytes,
9040            m.up_exps.row_bytes,
9041        )?;
9042        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
9043        e.moe_down8_fma_into(
9044            crate::WPtr8(d),
9045            crate::F32x8(wv),
9046            &act,
9047            &mut dst,
9048            n_ff_exp,
9049            n_embd,
9050            n_used,
9051            m.down_exps.qtype,
9052            m.down_exps.row_bytes,
9053        )?;
9054        Ok(true)
9055    }
9056
9057    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
9058    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
9059    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
9060    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
9061    fn moe_cached_gemm_q8(
9062        e: &Engine,
9063        il: u16,
9064        proj: u8,
9065        ex: usize,
9066        m: &MoeWeights,
9067        max_block: usize,
9068        aq: &CudaSlice<i8>,
9069        ad: &CudaSlice<f32>,
9070    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9071        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
9072        let exps = match proj {
9073            PROJ_GATE => &m.gate_exps,
9074            PROJ_UP => &m.up_exps,
9075            _ => &m.down_exps,
9076        };
9077        let layout = exps.expert_layout(ex);
9078        let id = BlockId::new(il, proj, ex as u16);
9079        let source = exps.expert_source(ex);
9080        e.with_moe_cache(max_block, |c, eng| {
9081            let slot = c.dispatch_source(id, source, eng)?;
9082            let DispatchSlot::Resident(sl) = slot;
9083            let buf = c.slot(sl);
9084            eng.qmatvec_expert_q8(
9085                buf,
9086                0..layout.len,
9087                aq,
9088                ad,
9089                1,
9090                exps.in_f,
9091                exps.out_f,
9092                layout.qtype,
9093                layout.row_bytes,
9094            )
9095        })
9096    }
9097
9098    fn moe_cached_gemm(
9099        e: &Engine,
9100        il: u16,
9101        proj: u8,
9102        ex: usize,
9103        m: &MoeWeights,
9104        max_block: usize,
9105        x: &cudarc::driver::CudaView<f32>,
9106    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9107        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
9108        let exps = match proj {
9109            PROJ_GATE => &m.gate_exps,
9110            PROJ_UP => &m.up_exps,
9111            _ => &m.down_exps,
9112        };
9113        let layout = exps.expert_layout(ex);
9114        let id = BlockId::new(il, proj, ex as u16);
9115        let source = exps.expert_source(ex);
9116        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
9117        e.with_moe_cache(max_block, |c, eng| {
9118            let slot = c.dispatch_source(id, source, eng)?;
9119            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
9120            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
9121            let DispatchSlot::Resident(sl) = slot;
9122            let buf = c.slot(sl);
9123            eng.qmatvec_view(
9124                buf,
9125                0..layout.len,
9126                x,
9127                1,
9128                exps.in_f,
9129                exps.out_f,
9130                layout.qtype,
9131                layout.row_bytes,
9132            )
9133        })
9134    }
9135
9136    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
9137    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
9138    /// so the current forward's backend assignment and output remain unchanged.
9139    fn moe_profile_admit_expert(
9140        e: &Engine,
9141        il: u16,
9142        ex: usize,
9143        m: &MoeWeights,
9144        max_block: usize,
9145    ) -> Result<(), Box<dyn std::error::Error>> {
9146        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9147        e.with_moe_cache(max_block, |cache, eng| {
9148            for (proj, exps) in [
9149                (PROJ_GATE, &m.gate_exps),
9150                (PROJ_UP, &m.up_exps),
9151                (PROJ_DOWN, &m.down_exps),
9152            ] {
9153                let id = BlockId::new(il, proj, ex as u16);
9154                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
9155            }
9156            Ok(())
9157        })
9158    }
9159
9160    /// Read a projection from the immutable residency set when present; otherwise use one
9161    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
9162    #[allow(clippy::too_many_arguments)]
9163    fn moe_frozen_gemm(
9164        e: &Engine,
9165        il: u16,
9166        proj: u8,
9167        ex: usize,
9168        m: &MoeWeights,
9169        max_block: usize,
9170        x: &cudarc::driver::CudaView<f32>,
9171        scratch: &mut Option<CudaSlice<u8>>,
9172        scratch_len: usize,
9173    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9174        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
9175        let exps = match proj {
9176            PROJ_GATE => &m.gate_exps,
9177            PROJ_UP => &m.up_exps,
9178            _ => &m.down_exps,
9179        };
9180        let layout = exps.expert_layout(ex);
9181        let id = BlockId::new(il, proj, ex as u16);
9182        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
9183            let Some(slot) = cache.resident(id) else {
9184                return Ok(None);
9185            };
9186            let buf = cache.slot(slot);
9187            Ok(Some(eng.qmatvec_view(
9188                buf,
9189                0..layout.len,
9190                x,
9191                1,
9192                exps.in_f,
9193                exps.out_f,
9194                layout.qtype,
9195                layout.row_bytes,
9196            )?))
9197        })? {
9198            return Ok(output);
9199        }
9200        if scratch.is_none() {
9201            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
9202        }
9203        let scratch = scratch.as_mut().unwrap();
9204        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
9205        e.qmatvec_view(
9206            scratch,
9207            0..layout.len,
9208            x,
9209            1,
9210            exps.in_f,
9211            exps.out_f,
9212            layout.qtype,
9213            layout.row_bytes,
9214        )
9215    }
9216
9217    fn moe_prefetch_expert(
9218        e: &Engine,
9219        il: u16,
9220        ex: usize,
9221        m: &MoeWeights,
9222        max_block: usize,
9223        keep: &[crate::moe_cache::BlockId],
9224    ) -> Result<(), Box<dyn std::error::Error>> {
9225        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9226        e.with_moe_cache(max_block, |c, eng| {
9227            for (proj, exps) in [
9228                (PROJ_GATE, &m.gate_exps),
9229                (PROJ_UP, &m.up_exps),
9230                (PROJ_DOWN, &m.down_exps),
9231            ] {
9232                let id = BlockId::new(il, proj, ex as u16);
9233                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
9234            }
9235            Ok(())
9236        })
9237    }
9238
9239    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
9240    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
9241    fn moe_prefetch_disk_expert(
9242        e: &Engine,
9243        il: u16,
9244        ex: usize,
9245        m: &MoeWeights,
9246        max_block: usize,
9247        keep: &[crate::moe_cache::BlockId],
9248    ) -> Result<(), Box<dyn std::error::Error>> {
9249        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9250        e.with_moe_cache(max_block, |c, eng| {
9251            for (proj, exps) in [
9252                (PROJ_GATE, &m.gate_exps),
9253                (PROJ_UP, &m.up_exps),
9254                (PROJ_DOWN, &m.down_exps),
9255            ] {
9256                let source = exps.expert_source(ex);
9257                if let crate::model::ExpertSource::Disk { .. } = &source {
9258                    let id = BlockId::new(il, proj, ex as u16);
9259                    let _ = c.prefetch_source(id, source, keep, eng)?;
9260                }
9261            }
9262            Ok(())
9263        })
9264    }
9265
9266    #[inline]
9267    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
9268        let _ = m.gate_exps.prefetch_expert_pages(ex);
9269        let _ = m.up_exps.prefetch_expert_pages(ex);
9270        let _ = m.down_exps.prefetch_expert_pages(ex);
9271    }
9272}
9273
9274// ================================================================================================
9275// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
9276//
9277// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
9278// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
9279// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
9280//
9281// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
9282// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
9283// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
9284// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
9285// identical to the per-token loop regardless of expert processing order.
9286//
9287// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
9288// ================================================================================================
9289
9290impl HybridModel {
9291    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
9292    /// sequential fused q8 program over the token axis; clamped layers use the separate
9293    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
9294    #[allow(clippy::too_many_arguments)]
9295    fn moe_ffn_grouped_resident_q8(
9296        e: &Engine,
9297        m: &MoeWeights,
9298        z: &CudaSlice<f32>,
9299        t: usize,
9300        cfg: &ModelConfig,
9301        il: u16,
9302        sel_all: &[u32],
9303        w_all: &[f32],
9304        table: &CudaSlice<u64>,
9305        gu_il: bool,
9306    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9307        let moe = cfg.moe.as_ref().unwrap();
9308        let n_embd = cfg.n_embd as usize;
9309        let n_expert = moe.expert_count as usize;
9310        let n_used = moe.expert_used_count as usize;
9311        let n_ff_exp = moe.expert_ff_length as usize;
9312        let n_pairs = t * n_used;
9313        debug_assert_eq!(sel_all.len(), n_pairs);
9314        debug_assert_eq!(w_all.len(), n_pairs);
9315        debug_assert!(
9316            m.gate_exps.macros.is_none()
9317                && m.up_exps.macros.is_none()
9318                && m.down_exps.macros.is_none(),
9319            "resident grouped q8 does not fold per-expert macro scales",
9320        );
9321
9322        // The rows twins run the resident sequential program verbatim on grid.z = token:
9323        // fused gate/up/SiLU per slot, batched activation quantization, then the original
9324        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
9325        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
9326        // never enter the softmax router.
9327        if !cfg.swiglu_clamped_at(il as u32) {
9328            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9329            let sel_d = e.htod_i32(&sel)?;
9330            let w_d = e.htod(w_all)?;
9331            let (gate_row_bytes, up_row_bytes) = if gu_il {
9332                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9333                (combined, combined)
9334            } else {
9335                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9336            };
9337            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9338            let act = e.moe_gate_up_silu8_dev_q8_rows(
9339                table,
9340                &sel_d,
9341                &zq,
9342                &zd,
9343                t,
9344                n_embd,
9345                n_ff_exp,
9346                n_used,
9347                n_expert,
9348                m.gate_exps.qtype,
9349                m.up_exps.qtype,
9350                gate_row_bytes,
9351                up_row_bytes,
9352                &m.dev_macros,
9353            )?;
9354            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9355            let mut moe_out = e.uninit(t * n_embd)?;
9356            e.moe_down8_fma_dev_q8_rows_g(
9357                table,
9358                &sel_d,
9359                &w_d,
9360                &aq2,
9361                &ad2,
9362                &mut moe_out,
9363                t,
9364                n_ff_exp,
9365                n_embd,
9366                n_used,
9367                n_expert,
9368                m.down_exps.qtype,
9369                m.down_exps.row_bytes,
9370            )?;
9371
9372            if std::env::var("MEMRA_MOE_STATS").is_ok() {
9373                let mut counts = vec![0usize; n_expert];
9374                for &expert in sel_all {
9375                    counts[expert as usize] += 1;
9376                }
9377                let mut sizes: Vec<usize> =
9378                    counts.into_iter().filter(|&count| count != 0).collect();
9379                sizes.sort_unstable();
9380                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9381                println!(
9382                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
9383                     m_e: min={} median={} mean={mean:.1} max={}",
9384                    sizes.len(),
9385                    n_expert,
9386                    sizes.first().copied().unwrap_or(0),
9387                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9388                    sizes.last().copied().unwrap_or(0),
9389                );
9390            }
9391            return Ok(moe_out);
9392        }
9393
9394        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
9395        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
9396        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
9397        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
9398        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9399        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
9400        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
9401
9402        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
9403        for (pair, &expert) in pair_ex.iter().enumerate() {
9404            by_expert[expert as usize].push(pair as i32);
9405        }
9406
9407        let pair_tok_d = e.htod_i32(&pair_tok)?;
9408        let pair_ex_d = e.htod_i32(&pair_ex)?;
9409        let pair_w_d = e.htod(w_all)?;
9410        let tok_off_d = e.htod_i32(&tok_off)?;
9411        let tok_ids_d = e.htod_i32(&tok_ids)?;
9412
9413        let matvec = |proj: i32,
9414                      pair_rows: &CudaSlice<i32>,
9415                      aq: &CudaSlice<i8>,
9416                      ad: &CudaSlice<f32>,
9417                      in_f: usize,
9418                      out_f: usize,
9419                      qtype: i32,
9420                      row_bytes: usize|
9421         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9422            e.moe_pairs_matvec_q8(
9423                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9424                row_bytes,
9425            )
9426        };
9427
9428        let (gate_row_bytes, up_row_bytes) = if gu_il {
9429            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9430            (combined, combined)
9431        } else {
9432            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9433        };
9434        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9435        let gate = matvec(
9436            0,
9437            &pair_tok_d,
9438            &zq,
9439            &zd,
9440            n_embd,
9441            n_ff_exp,
9442            m.gate_exps.qtype,
9443            gate_row_bytes,
9444        )?;
9445        let up = matvec(
9446            1,
9447            &pair_tok_d,
9448            &zq,
9449            &zd,
9450            n_embd,
9451            n_ff_exp,
9452            m.up_exps.qtype,
9453            up_row_bytes,
9454        )?;
9455        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9456        Self::ffn_act_lim(
9457            e,
9458            cfg,
9459            &gate,
9460            &up,
9461            1.0,
9462            1.0,
9463            cfg.clamp_exp_at(il as u32),
9464            &mut act,
9465            n_pairs * n_ff_exp,
9466        )?;
9467        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9468        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9469        let pair_self_d = e.htod_i32(&pair_self)?;
9470        let down = matvec(
9471            2,
9472            &pair_self_d,
9473            &aq2,
9474            &ad2,
9475            n_ff_exp,
9476            n_embd,
9477            m.down_exps.qtype,
9478            m.down_exps.row_bytes,
9479        )?;
9480        let mut moe_out = e.uninit(t * n_embd)?;
9481        e.moe_pairs_scatter(
9482            &down,
9483            &pair_w_d,
9484            &tok_off_d,
9485            &tok_ids_d,
9486            &mut moe_out,
9487            t,
9488            n_embd,
9489        )?;
9490
9491        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9492            let mut sizes: Vec<usize> = by_expert
9493                .iter()
9494                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9495                .collect();
9496            sizes.sort_unstable();
9497            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9498            println!(
9499                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9500                 m_e: min={} median={} mean={mean:.1} max={}",
9501                sizes.len(),
9502                n_expert,
9503                sizes.first().copied().unwrap_or(0),
9504                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9505                sizes.last().copied().unwrap_or(0),
9506            );
9507        }
9508        Ok(moe_out)
9509    }
9510
9511    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9512    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9513    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9514    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9515    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9516    #[allow(clippy::too_many_arguments)]
9517    fn shexp_split_matvec(
9518        e: &Engine,
9519        rank1: &Engine,
9520        wg: &CudaSlice<u8>,
9521        wu: &CudaSlice<u8>,
9522        wd: &CudaSlice<u8>,
9523        z: &CudaSlice<f32>,
9524        lim: Option<f32>,
9525        cfg: &ModelConfig,
9526        il: u16,
9527        n_embd: usize,
9528        n_ff_sh: usize,
9529    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9530        use cudarc::driver::DevicePtr;
9531        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9532            return Ok(None);
9533        }
9534        let hf = n_ff_sh / 2;
9535        let nd = n_embd / 2;
9536        struct Rep {
9537            wg1: CudaSlice<u8>,
9538            wu1: CudaSlice<u8>,
9539            wd1: CudaSlice<u8>,
9540        }
9541        struct SplitWs {
9542            pin_dev: usize,
9543            // e side
9544            gate0: CudaSlice<f32>,
9545            up0: CudaSlice<f32>,
9546            act: CudaSlice<f32>,
9547            sh_buf: CudaSlice<f32>,
9548            ev_z: cudarc::driver::CudaEvent,
9549            ev_act0: cudarc::driver::CudaEvent,
9550            // rank1 side
9551            z1: CudaSlice<f32>,
9552            g1: CudaSlice<f32>,
9553            u1: CudaSlice<f32>,
9554            a1h: CudaSlice<f32>,
9555            act1: CudaSlice<f32>,
9556            y1: CudaSlice<f32>,
9557            ev_act1: cudarc::driver::CudaEvent,
9558            ev_y1: cudarc::driver::CudaEvent,
9559            raw_act_e: u64,
9560            raw_sh_e: u64,
9561            raw_z1: u64,
9562            raw_a1h: u64,
9563            raw_act1: u64,
9564            raw_y1: u64,
9565        }
9566        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9567        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9568            std::sync::Mutex::new(None);
9569        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9570        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9571        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9572        let pins = e.ctx().ordinal();
9573        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9574            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9575                let _m = e.gpu.enter_main()?;
9576                (
9577                    e.htod(&vec![0.0f32; hf])?,
9578                    e.htod(&vec![0.0f32; hf])?,
9579                    e.htod(&vec![0.0f32; n_ff_sh])?,
9580                    e.htod(&vec![0.0f32; n_embd])?,
9581                    e.ctx().new_event(None)?,
9582                    e.ctx().new_event(None)?,
9583                )
9584            };
9585            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9586                let _r = rank1.gpu.enter_main()?;
9587                (
9588                    rank1.htod(&vec![0.0f32; n_embd])?,
9589                    rank1.htod(&vec![0.0f32; hf])?,
9590                    rank1.htod(&vec![0.0f32; hf])?,
9591                    rank1.htod(&vec![0.0f32; hf])?,
9592                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9593                    rank1.htod(&vec![0.0f32; nd])?,
9594                    rank1.ctx().new_event(None)?,
9595                    rank1.ctx().new_event(None)?,
9596                )
9597            };
9598            let (raw_act_e, raw_sh_e) = {
9599                let _m = e.gpu.enter_main()?;
9600                let stream = e.stream();
9601                let (a, _g0) = act.device_ptr(&stream);
9602                let (b, _g1) = sh_buf.device_ptr(&stream);
9603                (a as u64, b as u64)
9604            };
9605            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9606                let _r = rank1.gpu.enter_main()?;
9607                let rs = rank1.stream();
9608                let (a, _g0) = z1.device_ptr(&rs);
9609                let (b, _g1) = a1h.device_ptr(&rs);
9610                let (c, _g2) = act1.device_ptr(&rs);
9611                let (d, _g3) = y1.device_ptr(&rs);
9612                (a as u64, b as u64, c as u64, d as u64)
9613            };
9614            *guard = Some(SplitWs {
9615                pin_dev: pins,
9616                gate0,
9617                up0,
9618                act,
9619                sh_buf,
9620                ev_z,
9621                ev_act0,
9622                z1,
9623                g1,
9624                u1,
9625                a1h,
9626                act1,
9627                y1,
9628                ev_act1,
9629                ev_y1,
9630                raw_act_e,
9631                raw_sh_e,
9632                raw_z1,
9633                raw_a1h,
9634                raw_act1,
9635                raw_y1,
9636            });
9637        }
9638        let ws = guard.as_mut().expect("armed above");
9639        let wg_pin = {
9640            let _m = e.gpu.enter_main()?;
9641            let stream = e.stream();
9642            let (p, _g) = wg.device_ptr(&stream);
9643            p as u64
9644        };
9645        if !reps.contains_key(&wg_pin) {
9646            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9647            let mut up = |src: &CudaSlice<u8>,
9648                          off_bytes: usize,
9649                          len: usize|
9650             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9651                use cudarc::driver::sys;
9652                let sptr = {
9653                    let _m = e.gpu.enter_main()?;
9654                    let stream = e.stream();
9655                    let (p, _g) = src.device_ptr(&stream);
9656                    p as u64 + off_bytes as u64
9657                };
9658                let dst = {
9659                    let _r = rank1.gpu.enter_main()?;
9660                    rank1.alloc_u8_uninit(len)?
9661                };
9662                let dptr = {
9663                    let _r = rank1.gpu.enter_main()?;
9664                    let rs = rank1.stream();
9665                    let (p, _g) = dst.device_ptr(&rs);
9666                    p as u64
9667                };
9668                let _r = rank1.gpu.enter_main()?;
9669                let r = unsafe {
9670                    sys::cuMemcpyAsync(
9671                        dptr as sys::CUdeviceptr,
9672                        sptr as sys::CUdeviceptr,
9673                        len,
9674                        rank1.stream().cu_stream() as sys::CUstream,
9675                    )
9676                };
9677                if r != sys::CUresult::CUDA_SUCCESS {
9678                    return Err(format!("shexp split replica upload: {r:?}").into());
9679                }
9680                rank1.stream().synchronize()?;
9681                Ok(dst)
9682            };
9683            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9684            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9685            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9686            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9687        }
9688        let _ = il;
9689        // Per token, evented split flow.
9690        let raw_z = {
9691            let _m = e.gpu.enter_main()?;
9692            let stream = e.stream();
9693            let (p, _g) = z.device_ptr(&stream);
9694            ws.ev_z.record(&stream)?;
9695            p as u64
9696        };
9697        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9698        {
9699            let rep = reps.get(&wg_pin).expect("uploaded above");
9700            let _r = rank1.gpu.enter_main()?;
9701            rank1.stream().wait(&ws.ev_z)?;
9702            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9703            let SplitWs {
9704                z1, g1, u1, a1h, ..
9705            } = &mut *ws;
9706            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9707            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9708            // local place into act1[hf..] + P2P push into e's act[hf..]
9709            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9710            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9711            ws.ev_act1.record(&rank1.stream())?;
9712        }
9713        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9714        {
9715            let _m = e.gpu.enter_main()?;
9716            let SplitWs {
9717                gate0, up0, act, ..
9718            } = &mut *ws;
9719            let wg_lo = wg.slice(0..hf * n_embd * 2);
9720            let wu_lo = wu.slice(0..hf * n_embd * 2);
9721            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9722            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9723            ws.ev_act0.record(&e.stream())?;
9724        }
9725        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9726        {
9727            let rep = reps.get(&wg_pin).expect("uploaded above");
9728            let _r = rank1.gpu.enter_main()?;
9729            rank1.stream().wait(&ws.ev_act0)?;
9730            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9731            let SplitWs { act1, y1, .. } = &mut *ws;
9732            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9733            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9734            ws.ev_y1.record(&rank1.stream())?;
9735        }
9736        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9737        {
9738            let _m = e.gpu.enter_main()?;
9739            e.stream().wait(&ws.ev_act1)?;
9740            let SplitWs { act, sh_buf, .. } = &mut *ws;
9741            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9742            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9743            e.stream().wait(&ws.ev_y1)?;
9744            let mut sh = e.uninit(n_embd)?;
9745            {
9746                let mut dst = sh.slice_mut(0..n_embd);
9747                e.stream()
9748                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9749            }
9750            Ok(Some(sh))
9751        }
9752    }
9753
9754    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9755    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9756    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9757    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9758    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9759    /// the join with the exact add_scaled_rows expression: values unchanged.
9760    fn shexp_overlap_issue(
9761        e: &Engine,
9762        m: &MoeWeights,
9763        z: &CudaSlice<f32>,
9764        cfg: &ModelConfig,
9765        il: u16,
9766        n_embd: usize,
9767    ) -> Result<bool, Box<dyn std::error::Error>> {
9768        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9769            return Ok(false);
9770        }
9771        let (
9772            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9773            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9774            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9775        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9776        else {
9777            return Ok(false);
9778        };
9779        let n_ff_sh = m
9780            .gate_shexp
9781            .as_ref()
9782            .expect("matched Some above")
9783            .out_features();
9784        let lim = cfg.clamp_shexp_at(il as u32);
9785        let mut guard = SHEXP_OV_WS
9786            .lock()
9787            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9788        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9789        if guard
9790            .as_ref()
9791            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9792        {
9793            *guard = Some((
9794                pins.0,
9795                pins.1,
9796                pins.2,
9797                e.uninit(n_ff_sh)?,
9798                e.uninit(n_embd)?,
9799            ));
9800        }
9801        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9802        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9803        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9804        drop(guard);
9805        Ok(true)
9806    }
9807
9808    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9809    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9810    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9811    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9812    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9813    #[allow(clippy::too_many_arguments)]
9814    fn shexp_dev1_issue(
9815        e: &Engine,
9816        rank1: &Engine,
9817        m: &MoeWeights,
9818        z: &CudaSlice<f32>,
9819        cfg: &ModelConfig,
9820        il: u16,
9821        n_embd: usize,
9822    ) -> Result<bool, Box<dyn std::error::Error>> {
9823        use cudarc::driver::DevicePtr;
9824        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9825            return Ok(false);
9826        }
9827        let (
9828            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9829            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9830            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9831        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9832        else {
9833            return Ok(false);
9834        };
9835        let n_ff_sh = m
9836            .gate_shexp
9837            .as_ref()
9838            .expect("matched Some above")
9839            .out_features();
9840        let lim = cfg.clamp_shexp_at(il as u32);
9841        // Shared scratch, geometry-keyed.
9842        let mut ws_guard = SHEXP_D1_WS
9843            .lock()
9844            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9845        if ws_guard
9846            .as_ref()
9847            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9848        {
9849            let (act1, z1, ev_done) = {
9850                let _r1 = rank1.gpu.enter_main()?;
9851                (
9852                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9853                    rank1.htod(&vec![0.0f32; n_embd])?,
9854                    rank1.ctx().new_event(None)?,
9855                )
9856            };
9857            let (sh_root, ev_z) = {
9858                let _main = e.gpu.enter_main()?;
9859                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9860            };
9861            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9862        }
9863        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9864        let mut reps_guard = SHEXP_D1_REPS
9865            .lock()
9866            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9867        let reps = reps_guard.get_or_insert_with(Default::default);
9868        if !reps.contains_key(&il) {
9869            let (wg1, wu1, wd1) = {
9870                let _r1 = rank1.gpu.enter_main()?;
9871                (
9872                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9873                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9874                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9875                )
9876            };
9877            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9878                let s_ptr = {
9879                    let _main = e.gpu.enter_main()?;
9880                    let stream = e.stream();
9881                    let (p, _g) = src.device_ptr(&stream);
9882                    p as u64
9883                };
9884                let d_ptr = {
9885                    let _r1 = rank1.gpu.enter_main()?;
9886                    let stream = rank1.stream();
9887                    let (p, _g) = dst.device_ptr(&stream);
9888                    p as u64
9889                };
9890                let _r1 = rank1.gpu.enter_main()?;
9891                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9892            }
9893            {
9894                let _r1 = rank1.gpu.enter_main()?;
9895                rank1.stream().synchronize()?;
9896            }
9897            reps.insert(il, (wg1, wu1, wd1));
9898        }
9899        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9900        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9901        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9902        // row root-side (single store pass), rings ev_done.
9903        let (raw_z, raw_sh) = {
9904            let _main = e.gpu.enter_main()?;
9905            let stream = e.stream();
9906            let (a, _g0) = z.device_ptr(&stream);
9907            let (b, _g1) = sh_root.device_ptr(&stream);
9908            ev_z.record(&stream)?;
9909            (a as u64, b as u64)
9910        };
9911        {
9912            let _r1 = rank1.gpu.enter_main()?;
9913            rank1.stream().wait(ev_z)?;
9914            let raw_z1 = {
9915                let stream = rank1.stream();
9916                let (p, _g) = z1.device_ptr(&stream);
9917                p as u64
9918            };
9919            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9920            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9921            // down writes the ROOT-resident row over P2P via the raw-output twin of
9922            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9923            // cross-device, so launch on the raw pointer.
9924            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9925            ev_done.record(&rank1.stream())?;
9926        }
9927        Ok(true)
9928    }
9929
9930    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9931    fn shexp_dev1_apply(
9932        e: &Engine,
9933        output: &mut CudaSlice<f32>,
9934        n_embd: usize,
9935    ) -> Result<(), Box<dyn std::error::Error>> {
9936        let guard = SHEXP_D1_WS
9937            .lock()
9938            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9939        let (pin, _, _, sh_root, _, ev_done) =
9940            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9941        if pin.0 != n_embd {
9942            return Err("shexp dev1 width drifted".into());
9943        }
9944        let _main = e.gpu.enter_main()?;
9945        e.stream().wait(ev_done)?;
9946        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9947            std::sync::Mutex::new(None);
9948        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9949        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9950            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9951        }
9952        let ones = &og.as_ref().expect("armed above").1;
9953        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9954        Ok(())
9955    }
9956
9957    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9958    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9959    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9960    fn shexp_overlap_tail_ptrs(
9961        e: &Engine,
9962        m: &MoeWeights,
9963        cfg: &ModelConfig,
9964        n_embd: usize,
9965    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9966        use cudarc::driver::DevicePtr;
9967        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9968            return Ok(None);
9969        }
9970        let (
9971            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9972            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9973            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9974        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9975        else {
9976            return Ok(None);
9977        };
9978        let n_ff_sh = m
9979            .gate_shexp
9980            .as_ref()
9981            .expect("matched Some above")
9982            .out_features();
9983        let mut guard = SHEXP_OV_WS
9984            .lock()
9985            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9986        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9987        if guard
9988            .as_ref()
9989            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9990        {
9991            *guard = Some((
9992                pins.0,
9993                pins.1,
9994                pins.2,
9995                e.uninit(n_ff_sh)?,
9996                e.uninit(n_embd)?,
9997            ));
9998        }
9999        let sh_raw = {
10000            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
10001            let stream = e.stream();
10002            let (p, _g) = sh.device_ptr(&stream);
10003            p as u64
10004        };
10005        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10006            std::sync::Mutex::new(None);
10007        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
10008        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10009            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10010        }
10011        let ones_raw = {
10012            let stream = e.stream();
10013            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
10014            p as u64
10015        };
10016        Ok(Some((sh_raw, ones_raw)))
10017    }
10018
10019    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
10020    /// add_scaled_rows program the split path used (persistent ones row, no htod).
10021    fn shexp_overlap_apply(
10022        e: &Engine,
10023        output: &mut CudaSlice<f32>,
10024        n_embd: usize,
10025    ) -> Result<(), Box<dyn std::error::Error>> {
10026        let guard = SHEXP_OV_WS
10027            .lock()
10028            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
10029        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
10030        if *ne != n_embd {
10031            return Err("shexp overlap width drifted".into());
10032        }
10033        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10034            std::sync::Mutex::new(None);
10035        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
10036        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10037            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10038        }
10039        let ones = &og.as_ref().expect("armed above").1;
10040        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
10041        Ok(())
10042    }
10043
10044    fn moe_ffn_grouped_add_shared(
10045        e: &Engine,
10046        m: &MoeWeights,
10047        z: &CudaSlice<f32>,
10048        t: usize,
10049        cfg: &ModelConfig,
10050        il: u16,
10051        moe_out: &mut CudaSlice<f32>,
10052    ) -> Result<(), Box<dyn std::error::Error>> {
10053        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
10054        // queued matmuls here rather than at the next host readback).
10055        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10056        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10057        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10058        let shexp_started = shexp_timing.then(std::time::Instant::now);
10059        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
10060        if let Some(started) = shexp_started {
10061            use std::sync::atomic::Ordering;
10062            e.stream().synchronize()?;
10063            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10064                + started.elapsed().as_nanos() as u64;
10065            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10066            if calls % 430 == 0 {
10067                eprintln!(
10068                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10069                    ns as f64 / 1.0e6,
10070                    ns as f64 / calls as f64 / 1.0e3,
10071                );
10072            }
10073        }
10074        result
10075    }
10076
10077    #[allow(clippy::too_many_arguments)]
10078    fn moe_ffn_grouped_add_shared_inner(
10079        e: &Engine,
10080        m: &MoeWeights,
10081        z: &CudaSlice<f32>,
10082        t: usize,
10083        cfg: &ModelConfig,
10084        il: u16,
10085        moe_out: &mut CudaSlice<f32>,
10086    ) -> Result<(), Box<dyn std::error::Error>> {
10087        let n_embd = cfg.n_embd as usize;
10088        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10089            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10090        {
10091            let n_ff_sh = gate_shexp.out_features();
10092            let lim = cfg.clamp_shexp_at(il as u32);
10093            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
10094            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
10095            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
10096            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
10097            // operand pre-quantized (kernel_check-proven identities). This path measured
10098            // 167us/layer as separate matmuls + 5 allocs at decode.
10099            let fused = t == 1
10100                && lim.is_none()
10101                && cfg.m3.is_none()
10102                && e.uses_q8_1_fast(gate_shexp)
10103                && e.uses_q8_1_fast(up_shexp);
10104            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
10105            // the two matvec_bf16 launches matmul would issue).
10106            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
10107                match (gate_shexp, up_shexp) {
10108                    (
10109                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
10110                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
10111                    ) => Some((wg, wu)),
10112                    _ => None,
10113                }
10114            } else {
10115                None
10116            };
10117            let sh = if let Some((wg, wu)) = bf16_dual {
10118                // Persistent shared-expert workspace: sizes are constant across every MoE
10119                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
10120                // the four per-layer allocations. Buffers are fully overwritten each call.
10121                static SHEXP_WS: std::sync::Mutex<
10122                    Option<(
10123                        usize,
10124                        usize,
10125                        usize,
10126                        CudaSlice<f32>,
10127                        CudaSlice<f32>,
10128                        CudaSlice<f32>,
10129                        CudaSlice<f32>,
10130                    )>,
10131                > = std::sync::Mutex::new(None);
10132                let down_bf16 = match down_shexp {
10133                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
10134                    _ => None,
10135                };
10136                let mut guard = SHEXP_WS
10137                    .lock()
10138                    .map_err(|_| "shexp workspace lock is poisoned")?;
10139                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
10140                if guard
10141                    .as_ref()
10142                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
10143                {
10144                    *guard = Some((
10145                        pins.0,
10146                        pins.1,
10147                        pins.2,
10148                        e.uninit(n_ff_sh)?,
10149                        e.uninit(n_ff_sh)?,
10150                        e.uninit(n_ff_sh)?,
10151                        e.uninit(n_embd)?,
10152                    ));
10153                }
10154                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
10155                // through to the single-device arm when ineligible.
10156                {
10157                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10158                    let split_on = *ON
10159                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
10160                    if split_on {
10161                        if let (Some(wd), Some(rank1)) = (
10162                            match down_shexp {
10163                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
10164                                _ => None,
10165                            },
10166                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
10167                        ) {
10168                            if let Some(sh) = Self::shexp_split_matvec(
10169                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
10170                            )? {
10171                                drop(guard);
10172                                let gate = match &m.gate_inp_shexp {
10173                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
10174                                        z,
10175                                        gate_inp_shexp.float_data(),
10176                                        n_embd,
10177                                        t,
10178                                    )?,
10179                                    None => e.htod(&vec![1.0f32; t])?,
10180                                };
10181                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
10182                                return Ok(());
10183                            }
10184                        }
10185                    }
10186                }
10187                let (_, _, _, gate, up, act, sh_buf) =
10188                    guard.as_mut().expect("shexp workspace initialized above");
10189                if cfg.m3.is_none() {
10190                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
10191                    // per-row program + exact silu/clamped expression, bit-identical.
10192                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
10193                    let _ = (&gate, &up);
10194                } else {
10195                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
10196                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
10197                }
10198                if let Some(down) = down_bf16 {
10199                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
10200                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
10201                    // exact f32acc per-row program + the exact add_scaled_rows expression
10202                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
10203                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
10204                    // accumulate consumes the same f32 the split path stored and reloaded.
10205                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10206                    let fuse_da = *FUSE_DA.get_or_init(|| {
10207                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
10208                    });
10209                    if fuse_da && m.gate_inp_shexp.is_none() {
10210                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10211                            std::sync::Mutex::new(None);
10212                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
10213                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10214                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10215                        }
10216                        let ones = &og.as_ref().expect("armed above").1;
10217                        e.matvec_bf16_down_addscale_into(
10218                            down, act, ones, moe_out, n_ff_sh, n_embd,
10219                        )?;
10220                        return Ok(());
10221                    }
10222                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
10223                    let sh = e.uninit(n_embd)?;
10224                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
10225                    let mut sh = sh;
10226                    {
10227                        let mut dst = sh.slice_mut(0..n_embd);
10228                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
10229                    }
10230                    sh
10231                } else {
10232                    e.matmul(down_shexp, act, 1)?
10233                }
10234            } else if fused {
10235                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
10236                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
10237                    Some((gate, up)) => Some((gate, up)),
10238                    None => {
10239                        match (
10240                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
10241                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
10242                        ) {
10243                            (Some(gate), Some(up)) => Some((gate, up)),
10244                            _ => None,
10245                        }
10246                    }
10247                };
10248                match pair {
10249                    Some(((gate, gs), (up, us))) => {
10250                        if e.uses_q8_1_fast(down_shexp) {
10251                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
10252                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
10253                        } else {
10254                            let mut act = e.uninit(n_ff_sh)?;
10255                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
10256                            e.matmul(down_shexp, &act, 1)?
10257                        }
10258                    }
10259                    None => {
10260                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
10261                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
10262                        let mut act = e.uninit(n_ff_sh)?;
10263                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
10264                        e.matmul(down_shexp, &act, 1)?
10265                    }
10266                }
10267            } else {
10268                let sg_gate = e.matmul(gate_shexp, z, t)?;
10269                let sg_up = e.matmul(up_shexp, z, t)?;
10270                let mut sa = e.uninit(t * n_ff_sh)?;
10271                Self::ffn_act_lim(
10272                    e,
10273                    cfg,
10274                    &sg_gate,
10275                    &sg_up,
10276                    1.0,
10277                    1.0,
10278                    lim,
10279                    &mut sa,
10280                    t * n_ff_sh,
10281                )?;
10282                e.matmul(down_shexp, &sa, t)?
10283            };
10284            let gate = match &m.gate_inp_shexp {
10285                Some(gate_inp_shexp) => {
10286                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
10287                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
10288                    } else {
10289                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
10290                        let mut gate = e.uninit(t)?;
10291                        e.sigmoid(&raw, &mut gate, t)?;
10292                        gate
10293                    }
10294                }
10295                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
10296                // synchronizes the stream — measured as the biggest per-layer host gap
10297                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
10298                // device serves every layer; larger t (prefill) keeps the plain htod.
10299                None if t == 1 => {
10300                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10301                        std::sync::Mutex::new(None);
10302                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
10303                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10304                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10305                    }
10306                    let ones = &guard.as_ref().expect("armed above").1;
10307                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
10308                    return Ok(());
10309                }
10310                None => e.htod(&vec![1.0f32; t])?,
10311            };
10312            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
10313        }
10314        Ok(())
10315    }
10316
10317    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
10318    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
10319    pub(crate) fn moe_ffn_grouped(
10320        e: &Engine,
10321        m: &MoeWeights,
10322        z: &CudaSlice<f32>,
10323        t: usize,
10324        cfg: &ModelConfig,
10325        il: u16,
10326        max_block: usize,
10327    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10328        let moe = cfg.moe.as_ref().unwrap();
10329        let n_embd = cfg.n_embd as usize;
10330        let n_expert = moe.expert_count as usize;
10331        let n_used = moe.expert_used_count as usize;
10332        let n_ff_exp = moe.expert_ff_length as usize;
10333        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10334        let lim_exp = cfg.clamp_exp_at(il as u32);
10335
10336        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
10337        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
10338        // enters the softmax-only pairs/dev router.
10339        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
10340        if let Some(sig) = cfg.sigmoid_router() {
10341            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
10342        }
10343        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10344            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
10345        } else {
10346            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
10347        };
10348        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
10349        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
10350        Self::trace_moe_input(e, il, t, n_embd, z)?;
10351
10352        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
10353        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
10354        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
10355        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
10356        let no_exp_macros = m.gate_exps.macros.is_none()
10357            && m.up_exps.macros.is_none()
10358            && m.down_exps.macros.is_none();
10359        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
10360            m.has_uniform_expert_layout()
10361                && no_exp_macros
10362                && moe_q8_enabled()
10363                && q8_expert_supported(m.gate_exps.qtype)
10364                && q8_expert_supported(m.up_exps.qtype)
10365                && q8_expert_supported(m.down_exps.qtype)
10366                && moe_slab_enabled()
10367                && dev.dev == e.ctx().ordinal()
10368        });
10369        if let Some(dev) = resident_q8 {
10370            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
10371                e,
10372                m,
10373                z,
10374                t,
10375                cfg,
10376                il,
10377                &sel_all,
10378                &w_all,
10379                &dev.ptr_row,
10380                dev.gu_il,
10381            )?;
10382            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10383            return Ok(moe_out);
10384        }
10385
10386        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
10387        // For each expert e, we need: which tokens use it, their positions in z, their top-k
10388        // slot index (for bit-identical accumulation), and their weights.
10389        struct ExpertGroup {
10390            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
10391            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
10392            weights: Vec<f32>,      // renormalized weight for that token-expert pair
10393        }
10394        let mut groups: Vec<ExpertGroup> = (0..n_expert)
10395            .map(|_| ExpertGroup {
10396                tok_indices: Vec::new(),
10397                slot_indices: Vec::new(),
10398                weights: Vec::new(),
10399            })
10400            .collect();
10401
10402        for tok in 0..t {
10403            for j in 0..n_used {
10404                let ex = sel_all[tok * n_used + j] as usize;
10405                let w = w_all[tok * n_used + j];
10406                groups[ex].tok_indices.push(tok as i32);
10407                groups[ex].slot_indices.push(j as i32);
10408                groups[ex].weights.push(w);
10409            }
10410        }
10411
10412        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
10413        // Each token's 8 expert contributions land in their respective slots.
10414        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
10415        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
10416
10417        // Expert weight dimensions (used in both cache and staging paths).
10418        let g_len = m.gate_exps.max_expert_bytes();
10419        let u_len = m.up_exps.max_expert_bytes();
10420        let d_len = m.down_exps.max_expert_bytes();
10421        let moe_q8 = m.has_uniform_expert_layout()
10422            && moe_q8_enabled()
10423            && q8_expert_supported(m.gate_exps.qtype)
10424            && q8_expert_supported(m.up_exps.qtype)
10425            && q8_expert_supported(m.down_exps.qtype);
10426        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10427        // Interleaved GU slabs require the pointer-table fast path above.
10428        let slab_local = m
10429            .dev_exps
10430            .as_ref()
10431            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10432        let use_cache =
10433            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10434        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10435        // also does: a local resident slab or a live SLRU dispatch.
10436        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10437
10438        // GPU scratch for staging (only allocated without a local slab or cache).
10439        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10440            (
10441                Some(e.alloc_u8(g_len)?),
10442                Some(e.alloc_u8(u_len)?),
10443                Some(e.alloc_u8(d_len)?),
10444            )
10445        } else {
10446            (None, None, None)
10447        };
10448
10449        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10450        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10451        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10452        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10453        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10454        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10455        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10456        // at long prompts where every expert stages regardless. Order is FREE to change without
10457        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10458        // regardless of expert processing order (the whole point of the slots).
10459        let mut order: Vec<usize> = (0..n_expert)
10460            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10461            .collect();
10462        order.sort_by(|&a, &b| {
10463            groups[b]
10464                .tok_indices
10465                .len()
10466                .cmp(&groups[a].tok_indices.len())
10467                .then(a.cmp(&b))
10468        });
10469        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10470        let page_window = moe_page_prefetch_window();
10471        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10472        if worker_disk_prefetch {
10473            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10474                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10475            }
10476        }
10477        for (order_pos, &ex) in order.iter().enumerate() {
10478            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10479                Self::moe_prefetch_host_expert(order[next], m);
10480            }
10481            if worker_disk_prefetch {
10482                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10483                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10484                    let keep = [
10485                        BlockId::new(il, PROJ_GATE, ex as u16),
10486                        BlockId::new(il, PROJ_UP, ex as u16),
10487                        BlockId::new(il, PROJ_DOWN, ex as u16),
10488                    ];
10489                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10490                }
10491            }
10492            let grp = &groups[ex];
10493            let m_e = grp.tok_indices.len();
10494            m_dist.push(m_e);
10495            let gl = m.gate_exps.expert_layout(ex);
10496            let ul = m.up_exps.expert_layout(ex);
10497            let dl = m.down_exps.expert_layout(ex);
10498
10499            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10500            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10501            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10502            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10503            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10504            let dmac = m.down_exps.macro_scale(ex);
10505            let weight_d = if dmac == 1.0 {
10506                e.htod(&grp.weights)?
10507            } else {
10508                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10509                e.htod(&scaled)?
10510            };
10511
10512            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10513            let mut gathered = e.zeros(m_e * n_embd)?;
10514            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10515            let gv = gathered.slice(0..m_e * n_embd);
10516
10517            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10518            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10519            let y = if let Some(dev) = slab_local {
10520                let gate_start = ex * m.gate_exps.expert_stride;
10521                let up_start = ex * m.up_exps.expert_stride;
10522                let down_start = ex * m.down_exps.expert_stride;
10523                if grouped_q8 {
10524                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10525                    let gate = e.qmatvec_expert_q8(
10526                        &dev.gate,
10527                        gate_start..gate_start + gl.len,
10528                        &zq,
10529                        &zd,
10530                        m_e,
10531                        m.gate_exps.in_f,
10532                        m.gate_exps.out_f,
10533                        gl.qtype,
10534                        gl.row_bytes,
10535                    )?;
10536                    let up = e.qmatvec_expert_q8(
10537                        &dev.up,
10538                        up_start..up_start + ul.len,
10539                        &zq,
10540                        &zd,
10541                        m_e,
10542                        m.up_exps.in_f,
10543                        m.up_exps.out_f,
10544                        ul.qtype,
10545                        ul.row_bytes,
10546                    )?;
10547                    let mut act = e.uninit(m_e * n_ff_exp)?;
10548                    Self::ffn_act_lim(
10549                        e,
10550                        cfg,
10551                        &gate,
10552                        &up,
10553                        m.gate_exps.macro_scale(ex),
10554                        m.up_exps.macro_scale(ex),
10555                        lim_exp,
10556                        &mut act,
10557                        m_e * n_ff_exp,
10558                    )?;
10559                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10560                    e.qmatvec_expert_q8(
10561                        &dev.down,
10562                        down_start..down_start + dl.len,
10563                        &aq2,
10564                        &ad2,
10565                        m_e,
10566                        m.down_exps.in_f,
10567                        m.down_exps.out_f,
10568                        dl.qtype,
10569                        dl.row_bytes,
10570                    )?
10571                } else {
10572                    let gate = e.qmatvec_view(
10573                        &dev.gate,
10574                        gate_start..gate_start + gl.len,
10575                        &gv,
10576                        m_e,
10577                        m.gate_exps.in_f,
10578                        m.gate_exps.out_f,
10579                        gl.qtype,
10580                        gl.row_bytes,
10581                    )?;
10582                    let up = e.qmatvec_view(
10583                        &dev.up,
10584                        up_start..up_start + ul.len,
10585                        &gv,
10586                        m_e,
10587                        m.up_exps.in_f,
10588                        m.up_exps.out_f,
10589                        ul.qtype,
10590                        ul.row_bytes,
10591                    )?;
10592                    let mut act = e.uninit(m_e * n_ff_exp)?;
10593                    Self::ffn_act_lim(
10594                        e,
10595                        cfg,
10596                        &gate,
10597                        &up,
10598                        m.gate_exps.macro_scale(ex),
10599                        m.up_exps.macro_scale(ex),
10600                        lim_exp,
10601                        &mut act,
10602                        m_e * n_ff_exp,
10603                    )?;
10604                    let actv = act.slice(0..m_e * n_ff_exp);
10605                    e.qmatvec_view(
10606                        &dev.down,
10607                        down_start..down_start + dl.len,
10608                        &actv,
10609                        m_e,
10610                        m.down_exps.in_f,
10611                        m.down_exps.out_f,
10612                        dl.qtype,
10613                        dl.row_bytes,
10614                    )?
10615                }
10616            } else if use_cache {
10617                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10618                if grouped_q8 {
10619                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10620                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10621                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10622                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10623                        eng.qmatvec_expert_q8(
10624                            cache.buf(slot),
10625                            0..gl.len,
10626                            &zq,
10627                            &zd,
10628                            m_e,
10629                            m.gate_exps.in_f,
10630                            m.gate_exps.out_f,
10631                            gl.qtype,
10632                            gl.row_bytes,
10633                        )
10634                    })?;
10635                    let up = e.with_moe_cache(max_block, |cache, eng| {
10636                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10637                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10638                        eng.qmatvec_expert_q8(
10639                            cache.buf(slot),
10640                            0..ul.len,
10641                            &zq,
10642                            &zd,
10643                            m_e,
10644                            m.up_exps.in_f,
10645                            m.up_exps.out_f,
10646                            ul.qtype,
10647                            ul.row_bytes,
10648                        )
10649                    })?;
10650                    let mut act = e.uninit(m_e * n_ff_exp)?;
10651                    Self::ffn_act_lim(
10652                        e,
10653                        cfg,
10654                        &gate,
10655                        &up,
10656                        m.gate_exps.macro_scale(ex),
10657                        m.up_exps.macro_scale(ex),
10658                        lim_exp,
10659                        &mut act,
10660                        m_e * n_ff_exp,
10661                    )?;
10662                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10663                    e.with_moe_cache(max_block, |cache, eng| {
10664                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10665                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10666                        eng.qmatvec_expert_q8(
10667                            cache.buf(slot),
10668                            0..dl.len,
10669                            &aq2,
10670                            &ad2,
10671                            m_e,
10672                            m.down_exps.in_f,
10673                            m.down_exps.out_f,
10674                            dl.qtype,
10675                            dl.row_bytes,
10676                        )
10677                    })?
10678                } else {
10679                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10680                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10681                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10682                        eng.qmatvec_view(
10683                            cache.buf(slot),
10684                            0..gl.len,
10685                            &gv,
10686                            m_e,
10687                            m.gate_exps.in_f,
10688                            m.gate_exps.out_f,
10689                            gl.qtype,
10690                            gl.row_bytes,
10691                        )
10692                    })?;
10693                    let up = e.with_moe_cache(max_block, |cache, eng| {
10694                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10695                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10696                        eng.qmatvec_view(
10697                            cache.buf(slot),
10698                            0..ul.len,
10699                            &gv,
10700                            m_e,
10701                            m.up_exps.in_f,
10702                            m.up_exps.out_f,
10703                            ul.qtype,
10704                            ul.row_bytes,
10705                        )
10706                    })?;
10707                    let mut act = e.uninit(m_e * n_ff_exp)?;
10708                    Self::ffn_act_lim(
10709                        e,
10710                        cfg,
10711                        &gate,
10712                        &up,
10713                        m.gate_exps.macro_scale(ex),
10714                        m.up_exps.macro_scale(ex),
10715                        lim_exp,
10716                        &mut act,
10717                        m_e * n_ff_exp,
10718                    )?;
10719                    let actv = act.slice(0..m_e * n_ff_exp);
10720                    e.with_moe_cache(max_block, |cache, eng| {
10721                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10722                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10723                        eng.qmatvec_view(
10724                            cache.buf(slot),
10725                            0..dl.len,
10726                            &actv,
10727                            m_e,
10728                            m.down_exps.in_f,
10729                            m.down_exps.out_f,
10730                            dl.qtype,
10731                            dl.row_bytes,
10732                        )
10733                    })?
10734                }
10735            } else {
10736                let sg = scratch_g.as_mut().unwrap();
10737                let su = scratch_u.as_mut().unwrap();
10738                let sd = scratch_d.as_mut().unwrap();
10739                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10740                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10741                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10742                if grouped_q8 {
10743                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10744                    let gate = e.qmatvec_expert_q8(
10745                        sg,
10746                        0..gl.len,
10747                        &zq,
10748                        &zd,
10749                        m_e,
10750                        m.gate_exps.in_f,
10751                        m.gate_exps.out_f,
10752                        gl.qtype,
10753                        gl.row_bytes,
10754                    )?;
10755                    let up = e.qmatvec_expert_q8(
10756                        su,
10757                        0..ul.len,
10758                        &zq,
10759                        &zd,
10760                        m_e,
10761                        m.up_exps.in_f,
10762                        m.up_exps.out_f,
10763                        ul.qtype,
10764                        ul.row_bytes,
10765                    )?;
10766                    let mut act = e.uninit(m_e * n_ff_exp)?;
10767                    Self::ffn_act_lim(
10768                        e,
10769                        cfg,
10770                        &gate,
10771                        &up,
10772                        m.gate_exps.macro_scale(ex),
10773                        m.up_exps.macro_scale(ex),
10774                        lim_exp,
10775                        &mut act,
10776                        m_e * n_ff_exp,
10777                    )?;
10778                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10779                    e.qmatvec_expert_q8(
10780                        sd,
10781                        0..dl.len,
10782                        &aq2,
10783                        &ad2,
10784                        m_e,
10785                        m.down_exps.in_f,
10786                        m.down_exps.out_f,
10787                        dl.qtype,
10788                        dl.row_bytes,
10789                    )?
10790                } else {
10791                    let gate = e.qmatvec_view(
10792                        sg,
10793                        0..gl.len,
10794                        &gv,
10795                        m_e,
10796                        m.gate_exps.in_f,
10797                        m.gate_exps.out_f,
10798                        gl.qtype,
10799                        gl.row_bytes,
10800                    )?;
10801                    let up = e.qmatvec_view(
10802                        su,
10803                        0..ul.len,
10804                        &gv,
10805                        m_e,
10806                        m.up_exps.in_f,
10807                        m.up_exps.out_f,
10808                        ul.qtype,
10809                        ul.row_bytes,
10810                    )?;
10811                    let mut act = e.uninit(m_e * n_ff_exp)?;
10812                    Self::ffn_act_lim(
10813                        e,
10814                        cfg,
10815                        &gate,
10816                        &up,
10817                        m.gate_exps.macro_scale(ex),
10818                        m.up_exps.macro_scale(ex),
10819                        lim_exp,
10820                        &mut act,
10821                        m_e * n_ff_exp,
10822                    )?;
10823                    let actv = act.slice(0..m_e * n_ff_exp);
10824                    e.qmatvec_view(
10825                        sd,
10826                        0..dl.len,
10827                        &actv,
10828                        m_e,
10829                        m.down_exps.in_f,
10830                        m.down_exps.out_f,
10831                        dl.qtype,
10832                        dl.row_bytes,
10833                    )?
10834                }
10835            };
10836
10837            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10838            e.scatter_slot(
10839                &y,
10840                &tok_idx_d,
10841                &slot_idx_d,
10842                &weight_d,
10843                &mut slot_buf,
10844                &mut wbuf,
10845                n_embd,
10846                n_used,
10847                m_e,
10848            )?;
10849        }
10850
10851        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10852        let mut moe_out = e.zeros(t * n_embd)?;
10853        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10854
10855        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10856        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10857            m_dist.sort_unstable();
10858            let active = m_dist.len();
10859            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10860            let median = m_dist[active / 2];
10861            let max_m = *m_dist.last().unwrap();
10862            let min_m = m_dist[0];
10863            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10864            println!(
10865                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10866                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10867                      above_gemm_threshold(>=16)={above16}/{active}"
10868            );
10869        }
10870
10871        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10872        Ok(moe_out)
10873    }
10874
10875    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10876    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10877    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10878    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10879    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10880    /// expert-sum order identical to the sequential path.
10881    pub(crate) fn moe_ffn_lockstep(
10882        &self,
10883        e: &Engine,
10884        m: &MoeWeights,
10885        zbatch: &CudaSlice<f32>,
10886        mrows: usize,
10887        il: u16,
10888        max_block: usize,
10889    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10890        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10891        let cfg = &self.cfg;
10892        let moe = cfg.moe.as_ref().unwrap();
10893        let n_embd = cfg.n_embd as usize;
10894        let n_expert = moe.expert_count as usize;
10895        let n_used = moe.expert_used_count as usize;
10896        let n_ff_exp = moe.expert_ff_length as usize;
10897        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10898        let lim_exp = cfg.clamp_exp_at(il as u32);
10899        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10900
10901        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10902        if let Some(sig) = cfg.sigmoid_router() {
10903            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10904        }
10905        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10906            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10907        } else {
10908            Self::moe_route_cfg(
10909                e,
10910                &logits,
10911                mrows,
10912                n_expert,
10913                n_used,
10914                m.active_experts.as_deref(),
10915            )?
10916        };
10917        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10918
10919        // Residency split at whole-expert granularity against the (frozen) cache.
10920        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10921            Ok((0..n_expert)
10922                .map(|ex| {
10923                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10924                        .into_iter()
10925                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10926                })
10927                .collect())
10928        })?;
10929
10930        struct Group {
10931            rows: Vec<i32>,
10932            slots: Vec<i32>,
10933            weights: Vec<f32>,
10934        }
10935        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10936        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10937        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10938            Default::default();
10939        for row in 0..mrows {
10940            for j in 0..n_used {
10941                let ex = sel_all[row * n_used + j] as usize;
10942                let w = w_all[row * n_used + j];
10943                if resident_expert[ex] {
10944                    let group = groups.entry(ex).or_insert_with(|| Group {
10945                        rows: Vec::new(),
10946                        slots: Vec::new(),
10947                        weights: Vec::new(),
10948                    });
10949                    group.rows.push(row as i32);
10950                    group.slots.push(j as i32);
10951                    group.weights.push(w);
10952                } else {
10953                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10954                    cpu_rows[row].push((ex, w));
10955                    cpu_by_expert.entry(ex).or_default().push((row, w));
10956                }
10957            }
10958        }
10959
10960        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10961        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10962        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10963        // order per row differs from the sequential single-call chunk — part of the
10964        // documented lockstep numeric class.
10965        let host_rows = e.dtoh(zbatch)?;
10966        let rows_ok = crate::cpu_experts::rows_supported();
10967        enum CpuPart {
10968            Single { row: usize },
10969            Rows { rows: Vec<usize> },
10970        }
10971        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10972        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10973        if rows_ok {
10974            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10975                .into_iter()
10976                .filter(|(_, rows)| rows.len() >= 2)
10977                .collect();
10978            shared.sort_by_key(|(ex, _)| *ex);
10979            for (ex, mut row_weights) in shared {
10980                row_weights.sort_by_key(|(row, _)| *row);
10981                let inputs: Vec<(&[f32], f32)> = row_weights
10982                    .iter()
10983                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10984                    .collect();
10985                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10986                    .map_err(std::io::Error::other)?;
10987                for &(row, _) in &row_weights {
10988                    rows_served.insert((row, ex));
10989                }
10990                tickets.push((
10991                    CpuPart::Rows {
10992                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10993                    },
10994                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10995                ));
10996            }
10997        }
10998        for (row, selected) in cpu_rows.iter().enumerate() {
10999            let leftover: Vec<(usize, f32)> = selected
11000                .iter()
11001                .copied()
11002                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
11003                .collect();
11004            if leftover.is_empty() {
11005                continue;
11006            }
11007            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
11008            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
11009                .map_err(std::io::Error::other)?;
11010            tickets.push((
11011                CpuPart::Single { row },
11012                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
11013            ));
11014        }
11015
11016        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
11017        let mut wbuf = e.zeros(mrows * n_used)?;
11018        let mut order: Vec<usize> = groups.keys().copied().collect();
11019        order.sort_by(|&a, &b| {
11020            groups[&b]
11021                .rows
11022                .len()
11023                .cmp(&groups[&a].rows.len())
11024                .then(a.cmp(&b))
11025        });
11026        for &ex in &order {
11027            let group = &groups[&ex];
11028            let m_e = group.rows.len();
11029            let gl = m.gate_exps.expert_layout(ex);
11030            let ul = m.up_exps.expert_layout(ex);
11031            let dl = m.down_exps.expert_layout(ex);
11032            let row_idx_d = e.htod_i32(&group.rows)?;
11033            let slot_idx_d = e.htod_i32(&group.slots)?;
11034            let dmac = m.down_exps.macro_scale(ex);
11035            let weight_d = if dmac == 1.0 {
11036                e.htod(&group.weights)?
11037            } else {
11038                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
11039                e.htod(&scaled)?
11040            };
11041            let mut gathered = e.zeros(m_e * n_embd)?;
11042            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
11043            let gv = gathered.slice(0..m_e * n_embd);
11044            let gate = e.with_moe_cache(max_block, |c, eng| {
11045                let slot = c
11046                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
11047                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11048                eng.qmatvec_view(
11049                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11050                    0..gl.len,
11051                    &gv,
11052                    m_e,
11053                    m.gate_exps.in_f,
11054                    m.gate_exps.out_f,
11055                    gl.qtype,
11056                    gl.row_bytes,
11057                )
11058            })?;
11059            let up = e.with_moe_cache(max_block, |c, eng| {
11060                let slot = c
11061                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
11062                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11063                eng.qmatvec_view(
11064                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11065                    0..ul.len,
11066                    &gv,
11067                    m_e,
11068                    m.up_exps.in_f,
11069                    m.up_exps.out_f,
11070                    ul.qtype,
11071                    ul.row_bytes,
11072                )
11073            })?;
11074            let mut act = e.zeros(m_e * n_ff_exp)?;
11075            Self::ffn_act_lim(
11076                e,
11077                cfg,
11078                &gate,
11079                &up,
11080                m.gate_exps.macro_scale(ex),
11081                m.up_exps.macro_scale(ex),
11082                lim_exp,
11083                &mut act,
11084                m_e * n_ff_exp,
11085            )?;
11086            let actv = act.slice(0..m_e * n_ff_exp);
11087            let y = e.with_moe_cache(max_block, |c, eng| {
11088                let slot = c
11089                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
11090                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11091                eng.qmatvec_view(
11092                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11093                    0..dl.len,
11094                    &actv,
11095                    m_e,
11096                    m.down_exps.in_f,
11097                    m.down_exps.out_f,
11098                    dl.qtype,
11099                    dl.row_bytes,
11100                )
11101            })?;
11102            e.scatter_slot(
11103                &y,
11104                &row_idx_d,
11105                &slot_idx_d,
11106                &weight_d,
11107                &mut slot_buf,
11108                &mut wbuf,
11109                n_embd,
11110                n_used,
11111                m_e,
11112            )?;
11113        }
11114        let mut moe_out = e.zeros(mrows * n_embd)?;
11115        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
11116
11117        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
11118        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
11119        for (part, ticket) in tickets {
11120            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
11121            let mut add_row = |row: usize, chunk: &[f32]| {
11122                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
11123                for (accumulator, value) in sum.iter_mut().zip(chunk) {
11124                    *accumulator += value;
11125                }
11126            };
11127            match part {
11128                CpuPart::Single { row } => add_row(row, &cpu_output),
11129                CpuPart::Rows { rows } => {
11130                    for (slot, row) in rows.into_iter().enumerate() {
11131                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
11132                    }
11133                }
11134            }
11135        }
11136        for (row, sum) in row_sums.into_iter().enumerate() {
11137            let Some(sum) = sum else { continue };
11138            let cpu_output = e.htod(&sum)?;
11139            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
11140            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
11141        }
11142
11143        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
11144            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
11145        {
11146            let n_ff_sh = gate_shexp.out_features();
11147            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
11148            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
11149            let mut sa = e.zeros(mrows * n_ff_sh)?;
11150            Self::ffn_act_lim(
11151                e,
11152                cfg,
11153                &sg_gate,
11154                &sg_up,
11155                1.0,
11156                1.0,
11157                lim_shexp,
11158                &mut sa,
11159                mrows * n_ff_sh,
11160            )?;
11161            let sh = e.matmul(down_shexp, &sa, mrows)?;
11162            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
11163            // decode matches the single-sequence decode chain bit-for-bit.
11164            let g = match &m.gate_inp_shexp {
11165                Some(gate_inp_shexp) => {
11166                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
11167                }
11168                None => e.htod(&vec![1.0f32; mrows])?,
11169            };
11170            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
11171        }
11172
11173        Ok(moe_out)
11174    }
11175}
11176
11177// ============================ gemma4 (R8 verified wiring) ==================================
11178// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
11179// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
11180// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
11181// gemma variants after the correctness gate).
11182impl HybridModel {
11183    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
11184    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
11185    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
11186    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
11187    ///
11188    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
11189    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
11190    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
11191    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
11192    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
11193    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
11194    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
11195    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
11196    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
11197        let g = self
11198            .cfg
11199            .gemma4
11200            .as_ref()
11201            .expect("gemma4_rope_dims on a non-gemma4 config");
11202        if g.swa_pattern[il] {
11203            g.rope_dims_swa as usize
11204        } else {
11205            g.rope_dims_global as usize
11206        }
11207    }
11208
11209    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
11210        let g = self.cfg.gemma4.as_ref().unwrap();
11211        let swa = g.swa_pattern[il];
11212        let hd = if swa {
11213            g.key_length_swa
11214        } else {
11215            g.key_length_global
11216        } as usize;
11217        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
11218        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
11219        // rows exact (softmax over one element) while every later position drifted).
11220        (
11221            hd,
11222            g.head_count_kv[il] as usize,
11223            self.cfg.n_head as usize,
11224            if swa {
11225                g.rope_base_swa
11226            } else {
11227                g.rope_base_global
11228            },
11229            1.0,
11230            swa,
11231        )
11232    }
11233
11234    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
11235    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
11236    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
11237    pub(crate) fn gemma4_suppress(
11238        &self,
11239        e: &Engine,
11240        ld: &mut CudaSlice<f32>,
11241        t: usize,
11242    ) -> Result<(), Box<dyn std::error::Error>> {
11243        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
11244            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
11245            // stage as primary, and this tail runs only after the last stage). The assert turns
11246            // that argued invariant into a checked one: any topology violating primary==head
11247            // trips here in debug instead of silently peer-reading a device-0 buffer.
11248            #[cfg(debug_assertions)]
11249            crate::debug_assert_tensor_stream_device(
11250                ids,
11251                &e.stream(),
11252                "gemma4_suppress.suppress_d",
11253            );
11254            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
11255        }
11256        Ok(())
11257    }
11258
11259    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
11260    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
11261    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
11262    /// only (v0): attends within `tokens` via the f32 sdpa.
11263    #[allow(clippy::too_many_arguments)]
11264    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
11265    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
11266    /// switching program at `t > sliding_window`. The door is the measured cause of the
11267    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
11268    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
11269    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
11270    /// published prefix KV stops depending on the total prompt length. Off by default because
11271    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
11272    fn gemma_fa_one_program() -> bool {
11273        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11274        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
11275    }
11276
11277    fn gemma4_attn_prime(
11278        &self,
11279        e: &Engine,
11280        fa: &crate::hybrid::FullAttnLayer,
11281        il: usize,
11282        h: &CudaSlice<f32>,
11283        pos_d: &CudaSlice<i32>,
11284        t: usize,
11285        cache: Option<&mut Cache>,
11286        island: Option<&CudaSlice<i32>>,
11287    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11288        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
11289        let eps = self.cfg.rms_eps;
11290        let aux = self.gemma4_aux.as_ref().unwrap();
11291        let ones = aux.ones(e);
11292        #[cfg(debug_assertions)]
11293        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
11294
11295        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
11296        // (h stays borrowed across the triple, so the cache key can't go stale).
11297        e.mmq_act_begin();
11298        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
11299        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
11300            let v = e.dtoh(&q0)?;
11301            let nan = v.iter().filter(|x| x.is_nan()).count();
11302            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
11303            eprintln!(
11304                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
11305                v.len()
11306            );
11307        }
11308        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
11309        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
11310        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
11311        let v0 = if swa {
11312            e.matmul(&fa.wv, h, t)?
11313        } else {
11314            e.clone_dtod(&k0)?
11315        };
11316        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
11317            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
11318                let v = e.dtoh(buf)?;
11319                let nan = v.iter().filter(|x| x.is_nan()).count();
11320                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
11321                eprintln!(
11322                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
11323                    v.len()
11324                );
11325            }
11326        }
11327
11328        let mut q = e.uninit(t * nh * hd)?;
11329        let mut k = e.uninit(t * nkv * hd)?;
11330        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
11331        let mut v = e.uninit(t * nkv * hd)?;
11332        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
11333        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
11334        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
11335        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11336        // Island primes take the mask-capable naive kernel below; keep the operands f32
11337        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
11338        let emit = island.is_none()
11339            && t >= 16
11340            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
11341            && *EMIT.get_or_init(|| {
11342                std::env::var("MEMRA_FA_EMIT")
11343                    .map(|s| s != "0")
11344                    .unwrap_or(true)
11345            });
11346        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
11347        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
11348        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
11349        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
11350        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
11351        let v_f16 = emit
11352            && crate::fa_f16pv_on()
11353            && match hd {
11354                512 => true,
11355                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
11356                _ => false,
11357            };
11358        if emit {
11359            e.rms_norm_qkv_w4b(
11360                &q0,
11361                &k0,
11362                &v0,
11363                fa.q_norm.float_data(),
11364                fa.k_norm.float_data(),
11365                ones,
11366                &mut q,
11367                &mut k,
11368                &mut v,
11369                &mut vb,
11370                hd,
11371                nh * t,
11372                nkv * t,
11373                eps,
11374                v_f16,
11375            )?;
11376        } else {
11377            e.rms_norm_qkv(
11378                &q0,
11379                &k0,
11380                &v0,
11381                fa.q_norm.float_data(),
11382                fa.k_norm.float_data(),
11383                ones,
11384                &mut q,
11385                &mut k,
11386                &mut v,
11387                hd,
11388                nh * t,
11389                nkv * t,
11390                eps,
11391            )?;
11392        }
11393
11394        let ff = if swa {
11395            None
11396        } else {
11397            Some(
11398                aux.rope_freqs(e)
11399                    .expect("gemma4 global rope needs rope_freqs.weight"),
11400            )
11401        };
11402        #[cfg(debug_assertions)]
11403        if let Some(ff) = ff {
11404            crate::debug_assert_tensor_stream_device(
11405                ff,
11406                &e.stream(),
11407                "gemma4_attn_prime.rope_freqs",
11408            );
11409        }
11410        if emit {
11411            e.rope_neox2_bf16e(
11412                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
11413            )?;
11414        } else {
11415            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
11416        }
11417
11418        if let Some(cache) = cache {
11419            let kvl = cache.kv[il].as_mut().unwrap();
11420            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11421            e.append_kv_quantized_rows(
11422                &k,
11423                &v,
11424                &mut kvl.k,
11425                &mut kvl.v,
11426                kvl.len,
11427                t,
11428                kvl.kv_dim_k,
11429                kvl.kv_dim_v,
11430                kvl.k_tok_bytes,
11431                kvl.v_tok_bytes,
11432                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11433            )?;
11434            kvl.len += t;
11435        }
11436        let mut attn = e.zeros(t * nh * hd)?;
11437        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11438        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11439        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11440        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11441        if let Some(span) = island {
11442            // Masked-prefill arm: every layer routes through the island-aware naive
11443            // kernel (correctness-first, same posture as the vision tower v1). The
11444            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11445            // window, the real window beyond it.
11446            let w = if swa && t > win { win } else { 0 };
11447            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11448        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11449            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11450                if emit {
11451                    e.fa_prefill_w_pre(
11452                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11453                    )?;
11454                } else {
11455                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11456                }
11457            } else {
11458                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11459            }
11460        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11461            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11462        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11463            if emit {
11464                e.fa_prefill_hd512_pre(
11465                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11466                )?;
11467            } else {
11468                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11469            }
11470        } else {
11471            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11472        }
11473        Ok(e.matmul(&fa.wo, &attn, t)?)
11474    }
11475
11476    /// Back-compat wrapper (pure prefill, no cache).
11477    fn gemma4_attn(
11478        &self,
11479        e: &Engine,
11480        fa: &crate::hybrid::FullAttnLayer,
11481        il: usize,
11482        h: &CudaSlice<f32>,
11483        pos_d: &CudaSlice<i32>,
11484        t: usize,
11485    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11486        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11487    }
11488
11489    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11490    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11491    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11492    /// the q8z epilogue is quantize_q8_1 verbatim).
11493    fn gemma4_moe_q8(
11494        &self,
11495        e: &Engine,
11496        m: &crate::hybrid::MoeWeights,
11497        bits: &crate::hybrid::Gemma4MoeBits,
11498        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11499        router_in: &CudaSlice<f32>,
11500        t: usize,
11501    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11502        let cfg = &self.cfg;
11503        let moe = cfg.moe.as_ref().unwrap();
11504        let n_embd = cfg.n_embd as usize;
11505        let n_expert = moe.expert_count as usize;
11506        let n_used = moe.expert_used_count as usize;
11507        let n_ff_exp = moe.expert_ff_length as usize;
11508        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11509        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11510        // the pair's 12us is kernel time, not launch gaps.
11511        let logits = if crate::router_kernel_on() {
11512            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11513        } else {
11514            e.matmul(&m.gate_inp, router_in, t)?
11515        };
11516        let dev = m.dev_exps.as_ref().unwrap();
11517        let (sel_d, w_d) =
11518            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11519        let (zq, zd) = mq;
11520        if t == 1 {
11521            let selv = sel_d.slice(0..n_used);
11522            let wv = w_d.slice(0..n_used);
11523            let act = e.moe_gate_up_gelu8_dev_q8(
11524                &dev.ptr_row,
11525                &selv,
11526                zq,
11527                zd,
11528                n_embd,
11529                n_ff_exp,
11530                n_used,
11531                n_expert,
11532                m.gate_exps.qtype,
11533                m.up_exps.qtype,
11534                m.gate_exps.row_bytes,
11535                m.up_exps.row_bytes,
11536            )?;
11537            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11538            let mut moe_out = e.uninit(n_embd)?;
11539            e.moe_down8_fma_dev_q8(
11540                &dev.ptr_row,
11541                &selv,
11542                &wv,
11543                &aq2,
11544                &ad2,
11545                &mut moe_out.slice_mut(0..n_embd),
11546                n_ff_exp,
11547                n_embd,
11548                n_used,
11549                n_expert,
11550                m.down_exps.qtype,
11551                m.down_exps.row_bytes,
11552            )?;
11553            return Ok(moe_out);
11554        }
11555        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11556        let act = if csr {
11557            e.moe_gate_up_gelu8_dev_q8_csr(
11558                &dev.ptr_row,
11559                &sel_d,
11560                zq,
11561                zd,
11562                t * n_used,
11563                n_embd,
11564                n_ff_exp,
11565                n_used,
11566                n_expert,
11567                m.gate_exps.qtype,
11568                m.up_exps.qtype,
11569                m.gate_exps.row_bytes,
11570                m.up_exps.row_bytes,
11571            )?
11572        } else {
11573            e.moe_gate_up_gelu8_dev_q8_rows(
11574                &dev.ptr_row,
11575                &sel_d,
11576                zq,
11577                zd,
11578                t,
11579                n_embd,
11580                n_ff_exp,
11581                n_used,
11582                n_expert,
11583                m.gate_exps.qtype,
11584                m.up_exps.qtype,
11585                m.gate_exps.row_bytes,
11586                m.up_exps.row_bytes,
11587            )?
11588        };
11589        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11590        let mut moe_out = e.uninit(t * n_embd)?;
11591        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11592        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11593        e.moe_down8_fma_dev_q8_rows_g(
11594            &dev.ptr_row,
11595            &sel_d,
11596            &w_d,
11597            &aq2,
11598            &ad2,
11599            &mut moe_out,
11600            t,
11601            n_ff_exp,
11602            n_embd,
11603            n_used,
11604            n_expert,
11605            m.down_exps.qtype,
11606            m.down_exps.row_bytes,
11607        )?;
11608        Ok(moe_out)
11609    }
11610
11611    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11612    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11613    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11614    fn gemma4_moe(
11615        &self,
11616        e: &Engine,
11617        m: &crate::hybrid::MoeWeights,
11618        bits: &crate::hybrid::Gemma4MoeBits,
11619        moe_in: &CudaSlice<f32>,
11620        router_in: &CudaSlice<f32>,
11621        t: usize,
11622    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11623        let cfg = &self.cfg;
11624        let moe = cfg.moe.as_ref().unwrap();
11625        let n_embd = cfg.n_embd as usize;
11626        let n_expert = moe.expert_count as usize;
11627        let n_used = moe.expert_used_count as usize;
11628        let n_ff_exp = moe.expert_ff_length as usize;
11629
11630        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11631        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11632        // batched matmul only at real prefill.
11633        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11634            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11635        } else {
11636            e.matmul(&m.gate_inp, router_in, t)?
11637        };
11638
11639        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11640        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11641        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11642        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11643        if t < PRIME_MIN_T
11644            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11645            && expert_dp4a_supported(m.gate_exps.qtype)
11646            && expert_dp4a_supported(m.up_exps.qtype)
11647            && expert_dp4a_supported(m.down_exps.qtype)
11648            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11649        {
11650            let dev = m.dev_exps.as_ref().unwrap();
11651            let (sel_d, w_d) =
11652                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11653            if t == 1 {
11654                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11655                let selv = sel_d.slice(0..n_used);
11656                let wv = w_d.slice(0..n_used);
11657                let act = e.moe_gate_up_gelu8_dev_q8(
11658                    &dev.ptr_row,
11659                    &selv,
11660                    &zq,
11661                    &zd,
11662                    n_embd,
11663                    n_ff_exp,
11664                    n_used,
11665                    n_expert,
11666                    m.gate_exps.qtype,
11667                    m.up_exps.qtype,
11668                    m.gate_exps.row_bytes,
11669                    m.up_exps.row_bytes,
11670                )?;
11671                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11672                let mut moe_out = e.uninit(n_embd)?;
11673                e.moe_down8_fma_dev_q8(
11674                    &dev.ptr_row,
11675                    &selv,
11676                    &wv,
11677                    &aq2,
11678                    &ad2,
11679                    &mut moe_out.slice_mut(0..n_embd),
11680                    n_ff_exp,
11681                    n_embd,
11682                    n_used,
11683                    n_expert,
11684                    m.down_exps.qtype,
11685                    m.down_exps.row_bytes,
11686                )?;
11687                return Ok(moe_out);
11688            }
11689            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11690            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11691            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11692            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11693            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11694            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11695            let act = if csr {
11696                e.moe_gate_up_gelu8_dev_q8_csr(
11697                    &dev.ptr_row,
11698                    &sel_d,
11699                    &zq,
11700                    &zd,
11701                    t * n_used,
11702                    n_embd,
11703                    n_ff_exp,
11704                    n_used,
11705                    n_expert,
11706                    m.gate_exps.qtype,
11707                    m.up_exps.qtype,
11708                    m.gate_exps.row_bytes,
11709                    m.up_exps.row_bytes,
11710                )?
11711            } else {
11712                e.moe_gate_up_gelu8_dev_q8_rows(
11713                    &dev.ptr_row,
11714                    &sel_d,
11715                    &zq,
11716                    &zd,
11717                    t,
11718                    n_embd,
11719                    n_ff_exp,
11720                    n_used,
11721                    n_expert,
11722                    m.gate_exps.qtype,
11723                    m.up_exps.qtype,
11724                    m.gate_exps.row_bytes,
11725                    m.up_exps.row_bytes,
11726                )?
11727            };
11728            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11729            let mut moe_out = e.uninit(t * n_embd)?;
11730            e.moe_down8_fma_dev_q8_rows_g(
11731                &dev.ptr_row,
11732                &sel_d,
11733                &w_d,
11734                &aq2,
11735                &ad2,
11736                &mut moe_out,
11737                t,
11738                n_ff_exp,
11739                n_embd,
11740                n_used,
11741                n_expert,
11742                m.down_exps.qtype,
11743                m.down_exps.row_bytes,
11744            )?;
11745            return Ok(moe_out);
11746        }
11747
11748        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11749        for (i, &sx) in sel_all.iter().enumerate() {
11750            w_all[i] *= bits.per_expert_scale[sx as usize];
11751        }
11752
11753        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11754        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11755        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11756        if t >= PRIME_MIN_T
11757            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11758            && expert_dp4a_supported(m.gate_exps.qtype)
11759            && expert_dp4a_supported(m.up_exps.qtype)
11760            && expert_dp4a_supported(m.down_exps.qtype)
11761            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11762        {
11763            let dev = m.dev_exps.as_ref().unwrap();
11764            let n_pairs = t * n_used;
11765            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11766            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11767            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11768            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11769            let pt = e.htod_i32(&pair_tok)?;
11770            let pw = e.htod(&w_all)?;
11771            let toff = e.htod_i32(&tok_off)?;
11772            let tids = e.htod_i32(&tok_ids)?;
11773            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11774            for p in 0..n_pairs {
11775                by_ex[pair_ex[p] as usize].push(p as i32);
11776            }
11777            let mut ex_ids: Vec<i32> = Vec::new();
11778            let mut ex_off: Vec<i32> = vec![0];
11779            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11780            for (ex, list) in by_ex.iter().enumerate() {
11781                if list.is_empty() {
11782                    continue;
11783                }
11784                ex_ids.push(ex as i32);
11785                ex_pairs.extend_from_slice(list);
11786                ex_off.push(ex_pairs.len() as i32);
11787            }
11788            let n_active = ex_ids.len();
11789            let exi = e.htod_i32(&ex_ids)?;
11790            let exo = e.htod_i32(&ex_off)?;
11791            let exp_d = e.htod_i32(&ex_pairs)?;
11792            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11793            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11794            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11795            // ragged down k (704) needs no padding here — cublas takes any k.
11796            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11797            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11798            // Hopper default — see moe_f16g_gemma_on.
11799            if crate::moe_f16g_gemma_on()
11800                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11801                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11802                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11803            {
11804                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11805                let csr_tok_d = e.htod_i32(&csr_tok)?;
11806                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11807                let g_csr = e.moe_f16_grouped(
11808                    &dev.ptr_row,
11809                    0,
11810                    n_expert,
11811                    &exi,
11812                    &ex_off,
11813                    &exo,
11814                    &z_f16,
11815                    &z_s,
11816                    n_embd,
11817                    n_ff_exp,
11818                    n_active,
11819                    n_pairs,
11820                    m.gate_exps.qtype,
11821                    m.gate_exps.row_bytes,
11822                )?;
11823                let u_csr = e.moe_f16_grouped(
11824                    &dev.ptr_row,
11825                    1,
11826                    n_expert,
11827                    &exi,
11828                    &ex_off,
11829                    &exo,
11830                    &z_f16,
11831                    &z_s,
11832                    n_embd,
11833                    n_ff_exp,
11834                    n_active,
11835                    n_pairs,
11836                    m.up_exps.qtype,
11837                    m.up_exps.row_bytes,
11838                )?;
11839                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11840                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11841                let d_csr = e.moe_f16_grouped(
11842                    &dev.ptr_row,
11843                    2,
11844                    n_expert,
11845                    &exi,
11846                    &ex_off,
11847                    &exo,
11848                    &a_f16,
11849                    &a_s,
11850                    n_ff_exp,
11851                    n_embd,
11852                    n_active,
11853                    n_pairs,
11854                    m.down_exps.qtype,
11855                    m.down_exps.row_bytes,
11856                )?;
11857                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11858                let mut moe_out = e.uninit(t * n_embd)?;
11859                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11860                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11861                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11862                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11863                    eprintln!(
11864                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11865                        scan(&yd),
11866                        scan(&mo)
11867                    );
11868                }
11869                return Ok(moe_out);
11870            }
11871            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11872            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11873            let mma =
11874                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11875            let (gate, up) = if mma {
11876                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11877                (
11878                    e.mmq_iq_experts(
11879                        &dev.ptr_row,
11880                        0,
11881                        n_expert,
11882                        &exi,
11883                        &exo,
11884                        &exp_d,
11885                        &pt,
11886                        &z_scr,
11887                        n_embd,
11888                        n_ff_exp,
11889                        n_active,
11890                        n_pairs,
11891                        t,
11892                        m.gate_exps.qtype,
11893                        m.gate_exps.row_bytes,
11894                    )?,
11895                    e.mmq_iq_experts(
11896                        &dev.ptr_row,
11897                        1,
11898                        n_expert,
11899                        &exi,
11900                        &exo,
11901                        &exp_d,
11902                        &pt,
11903                        &z_scr,
11904                        n_embd,
11905                        n_ff_exp,
11906                        n_active,
11907                        n_pairs,
11908                        t,
11909                        m.up_exps.qtype,
11910                        m.up_exps.row_bytes,
11911                    )?,
11912                )
11913            } else {
11914                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11915                (
11916                    e.moe_pairs_matvec_q8_dec(
11917                        &dev.ptr_row,
11918                        0,
11919                        &exi,
11920                        &exo,
11921                        &exp_d,
11922                        &pt,
11923                        &zq,
11924                        &zd,
11925                        n_embd,
11926                        n_ff_exp,
11927                        n_expert,
11928                        n_active,
11929                        n_pairs,
11930                        m.gate_exps.qtype,
11931                        m.gate_exps.row_bytes,
11932                    )?,
11933                    e.moe_pairs_matvec_q8_dec(
11934                        &dev.ptr_row,
11935                        1,
11936                        &exi,
11937                        &exo,
11938                        &exp_d,
11939                        &pt,
11940                        &zq,
11941                        &zd,
11942                        n_embd,
11943                        n_ff_exp,
11944                        n_expert,
11945                        n_active,
11946                        n_pairs,
11947                        m.up_exps.qtype,
11948                        m.up_exps.row_bytes,
11949                    )?,
11950                )
11951            };
11952            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11953            let pself = e.htod_i32(&pair_self)?;
11954            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11955            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11956            // to the 256-val superblock (768) while the act quantizer's zero padding
11957            // makes every padded-k product exactly zero (weight overread bytes multiply
11958            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11959            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11960            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11961            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11962            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11963            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11964            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11965            let y_down = if mma {
11966                let in_pad = n_ff_exp.div_ceil(256) * 256;
11967                let a_scr = if crate::moe_fuse_actq_on() {
11968                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11969                } else {
11970                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11971                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11972                };
11973                e.mmq_iq_experts(
11974                    &dev.ptr_row,
11975                    2,
11976                    n_expert,
11977                    &exi,
11978                    &exo,
11979                    &exp_d,
11980                    &pself,
11981                    &a_scr,
11982                    in_pad,
11983                    n_embd,
11984                    n_active,
11985                    n_pairs,
11986                    n_pairs,
11987                    m.down_exps.qtype,
11988                    m.down_exps.row_bytes,
11989                )?
11990            } else {
11991                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11992                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11993                e.moe_pairs_matvec_q8_dec(
11994                    &dev.ptr_row,
11995                    2,
11996                    &exi,
11997                    &exo,
11998                    &exp_d,
11999                    &pself,
12000                    &aq2,
12001                    &ad2,
12002                    n_ff_exp,
12003                    n_embd,
12004                    n_expert,
12005                    n_active,
12006                    n_pairs,
12007                    m.down_exps.qtype,
12008                    m.down_exps.row_bytes,
12009                )?
12010            };
12011            let mut moe_out = e.uninit(t * n_embd)?;
12012            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
12013            return Ok(moe_out);
12014        }
12015
12016        let g_len = m.gate_exps.expert_stride;
12017        let u_len = m.up_exps.expert_stride;
12018        let d_len = m.down_exps.expert_stride;
12019        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
12020        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
12021        // the spill fallback.
12022        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
12023        let (mut sg, mut su, mut sd) = if dev.is_some() {
12024            (None, None, None)
12025        } else {
12026            (
12027                Some(e.alloc_u8_uninit(g_len)?),
12028                Some(e.alloc_u8_uninit(u_len)?),
12029                Some(e.alloc_u8_uninit(d_len)?),
12030            )
12031        };
12032        let mut moe_out = e.zeros(t * n_embd)?;
12033        for tok in 0..t {
12034            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
12035            let w = &w_all[tok * n_used..(tok + 1) * n_used];
12036            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
12037            for (j, &ex) in sel.iter().enumerate() {
12038                let ex = ex as usize;
12039                let gate = match dev {
12040                    Some(d) => e.qmatvec_view(
12041                        &d.gate,
12042                        ex * g_len..(ex + 1) * g_len,
12043                        &zt,
12044                        1,
12045                        m.gate_exps.in_f,
12046                        m.gate_exps.out_f,
12047                        m.gate_exps.qtype,
12048                        m.gate_exps.row_bytes,
12049                    )?,
12050                    None => {
12051                        let sg = sg.as_mut().unwrap();
12052                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
12053                        e.qmatvec_view(
12054                            sg,
12055                            0..g_len,
12056                            &zt,
12057                            1,
12058                            m.gate_exps.in_f,
12059                            m.gate_exps.out_f,
12060                            m.gate_exps.qtype,
12061                            m.gate_exps.row_bytes,
12062                        )?
12063                    }
12064                };
12065                let up = match dev {
12066                    Some(d) => e.qmatvec_view(
12067                        &d.up,
12068                        ex * u_len..(ex + 1) * u_len,
12069                        &zt,
12070                        1,
12071                        m.up_exps.in_f,
12072                        m.up_exps.out_f,
12073                        m.up_exps.qtype,
12074                        m.up_exps.row_bytes,
12075                    )?,
12076                    None => {
12077                        let su = su.as_mut().unwrap();
12078                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
12079                        e.qmatvec_view(
12080                            su,
12081                            0..u_len,
12082                            &zt,
12083                            1,
12084                            m.up_exps.in_f,
12085                            m.up_exps.out_f,
12086                            m.up_exps.qtype,
12087                            m.up_exps.row_bytes,
12088                        )?
12089                    }
12090                };
12091                let mut act = e.uninit(n_ff_exp)?;
12092                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
12093                let actv = act.slice(0..n_ff_exp);
12094                let y = match dev {
12095                    Some(d) => e.qmatvec_view(
12096                        &d.down,
12097                        ex * d_len..(ex + 1) * d_len,
12098                        &actv,
12099                        1,
12100                        m.down_exps.in_f,
12101                        m.down_exps.out_f,
12102                        m.down_exps.qtype,
12103                        m.down_exps.row_bytes,
12104                    )?,
12105                    None => {
12106                        let sd = sd.as_mut().unwrap();
12107                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
12108                        e.qmatvec_view(
12109                            sd,
12110                            0..d_len,
12111                            &actv,
12112                            1,
12113                            m.down_exps.in_f,
12114                            m.down_exps.out_f,
12115                            m.down_exps.qtype,
12116                            m.down_exps.row_bytes,
12117                        )?
12118                    }
12119                };
12120                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
12121                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
12122            }
12123        }
12124        Ok(moe_out)
12125    }
12126
12127    /// One gemma4 trunk layer (R8): x -> x_next.
12128    fn gemma4_layer(
12129        &self,
12130        e: &Engine,
12131        il: usize,
12132        layer: &crate::hybrid::HybridLayer,
12133        x: &CudaSlice<f32>,
12134        pos_d: &CudaSlice<i32>,
12135        t: usize,
12136    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12137        let n_embd = self.cfg.n_embd as usize;
12138        let eps = self.cfg.rms_eps;
12139
12140        let mut h = e.zeros(t * n_embd)?;
12141        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12142        let Mixer::Full(fa) = &layer.mixer else {
12143            panic!("gemma4 layer {il} not full-attn")
12144        };
12145        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
12146        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
12147        let mut cur = e.zeros(t * n_embd)?;
12148        e.rms_norm(
12149            &o,
12150            layer.post_attn_norm.float_data(),
12151            &mut cur,
12152            n_embd,
12153            t,
12154            eps,
12155        )?;
12156        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
12157    }
12158
12159    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
12160    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
12161    /// layer scale — shared verbatim by the prefill, decode and verify paths.
12162    fn gemma4_layer_tail_add(
12163        &self,
12164        e: &Engine,
12165        layer: &crate::hybrid::HybridLayer,
12166        cur: &CudaSlice<f32>,
12167        x: &CudaSlice<f32>,
12168        t: usize,
12169    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12170        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
12171    }
12172
12173    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
12174    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
12175    fn gemma4_layer_tail_add_n(
12176        &self,
12177        e: &Engine,
12178        layer: &crate::hybrid::HybridLayer,
12179        cur: &CudaSlice<f32>,
12180        x: &CudaSlice<f32>,
12181        t: usize,
12182        next_norm: Option<&CudaSlice<f32>>,
12183    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
12184        let n_embd = self.cfg.n_embd as usize;
12185        let bits = layer.gemma4.as_ref().unwrap();
12186        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12187        let mut xn = e.uninit(t * n_embd)?;
12188        match next_norm {
12189            Some(w) => {
12190                let mut hn = e.uninit(t * n_embd)?;
12191                e.add_scale_rms_norm(
12192                    &sn,
12193                    &attn_out,
12194                    bits.layer_scale,
12195                    w,
12196                    &mut xn,
12197                    &mut hn,
12198                    n_embd,
12199                    t,
12200                    self.cfg.rms_eps,
12201                )?;
12202                Ok((xn, Some(hn)))
12203            }
12204            None => {
12205                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12206                Ok((xn, None))
12207            }
12208        }
12209    }
12210
12211    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
12212    /// norm — returns (sn, attn_out) for the closing add+scale variants.
12213    fn gemma4_layer_tail_core(
12214        &self,
12215        e: &Engine,
12216        layer: &crate::hybrid::HybridLayer,
12217        cur: &CudaSlice<f32>,
12218        x: &CudaSlice<f32>,
12219        t: usize,
12220    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12221        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
12222    }
12223
12224    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
12225    /// means `cur` is the RAW attention output and the dense entry runs
12226    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
12227    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
12228    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
12229    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
12230    fn gemma4_layer_tail_core_pn(
12231        &self,
12232        e: &Engine,
12233        layer: &crate::hybrid::HybridLayer,
12234        cur: &CudaSlice<f32>,
12235        x: &CudaSlice<f32>,
12236        t: usize,
12237        pre_norm: Option<&CudaSlice<f32>>,
12238        defer_post_norm: bool,
12239    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12240        let n_embd = self.cfg.n_embd as usize;
12241        let eps = self.cfg.rms_eps;
12242        let bits = layer.gemma4.as_ref().unwrap();
12243
12244        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
12245        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
12246        let Some(mbits) = bits.moe_bits.as_ref() else {
12247            let crate::hybrid::Ffn::Dense {
12248                ffn_gate,
12249                ffn_up,
12250                ffn_down,
12251            } = &layer.ffn
12252            else {
12253                panic!("gemma4 dense layer without Dense ffn")
12254            };
12255            let mut attn_out = e.uninit(t * n_embd)?;
12256            let mut zsh = e.uninit(t * n_embd)?;
12257            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
12258            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
12259            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12260            match pre_norm {
12261                Some(wa) if t == 1 => {
12262                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
12263                        cur,
12264                        wa,
12265                        x,
12266                        bits.ffn_norm.float_data(),
12267                        &mut attn_out,
12268                        &mut zsh,
12269                        n_embd,
12270                        t,
12271                        eps,
12272                    )?);
12273                }
12274                Some(wa) => e.rms_pre_add_rms_norm(
12275                    cur,
12276                    wa,
12277                    x,
12278                    bits.ffn_norm.float_data(),
12279                    &mut attn_out,
12280                    &mut zsh,
12281                    n_embd,
12282                    t,
12283                    eps,
12284                )?,
12285                None => e.add_rms_norm(
12286                    cur,
12287                    x,
12288                    bits.ffn_norm.float_data(),
12289                    &mut attn_out,
12290                    &mut zsh,
12291                    n_embd,
12292                    t,
12293                    eps,
12294                )?,
12295            }
12296            let n_ff = ffn_gate.out_features();
12297            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
12298            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
12299            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
12300            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
12301            // rescue segment C — the megakernel front is closed for the dense tail.
12302            let (gate, up) = if t == 1 {
12303                let (zq, zd) = match zpair {
12304                    Some(p) => p,
12305                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
12306                };
12307                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
12308                    Some(p) => p,
12309                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
12310                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
12311                        Some(p) => p,
12312                        None => (
12313                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
12314                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
12315                        ),
12316                    },
12317                }
12318            } else {
12319                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
12320                // launch for the verify's gate+up — the up segment's blocks fill SMs as
12321                // the gate segment drains (the launch-tail mechanism behind the b-tier
12322                // plateau; first positive after six falsified in-kernel variants).
12323                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12324                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
12325                let fused = if f2b {
12326                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
12327                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
12328                } else {
12329                    None
12330                };
12331                match fused {
12332                    Some(p) => p,
12333                    None => {
12334                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
12335                        e.mmq_act_begin();
12336                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
12337                    }
12338                }
12339            };
12340            let mut act = e.uninit(t * n_ff)?;
12341            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
12342            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
12343            let f0 = if e.uses_q8_1_fast(ffn_down) {
12344                let upv = e.view(&up, t * n_ff);
12345                let up_all = upv.slice(0..t * n_ff);
12346                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
12347                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
12348            } else {
12349                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12350                e.matmul(ffn_down, &act, t)?
12351            };
12352            if defer_post_norm {
12353                return Ok((f0, attn_out));
12354            }
12355            let mut sn = e.uninit(t * n_embd)?;
12356            e.rms_norm(
12357                &f0,
12358                bits.post_ffw_norm.float_data(),
12359                &mut sn,
12360                n_embd,
12361                t,
12362                eps,
12363            )?;
12364            return Ok((sn, attn_out));
12365        };
12366
12367        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
12368        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
12369        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
12370        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
12371        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
12372        let mut attn_out = e.uninit(t * n_embd)?;
12373        let mut router_in = e.uninit(t * n_embd)?;
12374        let fast_moe = match &layer.ffn {
12375            crate::hybrid::Ffn::Moe(m) => {
12376                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
12377                    && expert_dp4a_supported(m.gate_exps.qtype)
12378                    && expert_dp4a_supported(m.up_exps.qtype)
12379                    && expert_dp4a_supported(m.down_exps.qtype)
12380                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
12381            }
12382            _ => false,
12383        };
12384        let q8z = t < PRIME_MIN_T && fast_moe;
12385        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
12386            let (z0, m2) = e.add_rms_norm3_q8z(
12387                cur,
12388                x,
12389                bits.ffn_norm.float_data(),
12390                &mbits.router_scale_pre,
12391                mbits.pre_ffw_norm_2.float_data(),
12392                &mut attn_out,
12393                &mut router_in,
12394                n_embd,
12395                t,
12396                eps,
12397            )?;
12398            (None, Some(z0), Some(m2))
12399        } else {
12400            let mut zsh = e.uninit(t * n_embd)?;
12401            let mut moe_in = e.uninit(t * n_embd)?;
12402            e.add_rms_norm3(
12403                cur,
12404                x,
12405                bits.ffn_norm.float_data(),
12406                &mbits.router_scale_pre,
12407                mbits.pre_ffw_norm_2.float_data(),
12408                &mut attn_out,
12409                &mut zsh,
12410                &mut router_in,
12411                &mut moe_in,
12412                n_embd,
12413                t,
12414                eps,
12415            )?;
12416            (Some((zsh, moe_in)), None, None)
12417        };
12418        let attn_out2 = attn_out;
12419        #[allow(unused_variables)]
12420        let attn_out = &attn_out2;
12421        let n_ff = mbits.shared_gate.out_features();
12422        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12423            if t == 1 {
12424                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12425                    Some(p) => p,
12426                    None => match e.matmul_nvfp4_fused2(
12427                        &mbits.shared_gate,
12428                        &mbits.shared_up,
12429                        zq,
12430                        zd,
12431                        1,
12432                    )? {
12433                        Some(p) => p,
12434                        None => {
12435                            let h0 = e.zeros(0)?;
12436                            (
12437                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12438                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12439                            )
12440                        }
12441                    },
12442                }
12443            } else {
12444                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12445                let h0 = e.zeros(0)?;
12446                (
12447                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12448                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12449                )
12450            }
12451        } else {
12452            let (zsh, _) = zsh_f32.as_ref().unwrap();
12453            (
12454                e.matmul(&mbits.shared_gate, zsh, t)?,
12455                e.matmul(&mbits.shared_up, zsh, t)?,
12456            )
12457        };
12458        let mut act = e.uninit(t * n_ff)?;
12459        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12460        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12461        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12462            panic!("gemma4 layer not MoE")
12463        };
12464        let moe0 = match (&moe_q8, &zsh_f32) {
12465            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12466            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12467            _ => unreachable!(),
12468        };
12469        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12470        let mut mlp = e.uninit(t * n_embd)?;
12471        let mut moe = e.uninit(t * n_embd)?;
12472        e.rms_norm2x(
12473            &mlp0,
12474            &moe0,
12475            mbits.post_ffw_norm_1.float_data(),
12476            mbits.post_ffw_norm_2.float_data(),
12477            &mut mlp,
12478            &mut moe,
12479            n_embd,
12480            t,
12481            eps,
12482        )?;
12483
12484        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12485        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12486        let mut sum = e.uninit(t * n_embd)?;
12487        let mut sn = e.uninit(t * n_embd)?;
12488        e.add_rms_norm(
12489            &mlp,
12490            &moe,
12491            bits.post_ffw_norm.float_data(),
12492            &mut sum,
12493            &mut sn,
12494            n_embd,
12495            t,
12496            eps,
12497        )?;
12498        Ok((sn, attn_out2))
12499    }
12500
12501    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12502    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12503    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12504    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12505    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12506    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12507    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12508    /// decode == verify == graph parity holds by construction at either seam value.
12509    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12510    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12511        &self,
12512        e: &Engine,
12513        layer: &crate::hybrid::HybridLayer,
12514        o: &CudaSlice<f32>,
12515        x: &CudaSlice<f32>,
12516        t: usize,
12517        next_norm: Option<&CudaSlice<f32>>,
12518    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12519    {
12520        let n_embd = self.cfg.n_embd as usize;
12521        let eps = self.cfg.rms_eps;
12522        let bits = layer.gemma4.as_ref().unwrap();
12523        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12524            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12525                e,
12526                layer,
12527                o,
12528                x,
12529                t,
12530                Some(layer.post_attn_norm.float_data()),
12531                true,
12532            )?;
12533            let mut xn = e.uninit(t * n_embd)?;
12534            return match next_norm {
12535                Some(w) => {
12536                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12537                        &f0,
12538                        bits.post_ffw_norm.float_data(),
12539                        &attn_out,
12540                        bits.layer_scale,
12541                        w,
12542                        &mut xn,
12543                        n_embd,
12544                        t,
12545                        eps,
12546                    )?;
12547                    Ok((xn, Some(pair)))
12548                }
12549                None => {
12550                    let mut sn = e.uninit(t * n_embd)?;
12551                    e.rms_norm(
12552                        &f0,
12553                        bits.post_ffw_norm.float_data(),
12554                        &mut sn,
12555                        n_embd,
12556                        t,
12557                        eps,
12558                    )?;
12559                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12560                    Ok((xn, None))
12561                }
12562            };
12563        }
12564        let mut cur = e.uninit(t * n_embd)?;
12565        e.rms_norm(
12566            o,
12567            layer.post_attn_norm.float_data(),
12568            &mut cur,
12569            n_embd,
12570            t,
12571            eps,
12572        )?;
12573        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12574    }
12575
12576    pub(crate) fn gemma4_layer_tail_add_nq(
12577        &self,
12578        e: &Engine,
12579        layer: &crate::hybrid::HybridLayer,
12580        cur: &CudaSlice<f32>,
12581        x: &CudaSlice<f32>,
12582        t: usize,
12583        next_norm: Option<&CudaSlice<f32>>,
12584    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12585    {
12586        let n_embd = self.cfg.n_embd as usize;
12587        let bits = layer.gemma4.as_ref().unwrap();
12588        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12589        let mut xn = e.uninit(t * n_embd)?;
12590        match next_norm {
12591            Some(w) => {
12592                let pair = e.add_scale_rms_norm_q8_1(
12593                    &sn,
12594                    &attn_out,
12595                    bits.layer_scale,
12596                    w,
12597                    &mut xn,
12598                    n_embd,
12599                    t,
12600                    self.cfg.rms_eps,
12601                )?;
12602                Ok((xn, Some(pair)))
12603            }
12604            None => {
12605                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12606                Ok((xn, None))
12607            }
12608        }
12609    }
12610
12611    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12612    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12613    fn gemma4_forward(
12614        &self,
12615        e: &Engine,
12616        tokens: &[u32],
12617        last_only: bool,
12618    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12619        // E4B routes to its own forward regardless of the caller's entry point (forward /
12620        // forward_last / prime paths all funnel here for gemma4).
12621        if self.is_gemma4_e4b() {
12622            return self.gemma4_e4b_forward(e, tokens, last_only);
12623        }
12624        let n_embd = self.cfg.n_embd as usize;
12625        let t = tokens.len();
12626        let pos: Vec<i32> = (0..t as i32).collect();
12627        let pos_d = e.htod_i32(&pos)?;
12628
12629        let mut x = self.embed(e, tokens)?;
12630        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12631        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12632        // the bring-up bisect vs llama-eval-callback node stats.
12633        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12634        let stat =
12635            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12636                let h = e.dtoh(x)?;
12637                let bad = h.iter().filter(|v| !v.is_finite()).count();
12638                let mx = h
12639                    .iter()
12640                    .filter(|v| v.is_finite())
12641                    .fold(0.0f32, |m, v| m.max(v.abs()));
12642                eprintln!(
12643                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12644                    &h[..3]
12645                );
12646                Ok(())
12647            };
12648        if probe {
12649            stat(e, &x, "embed")?;
12650        }
12651        for (il, layer) in self.layers.iter().enumerate() {
12652            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12653            if probe {
12654                stat(e, &x, &format!("L{il}"))?;
12655            }
12656        }
12657        let mut hn = e.zeros(t * n_embd)?;
12658        e.rms_norm(
12659            &x,
12660            self.output_norm.float_data(),
12661            &mut hn,
12662            n_embd,
12663            t,
12664            self.cfg.rms_eps,
12665        )?;
12666        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12667        let n_vocab = self.output.out_features();
12668        let logits = if last_only {
12669            let hv = e.view(&hn, t * n_embd);
12670            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12671            let mut hlast = e.zeros(n_embd)?;
12672            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12673            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12674            e.softcap(&mut ld, cap, n_vocab)?;
12675            self.gemma4_suppress(e, &mut ld, 1)?;
12676            e.dtoh(&ld)?
12677        } else {
12678            let mut ld = e.matmul(&self.output, &hn, t)?;
12679            e.softcap(&mut ld, cap, t * n_vocab)?;
12680            self.gemma4_suppress(e, &mut ld, t)?;
12681            e.dtoh(&ld)?
12682        };
12683        Ok(logits)
12684    }
12685
12686    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12687    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12688    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12689    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12690    pub(crate) fn gemma4_prime(
12691        &self,
12692        e: &Engine,
12693        tokens: &[u32],
12694        cache: &mut Cache,
12695        overlay: Option<&crate::vision::EmbedOverlay>,
12696    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12697        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12698        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12699        // whole worker process on this line. The worker now primes gemma4 monolithically and
12700        // routes continuation suffixes tokenwise; this is the per-request backstop.
12701        if cache.pos != 0 {
12702            return Err(
12703                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12704                        — prime the full prompt in one call or decode tokenwise"
12705                    .into(),
12706            );
12707        }
12708        let n_embd = self.cfg.n_embd as usize;
12709        let eps = self.cfg.rms_eps;
12710        let t = tokens.len();
12711        let pos: Vec<i32> = (0..t as i32).collect();
12712        let pos_d = e.htod_i32(&pos)?;
12713        let mut x = self.embed(e, tokens)?;
12714        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12715        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12716        // sqrt(n_embd) text scale — the reference scales token batches only
12717        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12718        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12719        // bidirectional within itself, causal+SWA everywhere else, matching the
12720        // reference's llama_set_causal_attn(false) image batch exactly.
12721        let island: Option<CudaSlice<i32>> = match overlay {
12722            Some(ov) => {
12723                let mut span_id = vec![-1i32; t];
12724                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12725                    if pos + n_rows > t {
12726                        return Err(format!(
12727                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12728                            pos + n_rows
12729                        )
12730                        .into());
12731                    }
12732                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12733                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12734                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12735                        *s = i as i32;
12736                    }
12737                }
12738                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12739                // keep the plain causal mask. Exists only so the decisive probe can show
12740                // the island mask itself changes the answer; never on in serving.
12741                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12742                    None
12743                } else {
12744                    Some(e.htod_i32(&span_id)?)
12745                }
12746            }
12747            None => None,
12748        };
12749        for (il, layer) in self.layers.iter().enumerate() {
12750            let mut h = e.zeros(t * n_embd)?;
12751            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12752            let Mixer::Full(fa) = &layer.mixer else {
12753                panic!("gemma4 layer not full-attn")
12754            };
12755            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12756            if trace {
12757                let v = e.dtoh(&h)?;
12758                let nan = v.iter().filter(|x| x.is_nan()).count();
12759                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12760            }
12761            let o =
12762                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12763            if trace {
12764                let v = e.dtoh(&o)?;
12765                let nan = v.iter().filter(|x| x.is_nan()).count();
12766                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12767            }
12768            let mut cur = e.zeros(t * n_embd)?;
12769            e.rms_norm(
12770                &o,
12771                layer.post_attn_norm.float_data(),
12772                &mut cur,
12773                n_embd,
12774                t,
12775                eps,
12776            )?;
12777            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12778            self.dflash_tap(e, cache, il, &x, t)?;
12779            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12780            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12781                let h = e.dtoh(&x)?;
12782                let nan = h.iter().filter(|v| v.is_nan()).count();
12783                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12784                eprintln!(
12785                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12786                    h.len()
12787                );
12788                if nan > 0 {
12789                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12790                }
12791            }
12792        }
12793        cache.pos += t;
12794        let hiddens = e.clone_dtod(&x)?;
12795        let xv = e.view(&x, t * n_embd);
12796        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12797        let mut h_seed = e.zeros(n_embd)?;
12798        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12799        let mut hn = e.uninit(n_embd)?;
12800        e.rms_norm(
12801            &h_seed,
12802            self.output_norm.float_data(),
12803            &mut hn,
12804            n_embd,
12805            1,
12806            eps,
12807        )?;
12808        let mut ld = e.matmul(&self.output, &hn, 1)?;
12809        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12810        e.softcap(&mut ld, cap, self.output.out_features())?;
12811        self.gemma4_suppress(e, &mut ld, 1)?;
12812        let logits = e.dtoh(&ld)?;
12813        Ok((logits, h_seed, hiddens))
12814    }
12815
12816    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12817    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12818    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12819    /// fused norm emits q8 directly — the f32 h never materializes).
12820    fn gemma4_decode_attn(
12821        &self,
12822        e: &Engine,
12823        fa: &crate::hybrid::FullAttnLayer,
12824        il: usize,
12825        hq: &CudaSlice<i8>,
12826        hdq: &CudaSlice<f32>,
12827        pos_d: &CudaSlice<i32>,
12828        cache: &mut Cache,
12829    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12830        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12831        let eps = self.cfg.rms_eps;
12832        let aux = self.gemma4_aux.as_ref().unwrap();
12833        let ones = aux.ones(e);
12834        #[cfg(debug_assertions)]
12835        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12836        let (hq, hdq) = (hq, hdq);
12837        let h0 = e.zeros(0)?;
12838        let h = &h0;
12839        let (q0, k0, v0) = if swa {
12840            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12841                Some(t3) => t3,
12842                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12843                // match — fuse the uniform (q,k) pair and take v as its own single.
12844                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12845                    Some((q0, k0)) => {
12846                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12847                        (q0, k0, v0)
12848                    }
12849                    None => (
12850                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12851                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12852                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12853                    ),
12854                },
12855            }
12856        } else {
12857            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12858                Some(p) => p,
12859                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12860                    Some(p) => p,
12861                    None => (
12862                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12863                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12864                    ),
12865                },
12866            };
12867            let v0 = e.clone_dtod(&k0)?;
12868            (q0, k0, v0)
12869        };
12870        let mut q = e.uninit(nh * hd)?;
12871        let mut k = e.uninit(nkv * hd)?;
12872        let mut v = e.uninit(nkv * hd)?;
12873        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12874        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12875        let ff = if swa {
12876            None
12877        } else {
12878            Some(
12879                aux.rope_freqs(e)
12880                    .expect("gemma4 global rope needs rope_freqs.weight"),
12881            )
12882        };
12883        #[cfg(debug_assertions)]
12884        if let Some(ff) = ff {
12885            crate::debug_assert_tensor_stream_device(
12886                ff,
12887                &e.stream(),
12888                "gemma4_decode_attn.rope_freqs",
12889            );
12890        }
12891        let kvl = cache.kv[il].as_mut().unwrap();
12892        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12893        if crate::Engine::qkv_append_on() {
12894            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12895            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12896            // twin of the dc fold — bit-identical bodies, one launch per layer.
12897            e.rms_norm_qkv_rope_append(
12898                &q0,
12899                &k0,
12900                &v0,
12901                fa.q_norm.float_data(),
12902                fa.k_norm.float_data(),
12903                ones,
12904                &mut q,
12905                &mut k,
12906                &mut v,
12907                hd,
12908                self.gemma4_rope_dims(il),
12909                nh,
12910                nkv,
12911                pos_d,
12912                nh,
12913                nkv,
12914                base,
12915                1.0,
12916                ff,
12917                eps,
12918                &mut kvl.k,
12919                &mut kvl.v,
12920                kvl.len,
12921                kvl.k_tok_bytes,
12922                kvl.v_tok_bytes,
12923                kv_fp8,
12924            )?;
12925        } else {
12926            e.rms_norm_qkv_rope(
12927                &q0,
12928                &k0,
12929                &v0,
12930                fa.q_norm.float_data(),
12931                fa.k_norm.float_data(),
12932                ones,
12933                &mut q,
12934                &mut k,
12935                &mut v,
12936                hd,
12937                self.gemma4_rope_dims(il),
12938                nh,
12939                nkv,
12940                pos_d,
12941                nh,
12942                nkv,
12943                base,
12944                1.0,
12945                ff,
12946                eps,
12947            )?;
12948            e.append_kv_quantized(
12949                &k,
12950                &v,
12951                &mut kvl.k,
12952                &mut kvl.v,
12953                kvl.len,
12954                kvl.kv_dim_k,
12955                kvl.kv_dim_v,
12956                kvl.k_tok_bytes,
12957                kvl.v_tok_bytes,
12958                kv_fp8,
12959            )?;
12960        }
12961        kvl.len += 1;
12962        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12963        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12964        // positional). Globals attend the full history.
12965        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12966        let mut attn = e.uninit(nh * hd)?;
12967        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12968        if !swa
12969            && hd == 512
12970            && kvl.len >= crate::fa512_min_tkv()
12971            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12972        {
12973            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12974            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12975            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12976            let base = kvl.len as i32;
12977            e.i32_set_k(&mut kvl.len_d, base)?;
12978            e.fa_decode_rows(
12979                &q,
12980                &kp,
12981                &vp,
12982                &mut attn,
12983                hd,
12984                nh,
12985                nkv,
12986                kvl.len - 1,
12987                1,
12988                scale,
12989                kvl.k_tok_bytes,
12990                kvl.v_tok_bytes,
12991                Some((&kvl.len_d, -1)),
12992                false,
12993                false,
12994                None,
12995            )?;
12996            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12997        }
12998        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12999        if swa
13000            && kvl.len > win
13001            && hd == 256
13002            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13003        {
13004            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13005            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13006            let base = kvl.len as i32;
13007            e.i32_set_k(&mut kvl.len_d, base)?;
13008            e.fa_decode_rows_w(
13009                &q,
13010                &kp,
13011                &vp,
13012                &mut attn,
13013                hd,
13014                nh,
13015                nkv,
13016                &kvl.len_d,
13017                -1,
13018                1,
13019                scale,
13020                win,
13021                kvl.k_tok_bytes,
13022                kvl.v_tok_bytes,
13023                None,
13024            )?;
13025            return Ok(e.matmul(&fa.wo, &attn, 1)?);
13026        }
13027        let (off_tok, t_kv) = if swa && kvl.len > win {
13028            (kvl.len - win, win)
13029        } else {
13030            (0, kvl.len)
13031        };
13032        let k_view = e.view_u8_range(
13033            &kvl.k,
13034            off_tok * kvl.k_tok_bytes,
13035            (off_tok + t_kv) * kvl.k_tok_bytes,
13036        );
13037        let v_view = e.view_u8_range(
13038            &kvl.v,
13039            off_tok * kvl.v_tok_bytes,
13040            (off_tok + t_kv) * kvl.v_tok_bytes,
13041        );
13042        e.fa_decode_kvmod(
13043            &q,
13044            &k_view,
13045            &v_view,
13046            &mut attn,
13047            hd,
13048            nh,
13049            nkv,
13050            t_kv,
13051            scale,
13052            kvl.k_tok_bytes,
13053            kvl.v_tok_bytes,
13054            swa && crate::Engine::wkv_on(),
13055        )?;
13056        Ok(e.matmul(&fa.wo, &attn, 1)?)
13057    }
13058
13059    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
13060    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
13061    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
13062    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
13063    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
13064    /// in-graph; the driver gates).
13065    #[allow(clippy::too_many_arguments)]
13066    pub fn gemma4_decode_step_dc(
13067        &self,
13068        e: &Engine,
13069        token_d: &CudaSlice<u32>,
13070        pos_d: &mut CudaSlice<i32>,
13071        embd_gpu: &CudaSlice<u8>,
13072        embd_qt: i32,
13073        embd_rb: usize,
13074        cache: &mut Cache,
13075        n_vocab: usize,
13076        cap_bucket_max: Option<(usize, usize)>,
13077    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
13078        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
13079        self.gemma4_decode_step_dc_into(
13080            e,
13081            token_d,
13082            pos_d,
13083            embd_gpu,
13084            embd_qt,
13085            embd_rb,
13086            cache,
13087            n_vocab,
13088            cap_bucket_max,
13089            &mut tok_out,
13090        )?;
13091        Ok(tok_out)
13092    }
13093
13094    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
13095    /// every replay; pass `token_d` itself for the self-feeding graph loop).
13096    #[allow(clippy::too_many_arguments)]
13097    pub fn gemma4_decode_step_dc_into(
13098        &self,
13099        e: &Engine,
13100        token_d: &CudaSlice<u32>,
13101        pos_d: &mut CudaSlice<i32>,
13102        embd_gpu: &CudaSlice<u8>,
13103        embd_qt: i32,
13104        embd_rb: usize,
13105        cache: &mut Cache,
13106        n_vocab: usize,
13107        cap_bucket_max: Option<(usize, usize)>,
13108        tok_out: &mut CudaSlice<u32>,
13109    ) -> Result<(), Box<dyn std::error::Error>> {
13110        let n_embd = self.cfg.n_embd as usize;
13111        let eps = self.cfg.rms_eps;
13112        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
13113        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
13114        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13115        let n_layers = self.layers.len();
13116        for (il, layer) in self.layers.iter().enumerate() {
13117            let (hq, hdq) = match h_carry.take() {
13118                Some(p) => p,
13119                None => {
13120                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
13121                }
13122            };
13123            let Mixer::Full(fa) = &layer.mixer else {
13124                panic!("gemma4 layer {il} not full-attn")
13125            };
13126            let o =
13127                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
13128            let next_norm = if il + 1 < n_layers {
13129                Some(self.layers[il + 1].attn_norm.float_data())
13130            } else {
13131                None
13132            };
13133            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
13134            x = xn;
13135            h_carry = hn;
13136        }
13137        let mut hn = e.uninit(n_embd)?;
13138        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
13139        let mut logits = e.matmul(&self.output, &hn, 1)?;
13140        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
13141        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
13142        e.inc_seqlen(pos_d)?;
13143        if cap_bucket_max.is_none() {
13144            cache.pos += 1;
13145        }
13146        Ok(())
13147    }
13148
13149    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
13150    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
13151    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
13152    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
13153
13154    /// Build the slot set (call OUTSIDE any capture).
13155    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
13156        let n_embd = self.cfg.n_embd as usize;
13157        let n_vocab = self.output.out_features();
13158        let n_layers = self.layers.len();
13159        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
13160        for il in 0..n_layers {
13161            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
13162            qmax = qmax.max(nh * hd);
13163            kvmax = kvmax.max(nkv * hd);
13164            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
13165                ffmax = ffmax.max(ffn_gate.out_features());
13166            }
13167        }
13168        Ok(G4DcSlots {
13169            x: e.uninit(n_embd)?,
13170            xn: e.uninit(n_embd)?,
13171            cur: e.uninit(n_embd)?,
13172            hq: e.alloc_i8_uninit(n_embd)?,
13173            hd_: e.uninit(n_embd / 32)?,
13174            q0: e.uninit(qmax)?,
13175            k0: e.uninit(kvmax)?,
13176            v0: e.uninit(kvmax)?,
13177            q: e.uninit(qmax)?,
13178            k: e.uninit(kvmax)?,
13179            v: e.uninit(kvmax)?,
13180            attn: e.uninit(qmax)?,
13181            o: e.uninit(n_embd)?,
13182            attn_out: e.uninit(n_embd)?,
13183            zsh: e.uninit(n_embd)?,
13184            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
13185            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
13186            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
13187            zd: e.uninit(n_embd.max(qmax) / 32)?,
13188            gate: e.uninit(ffmax)?,
13189            up: e.uninit(ffmax)?,
13190            act: e.uninit(ffmax)?,
13191            actq: e.alloc_i8_uninit(ffmax)?,
13192            actd: e.uninit(ffmax / 32)?,
13193            f0: e.uninit(n_embd)?,
13194            sn: e.uninit(n_embd)?,
13195            hn: e.uninit(n_embd)?,
13196            logits: e.uninit(n_vocab)?,
13197        })
13198    }
13199
13200    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
13201    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
13202    fn g4_matvec_m1_into(
13203        &self,
13204        e: &Engine,
13205        w: &crate::model::GpuTensor,
13206        aq: &CudaSlice<i8>,
13207        ad: &CudaSlice<f32>,
13208        y: &mut CudaSlice<f32>,
13209    ) -> Result<(), Box<dyn std::error::Error>> {
13210        use crate::model::GpuTensor;
13211        let (bytes, qtype, row_bytes, scale, rp) = match w {
13212            GpuTensor::Quant {
13213                bytes,
13214                qtype,
13215                row_bytes,
13216                scale,
13217                rp,
13218                ..
13219            } => (bytes, *qtype, *row_bytes, *scale, *rp),
13220            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
13221        };
13222        let (mbytes, mrp) = match w {
13223            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13224            _ => (bytes, rp),
13225        };
13226        e.qmatvec_mmvq_into(
13227            mbytes,
13228            aq,
13229            ad,
13230            1,
13231            w.in_features(),
13232            w.out_features(),
13233            qtype,
13234            row_bytes,
13235            scale,
13236            mrp,
13237            y,
13238        )
13239    }
13240
13241    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
13242    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
13243    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
13244    #[allow(clippy::too_many_arguments)]
13245    pub fn gemma4_decode_step_dc_slotted(
13246        &self,
13247        e: &Engine,
13248        token_d: &CudaSlice<u32>,
13249        pos_d: &mut CudaSlice<i32>,
13250        embd_gpu: &CudaSlice<u8>,
13251        embd_qt: i32,
13252        embd_rb: usize,
13253        cache: &mut Cache,
13254        n_vocab: usize,
13255        cap_bucket_max: Option<(usize, usize)>,
13256        sl: &mut G4DcSlots,
13257        tok_out: &mut CudaSlice<u32>,
13258        ring: Option<(&mut CudaSlice<u32>, usize)>,
13259    ) -> Result<(), Box<dyn std::error::Error>> {
13260        let n_embd = self.cfg.n_embd as usize;
13261        let eps = self.cfg.rms_eps;
13262        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
13263        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
13264        let n_layers = self.layers.len();
13265        let mut has_carry = false;
13266        for il in 0..n_layers {
13267            if !has_carry {
13268                e.rms_norm_q8_1_into(
13269                    &sl.x,
13270                    self.layers[il].attn_norm.float_data(),
13271                    n_embd,
13272                    1,
13273                    eps,
13274                    &mut sl.hq,
13275                    &mut sl.hd_,
13276                )?;
13277            }
13278            has_carry = true;
13279            let layer = &self.layers[il];
13280            let Mixer::Full(fa) = &layer.mixer else {
13281                panic!("gemma4 layer {il} not full-attn")
13282            };
13283            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
13284            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
13285            // the standalone norm only survives on the unfused seam arm.
13286            if !Engine::g4_pnfold_on() {
13287                e.rms_norm(
13288                    &sl.o,
13289                    layer.post_attn_norm.float_data(),
13290                    &mut sl.cur,
13291                    n_embd,
13292                    1,
13293                    eps,
13294                )?;
13295            }
13296            let next_norm = if il + 1 < n_layers {
13297                Some(self.layers[il + 1].attn_norm.float_data())
13298            } else {
13299                None
13300            };
13301            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
13302            std::mem::swap(&mut sl.x, &mut sl.xn);
13303        }
13304        e.rms_norm(
13305            &sl.x,
13306            self.output_norm.float_data(),
13307            &mut sl.hn,
13308            n_embd,
13309            1,
13310            eps,
13311        )?;
13312        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13313        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
13314        {
13315            let (zq, zd) = (&sl.zq, &sl.zd);
13316            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
13317            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
13318            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
13319        }
13320        self.gemma4_suppress(e, &mut sl.logits, 1)?;
13321        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
13322        if let Some((ring, base)) = ring {
13323            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
13324            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
13325            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
13326            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
13327        }
13328        e.inc_seqlen(pos_d)?;
13329        if cap_bucket_max.is_none() {
13330            cache.pos += 1;
13331        }
13332        Ok(())
13333    }
13334
13335    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
13336    #[allow(clippy::too_many_arguments)]
13337    fn gemma4_decode_attn_dc_slotted(
13338        &self,
13339        e: &Engine,
13340        fa: &crate::hybrid::FullAttnLayer,
13341        il: usize,
13342        pos_d: &CudaSlice<i32>,
13343        cache: &mut Cache,
13344        cap_bucket_max: Option<(usize, usize)>,
13345        sl: &mut G4DcSlots,
13346    ) -> Result<(), Box<dyn std::error::Error>> {
13347        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13348        let eps = self.cfg.rms_eps;
13349        let aux = self.gemma4_aux.as_ref().unwrap();
13350        let ones = aux.ones(e);
13351        #[cfg(debug_assertions)]
13352        crate::debug_assert_tensor_stream_device(
13353            ones,
13354            &e.stream(),
13355            "gemma4_decode_attn_dc_slotted.ones",
13356        );
13357        {
13358            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
13359            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
13360            if swa {
13361                if !e.matmul_q4_fused3_into(
13362                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
13363                )? {
13364                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
13365                    // (q,k) pair, v through the generic m1 slot matvec — the same two
13366                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
13367                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13368                    {
13369                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
13370                    } else {
13371                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
13372                    }
13373                }
13374            } else {
13375                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13376                    && !e
13377                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13378                {
13379                    return Err("slotted step: fused2 unavailable".into());
13380                }
13381                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
13382                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
13383            }
13384        }
13385        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
13386        // kernel-for-kernel (graph stream-identity gate).
13387        let ff = if swa {
13388            None
13389        } else {
13390            Some(
13391                aux.rope_freqs(e)
13392                    .expect("gemma4 global rope needs rope_freqs.weight"),
13393            )
13394        };
13395        #[cfg(debug_assertions)]
13396        if let Some(ff) = ff {
13397            crate::debug_assert_tensor_stream_device(
13398                ff,
13399                &e.stream(),
13400                "gemma4_decode_attn_dc_slotted.rope_freqs",
13401            );
13402        }
13403        let kvl = cache.kv[il].as_mut().unwrap();
13404        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13405        if crate::Engine::qkv_append_on() {
13406            // append fold (2026-07-23): mirrors dc_into.
13407            e.rms_norm_qkv_rope_append_dc(
13408                &sl.q0,
13409                &sl.k0,
13410                &sl.v0,
13411                fa.q_norm.float_data(),
13412                fa.k_norm.float_data(),
13413                ones,
13414                &mut sl.q,
13415                &mut sl.k,
13416                &mut sl.v,
13417                hd,
13418                self.gemma4_rope_dims(il),
13419                nh,
13420                nkv,
13421                pos_d,
13422                nh,
13423                nkv,
13424                base,
13425                1.0,
13426                ff,
13427                eps,
13428                &mut kvl.k,
13429                &mut kvl.v,
13430                &kvl.len_d,
13431                kvl.k_tok_bytes,
13432                kvl.v_tok_bytes,
13433                kv_fp8,
13434            )?;
13435        } else {
13436            e.rms_norm_qkv_rope(
13437                &sl.q0,
13438                &sl.k0,
13439                &sl.v0,
13440                fa.q_norm.float_data(),
13441                fa.k_norm.float_data(),
13442                ones,
13443                &mut sl.q,
13444                &mut sl.k,
13445                &mut sl.v,
13446                hd,
13447                self.gemma4_rope_dims(il),
13448                nh,
13449                nkv,
13450                pos_d,
13451                nh,
13452                nkv,
13453                base,
13454                1.0,
13455                ff,
13456                eps,
13457            )?;
13458            e.append_kv_quantized_dc(
13459                &sl.k,
13460                &sl.v,
13461                &mut kvl.k,
13462                &mut kvl.v,
13463                &kvl.len_d,
13464                kvl.kv_dim_k,
13465                kvl.kv_dim_v,
13466                kvl.k_tok_bytes,
13467                kvl.v_tok_bytes,
13468                kv_fp8,
13469            )?;
13470        }
13471        e.inc_seqlen(&mut kvl.len_d)?;
13472        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13473        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13474        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13475        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13476        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13477        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13478        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13479        // the dc_into arm branch-for-branch (stream gate).
13480        let mut fa_q8 = false;
13481        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13482            e.fa_decode_rows(
13483                &sl.q,
13484                &k_view,
13485                &v_view,
13486                &mut sl.attn,
13487                hd,
13488                nh,
13489                nkv,
13490                b_glob - 1,
13491                1,
13492                scale,
13493                kvl.k_tok_bytes,
13494                kvl.v_tok_bytes,
13495                Some((&kvl.len_d, -1)),
13496                false,
13497                false,
13498                Some((&mut sl.zq, &mut sl.zd)),
13499            )?;
13500            fa_q8 = true;
13501        } else if swa && b_swa > win && hd == 256 && rows_on {
13502            e.fa_decode_rows_w(
13503                &sl.q,
13504                &k_view,
13505                &v_view,
13506                &mut sl.attn,
13507                hd,
13508                nh,
13509                nkv,
13510                &kvl.len_d,
13511                -1,
13512                1,
13513                scale,
13514                win,
13515                kvl.k_tok_bytes,
13516                kvl.v_tok_bytes,
13517                Some((&mut sl.zq, &mut sl.zd)),
13518            )?;
13519            fa_q8 = true;
13520        } else {
13521            let b = if swa { b_swa } else { b_glob };
13522            e.fa_decode_dc(
13523                &sl.q,
13524                &k_view,
13525                &v_view,
13526                &mut sl.attn,
13527                hd,
13528                nh,
13529                nkv,
13530                &kvl.len_d,
13531                b,
13532                scale,
13533                kvl.k_tok_bytes,
13534                kvl.v_tok_bytes,
13535                swa && crate::Engine::wkv_on(),
13536            )?;
13537        }
13538        if !fa_q8 {
13539            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13540            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13541        }
13542        {
13543            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13544            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13545            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13546        }
13547        Ok(())
13548    }
13549
13550    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13551    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13552    fn gemma4_layer_tail_slotted(
13553        &self,
13554        e: &Engine,
13555        layer: &crate::hybrid::HybridLayer,
13556        next_norm: Option<&CudaSlice<f32>>,
13557        sl: &mut G4DcSlots,
13558    ) -> Result<(), Box<dyn std::error::Error>> {
13559        let n_embd = self.cfg.n_embd as usize;
13560        let eps = self.cfg.rms_eps;
13561        let bits = layer.gemma4.as_ref().unwrap();
13562        let crate::hybrid::Ffn::Dense {
13563            ffn_gate,
13564            ffn_up,
13565            ffn_down,
13566        } = &layer.ffn
13567        else {
13568            return Err("slotted tail: dense ffn only".into());
13569        };
13570        let pnfold = Engine::g4_pnfold_on();
13571        if pnfold {
13572            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13573            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13574            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13575            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13576            e.rms_pre_add_rms_norm_q8z_into(
13577                or,
13578                layer.post_attn_norm.float_data(),
13579                xr,
13580                bits.ffn_norm.float_data(),
13581                &mut sl.attn_out,
13582                &mut sl.zsh,
13583                n_embd,
13584                1,
13585                eps,
13586                &mut sl.zq,
13587                &mut sl.zd,
13588            )?;
13589        } else {
13590            e.add_rms_norm(
13591                &sl.cur,
13592                &sl.x,
13593                bits.ffn_norm.float_data(),
13594                &mut sl.attn_out,
13595                &mut sl.zsh,
13596                n_embd,
13597                1,
13598                eps,
13599            )?;
13600        }
13601        let n_ff = ffn_gate.out_features();
13602        if !pnfold {
13603            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13604            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13605        }
13606        {
13607            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13608            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13609            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13610                && !e.matmul_nvfp4_fused2_into(
13611                    ffn_gate,
13612                    ffn_up,
13613                    zq,
13614                    zd,
13615                    &mut sl.gate,
13616                    &mut sl.up,
13617                )?
13618            {
13619                return Err("slotted tail: ffn fused2 unavailable".into());
13620            }
13621        }
13622        debug_assert!(e.uses_q8_1_fast(ffn_down));
13623        {
13624            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13625            let upv = e.view(upr, n_ff);
13626            let up_all = upv.slice(0..n_ff);
13627            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13628            e.gelu_tanh_mul_q8_1_into(
13629                gr,
13630                &up_all,
13631                &mut sl.act,
13632                n_ff,
13633                1,
13634                &mut sl.actq,
13635                &mut sl.actd,
13636            )?;
13637        }
13638        {
13639            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13640            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13641            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13642        }
13643        if pnfold {
13644            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13645            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13646            if let Some(w) = next_norm {
13647                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13648                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13649                e.rms_pre_add_scale_rms_norm_q8_1_into(
13650                    f0r,
13651                    bits.post_ffw_norm.float_data(),
13652                    aor,
13653                    bits.layer_scale,
13654                    w,
13655                    &mut sl.xn,
13656                    n_embd,
13657                    1,
13658                    eps,
13659                    &mut sl.hq,
13660                    &mut sl.hd_,
13661                )?;
13662                return Ok(());
13663            }
13664        }
13665        e.rms_norm(
13666            &sl.f0,
13667            bits.post_ffw_norm.float_data(),
13668            &mut sl.sn,
13669            n_embd,
13670            1,
13671            eps,
13672        )?;
13673        match next_norm {
13674            Some(w) => {
13675                e.add_scale_rms_norm_q8_1_into(
13676                    &sl.sn,
13677                    &sl.attn_out,
13678                    bits.layer_scale,
13679                    w,
13680                    &mut sl.xn,
13681                    n_embd,
13682                    1,
13683                    eps,
13684                    &mut sl.hq,
13685                    &mut sl.hd_,
13686                )?;
13687            }
13688            None => {
13689                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13690            }
13691        }
13692        Ok(())
13693    }
13694
13695    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13696    #[allow(clippy::too_many_arguments)]
13697    fn gemma4_decode_attn_dc(
13698        &self,
13699        e: &Engine,
13700        fa: &crate::hybrid::FullAttnLayer,
13701        il: usize,
13702        hq: &CudaSlice<i8>,
13703        hdq: &CudaSlice<f32>,
13704        pos_d: &CudaSlice<i32>,
13705        cache: &mut Cache,
13706        cap_bucket_max: Option<(usize, usize)>,
13707    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13708        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13709        let eps = self.cfg.rms_eps;
13710        let aux = self.gemma4_aux.as_ref().unwrap();
13711        let ones = aux.ones(e);
13712        #[cfg(debug_assertions)]
13713        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13714        let (q0, k0, v0) = if swa {
13715            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13716                Some(t3) => t3,
13717                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13718                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13719                    Some((q0, k0)) => {
13720                        let h0 = e.zeros(0)?;
13721                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13722                        (q0, k0, v0)
13723                    }
13724                    None => {
13725                        let h0 = e.zeros(0)?;
13726                        (
13727                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13728                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13729                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13730                        )
13731                    }
13732                },
13733            }
13734        } else {
13735            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13736                Some(p) => p,
13737                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13738                    Some(p) => p,
13739                    None => {
13740                        let h0 = e.zeros(0)?;
13741                        (
13742                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13743                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13744                        )
13745                    }
13746                },
13747            };
13748            let v0 = e.clone_dtod(&k0)?;
13749            (q0, k0, v0)
13750        };
13751        let mut q = e.uninit(nh * hd)?;
13752        let mut k = e.uninit(nkv * hd)?;
13753        let mut v = e.uninit(nkv * hd)?;
13754        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13755        let ff = if swa {
13756            None
13757        } else {
13758            Some(
13759                aux.rope_freqs(e)
13760                    .expect("gemma4 global rope needs rope_freqs.weight"),
13761            )
13762        };
13763        #[cfg(debug_assertions)]
13764        if let Some(ff) = ff {
13765            crate::debug_assert_tensor_stream_device(
13766                ff,
13767                &e.stream(),
13768                "gemma4_decode_attn_dc.rope_freqs",
13769            );
13770        }
13771        let kvl = cache.kv[il].as_mut().unwrap();
13772        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13773        if crate::Engine::qkv_append_on() {
13774            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13775            e.rms_norm_qkv_rope_append_dc(
13776                &q0,
13777                &k0,
13778                &v0,
13779                fa.q_norm.float_data(),
13780                fa.k_norm.float_data(),
13781                ones,
13782                &mut q,
13783                &mut k,
13784                &mut v,
13785                hd,
13786                self.gemma4_rope_dims(il),
13787                nh,
13788                nkv,
13789                pos_d,
13790                nh,
13791                nkv,
13792                base,
13793                1.0,
13794                ff,
13795                eps,
13796                &mut kvl.k,
13797                &mut kvl.v,
13798                &kvl.len_d,
13799                kvl.k_tok_bytes,
13800                kvl.v_tok_bytes,
13801                kv_fp8,
13802            )?;
13803        } else {
13804            e.rms_norm_qkv_rope(
13805                &q0,
13806                &k0,
13807                &v0,
13808                fa.q_norm.float_data(),
13809                fa.k_norm.float_data(),
13810                ones,
13811                &mut q,
13812                &mut k,
13813                &mut v,
13814                hd,
13815                self.gemma4_rope_dims(il),
13816                nh,
13817                nkv,
13818                pos_d,
13819                nh,
13820                nkv,
13821                base,
13822                1.0,
13823                ff,
13824                eps,
13825            )?;
13826            e.append_kv_quantized_dc(
13827                &k,
13828                &v,
13829                &mut kvl.k,
13830                &mut kvl.v,
13831                &kvl.len_d,
13832                kvl.kv_dim_k,
13833                kvl.kv_dim_v,
13834                kvl.k_tok_bytes,
13835                kvl.v_tok_bytes,
13836                kv_fp8,
13837            )?;
13838        }
13839        e.inc_seqlen(&mut kvl.len_d)?;
13840        let mut attn = e.uninit(nh * hd)?;
13841        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13842        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13843        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13844        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13845        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13846        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13847        // (gemma4_e4b_attn, +0.65% valid window).
13848        match cap_bucket_max {
13849            None => {
13850                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13851                // decode (SWA layers attend the last `sliding_window` keys); the device
13852                // counters carry only the append slot + the graph seam.
13853                kvl.len += 1;
13854                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13855                if !swa
13856                    && hd == 512
13857                    && kvl.len >= crate::fa512_min_tkv()
13858                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13859                {
13860                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13861                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13862                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13863                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13864                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13865                    e.fa_decode_rows(
13866                        &q,
13867                        &kp,
13868                        &vp,
13869                        &mut attn,
13870                        hd,
13871                        nh,
13872                        nkv,
13873                        kvl.len - 1,
13874                        1,
13875                        scale,
13876                        kvl.k_tok_bytes,
13877                        kvl.v_tok_bytes,
13878                        Some((&kvl.len_d, -1)),
13879                        false,
13880                        false,
13881                        Some((&mut aq8, &mut ad8)),
13882                    )?;
13883                    fa_q8 = Some((aq8, ad8));
13884                } else if swa
13885                    && kvl.len > win
13886                    && hd == 256
13887                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13888                {
13889                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13890                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13891                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13892                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13893                    e.fa_decode_rows_w(
13894                        &q,
13895                        &kp,
13896                        &vp,
13897                        &mut attn,
13898                        hd,
13899                        nh,
13900                        nkv,
13901                        &kvl.len_d,
13902                        -1,
13903                        1,
13904                        scale,
13905                        win,
13906                        kvl.k_tok_bytes,
13907                        kvl.v_tok_bytes,
13908                        Some((&mut aq8, &mut ad8)),
13909                    )?;
13910                    fa_q8 = Some((aq8, ad8));
13911                } else {
13912                    let (off_tok, t_kv) = if swa && kvl.len > win {
13913                        (kvl.len - win, win)
13914                    } else {
13915                        (0, kvl.len)
13916                    };
13917                    let k_view = e.view_u8_range(
13918                        &kvl.k,
13919                        off_tok * kvl.k_tok_bytes,
13920                        (off_tok + t_kv) * kvl.k_tok_bytes,
13921                    );
13922                    let v_view = e.view_u8_range(
13923                        &kvl.v,
13924                        off_tok * kvl.v_tok_bytes,
13925                        (off_tok + t_kv) * kvl.v_tok_bytes,
13926                    );
13927                    e.fa_decode_kvmod(
13928                        &q,
13929                        &k_view,
13930                        &v_view,
13931                        &mut attn,
13932                        hd,
13933                        nh,
13934                        nkv,
13935                        t_kv,
13936                        scale,
13937                        kvl.k_tok_bytes,
13938                        kvl.v_tok_bytes,
13939                        swa && crate::Engine::wkv_on(),
13940                    )?;
13941                }
13942            }
13943            Some((b_swa, b_glob)) => {
13944                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13945                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13946                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13947                // the RUNG max for the rows family (kernels derive per-replay splits from
13948                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13949                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13950                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13951                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13952                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13953                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13954                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13955                    e.fa_decode_rows(
13956                        &q,
13957                        &k_view,
13958                        &v_view,
13959                        &mut attn,
13960                        hd,
13961                        nh,
13962                        nkv,
13963                        b_glob - 1,
13964                        1,
13965                        scale,
13966                        kvl.k_tok_bytes,
13967                        kvl.v_tok_bytes,
13968                        Some((&kvl.len_d, -1)),
13969                        false,
13970                        false,
13971                        Some((&mut aq8, &mut ad8)),
13972                    )?;
13973                    fa_q8 = Some((aq8, ad8));
13974                } else if swa && b_swa > win && hd == 256 && rows_on {
13975                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13976                    e.fa_decode_rows_w(
13977                        &q,
13978                        &k_view,
13979                        &v_view,
13980                        &mut attn,
13981                        hd,
13982                        nh,
13983                        nkv,
13984                        &kvl.len_d,
13985                        -1,
13986                        1,
13987                        scale,
13988                        win,
13989                        kvl.k_tok_bytes,
13990                        kvl.v_tok_bytes,
13991                        Some((&mut aq8, &mut ad8)),
13992                    )?;
13993                    fa_q8 = Some((aq8, ad8));
13994                } else {
13995                    let b = if swa { b_swa } else { b_glob };
13996                    e.fa_decode_dc(
13997                        &q,
13998                        &k_view,
13999                        &v_view,
14000                        &mut attn,
14001                        hd,
14002                        nh,
14003                        nkv,
14004                        &kvl.len_d,
14005                        b,
14006                        scale,
14007                        kvl.k_tok_bytes,
14008                        kvl.v_tok_bytes,
14009                        swa && crate::Engine::wkv_on(),
14010                    )?;
14011                }
14012            }
14013        }
14014        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
14015        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
14016        if let Some((aq8, ad8)) = fa_q8 {
14017            let mut y = e.uninit(fa.wo.out_features())?;
14018            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
14019            return Ok(y);
14020        }
14021        Ok(e.matmul(&fa.wo, &attn, 1)?)
14022    }
14023
14024    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
14025    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
14026    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
14027    /// views in-graph); caller gates and falls back to the dc-eager loop.
14028    pub fn gemma4_generate_graph(
14029        &self,
14030        e: &Engine,
14031        prompt_pos: usize,
14032        first_token: u32,
14033        cache: &mut Cache,
14034        max_new: usize,
14035        eos: &[u32],
14036        mut on_token: impl FnMut(u32) -> bool,
14037    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
14038        if self.is_gemma4_e4b() {
14039            return Err(
14040                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
14041                    .into(),
14042            );
14043        }
14044        use crate::decode::StopReason;
14045        let n_vocab = self.output.out_features();
14046        let n_embd = self.cfg.n_embd as usize;
14047        let embd_gpu = self
14048            .embd_gpu
14049            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14050        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14051        for kvl in cache.kv.iter_mut().flatten() {
14052            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
14053        }
14054        let mut token_d = e.stream().clone_htod(&[first_token])?;
14055        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
14056        let g4 = self.cfg.gemma4.as_ref().unwrap();
14057        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
14058        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
14059        let nkv_s = g4
14060            .head_count_kv
14061            .iter()
14062            .zip(g4.swa_pattern.iter())
14063            .find(|p| *p.1)
14064            .map(|p| *p.0 as usize)
14065            .unwrap_or(8);
14066        let nkv_g = g4
14067            .head_count_kv
14068            .iter()
14069            .zip(g4.swa_pattern.iter())
14070            .find(|p| !*p.1)
14071            .map(|p| *p.0 as usize)
14072            .unwrap_or(2);
14073        let mut graphs: std::collections::HashMap<
14074            ((bool, usize), (bool, usize), bool, bool),
14075            (
14076                cudarc::driver::CudaGraph,
14077                Vec<Box<dyn std::any::Any + Send>>,
14078            ),
14079        > = Default::default();
14080        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
14081        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
14082        let mut slots = self.g4_dc_slots(e)?;
14083        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
14084        // baked at the door entry (the modulo keeps every capture valid indefinitely).
14085        const RING: usize = 64;
14086        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
14087        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
14088        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
14089        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
14090        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
14091        const DRAIN: usize = 1;
14092        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
14093        let ring_base = prompt_pos;
14094        let mut out = Vec::with_capacity(max_new);
14095        let mut reason = StopReason::MaxNew;
14096        let mut next = first_token;
14097        let mut captures = 0usize;
14098        for _ in 0..max_new {
14099            out.push(next);
14100            if eos.contains(&next) {
14101                reason = StopReason::Eos;
14102                break;
14103            }
14104            if !on_token(next) {
14105                reason = StopReason::Callback;
14106                break;
14107            }
14108            let t_kv = cache.pos + 1;
14109            // Bucket key per ARM (graph arc step 3):
14110            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
14111            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
14112            //    the component collapses to a single marker).
14113            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
14114            //    at/above it — the kernel derives splits from len_d per replay, so buckets
14115            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
14116            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14117            let f512 = crate::fa512_min_tkv();
14118            let key_s = if t_kv > win {
14119                (true, usize::MAX)
14120            } else {
14121                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
14122            };
14123            let (key_g, rung_end) = if t_kv >= f512 {
14124                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
14125                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
14126                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
14127                ((true, end), end)
14128            } else {
14129                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
14130            };
14131            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
14132            if !graphs.contains_key(&key) {
14133                let bucket_max = (t_kv, rung_end);
14134                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
14135                let snap = cache.snapshot(e)?;
14136                let pos_save = e.dtoh_i32_one(&pos_d)?;
14137                let len_save: Vec<Option<i32>> = cache
14138                    .kv
14139                    .iter()
14140                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
14141                    .collect();
14142                let tok_save = e.dtoh_u32_one(&token_d)?;
14143                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
14144                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
14145                // regression class, and this door's measured -8.8%. The keeper pins warmup
14146                // transients so the captured graph holds kernel nodes only.
14147                let graph = {
14148                    let tok_ref = &mut token_d;
14149                    let pos_ref = &mut pos_d;
14150                    let cache_ref = &mut *cache;
14151                    let slots_ref = &mut slots;
14152                    let ring_ref = &mut ring;
14153                    e.capture_graph_retained_flags(
14154                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
14155                        |e| {
14156                        // self-feeding: the argmax writes token_d itself.
14157                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
14158                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
14159                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
14160                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
14161                                                           cache_ref, n_vocab, Some(bucket_max),
14162                                                           sl, tok_ref, Some((rg, ring_base)))
14163                    })?
14164                };
14165                cache.rollback(e, &snap, 0)?;
14166                e.set_i32_one(&mut pos_d, pos_save)?;
14167                for (il, ls) in len_save.iter().enumerate() {
14168                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
14169                        e.set_i32_one(&mut kvl.len_d, *v)?;
14170                    }
14171                }
14172                e.set_u32_one(&mut token_d, tok_save)?;
14173                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
14174                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
14175                        eprintln!("[graph-census] {c:?}");
14176                    }
14177                }
14178                graphs.insert(key, graph);
14179                captures += 1;
14180            }
14181            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
14182            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
14183            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
14184            // the budget; capture warmups already emitted their tokens through the ring.
14185            let mut chunk = 1usize;
14186            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
14187                .ok()
14188                .and_then(|v| v.parse().ok())
14189                .unwrap_or(DRAIN);
14190            while chunk < drain_cap && out.len() + chunk < max_new {
14191                let t_next = cache.pos + 1 + chunk;
14192                let key_s2 = if t_next > win {
14193                    (true, usize::MAX)
14194                } else {
14195                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
14196                };
14197                let key_g2 = if t_next >= f512 {
14198                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
14199                } else {
14200                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
14201                };
14202                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
14203                    break;
14204                }
14205                chunk += 1;
14206            }
14207            let g = &graphs.get(&key).unwrap().0;
14208            for _ in 0..chunk {
14209                g.launch()?;
14210            }
14211            e.stream().synchronize()?;
14212            let ringh = e.dtoh_u32(&ring)?;
14213            for j in 0..chunk {
14214                let pos_j = cache.pos + j;
14215                let tok_j = ringh[(pos_j - ring_base) % RING];
14216                cache.pos += 0; // advanced below in one shot
14217                if j + 1 == chunk {
14218                    next = tok_j;
14219                } else {
14220                    out.push(tok_j);
14221                    if eos.contains(&tok_j) || !on_token(tok_j) {
14222                        reason = if eos.contains(&tok_j) {
14223                            StopReason::Eos
14224                        } else {
14225                            StopReason::Callback
14226                        };
14227                        // roll device/host state back to the stop point.
14228                        let keep = cache.pos + j + 1;
14229                        e.set_i32_one(&mut pos_d, keep as i32)?;
14230                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
14231                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
14232                            kvl.len = keep;
14233                        }
14234                        cache.pos = keep;
14235                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
14236                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
14237                        }
14238                        return Ok((out, reason));
14239                    }
14240                }
14241            }
14242            cache.pos += chunk;
14243            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
14244                kvl.len += chunk;
14245            }
14246        }
14247        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
14248            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
14249        }
14250        Ok((out, reason))
14251    }
14252
14253    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
14254    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
14255    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
14256    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
14257    /// logits (host) + advances cache.pos by t.
14258    pub(crate) fn gemma4_decode_step_t(
14259        &self,
14260        e: &Engine,
14261        tokens: &[u32],
14262        pos0: usize,
14263        cache: &mut Cache,
14264    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
14265        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
14266    }
14267
14268    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
14269    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
14270    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
14271    pub(crate) fn gemma4_decode_step_t_am(
14272        &self,
14273        e: &Engine,
14274        tokens: &[u32],
14275        pos0: usize,
14276        cache: &mut Cache,
14277    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14278        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
14279        let t = tokens.len();
14280        let n_vocab = self.output.out_features();
14281        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
14282        for i in 0..t {
14283            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
14284        }
14285        Ok((e.dtoh_u32(&toks)?, hn))
14286    }
14287
14288    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
14289    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
14290    pub(crate) fn gemma4_decode_step_t_am_dev(
14291        &self,
14292        e: &Engine,
14293        tok_d: &CudaSlice<u32>,
14294        t: usize,
14295        pos0: usize,
14296        cache: &mut Cache,
14297    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14298        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
14299        let n_vocab = self.output.out_features();
14300        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14301        for i in 0..t {
14302            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14303        }
14304        Ok((vam, hn))
14305    }
14306
14307    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
14308    /// llama's h_nextn convention).
14309    pub(crate) fn gemma4_decode_step_t_h(
14310        &self,
14311        e: &Engine,
14312        tokens: &[u32],
14313        pos0: usize,
14314        cache: &mut Cache,
14315    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14316        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
14317        let t = tokens.len();
14318        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14319        e.softcap(&mut ld, cap, t * self.output.out_features())?;
14320        Ok((e.dtoh(&ld)?, hn))
14321    }
14322
14323    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
14324    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
14325    pub(crate) fn verify_stream_scratch(
14326        &self,
14327        e: &Engine,
14328        cap: usize,
14329    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
14330        Ok(VerifyStreamScratch {
14331            pos_d: e.htod_i32(&vec![0i32; cap])?,
14332            row_ctrs: (0..cap)
14333                .map(|_| e.htod_i32(&[0]))
14334                .collect::<Result<_, _>>()?,
14335        })
14336    }
14337
14338    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
14339    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
14340    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
14341    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
14342    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
14343    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
14344    /// sync, exactly the turnaround the burst exists to remove.
14345    pub(crate) fn gemma4_verify_t_am_stream(
14346        &self,
14347        e: &Engine,
14348        tok_d: &CudaSlice<u32>,
14349        t: usize,
14350        ctr: &CudaSlice<i32>,
14351        hint: usize,
14352        cache: &mut Cache,
14353        scr: &mut VerifyStreamScratch,
14354    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14355        let n_embd = self.cfg.n_embd as usize;
14356        let eps = self.cfg.rms_eps;
14357        assert!(t <= scr.row_ctrs.len() && t <= 64);
14358        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
14359        for i in 0..t {
14360            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
14361        }
14362        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
14363        let embd_gpu = self
14364            .embd_gpu
14365            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14366        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14367        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
14368        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14369        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14370        let n_layers = self.layers.len();
14371        for (il, layer) in self.layers.iter().enumerate() {
14372            let (hq, hdq) = match h_carry.take() {
14373                Some(p) => p,
14374                None => {
14375                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14376                }
14377            };
14378            let Mixer::Full(fa) = &layer.mixer else {
14379                panic!("gemma4 layer {il} not full-attn")
14380            };
14381            let o = self
14382                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
14383            let next_norm = if il + 1 < n_layers {
14384                Some(self.layers[il + 1].attn_norm.float_data())
14385            } else {
14386                None
14387            };
14388            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14389            x = xn;
14390            h_carry = hn;
14391            self.dflash_tap(e, cache, il, &x, t)?;
14392        }
14393        let mut hn = e.uninit(t * n_embd)?;
14394        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14395        let ld = e.matmul(&self.output, &hn, t)?;
14396        let n_vocab = self.output.out_features();
14397        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14398        for i in 0..t {
14399            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14400        }
14401        Ok((vam, hn))
14402    }
14403
14404    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
14405    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
14406    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
14407    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
14408    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
14409    /// kernel later if it shows in the profile).
14410    pub(crate) fn dflash_tap(
14411        &self,
14412        e: &Engine,
14413        cache: &mut Cache,
14414        il: usize,
14415        x: &CudaSlice<f32>,
14416        t: usize,
14417    ) -> Result<(), Box<dyn std::error::Error>> {
14418        let Some(taps) = cache.dflash_taps.as_mut() else {
14419            return Ok(());
14420        };
14421        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14422            return Ok(());
14423        };
14424        let h = taps.hidden;
14425        let n_taps = taps.layer_ids.len();
14426        let base = taps.base;
14427        debug_assert!(
14428            base + t <= taps.t,
14429            "tap window {base}+{t} exceeds sink {}",
14430            taps.t
14431        );
14432        let xv = e.view(x, t * h);
14433        for r in 0..t {
14434            let row = xv.slice(r * h..(r + 1) * h);
14435            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14436        }
14437        Ok(())
14438    }
14439
14440    fn gemma4_verify_trunk(
14441        &self,
14442        e: &Engine,
14443        tokens: &[u32],
14444        pos0: usize,
14445        cache: &mut Cache,
14446        tok_dev: Option<&CudaSlice<u32>>,
14447    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14448        let n_embd = self.cfg.n_embd as usize;
14449        let eps = self.cfg.rms_eps;
14450        let t = tokens.len();
14451        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14452        let pos_d = e.htod_i32(&pos)?;
14453        let mut x = match tok_dev {
14454            Some(td) => {
14455                let embd_gpu = self
14456                    .embd_gpu
14457                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14458                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14459                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14460            }
14461            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14462        };
14463        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14464        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14465        let n_layers = self.layers.len();
14466        for (il, layer) in self.layers.iter().enumerate() {
14467            let (hq, hdq) = match h_carry.take() {
14468                Some(p) => p,
14469                None => {
14470                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14471                }
14472            };
14473            let Mixer::Full(fa) = &layer.mixer else {
14474                panic!("gemma4 layer {il} not full-attn")
14475            };
14476            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14477            let next_norm = if il + 1 < n_layers {
14478                Some(self.layers[il + 1].attn_norm.float_data())
14479            } else {
14480                None
14481            };
14482            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14483            x = xn;
14484            h_carry = hn;
14485            self.dflash_tap(e, cache, il, &x, t)?;
14486        }
14487        let mut hn = e.uninit(t * n_embd)?;
14488        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14489        let mut ld = e.matmul(&self.output, &hn, t)?;
14490        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14491        cache.pos += t;
14492        Ok((ld, hn))
14493    }
14494
14495    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14496    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14497    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14498    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14499    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14500    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14501    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14502    #[allow(clippy::too_many_arguments)]
14503    fn gemma4_verify_attn_stream(
14504        &self,
14505        e: &Engine,
14506        fa: &crate::hybrid::FullAttnLayer,
14507        il: usize,
14508        hq: &CudaSlice<i8>,
14509        hdq: &CudaSlice<f32>,
14510        pos_d: &CudaSlice<i32>,
14511        t: usize,
14512        cache: &mut Cache,
14513        hint: usize,
14514        row_ctrs: &[CudaSlice<i32>],
14515    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14516        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14517        let eps = self.cfg.rms_eps;
14518        let aux = self.gemma4_aux.as_ref().unwrap();
14519        let ones = aux.ones(e);
14520        #[cfg(debug_assertions)]
14521        crate::debug_assert_tensor_stream_device(
14522            ones,
14523            &e.stream(),
14524            "gemma4_verify_attn_stream.ones",
14525        );
14526        let h0 = e.zeros(0)?;
14527        let h = &h0;
14528        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14529        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14530        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14531        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14532        let fused_qkv = if f2b {
14533            if swa {
14534                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14535                    .map(|(a, b, c)| (a, b, Some(c)))
14536            } else {
14537                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14538                    .map(|(a, b)| (a, b, None))
14539            }
14540        } else {
14541            None
14542        };
14543        let (q0, k0, v0) = match fused_qkv {
14544            Some((a, b, cv)) => {
14545                let v = match cv {
14546                    Some(c) => c,
14547                    None => e.clone_dtod(&b)?,
14548                };
14549                (a, b, v)
14550            }
14551            None => {
14552                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14553                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14554                let v0 = if swa {
14555                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14556                } else {
14557                    e.clone_dtod(&k0)?
14558                };
14559                (q0, k0, v0)
14560            }
14561        };
14562        let mut q = e.uninit(t * nh * hd)?;
14563        let mut k = e.uninit(t * nkv * hd)?;
14564        let mut v = e.uninit(t * nkv * hd)?;
14565        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14566        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14567        let ff = if swa {
14568            None
14569        } else {
14570            Some(
14571                aux.rope_freqs(e)
14572                    .expect("gemma4 global rope needs rope_freqs.weight"),
14573            )
14574        };
14575        #[cfg(debug_assertions)]
14576        if let Some(ff) = ff {
14577            crate::debug_assert_tensor_stream_device(
14578                ff,
14579                &e.stream(),
14580                "gemma4_verify_attn_stream.rope_freqs",
14581            );
14582        }
14583        e.rms_norm_qkv_rope(
14584            &q0,
14585            &k0,
14586            &v0,
14587            fa.q_norm.float_data(),
14588            fa.k_norm.float_data(),
14589            ones,
14590            &mut q,
14591            &mut k,
14592            &mut v,
14593            hd,
14594            self.gemma4_rope_dims(il),
14595            nh * t,
14596            nkv * t,
14597            pos_d,
14598            nh,
14599            nkv,
14600            base,
14601            1.0,
14602            ff,
14603            eps,
14604        )?;
14605        let kvl = cache.kv[il].as_mut().unwrap();
14606        // append at the DEVICE slot; the counter advances by t on-device.
14607        e.append_kv_quantized_rows_dc(
14608            &k,
14609            &v,
14610            &mut kvl.k,
14611            &mut kvl.v,
14612            &kvl.len_d,
14613            t,
14614            kvl.kv_dim_k,
14615            kvl.kv_dim_v,
14616            kvl.k_tok_bytes,
14617            kvl.v_tok_bytes,
14618            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14619        )?;
14620        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14621        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14622        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14623        let mut attn = e.uninit(t * nh * hd)?;
14624        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14625        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14626        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14627        // and a stable window regime — the same rung/regime keys as the draft graph).
14628        if swa && hint + 1 >= win {
14629            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14630            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14631            e.fa_decode_rows_w(
14632                &q,
14633                &k_view,
14634                &v_view,
14635                &mut attn,
14636                hd,
14637                nh,
14638                nkv,
14639                &kvl.len_d,
14640                0,
14641                t,
14642                scale,
14643                win,
14644                kvl.k_tok_bytes,
14645                kvl.v_tok_bytes,
14646                None,
14647            )?;
14648        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14649            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14650            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14651            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14652            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14653            // for every row.
14654            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14655            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14656            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14657            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14658            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14659            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14660            // any bucket >= the live length is exact.
14661            let bucket = (hint + t + 2)
14662                .next_power_of_two()
14663                .min(crate::fa512_min_tkv().saturating_sub(1));
14664            let qv = e.view(&q, t * nh * hd);
14665            for i in 0..t {
14666                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14667                let mut q_one = e.uninit(nh * hd)?;
14668                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14669                let mut a_one = e.uninit(nh * hd)?;
14670                e.fa_decode_dc(
14671                    &q_one,
14672                    &k_view,
14673                    &v_view,
14674                    &mut a_one,
14675                    hd,
14676                    nh,
14677                    nkv,
14678                    &row_ctrs[i],
14679                    bucket,
14680                    scale,
14681                    kvl.k_tok_bytes,
14682                    kvl.v_tok_bytes,
14683                    false,
14684                )?;
14685                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14686            }
14687        } else if hd == 512 {
14688            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14689            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14690            e.fa_decode_rows(
14691                &q,
14692                &k_view,
14693                &v_view,
14694                &mut attn,
14695                hd,
14696                nh,
14697                nkv,
14698                hint,
14699                t,
14700                scale,
14701                kvl.k_tok_bytes,
14702                kvl.v_tok_bytes,
14703                Some((&kvl.len_d, 0)),
14704                false,
14705                false,
14706                None,
14707            )?;
14708        } else {
14709            // hd256 under-window: v4 device-len rows twin.
14710            e.fa_decode_rows_dc(
14711                &q,
14712                &k_view,
14713                &v_view,
14714                &mut attn,
14715                hd,
14716                nh,
14717                nkv,
14718                &kvl.len_d,
14719                hint + t,
14720                t,
14721                scale,
14722                kvl.k_tok_bytes,
14723                kvl.v_tok_bytes,
14724                0,
14725                swa && crate::Engine::wkv_on(),
14726            )?;
14727        }
14728        Ok(e.matmul(&fa.wo, &attn, t)?)
14729    }
14730
14731    fn gemma4_verify_attn(
14732        &self,
14733        e: &Engine,
14734        fa: &crate::hybrid::FullAttnLayer,
14735        il: usize,
14736        hq: &CudaSlice<i8>,
14737        hdq: &CudaSlice<f32>,
14738        pos_d: &CudaSlice<i32>,
14739        t: usize,
14740        cache: &mut Cache,
14741    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14742        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14743        let eps = self.cfg.rms_eps;
14744        let aux = self.gemma4_aux.as_ref().unwrap();
14745        let ones = aux.ones(e);
14746        #[cfg(debug_assertions)]
14747        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14748        let n_embd = self.cfg.n_embd as usize;
14749        let _ = n_embd;
14750
14751        let h0 = e.zeros(0)?;
14752        let h = &h0;
14753        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14754        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14755        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14756        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14757        let fused_qkv = if f2b {
14758            if swa {
14759                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14760                    .map(|(a, b, c)| (a, b, Some(c)))
14761            } else {
14762                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14763                    .map(|(a, b)| (a, b, None))
14764            }
14765        } else {
14766            None
14767        };
14768        let (q0, k0, v0) = match fused_qkv {
14769            Some((a, b, cv)) => {
14770                let v = match cv {
14771                    Some(c) => c,
14772                    None => e.clone_dtod(&b)?,
14773                };
14774                (a, b, v)
14775            }
14776            None => {
14777                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14778                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14779                let v0 = if swa {
14780                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14781                } else {
14782                    e.clone_dtod(&k0)?
14783                };
14784                (q0, k0, v0)
14785            }
14786        };
14787        let mut q = e.uninit(t * nh * hd)?;
14788        let mut k = e.uninit(t * nkv * hd)?;
14789        let mut v = e.uninit(t * nkv * hd)?;
14790        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14791        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14792        let ff = if swa {
14793            None
14794        } else {
14795            Some(
14796                aux.rope_freqs(e)
14797                    .expect("gemma4 global rope needs rope_freqs.weight"),
14798            )
14799        };
14800        #[cfg(debug_assertions)]
14801        if let Some(ff) = ff {
14802            crate::debug_assert_tensor_stream_device(
14803                ff,
14804                &e.stream(),
14805                "gemma4_verify_attn.rope_freqs",
14806            );
14807        }
14808        e.rms_norm_qkv_rope(
14809            &q0,
14810            &k0,
14811            &v0,
14812            fa.q_norm.float_data(),
14813            fa.k_norm.float_data(),
14814            ones,
14815            &mut q,
14816            &mut k,
14817            &mut v,
14818            hd,
14819            self.gemma4_rope_dims(il),
14820            nh * t,
14821            nkv * t,
14822            pos_d,
14823            nh,
14824            nkv,
14825            base,
14826            1.0,
14827            ff,
14828            eps,
14829        )?;
14830        let kvl = cache.kv[il].as_mut().unwrap();
14831        let base_len = kvl.len;
14832        e.append_kv_quantized_rows(
14833            &k,
14834            &v,
14835            &mut kvl.k,
14836            &mut kvl.v,
14837            base_len,
14838            t,
14839            kvl.kv_dim_k,
14840            kvl.kv_dim_v,
14841            kvl.k_tok_bytes,
14842            kvl.v_tok_bytes,
14843            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14844        )?;
14845        kvl.len += t;
14846        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14847        let mut attn = e.uninit(t * nh * hd)?;
14848        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14849        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14850        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14851            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14852            // decode rides the SAME symbol at t=1 (parity law).
14853            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14854        if rows_ok && (!swa || base_len + t <= win) {
14855            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14856            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14857            if hd == 512 {
14858                // device-len twin: sync the counter to the verify base (async arg-store).
14859                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14860                e.fa_decode_rows(
14861                    &q,
14862                    &k_view,
14863                    &v_view,
14864                    &mut attn,
14865                    hd,
14866                    nh,
14867                    nkv,
14868                    base_len,
14869                    t,
14870                    scale,
14871                    kvl.k_tok_bytes,
14872                    kvl.v_tok_bytes,
14873                    Some((&kvl.len_d, 0)),
14874                    false,
14875                    swa && crate::Engine::wkv_on(),
14876                    None,
14877                )?;
14878            } else {
14879                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14880                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14881                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14882                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14883                e.fa_decode_rows_dc(
14884                    &q,
14885                    &k_view,
14886                    &v_view,
14887                    &mut attn,
14888                    hd,
14889                    nh,
14890                    nkv,
14891                    &kvl.len_d,
14892                    base_len + t,
14893                    t,
14894                    scale,
14895                    kvl.k_tok_bytes,
14896                    kvl.v_tok_bytes,
14897                    0,
14898                    swa && crate::Engine::wkv_on(),
14899                )?;
14900            }
14901            return Ok(e.matmul(&fa.wo, &attn, t)?);
14902        }
14903        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14904        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14905        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14906        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14907        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14908        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14909        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14910        if hd == 256
14911            && swa
14912            && base_len + 1 >= win
14913            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14914        {
14915            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14916            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14917            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14918            e.fa_decode_rows_w(
14919                &q,
14920                &k_view,
14921                &v_view,
14922                &mut attn,
14923                hd,
14924                nh,
14925                nkv,
14926                &kvl.len_d,
14927                0,
14928                t,
14929                scale,
14930                win,
14931                kvl.k_tok_bytes,
14932                kvl.v_tok_bytes,
14933                None,
14934            )?;
14935            return Ok(e.matmul(&fa.wo, &attn, t)?);
14936        }
14937        for i in 0..t {
14938            let avail = base_len + i + 1;
14939            let (off_tok, t_kv) = if swa && avail > win {
14940                (avail - win, win)
14941            } else {
14942                (0, avail)
14943            };
14944            let k_view = e.view_u8_range(
14945                &kvl.k,
14946                off_tok * kvl.k_tok_bytes,
14947                (off_tok + t_kv) * kvl.k_tok_bytes,
14948            );
14949            let v_view = e.view_u8_range(
14950                &kvl.v,
14951                off_tok * kvl.v_tok_bytes,
14952                (off_tok + t_kv) * kvl.v_tok_bytes,
14953            );
14954            let qi = e.view(&q, t * nh * hd);
14955            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14956            let mut q_one = e.uninit(nh * hd)?;
14957            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14958            let mut a_one = e.uninit(nh * hd)?;
14959            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14960            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14961            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14962            if swa
14963                && avail > win
14964                && hd == 256
14965                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14966            {
14967                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14968                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14969                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14970                e.fa_decode_rows_w(
14971                    &q_one,
14972                    &kp,
14973                    &vp,
14974                    &mut a_one,
14975                    hd,
14976                    nh,
14977                    nkv,
14978                    &kvl.len_d,
14979                    0,
14980                    1,
14981                    scale,
14982                    win,
14983                    kvl.k_tok_bytes,
14984                    kvl.v_tok_bytes,
14985                    None,
14986                )?;
14987            } else if !swa
14988                && hd == 512
14989                && avail >= crate::fa512_min_tkv()
14990                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14991            {
14992                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14993                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14994                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14995                e.fa_decode_rows(
14996                    &q_one,
14997                    &kp,
14998                    &vp,
14999                    &mut a_one,
15000                    hd,
15001                    nh,
15002                    nkv,
15003                    avail - 1,
15004                    1,
15005                    scale,
15006                    kvl.k_tok_bytes,
15007                    kvl.v_tok_bytes,
15008                    Some((&kvl.len_d, 0)),
15009                    false,
15010                    false,
15011                    None,
15012                )?;
15013            } else {
15014                e.fa_decode_kvmod(
15015                    &q_one,
15016                    &k_view,
15017                    &v_view,
15018                    &mut a_one,
15019                    hd,
15020                    nh,
15021                    nkv,
15022                    t_kv,
15023                    scale,
15024                    kvl.k_tok_bytes,
15025                    kvl.v_tok_bytes,
15026                    swa && crate::Engine::wkv_on(),
15027                )?;
15028            }
15029            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
15030        }
15031        Ok(e.matmul(&fa.wo, &attn, t)?)
15032    }
15033
15034    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
15035    /// h_seed = pre-output_norm hidden). Advances cache.pos.
15036    pub(crate) fn gemma4_decode_step_h(
15037        &self,
15038        e: &Engine,
15039        token: u32,
15040        cache: &mut Cache,
15041    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15042        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
15043        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
15044        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
15045        // unsplit rather than guessing a fence.
15046        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
15047            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
15048        }
15049        if crate::pp::pp_cuts(self.layers.len()).is_some() {
15050            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
15051        }
15052        let n_embd = self.cfg.n_embd as usize;
15053        let eps = self.cfg.rms_eps;
15054        let pos_d = e.htod_i32(&[cache.pos as i32])?;
15055        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
15056        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15057        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
15058        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
15059        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
15060        let n_layers = self.layers.len();
15061        for (il, layer) in self.layers.iter().enumerate() {
15062            let (hq, hdq) = match h_carry.take() {
15063                Some(p) => p,
15064                None => {
15065                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
15066                }
15067            };
15068            let Mixer::Full(fa) = &layer.mixer else {
15069                panic!("gemma4 layer {il} not full-attn")
15070            };
15071            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
15072            let next_norm = if il + 1 < n_layers {
15073                Some(self.layers[il + 1].attn_norm.float_data())
15074            } else {
15075                None
15076            };
15077            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
15078            x = xn;
15079            h_carry = hn;
15080        }
15081        let mut hn = e.uninit(n_embd)?;
15082        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15083        let h_seed = e.clone_dtod(&x)?;
15084        let mut ld = e.matmul(&self.output, &hn, 1)?;
15085        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15086        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
15087        self.gemma4_suppress(e, &mut ld, 1)?;
15088        let logits = e.dtoh(&ld)?;
15089        cache.pos += 1;
15090        Ok((logits, h_seed))
15091    }
15092
15093    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
15094    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
15095    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
15096    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
15097    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
15098    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
15099    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
15100    fn gemma4_decode_layers(
15101        &self,
15102        e: &Engine,
15103        mut x: CudaSlice<f32>,
15104        lo: usize,
15105        hi: usize,
15106        pos_d: &CudaSlice<i32>,
15107        cache: &mut Cache,
15108    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15109        let n_embd = self.cfg.n_embd as usize;
15110        let eps = self.cfg.rms_eps;
15111        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
15112        for il in lo..hi {
15113            let layer = &self.layers[il];
15114            let (hq, hdq) = match h_carry.take() {
15115                Some(p) => p,
15116                // range head: il == lo — norm against THIS layer's attn_norm.
15117                None => {
15118                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
15119                }
15120            };
15121            let Mixer::Full(fa) = &layer.mixer else {
15122                panic!("gemma4 layer {il} not full-attn")
15123            };
15124            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
15125            let next_norm = if il + 1 < hi {
15126                Some(self.layers[il + 1].attn_norm.float_data())
15127            } else {
15128                None
15129            };
15130            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
15131            x = xn;
15132            h_carry = hn;
15133        }
15134        Ok(x)
15135    }
15136
15137    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
15138    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
15139    /// boundary handoff — same choreography as the generic arm (decode.rs), same
15140    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
15141    /// stage 1 = layers [split, n) + output_norm + softcapped head.
15142    /// Each stage uploads its own copy of the step's position scalar on its own stream.
15143    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
15144    fn gemma4_decode_step_h_pp2(
15145        &self,
15146        e: &Engine,
15147        token: u32,
15148        cache: &mut Cache,
15149        split: usize,
15150    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15151        if crate::pp::pp2_streams_off() {
15152            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
15153        }
15154        let rt = crate::pp::Pp2Rt::get(e)?;
15155        let e0 = rt.engine(0, e);
15156        let e1 = rt.engine(1, e);
15157        let n_embd = self.cfg.n_embd as usize;
15158        let eps = self.cfg.rms_eps;
15159        let pos = cache.pos as i32;
15160
15161        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
15162        let slot = {
15163            let _st0 = rt.enter(0);
15164            let pos_d = e0.htod_i32(&[pos])?;
15165            #[cfg(debug_assertions)]
15166            crate::debug_assert_tensor_stream_device(
15167                &pos_d,
15168                &e0.stream(),
15169                "gemma4_decode_step_h_pp2.stage0.pos_d",
15170            );
15171            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
15172            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15173            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
15174            rt.tx(0, &x, n_embd)?
15175        };
15176
15177        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
15178        let _st1 = rt.enter(1);
15179        let pos_d = e1.htod_i32(&[pos])?;
15180        #[cfg(debug_assertions)]
15181        crate::debug_assert_tensor_stream_device(
15182            &pos_d,
15183            &e1.stream(),
15184            "gemma4_decode_step_h_pp2.stage1.pos_d",
15185        );
15186        let x = rt.rx(0, slot, n_embd)?;
15187        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
15188
15189        let mut hn = e1.uninit(n_embd)?;
15190        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15191        let h_seed = e1.clone_dtod(&x)?;
15192        let mut ld = e1.matmul(&self.output, &hn, 1)?;
15193        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15194        e1.softcap(&mut ld, cap, self.output.out_features())?;
15195        self.gemma4_suppress(e1, &mut ld, 1)?;
15196        let logits = e1.dtoh(&ld)?;
15197        cache.pos += 1;
15198        Ok((logits, h_seed))
15199    }
15200
15201    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
15202    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
15203    fn gemma4_decode_step_h_pp2_samestream(
15204        &self,
15205        e: &Engine,
15206        token: u32,
15207        cache: &mut Cache,
15208        split: usize,
15209    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15210        let n_embd = self.cfg.n_embd as usize;
15211        let eps = self.cfg.rms_eps;
15212        let pos_d = e.htod_i32(&[cache.pos as i32])?;
15213
15214        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
15215        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
15216        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15217        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
15218
15219        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
15220        let boundary_tx = e.clone_dtod(&x)?;
15221        let boundary_rx = e.clone_dtod(&boundary_tx)?;
15222
15223        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
15224        let x =
15225            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
15226
15227        let mut hn = e.uninit(n_embd)?;
15228        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15229        let h_seed = e.clone_dtod(&x)?;
15230        let mut ld = e.matmul(&self.output, &hn, 1)?;
15231        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15232        e.softcap(&mut ld, cap, self.output.out_features())?;
15233        self.gemma4_suppress(e, &mut ld, 1)?;
15234        let logits = e.dtoh(&ld)?;
15235        cache.pos += 1;
15236        Ok((logits, h_seed))
15237    }
15238}
15239
15240// ============================ step35 (Step-3.7-Flash) ==================================
15241// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
15242// FAMILY and not a few branches inside the generic `full_attn*` chain:
15243//
15244//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
15245//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
15246//      shapes and the FA head counts would be wrong on 33 of 45 layers.
15247//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
15248//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
15249//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
15250//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
15251//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
15252//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
15253//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
15254//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
15255//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
15256//
15257// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
15258impl HybridModel {
15259    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
15260    /// synthesize a drafter or trunk layer from a neighboring class.
15261    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
15262        let geometry = self
15263            .cfg
15264            .layer_geometry(il as u32)
15265            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
15266        debug_assert_eq!(
15267            geometry.attention_gate,
15268            memra_gguf::config::AttentionGateKind::SeparateHead
15269        );
15270        geometry
15271    }
15272
15273    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
15274    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
15275    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
15276    ///
15277    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
15278    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
15279    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
15280    /// `cache`:
15281    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
15282    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
15283    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
15284    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
15285    ///     contract, lane/chunkinv-flip).
15286    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
15287    ///     q/k/v, no cache side effect.
15288    ///
15289    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
15290    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
15291    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
15292    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
15293    /// still contains must be masked per query. memra's window convention
15294    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
15295    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
15296    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
15297    ///
15298    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
15299    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
15300    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
15301    ///
15302    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
15303    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
15304    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
15305    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
15306    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
15307    /// hidden rows, and the generated text — a function of the chunk size:
15308    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
15309    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
15310    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
15311    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
15312    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
15313    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
15314    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
15315    ///   one-token change in a documented machine-config knob changed the answer.
15316    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
15317    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
15318    /// the same rows moves the logits by ~1.8.
15319    ///
15320    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
15321    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
15322    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
15323    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
15324    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
15325    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
15326    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
15327    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
15328    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
15329    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
15330    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
15331    /// those with t_kv <= win = 512.
15332    #[allow(clippy::too_many_arguments)]
15333    fn step35_attn_pre_wo(
15334        &self,
15335        e: &Engine,
15336        fa: &FullAttnLayer,
15337        mut g3: Vec<CudaSlice<f32>>,
15338        hg: Option<&CudaSlice<f32>>,
15339        gt_pre: Option<&CudaSlice<f32>>,
15340        pos_d: &CudaSlice<i32>,
15341        t: usize,
15342        cache: Option<&mut Cache>,
15343        il: usize,
15344        seq_end: usize,
15345    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15346        let geometry = self.step35_geom(il);
15347        let hd = geometry.head_dim_k as usize;
15348        let nkv = geometry.n_head_kv as usize;
15349        let nh = geometry.n_head as usize;
15350        let rbase = geometry.rope_base;
15351        let scale = geometry.attention_scale();
15352        let swa = geometry.window.is_some();
15353        let eps = self.cfg.rms_eps;
15354        let win = geometry.window.unwrap_or(0) as usize;
15355        let n_rot = geometry.n_rot as usize;
15356
15357        let v = g3.pop().unwrap();
15358        let k0 = g3.pop().unwrap();
15359        let q0 = g3.pop().unwrap();
15360
15361        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
15362        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
15363        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
15364        let mut q = e.uninit(t * nh * hd)?;
15365        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
15366        let mut k = e.uninit(t * nkv * hd)?;
15367        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
15368        let ff = if geometry.rope_factors {
15369            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
15370        } else {
15371            None
15372        };
15373        #[cfg(debug_assertions)]
15374        if let Some(ff) = ff {
15375            crate::debug_assert_tensor_stream_device(
15376                ff,
15377                &e.stream(),
15378                "step35_attn_pre_wo.rope_freqs",
15379            );
15380        }
15381        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
15382
15383        let mut attn = e.uninit(t * nh * hd)?;
15384        match cache {
15385            Some(cache) => {
15386                let base_len = cache.kv[il].as_ref().unwrap().len;
15387                // Read per layer call, never in a measured default.
15388                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
15389                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
15390                let off = if swa {
15391                    let raw = base_len.saturating_sub(win - 1);
15392                    if legacy_tkv || legacy_calllocal {
15393                        raw
15394                    } else {
15395                        raw & !31usize
15396                    }
15397                } else {
15398                    0
15399                };
15400                {
15401                    let kvl = cache.kv[il].as_mut().unwrap();
15402                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
15403                    let write_row = e.prepare_kv_append(kvl, off, t)?;
15404                    e.append_kv_quantized_rows(
15405                        &k,
15406                        &v,
15407                        &mut kvl.k,
15408                        &mut kvl.v,
15409                        write_row,
15410                        t,
15411                        kvl.kv_dim_k,
15412                        kvl.kv_dim_v,
15413                        kvl.k_tok_bytes,
15414                        kvl.v_tok_bytes,
15415                        crate::Engine::kv_fp8_on(),
15416                    )?;
15417                    kvl.len += t;
15418                    let new_len = kvl.len as i32;
15419                    e.set_i32_one(&mut kvl.len_d, new_len)?;
15420                }
15421                let kvl = cache.kv[il].as_ref().unwrap();
15422                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15423                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15424                // unaligned view offset here. Both halves are load-bearing for the canaries:
15425                // on the FA default the predicate arms agree bitwise wherever they can differ
15426                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15427                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15428                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15429                // on the current FA path: its tile grid starts at the chunk/call boundary.
15430                // SWA: trim the view to the oldest key any query in this chunk can reach —
15431                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15432                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15433                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15434                // the VIEW START — so an unaligned off regroups the same absolute keys into
15435                // different tiles at different chunk sizes = different (m,l) rounding =
15436                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15437                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15438                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15439                // size; the <=31 extra leading keys are older than EVERY query's window
15440                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15441                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15442                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15443                // the floor arm's bits do not move either (gated: G2f, battery 2).
15444                let t_kv = base_len + t - off;
15445                let physical = kvl.physical_rows(off, off + t_kv)?;
15446                let k_view = e.view_u8_range(
15447                    &kvl.k,
15448                    physical.start * kvl.k_tok_bytes,
15449                    physical.end * kvl.k_tok_bytes,
15450                );
15451                let v_view = e.view_u8_range(
15452                    &kvl.v,
15453                    physical.start * kvl.v_tok_bytes,
15454                    physical.end * kvl.v_tok_bytes,
15455                );
15456                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15457                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15458                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15459                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15460                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15461                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15462                // construction, so the invariance assertion MUST break under it (the seam whose
15463                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15464                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15465                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15466                // cached (probes flip it in-process). Never on in a measured default run.
15467                let swa_naive = if legacy_tkv {
15468                    t_kv > win
15469                } else {
15470                    seq_end > win
15471                };
15472                if swa && swa_naive {
15473                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15474                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15475                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15476                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15477                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15478                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15479                    // identically to the unwindowed one modulo the mask, which is the point.
15480                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15481                    // selected on `seq_end` like every arm here, so the class is uniform for
15482                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15483                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15484                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15485                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15486                        e.sdpa_naive_w_quantized_view(
15487                            &q,
15488                            &k_view,
15489                            &v_view,
15490                            &mut attn,
15491                            hd,
15492                            nh,
15493                            nkv,
15494                            t,
15495                            t_kv,
15496                            scale,
15497                            true,
15498                            win,
15499                            kvl.k_tok_bytes,
15500                            kvl.v_tok_bytes,
15501                        )?;
15502                    } else {
15503                        e.fa_prefill_view_ws_w_hd128(
15504                            &q,
15505                            &k_view,
15506                            &v_view,
15507                            &mut attn,
15508                            hd,
15509                            nh,
15510                            nkv,
15511                            t,
15512                            t_kv,
15513                            scale,
15514                            true,
15515                            win,
15516                            kvl.k_tok_bytes,
15517                            kvl.v_tok_bytes,
15518                        )?;
15519                    }
15520                } else if std::env::var("MEMRA_NOFA").is_ok() {
15521                    e.sdpa_naive_quantized_view(
15522                        &q,
15523                        &k_view,
15524                        &v_view,
15525                        &mut attn,
15526                        hd,
15527                        nh,
15528                        nkv,
15529                        t,
15530                        t_kv,
15531                        scale,
15532                        true,
15533                        kvl.k_tok_bytes,
15534                        kvl.v_tok_bytes,
15535                    )?;
15536                } else {
15537                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15538                    // reach past the window, so the window mask is a no-op under causal and every
15539                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15540                    // request either way, which is what makes the chunk size arithmetic-free.
15541                    e.fa_prefill_view_ws(
15542                        &q,
15543                        &k_view,
15544                        &v_view,
15545                        &mut attn,
15546                        hd,
15547                        nh,
15548                        nkv,
15549                        t,
15550                        t_kv,
15551                        scale,
15552                        true,
15553                        kvl.k_tok_bytes,
15554                        kvl.v_tok_bytes,
15555                        crate::Engine::kv_fp8_on(),
15556                    )?;
15557                }
15558            }
15559            None => {
15560                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15561                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15562                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15563                // seq_end here too or it re-opens the same door.
15564                debug_assert_eq!(
15565                    seq_end, t,
15566                    "step35 cacheless prefill is monolithic (seq_end == t)"
15567                );
15568                if swa && seq_end > win {
15569                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15570                } else if std::env::var("MEMRA_NOFA").is_ok() {
15571                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15572                } else {
15573                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15574                }
15575            }
15576        }
15577
15578        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15579        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15580        let gw = fa
15581            .attn_gate
15582            .as_ref()
15583            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15584        let gt_owned = if gt_pre.is_none() {
15585            Some(e.matmul(
15586                gw,
15587                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15588                t,
15589            )?)
15590        } else {
15591            None
15592        };
15593        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15594        let mut ag = e.uninit(t * nh * hd)?;
15595        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15596        Ok(ag)
15597    }
15598
15599    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15600    /// `forward_last`, t2probe). Post-`wo`.
15601    pub(crate) fn step35_attn(
15602        &self,
15603        e: &Engine,
15604        fa: &FullAttnLayer,
15605        h: &CudaSlice<f32>,
15606        pos_d: &CudaSlice<i32>,
15607        t: usize,
15608        il: usize,
15609    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15610        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15611            Some(g3) => g3,
15612            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15613        };
15614        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15615        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15616        self.step35_o(e, fa, &ag, t)
15617    }
15618
15619    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15620    /// resident quantized cache, attend through the cache view). Post-`wo`.
15621    ///
15622    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15623    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15624    /// own extent.
15625    #[allow(clippy::too_many_arguments)]
15626    pub(crate) fn step35_attn_prime(
15627        &self,
15628        e: &Engine,
15629        fa: &FullAttnLayer,
15630        h: &CudaSlice<f32>,
15631        hx: Option<&CudaSlice<u8>>,
15632        pos_d: &CudaSlice<i32>,
15633        t: usize,
15634        cache: &mut Cache,
15635        il: usize,
15636        seq_end: usize,
15637    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15638        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15639            if hx.is_some() {
15640                return Err(
15641                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15642                     pre-quantized prime path"
15643                        .into(),
15644                );
15645            }
15646            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15647        }
15648        let g3 = if fa.step_tp_qkv.is_some() {
15649            if hx.is_some() {
15650                return Err(
15651                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15652                     pre-quantized prime path"
15653                        .into(),
15654                );
15655            }
15656            self.step35_tp_qkv(e, fa, h, t)?
15657                .expect("Step Q/K/V TP disappeared after the presence check")
15658        } else {
15659            match hx {
15660                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15661                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15662            }
15663        };
15664        let ag =
15665            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15666        self.step35_o(e, fa, &ag, t)
15667    }
15668
15669    fn ensure_step_tp_kv_cache(
15670        &self,
15671        e: &Engine,
15672        fa: &FullAttnLayer,
15673        il: usize,
15674        cache: &mut Cache,
15675    ) -> Result<bool, Box<dyn std::error::Error>> {
15676        let tp = fa
15677            .step_tp_qkv
15678            .as_ref()
15679            .ok_or("Step TP cache hydration lost its resident projections")?;
15680        let geometry = self.step35_geom(il);
15681        let window = geometry.window.map(|window| window as usize);
15682        let ranks = tp.runtime.devices().len();
15683        let head_dim = geometry.head_dim_k as usize;
15684        let kv_heads = geometry.n_head_kv as usize;
15685        let max_ctx = cache.max_ctx;
15686
15687        if cache.tp_kv[il].is_some() {
15688            return Ok(false);
15689        }
15690        let local = cache.kv[il]
15691            .as_ref()
15692            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15693        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15694            return Err(format!(
15695                "Step TP layer {il} local KV geometry k={} v={} != {}",
15696                local.kv_dim_k,
15697                local.kv_dim_v,
15698                kv_heads * head_dim
15699            )
15700            .into());
15701        }
15702        let resident_start = window
15703            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15704            .unwrap_or(0);
15705        let resident_rows = local.len - resident_start;
15706        let physical = local.physical_rows(resident_start, local.len)?;
15707        let k_rows = if resident_rows == 0 {
15708            Vec::new()
15709        } else {
15710            e.dtoh_u8_view(&e.view_u8_range(
15711                &local.k,
15712                physical.start * local.k_tok_bytes,
15713                physical.end * local.k_tok_bytes,
15714            ))?
15715        };
15716        let v_rows = if resident_rows == 0 {
15717            Vec::new()
15718        } else {
15719            e.dtoh_u8_view(&e.view_u8_range(
15720                &local.v,
15721                physical.start * local.v_tok_bytes,
15722                physical.end * local.v_tok_bytes,
15723            ))?
15724        };
15725        let mut distributed = match window {
15726            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15727                kv_heads * head_dim,
15728                kv_heads * head_dim,
15729                max_ctx,
15730                window,
15731            )?,
15732            None => tp.runtime.allocate_tp_kv_cache(
15733                kv_heads * head_dim,
15734                kv_heads * head_dim,
15735                max_ctx,
15736            )?,
15737        };
15738        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15739            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15740        {
15741            return Err(format!(
15742                "Step TP layer {il} distributed/local KV token bytes disagree: \
15743                 k={}x{ranks}/{} v={}x{ranks}/{}",
15744                distributed.k_tok_bytes(),
15745                local.k_tok_bytes,
15746                distributed.v_tok_bytes(),
15747                local.v_tok_bytes,
15748            )
15749            .into());
15750        }
15751        tp.runtime.hydrate_tp_kv_cache_from(
15752            &mut distributed,
15753            local.len,
15754            resident_start,
15755            &k_rows,
15756            &v_rows,
15757        )?;
15758        cache.tp_kv[il] = Some(distributed);
15759        Ok(true)
15760    }
15761
15762    #[allow(clippy::too_many_arguments)]
15763    fn step35_tp_prefill_attn_resident(
15764        &self,
15765        e: &Engine,
15766        fa: &FullAttnLayer,
15767        il: usize,
15768        h: &CudaSlice<f32>,
15769        pos_d: &CudaSlice<i32>,
15770        tokens: usize,
15771        cache: &mut Cache,
15772        seq_end: usize,
15773    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15774        let tp = fa
15775            .step_tp_qkv
15776            .as_ref()
15777            .ok_or("Step TP prefill lost its resident projections")?;
15778        let attention = tp
15779            .attention
15780            .as_ref()
15781            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15782        let ranks = tp.runtime.devices().len();
15783        if !step_tp_prefill_shape(
15784            true,
15785            tokens,
15786            ranks,
15787            tp.runtime.native_p2p(),
15788            true,
15789            crate::Engine::kv_fp8_on(),
15790        ) {
15791            return Err(format!(
15792                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP2/TP4 native P2P, \
15793                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15794                 native_p2p={} fp8_kv={}",
15795                tp.runtime.native_p2p(),
15796                crate::Engine::kv_fp8_on(),
15797            )
15798            .into());
15799        }
15800        for seam in [
15801            "MEMRA_STEP35_SWA_TKV",
15802            "MEMRA_PRIME_CALLLOCAL",
15803            "MEMRA_PRIME_F32CHUNK0",
15804        ] {
15805            if std::env::var(seam).as_deref() == Ok("1") {
15806                return Err(format!(
15807                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15808                )
15809                .into());
15810            }
15811        }
15812
15813        let geometry = self.step35_geom(il);
15814        let window = geometry.window.map(|window| window as usize);
15815        let head_dim = geometry.head_dim_k as usize;
15816        let heads = geometry.n_head as usize;
15817        let kv_heads = geometry.n_head_kv as usize;
15818        if heads % ranks != 0 || kv_heads % ranks != 0 {
15819            return Err(format!(
15820                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15821            )
15822            .into());
15823        }
15824        let local_heads = heads / ranks;
15825        let local_kv_heads = kv_heads / ranks;
15826        let local_kv_dim = local_kv_heads * head_dim;
15827        let hidden = self.cfg.n_embd as usize;
15828        let expected_input = tokens
15829            .checked_mul(hidden)
15830            .ok_or("Step TP prefill input size overflow")?;
15831        if h.len() < expected_input {
15832            return Err(format!(
15833                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15834                h.len()
15835            )
15836            .into());
15837        }
15838        let positions = e.dtoh_i32(pos_d)?;
15839        if positions.len() != tokens {
15840            return Err(format!(
15841                "rank-local Step prefill positions {} != tokens {tokens}",
15842                positions.len()
15843            )
15844            .into());
15845        }
15846
15847        let mut active_input = e.uninit(expected_input)?;
15848        e.copy_view_into(
15849            &mut active_input,
15850            0,
15851            &h.slice(0..expected_input),
15852            expected_input,
15853        )?;
15854        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15855        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15856        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15857        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15858        // layer-count-amplified arm of the boot flake.
15859        e.stream().synchronize()?;
15860        tp.runtime
15861            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15862        let q_raw = tp
15863            .runtime
15864            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15865        let k_raw = tp
15866            .runtime
15867            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15868        let v_raw = tp
15869            .runtime
15870            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15871        let mut q = Vec::with_capacity(ranks);
15872        let mut k = Vec::with_capacity(ranks);
15873        for rank in 0..ranks {
15874            let engine = tp
15875                .runtime
15876                .rank_engine(rank)
15877                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15878            let _main = engine.gpu.enter_main()?;
15879            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15880            engine.rms_norm(
15881                &q_raw[rank],
15882                &attention.q_norm[rank],
15883                &mut q_rank,
15884                head_dim,
15885                tokens * local_heads,
15886                self.cfg.rms_eps,
15887            )?;
15888            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15889            engine.rms_norm(
15890                &k_raw[rank],
15891                &attention.k_norm[rank],
15892                &mut k_rank,
15893                head_dim,
15894                tokens * local_kv_heads,
15895                self.cfg.rms_eps,
15896            )?;
15897            let position = engine.htod_i32(&positions)?;
15898            let rope_freqs = if geometry.rope_factors {
15899                self.step35_aux
15900                    .as_ref()
15901                    .and_then(|aux| aux.rope_freqs(engine))
15902            } else {
15903                None
15904            };
15905            engine.rope_neox2(
15906                &mut q_rank,
15907                &mut k_rank,
15908                &position,
15909                head_dim,
15910                geometry.n_rot as usize,
15911                local_heads,
15912                local_kv_heads,
15913                tokens,
15914                geometry.rope_base,
15915                1.0,
15916                rope_freqs,
15917            )?;
15918            q.push(q_rank);
15919            k.push(k_rank);
15920        }
15921
15922        let gate_weight = fa
15923            .attn_gate
15924            .as_ref()
15925            .ok_or("step35 layer is missing attn_gate.weight")?;
15926        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15927        if gate.len() != tokens * heads {
15928            return Err(format!(
15929                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15930                gate.len()
15931            )
15932            .into());
15933        }
15934
15935        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15936        let base_len = cache.kv[il]
15937            .as_ref()
15938            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15939            .len;
15940        let distributed = cache.tp_kv[il]
15941            .as_ref()
15942            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15943        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15944            return Err(format!(
15945                "Step TP layer {il} cache lengths diverged before prefill: \
15946                 local={base_len} distributed={}/{}",
15947                distributed.committed_len(),
15948                distributed.staged_len()
15949            )
15950            .into());
15951        }
15952        let target_len = base_len
15953            .checked_add(tokens)
15954            .ok_or("Step TP prefill cache length overflow")?;
15955        if target_len > cache.max_ctx {
15956            return Err(format!(
15957                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15958                cache.max_ctx
15959            )
15960            .into());
15961        }
15962        if seq_end < target_len {
15963            return Err(format!(
15964                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15965            )
15966            .into());
15967        }
15968
15969        let transaction = cache.tp_kv[il]
15970            .as_mut()
15971            .expect("distributed cache checked above")
15972            .begin_transaction()?;
15973        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15974            cache.tp_kv[il]
15975                .as_mut()
15976                .expect("distributed cache checked above"),
15977            transaction,
15978            &k,
15979            &v_raw,
15980            tokens,
15981        ) {
15982            let _ = tp.runtime.rollback_tp_kv_transaction(
15983                cache.tp_kv[il]
15984                    .as_mut()
15985                    .expect("distributed cache checked above"),
15986                transaction,
15987            );
15988            return Err(error);
15989        }
15990
15991        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15992            let distributed = cache.tp_kv[il]
15993                .as_ref()
15994                .expect("distributed cache checked above");
15995            let staged_len = distributed.staged_len();
15996            let view_start = window
15997                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15998                .unwrap_or(0);
15999            let physical = distributed.physical_range(view_start, staged_len)?;
16000            let t_kv = staged_len - view_start;
16001            let swa_naive = window.is_some_and(|window| seq_end > window);
16002            let mut gated = Vec::with_capacity(ranks);
16003            for rank in 0..ranks {
16004                let engine = tp
16005                    .runtime
16006                    .rank_engine(rank)
16007                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16008                let _main = engine.gpu.enter_main()?;
16009                let rank_cache = distributed
16010                    .rank(rank)
16011                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16012                let k_view = engine.view_u8_range(
16013                    rank_cache.k(),
16014                    physical.start * distributed.k_tok_bytes(),
16015                    physical.end * distributed.k_tok_bytes(),
16016                );
16017                let v_view = engine.view_u8_range(
16018                    rank_cache.v(),
16019                    physical.start * distributed.v_tok_bytes(),
16020                    physical.end * distributed.v_tok_bytes(),
16021                );
16022                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
16023                if swa_naive {
16024                    let window = window.expect("SWA predicate requires a window");
16025                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
16026                        engine.sdpa_naive_w_quantized_view(
16027                            &q[rank],
16028                            &k_view,
16029                            &v_view,
16030                            &mut attention_out,
16031                            head_dim,
16032                            local_heads,
16033                            local_kv_heads,
16034                            tokens,
16035                            t_kv,
16036                            geometry.attention_scale(),
16037                            true,
16038                            window,
16039                            distributed.k_tok_bytes(),
16040                            distributed.v_tok_bytes(),
16041                        )?;
16042                    } else {
16043                        engine.fa_prefill_view_ws_w_hd128(
16044                            &q[rank],
16045                            &k_view,
16046                            &v_view,
16047                            &mut attention_out,
16048                            head_dim,
16049                            local_heads,
16050                            local_kv_heads,
16051                            tokens,
16052                            t_kv,
16053                            geometry.attention_scale(),
16054                            true,
16055                            window,
16056                            distributed.k_tok_bytes(),
16057                            distributed.v_tok_bytes(),
16058                        )?;
16059                    }
16060                } else if std::env::var("MEMRA_NOFA").is_ok() {
16061                    engine.sdpa_naive_quantized_view(
16062                        &q[rank],
16063                        &k_view,
16064                        &v_view,
16065                        &mut attention_out,
16066                        head_dim,
16067                        local_heads,
16068                        local_kv_heads,
16069                        tokens,
16070                        t_kv,
16071                        geometry.attention_scale(),
16072                        true,
16073                        distributed.k_tok_bytes(),
16074                        distributed.v_tok_bytes(),
16075                    )?;
16076                } else {
16077                    engine.fa_prefill_view_ws(
16078                        &q[rank],
16079                        &k_view,
16080                        &v_view,
16081                        &mut attention_out,
16082                        head_dim,
16083                        local_heads,
16084                        local_kv_heads,
16085                        tokens,
16086                        t_kv,
16087                        geometry.attention_scale(),
16088                        true,
16089                        distributed.k_tok_bytes(),
16090                        distributed.v_tok_bytes(),
16091                        false,
16092                    )?;
16093                }
16094
16095                let gate_start = rank * local_heads;
16096                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
16097                for token in 0..tokens {
16098                    let start = token * heads + gate_start;
16099                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
16100                }
16101                let gate_rank = engine.htod(&gate_rank)?;
16102                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
16103                engine.attn_head_gate(
16104                    &attention_out,
16105                    &gate_rank,
16106                    &mut gated_rank,
16107                    None,
16108                    head_dim,
16109                    local_heads,
16110                    tokens,
16111                )?;
16112                gated.push(gated_rank);
16113            }
16114            for rank in 1..ranks {
16115                let engine = tp
16116                    .runtime
16117                    .rank_engine(rank)
16118                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16119                let _main = engine.gpu.enter_main()?;
16120                engine.stream().synchronize()?;
16121            }
16122
16123            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
16124                let output = tp
16125                    .runtime
16126                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
16127                let k_shadow =
16128                    tp.runtime
16129                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
16130                let v_shadow =
16131                    tp.runtime
16132                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
16133                let root = tp
16134                    .runtime
16135                    .rank_engine(0)
16136                    .ok_or("Step TP prefill lost its root engine")?;
16137                let _main = root.gpu.enter_main()?;
16138                root.stream().synchronize()?;
16139                (output, k_shadow, v_shadow)
16140            } else {
16141                let attention = tp.runtime.gather_native_column_shards(
16142                    &gated,
16143                    tokens,
16144                    local_heads * head_dim,
16145                )?;
16146                let output = tp
16147                    .runtime
16148                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
16149                let k_shadow = tp
16150                    .runtime
16151                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
16152                let v_shadow =
16153                    tp.runtime
16154                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
16155                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
16156            };
16157            let local = cache.kv[il]
16158                .as_mut()
16159                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16160            if local.len != base_len {
16161                return Err(format!(
16162                    "Step TP layer {il} local cache changed during prefill: \
16163                     len={} base={base_len}",
16164                    local.len
16165                )
16166                .into());
16167            }
16168            let retain_from = window
16169                .map(|window| {
16170                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
16171                    let rollback_retain =
16172                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16173                    staged_retain.min(rollback_retain)
16174                })
16175                .unwrap_or(0);
16176            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
16177            e.append_kv_quantized_rows(
16178                &k_shadow,
16179                &v_shadow,
16180                &mut local.k,
16181                &mut local.v,
16182                write_row,
16183                tokens,
16184                local.kv_dim_k,
16185                local.kv_dim_v,
16186                local.k_tok_bytes,
16187                local.v_tok_bytes,
16188                false,
16189            )?;
16190            local.len = staged_len;
16191            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
16192            Ok(output)
16193        })();
16194
16195        let output = match staged {
16196            Ok(output) => output,
16197            Err(error) => {
16198                let _ = tp.runtime.rollback_tp_kv_transaction(
16199                    cache.tp_kv[il]
16200                        .as_mut()
16201                        .expect("distributed cache checked above"),
16202                    transaction,
16203                );
16204                if let Some(local) = cache.kv[il].as_mut() {
16205                    local.len = base_len;
16206                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16207                }
16208                return Err(error);
16209            }
16210        };
16211        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16212            cache.tp_kv[il]
16213                .as_mut()
16214                .expect("distributed cache checked above"),
16215            transaction,
16216            tokens,
16217        ) {
16218            let _ = tp.runtime.rollback_tp_kv_transaction(
16219                cache.tp_kv[il]
16220                    .as_mut()
16221                    .expect("distributed cache checked above"),
16222                transaction,
16223            );
16224            let local = cache.kv[il].as_mut().expect("local cache checked above");
16225            local.len = base_len;
16226            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16227            return Err(error);
16228        }
16229
16230        let committed = cache.tp_kv[il]
16231            .as_ref()
16232            .expect("distributed cache checked above")
16233            .committed_len();
16234        let local_len = cache.kv[il]
16235            .as_ref()
16236            .expect("local cache checked above")
16237            .len;
16238        if committed != local_len {
16239            return Err(format!(
16240                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16241            )
16242            .into());
16243        }
16244        eprintln!(
16245            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
16246             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16247             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16248             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
16249             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
16250             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
16251             output={} performance_claim=false",
16252            tp.layer,
16253            tp.devices,
16254            hydrated,
16255            if window.is_some() {
16256                "rank-local-swa-ring"
16257            } else {
16258                "rank-local-global"
16259            },
16260            tp.runtime.transport_label(),
16261            tp.runtime.bulk_p2p(),
16262            if tp.runtime.bulk_p2p() {
16263                "root-device"
16264            } else {
16265                "root-readback"
16266            },
16267        );
16268        Ok(output)
16269    }
16270
16271    fn step35_tp_decode_attn_resident(
16272        &self,
16273        e: &Engine,
16274        fa: &FullAttnLayer,
16275        il: usize,
16276        h: &CudaSlice<f32>,
16277        pos_d: &CudaSlice<i32>,
16278        cache: &mut Cache,
16279    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16280        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
16281        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
16282        // nvfp4-dev-routes counter.
16283        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16284        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16285        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
16286        let started = timing.then(std::time::Instant::now);
16287        let result = if crate::tp::step_tp_decode_v2_enabled()? {
16288            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
16289        } else {
16290            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
16291        };
16292        if let Some(started) = started {
16293            use std::sync::atomic::Ordering;
16294            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
16295                + started.elapsed().as_nanos() as u64;
16296            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16297            if calls % 430 == 0 {
16298                eprintln!(
16299                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
16300                    ns as f64 / 1.0e6,
16301                    ns as f64 / calls as f64 / 1.0e3,
16302                );
16303            }
16304        }
16305        result
16306    }
16307
16308    #[allow(clippy::too_many_arguments)]
16309    fn step35_tp_decode_attn_resident_inner(
16310        &self,
16311        e: &Engine,
16312        fa: &FullAttnLayer,
16313        il: usize,
16314        h: &CudaSlice<f32>,
16315        pos_d: &CudaSlice<i32>,
16316        cache: &mut Cache,
16317    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16318        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
16319        // drains every stream so queued async work is billed to the phase that queued it — the
16320        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
16321        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
16322        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16323        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16324        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16325        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16326        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16327        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16328        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16329        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16330        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16331        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
16332        fn lap(
16333            runtime: &crate::tp::TpE4m3HostBounce,
16334            e: &Engine,
16335            timer: &std::sync::atomic::AtomicU64,
16336            started: &mut Option<std::time::Instant>,
16337        ) -> Result<(), Box<dyn std::error::Error>> {
16338            let Some(start) = started.as_mut() else {
16339                return Ok(());
16340            };
16341            for rank in 0..runtime.devices().len() {
16342                if let Some(engine) = runtime.rank_engine(rank) {
16343                    let _main = engine.gpu.enter_main()?;
16344                    engine.stream().synchronize()?;
16345                }
16346            }
16347            e.stream().synchronize()?;
16348            timer.fetch_add(
16349                start.elapsed().as_nanos() as u64,
16350                std::sync::atomic::Ordering::Relaxed,
16351            );
16352            *start = std::time::Instant::now();
16353            Ok(())
16354        }
16355        let tp = fa
16356            .step_tp_qkv
16357            .as_ref()
16358            .ok_or("Step TP decode lost its resident projections")?;
16359        let attention = tp
16360            .attention
16361            .as_ref()
16362            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
16363        if !tp.runtime.native_p2p() {
16364            return Err("rank-local Step attention requires native P2P".into());
16365        }
16366        if crate::Engine::kv_fp8_on() {
16367            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
16368        }
16369
16370        let geometry = self.step35_geom(il);
16371        let window = geometry.window.map(|window| window as usize);
16372        let ranks = tp.runtime.devices().len();
16373        let head_dim = geometry.head_dim_k as usize;
16374        let heads = geometry.n_head as usize;
16375        let kv_heads = geometry.n_head_kv as usize;
16376        if heads % ranks != 0 || kv_heads % ranks != 0 {
16377            return Err(format!(
16378                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
16379            )
16380            .into());
16381        }
16382        let local_heads = heads / ranks;
16383        let local_kv_heads = kv_heads / ranks;
16384        let local_kv_dim = local_kv_heads * head_dim;
16385        let max_ctx = cache.max_ctx;
16386
16387        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
16388
16389        let base_len = cache.kv[il]
16390            .as_ref()
16391            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
16392            .len;
16393        let distributed = cache.tp_kv[il]
16394            .as_ref()
16395            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16396        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16397            return Err(format!(
16398                "Step TP layer {il} cache lengths diverged before decode: \
16399                 local={base_len} distributed={}/{}",
16400                distributed.committed_len(),
16401                distributed.staged_len()
16402            )
16403            .into());
16404        }
16405
16406        let mut lap_start = timing.then(std::time::Instant::now);
16407        let positions = e.dtoh_i32(pos_d)?;
16408        if positions.len() != 1 {
16409            return Err(format!(
16410                "rank-local Step decode requires one position, got {}",
16411                positions.len()
16412            )
16413            .into());
16414        }
16415        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
16416        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
16417            attention.decode_input.as_ref()
16418        {
16419            let mut decode_input = decode_input
16420                .lock()
16421                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16422            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16423            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16424            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16425            e.stream().synchronize()?;
16426            tp.runtime
16427                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16428            let q_raw = tp
16429                .runtime
16430                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16431            let k_raw = tp
16432                .runtime
16433                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16434            let v_raw = tp
16435                .runtime
16436                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16437            (q_raw, k_raw, v_raw, "root-device-replicated")
16438        } else {
16439            let activation = e.dtoh(h)?;
16440            let q_raw =
16441                tp.runtime
16442                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16443            let k_raw =
16444                tp.runtime
16445                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16446            let v_raw =
16447                tp.runtime
16448                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16449            (q_raw, k_raw, v_raw, "host-replicated")
16450        };
16451        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16452        let mut q = Vec::with_capacity(ranks);
16453        let mut k = Vec::with_capacity(ranks);
16454        for rank in 0..ranks {
16455            let engine = tp
16456                .runtime
16457                .rank_engine(rank)
16458                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16459            let _main = engine.gpu.enter_main()?;
16460            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16461            engine.rms_norm(
16462                &q_raw[rank],
16463                &attention.q_norm[rank],
16464                &mut q_rank,
16465                head_dim,
16466                local_heads,
16467                self.cfg.rms_eps,
16468            )?;
16469            let mut k_rank = engine.uninit(local_kv_dim)?;
16470            engine.rms_norm(
16471                &k_raw[rank],
16472                &attention.k_norm[rank],
16473                &mut k_rank,
16474                head_dim,
16475                local_kv_heads,
16476                self.cfg.rms_eps,
16477            )?;
16478            let position = engine.htod_i32(&positions)?;
16479            let rope_freqs = if geometry.rope_factors {
16480                self.step35_aux
16481                    .as_ref()
16482                    .and_then(|aux| aux.rope_freqs(engine))
16483            } else {
16484                None
16485            };
16486            engine.rope_neox2(
16487                &mut q_rank,
16488                &mut k_rank,
16489                &position,
16490                head_dim,
16491                geometry.n_rot as usize,
16492                local_heads,
16493                local_kv_heads,
16494                1,
16495                geometry.rope_base,
16496                1.0,
16497                rope_freqs,
16498            )?;
16499            q.push(q_rank);
16500            k.push(k_rank);
16501        }
16502        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16503
16504        let gate_weight = fa
16505            .attn_gate
16506            .as_ref()
16507            .ok_or("step35 layer is missing attn_gate.weight")?;
16508        let gate = e.matmul(gate_weight, h, 1)?;
16509        let gate = e.dtoh(&gate)?;
16510        if gate.len() != heads {
16511            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16512        }
16513        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16514
16515        let transaction = cache.tp_kv[il]
16516            .as_mut()
16517            .expect("distributed cache checked above")
16518            .begin_transaction()?;
16519        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16520            cache.tp_kv[il]
16521                .as_mut()
16522                .expect("distributed cache checked above"),
16523            transaction,
16524            &k,
16525            &v_raw,
16526            1,
16527        ) {
16528            let _ = tp.runtime.rollback_tp_kv_transaction(
16529                cache.tp_kv[il]
16530                    .as_mut()
16531                    .expect("distributed cache checked above"),
16532                transaction,
16533            );
16534            return Err(error);
16535        }
16536        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16537
16538        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16539            let distributed = cache.tp_kv[il]
16540                .as_ref()
16541                .expect("distributed cache checked above");
16542            let staged_len = distributed.staged_len();
16543            let view_start = window
16544                .map(|window| staged_len.saturating_sub(window))
16545                .unwrap_or(0);
16546            let physical = distributed.physical_range(view_start, staged_len)?;
16547            let t_kv = staged_len - view_start;
16548            let mut gated = Vec::with_capacity(ranks);
16549            for rank in 0..ranks {
16550                let engine = tp
16551                    .runtime
16552                    .rank_engine(rank)
16553                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16554                let _main = engine.gpu.enter_main()?;
16555                let rank_cache = distributed
16556                    .rank(rank)
16557                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16558                let k_view = engine.view_u8_range(
16559                    rank_cache.k(),
16560                    physical.start * distributed.k_tok_bytes(),
16561                    physical.end * distributed.k_tok_bytes(),
16562                );
16563                let v_view = engine.view_u8_range(
16564                    rank_cache.v(),
16565                    physical.start * distributed.v_tok_bytes(),
16566                    physical.end * distributed.v_tok_bytes(),
16567                );
16568                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16569                engine.fa_decode_kvmod(
16570                    &q[rank],
16571                    &k_view,
16572                    &v_view,
16573                    &mut attention_out,
16574                    head_dim,
16575                    local_heads,
16576                    local_kv_heads,
16577                    t_kv,
16578                    geometry.attention_scale(),
16579                    distributed.k_tok_bytes(),
16580                    distributed.v_tok_bytes(),
16581                    false,
16582                )?;
16583                let gate_start = rank * local_heads;
16584                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16585                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16586                engine.attn_head_gate(
16587                    &attention_out,
16588                    &gate_rank,
16589                    &mut gated_rank,
16590                    None,
16591                    head_dim,
16592                    local_heads,
16593                    1,
16594                )?;
16595                gated.push(gated_rank);
16596            }
16597            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16598
16599            let gathered =
16600                tp.runtime
16601                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16602            let output = tp
16603                .runtime
16604                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16605            let output = e.htod(&output)?;
16606            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16607
16608            let k_shadow = tp
16609                .runtime
16610                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16611            let v_shadow = tp
16612                .runtime
16613                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16614            let k_shadow = e.htod(&k_shadow)?;
16615            let v_shadow = e.htod(&v_shadow)?;
16616            let local = cache.kv[il]
16617                .as_mut()
16618                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16619            if local.len != base_len || base_len + 1 > max_ctx {
16620                return Err(format!(
16621                    "Step TP layer {il} local cache changed during decode: \
16622                     len={} base={base_len} max={max_ctx}",
16623                    local.len
16624                )
16625                .into());
16626            }
16627            let retain_from = window
16628                .map(|window| {
16629                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16630                    let rollback_retain =
16631                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16632                    staged_retain.min(rollback_retain)
16633                })
16634                .unwrap_or(0);
16635            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16636            e.append_kv_quantized(
16637                &k_shadow,
16638                &v_shadow,
16639                &mut local.k,
16640                &mut local.v,
16641                write_row,
16642                local.kv_dim_k,
16643                local.kv_dim_v,
16644                local.k_tok_bytes,
16645                local.v_tok_bytes,
16646                false,
16647            )?;
16648            local.len = base_len + 1;
16649            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16650            Ok(output)
16651        })();
16652
16653        let output = match staged {
16654            Ok(output) => output,
16655            Err(error) => {
16656                let _ = tp.runtime.rollback_tp_kv_transaction(
16657                    cache.tp_kv[il]
16658                        .as_mut()
16659                        .expect("distributed cache checked above"),
16660                    transaction,
16661                );
16662                if let Some(local) = cache.kv[il].as_mut() {
16663                    local.len = base_len;
16664                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16665                }
16666                return Err(error);
16667            }
16668        };
16669        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16670            cache.tp_kv[il]
16671                .as_mut()
16672                .expect("distributed cache checked above"),
16673            transaction,
16674            1,
16675        ) {
16676            let _ = tp.runtime.rollback_tp_kv_transaction(
16677                cache.tp_kv[il]
16678                    .as_mut()
16679                    .expect("distributed cache checked above"),
16680                transaction,
16681            );
16682            let local = cache.kv[il].as_mut().expect("local cache checked above");
16683            local.len = base_len;
16684            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16685            return Err(error);
16686        }
16687
16688        let committed = cache.tp_kv[il]
16689            .as_ref()
16690            .expect("distributed cache checked above")
16691            .committed_len();
16692        let local_len = cache.kv[il]
16693            .as_ref()
16694            .expect("local cache checked above")
16695            .len;
16696        if committed != local_len {
16697            return Err(format!(
16698                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16699            )
16700            .into());
16701        }
16702        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16703        if timing {
16704            use std::sync::atomic::Ordering;
16705            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16706            if calls % 430 == 0 {
16707                let avg = |t: &std::sync::atomic::AtomicU64| {
16708                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16709                };
16710                eprintln!(
16711                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16712                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16713                    avg(&T_POS),
16714                    avg(&T_QKV),
16715                    avg(&T_NORMROPE),
16716                    avg(&T_GATE),
16717                    avg(&T_APPEND),
16718                    avg(&T_ATTN),
16719                    avg(&T_OPROJ),
16720                    avg(&T_SHADOW),
16721                );
16722            }
16723        }
16724        eprintln!(
16725            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16726             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16727             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16728             attention_scope={} input_path={} kv_physical_rows={} \
16729             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16730             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16731             bulk_p2p={} output=root-readback performance_claim=false",
16732            tp.layer,
16733            tp.devices,
16734            hydrated,
16735            if window.is_some() {
16736                "rank-local-swa-ring"
16737            } else {
16738                "rank-local-global"
16739            },
16740            input_path,
16741            cache.tp_kv[il]
16742                .as_ref()
16743                .expect("distributed cache checked above")
16744                .physical_capacity(),
16745            tp.runtime.transport_label(),
16746            tp.runtime.bulk_p2p(),
16747        );
16748        Ok(output)
16749    }
16750
16751    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16752    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16753    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16754    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16755    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16756    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16757    #[allow(clippy::too_many_arguments)]
16758    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16759    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16760    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16761    /// the resident fused TP2 class (caller falls back to the per-row walk).
16762    pub(crate) fn step35_verify_qkv_precompute(
16763        &self,
16764        e: &Engine,
16765        il: usize,
16766        h_t: &CudaSlice<f32>,
16767        t: usize,
16768    ) -> Result<bool, Box<dyn std::error::Error>> {
16769        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16770            return Ok(false);
16771        };
16772        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16773            return Ok(false);
16774        };
16775        let Some(attention) = tp.attention.as_ref() else {
16776            return Ok(false);
16777        };
16778        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16779            return Ok(false);
16780        }
16781        let geometry = self.step35_geom(il);
16782        let heads = geometry.n_head as usize;
16783        let ws_index = tp
16784            .runtime
16785            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16786        let gate_shards = attention
16787            .gate_shards_bf16
16788            .as_deref()
16789            .map(crate::tp::StepTpGateShards::Bf16);
16790        tp.runtime.decode_v2_input_qkv_tcol(
16791            ws_index,
16792            e,
16793            h_t,
16794            t,
16795            &tp.q,
16796            &tp.k,
16797            &tp.v,
16798            gate_shards,
16799        )?;
16800        Ok(true)
16801    }
16802
16803    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16804    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16805    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16806    /// flag confirmed the defer engaged for every column.
16807    pub(crate) fn step35_verify_oproj_tcol(
16808        &self,
16809        e: &Engine,
16810        il: usize,
16811        t: usize,
16812    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16813        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16814            return Err("tcol o_proj join expects full attention".into());
16815        };
16816        let tp = fa
16817            .step_tp_qkv
16818            .as_ref()
16819            .ok_or("tcol o_proj join lost its resident projections")?;
16820        let heads = self.step35_geom(il).n_head as usize;
16821        let ws_index = tp
16822            .runtime
16823            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16824        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16825    }
16826
16827    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16828    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16829    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16830    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16831    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16832    /// walk runs the ordinary per-column program.
16833    pub(crate) fn step35_spec_fa2_precheck(
16834        &self,
16835        cache: &Cache,
16836        il: usize,
16837        pos0: usize,
16838    ) -> Result<bool, Box<dyn std::error::Error>> {
16839        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16840        // a silently-vacuous door is indistinguishable from a slow one without this.
16841        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16842            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16843            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16844            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16845                let mut seen = SEEN.lock().unwrap();
16846                if !seen.iter().any(|c| *c == clause) {
16847                    // leak: bounded by the clause-id set
16848                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16849                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16850                }
16851            }
16852            false
16853        }
16854        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16855        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16856        if let Some(only) =
16857            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16858        {
16859            if *only != il {
16860                return Ok(false);
16861            }
16862        }
16863        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16864            return Ok(nope("mixer", il, pos0));
16865        };
16866        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16867            return Ok(nope("step_tp", il, pos0));
16868        };
16869        let Some(attention) = tp.attention.as_ref() else {
16870            return Ok(nope("attention", il, pos0));
16871        };
16872        if !tp.runtime.native_p2p()
16873            || crate::Engine::kv_fp8_on()
16874            || !crate::tp::step_tp_dcw_enabled()?
16875            || !crate::tp::step_tp_qkv_fused_enabled()?
16876            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16877        {
16878            return Ok(nope("runtime-doors", il, pos0));
16879        }
16880        let geometry = self.step35_geom(il);
16881        let head_dim = geometry.head_dim_k as usize;
16882        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16883            return Ok(nope("fa-class", il, pos0));
16884        }
16885        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16886            return Ok(nope("tp-kv", il, pos0));
16887        };
16888        if distributed.staged_len() != pos0 {
16889            return Ok(nope("staged-len", il, pos0));
16890        }
16891        // Both appends must land without a ring rebase (rebase columns take the
16892        // host-row path, which cannot stash).
16893        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16894        if would_rebase {
16895            return Ok(nope("rebase", il, pos0));
16896        }
16897        let window = geometry.window.map(|w| w as usize);
16898        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16899        // shift by one key, so one shared tile grid cannot reproduce both rows'
16900        // per-column FP grouping) — and drifted verify logits change accept decisions,
16901        // breaking the spec==target contract. Engage only when BOTH rows' views start
16902        // at 0 (global, or SWA still inside its window): bitwise per row under the
16903        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16904        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16905        if let Some(w) = window {
16906            if pos0 + 2 > w {
16907                return Ok(nope("swa-capped", il, pos0));
16908            }
16909        }
16910        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16911        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16912        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16913        let (t0, t1) = (pos0 + 1, pos0 + 2);
16914        if t0 < 96 {
16915            return Ok(nope("dcw-floor", il, pos0));
16916        }
16917        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16918            return Ok(nope("vec-floor", il, pos0));
16919        }
16920        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16921        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16922        // the two rows' own launches — the joined kernel derives one grid from T1 and
16923        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16924        // the combine's merge rounding. Boundary rounds fall back per column.
16925        let ranks = tp.runtime.devices().len();
16926        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16927        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16928        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16929        if sp0 != sp1 {
16930            return Ok(nope("partition-sp", il, pos0));
16931        }
16932        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16933        if ns0 != ns1 {
16934            return Ok(nope("partition-ns", il, pos0));
16935        }
16936        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16937            return Ok(nope("partition-per", il, pos0));
16938        }
16939        Ok(true)
16940    }
16941
16942    /// T-ROW fa precheck (the rows kernel supersedes the dcw2 pair-join): every dynamic
16943    /// input of the engine-side dcw decision must hold for EVERY row — stashing is
16944    /// unrecoverable — plus the rows-launcher guards (big-rig ladder, no env split
16945    /// overrides). No partition or capped-SWA clauses: each row derives its OWN geometry.
16946    pub(crate) fn step35_fa_rows_precheck(
16947        &self,
16948        cache: &Cache,
16949        il: usize,
16950        pos0: usize,
16951        t: usize,
16952    ) -> Result<bool, Box<dyn std::error::Error>> {
16953        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16954            return Ok(false);
16955        };
16956        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16957            return Ok(false);
16958        };
16959        let Some(attention) = tp.attention.as_ref() else {
16960            return Ok(false);
16961        };
16962        if !tp.runtime.native_p2p()
16963            || crate::Engine::kv_fp8_on()
16964            || !crate::tp::step_tp_dcw_enabled()?
16965            || !crate::tp::step_tp_qkv_fused_enabled()?
16966            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16967        {
16968            return Ok(false);
16969        }
16970        let geometry = self.step35_geom(il);
16971        let head_dim = geometry.head_dim_k as usize;
16972        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16973            return Ok(false);
16974        }
16975        if crate::fa_sm_count() < 128
16976            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16977            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16978            || std::env::var("MEMRA_FA_SP16").is_ok()
16979            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16980        {
16981            return Ok(false);
16982        }
16983        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16984            return Ok(false);
16985        };
16986        if distributed.staged_len() != pos0 {
16987            return Ok(false);
16988        }
16989        let (_, would_rebase) = distributed.peek_append_ring(t)?;
16990        if would_rebase {
16991            return Ok(false);
16992        }
16993        // Row 0 sees the smallest view: its post-append effective t_kv must clear both
16994        // the dcw floor and the vec-class floor (later rows only grow).
16995        let window = geometry.window.map(|w| w as usize);
16996        let t0 = window.map(|w| (pos0 + 1).min(w)).unwrap_or(pos0 + 1);
16997        if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16998            return Ok(false);
16999        }
17000        Ok(true)
17001    }
17002
17003    /// T-ROW fa join for the verify walk (same-session rows: shared ring/len with
17004    /// len_back = t-1-r). Tables stage once per (layer, rank, ring, t) and live on the
17005    /// owning rank.
17006    pub(crate) fn step35_verify_fa_rows_join(
17007        &self,
17008        e: &Engine,
17009        il: usize,
17010        cache: &Cache,
17011        pos0: usize,
17012        t: usize,
17013    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17014        use cudarc::driver::DevicePtr;
17015        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17016            return Err("fa rows join expects full attention".into());
17017        };
17018        let tp = fa
17019            .step_tp_qkv
17020            .as_ref()
17021            .ok_or("fa rows join lost its resident projections")?;
17022        let geometry = self.step35_geom(il);
17023        let heads = geometry.n_head as usize;
17024        let head_dim = geometry.head_dim_k as usize;
17025        let window = geometry.window.map(|w| w as usize);
17026        let distributed = cache.tp_kv[il]
17027            .as_ref()
17028            .ok_or("fa rows join lost its distributed KV cache")?;
17029        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17030        // Host mirror of the kernel's big-rig ladder (launcher-guarded identical).
17031        let ladder = |t_kv: usize| -> usize {
17032            if t_kv <= 2048 {
17033                16
17034            } else if t_kv <= 16384 {
17035                64
17036            } else {
17037                128
17038            }
17039        };
17040        let mut max_ns = 1usize;
17041        for r in 0..t {
17042            let t_kv = window
17043                .map(|w| (pos0 + r + 1).min(w))
17044                .unwrap_or(pos0 + r + 1);
17045            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17046        }
17047        // Rebuild the tiny raw-pointer table from the live distributed cache immediately
17048        // before launch. A process-lifetime map cannot prove allocation generation: CUDA may
17049        // recycle len/base independently of the large K/V rings, making a pointer-key cache
17050        // hit refer to another session (Hermes `11339f5cd3c132a3`).
17051        let ranks = tp.runtime.devices().len();
17052        let mut tables = Vec::with_capacity(ranks);
17053        for rank in 0..ranks {
17054            let engine = tp
17055                .runtime
17056                .rank_engine(rank)
17057                .ok_or("fa rows join lost a rank engine")?;
17058            let rank_cache = distributed
17059                .rank(rank)
17060                .ok_or("fa rows join lost a KV cache rank")?;
17061            let _main = engine.gpu.enter_main()?;
17062            let s = engine.stream();
17063            let (kp, _g0) = rank_cache.k().device_ptr(&s);
17064            let (vp, _g1) = rank_cache.v().device_ptr(&s);
17065            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17066            let bp = match rank_cache.base_d() {
17067                Some(b) => {
17068                    let (p, _g) = b.device_ptr(&s);
17069                    p as u64
17070                }
17071                None => 0u64,
17072            };
17073            let mut host = Vec::with_capacity(t * 6);
17074            for r in 0..t {
17075                host.extend_from_slice(&[
17076                    kp as u64,
17077                    vp as u64,
17078                    lp as u64,
17079                    bp,
17080                    0u64,
17081                    (t - 1 - r) as u64,
17082                ]);
17083            }
17084            tables.push(engine.stream().clone_htod(&host)?);
17085        }
17086        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17087        let ws_index = tp
17088            .runtime
17089            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17090        tp.runtime.decode_v2_fa_rows_join(
17091            ws_index,
17092            e,
17093            &tp.o,
17094            &tabs,
17095            t,
17096            head_dim,
17097            window.unwrap_or(0),
17098            max_ns,
17099            geometry.attention_scale(),
17100            k_tok_bytes,
17101            v_tok_bytes,
17102        )
17103    }
17104
17105    /// Multi-session t-row fa precheck (the batched serving walk): the static doors of
17106    /// the rows kernel plus per-SESSION dynamic checks — every row's own cache must be
17107    /// hydrated, in sync, rebase-free and above both floors.
17108    pub(crate) fn step35_batch_fa_rows_precheck(
17109        &self,
17110        caches: &[&mut Cache],
17111        row_to_cache: impl Fn(usize) -> usize,
17112        positions: &[i32],
17113        il: usize,
17114    ) -> Result<bool, Box<dyn std::error::Error>> {
17115        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17116            return Ok(false);
17117        };
17118        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17119            return Ok(false);
17120        };
17121        let Some(attention) = tp.attention.as_ref() else {
17122            return Ok(false);
17123        };
17124        if !tp.runtime.native_p2p()
17125            || crate::Engine::kv_fp8_on()
17126            || !crate::tp::step_tp_dcw_enabled()?
17127            || !crate::tp::step_tp_qkv_fused_enabled()?
17128            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
17129        {
17130            return Ok(false);
17131        }
17132        let geometry = self.step35_geom(il);
17133        let head_dim = geometry.head_dim_k as usize;
17134        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
17135            return Ok(false);
17136        }
17137        if crate::fa_sm_count() < 128
17138            || std::env::var("MEMRA_FA_SPLIT").is_ok()
17139            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
17140            || std::env::var("MEMRA_FA_SP16").is_ok()
17141            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
17142        {
17143            return Ok(false);
17144        }
17145        let window = geometry.window.map(|w| w as usize);
17146        for (r, &pos) in positions.iter().enumerate() {
17147            let cache = &caches[row_to_cache(r)];
17148            let Some(distributed) = cache.tp_kv[il].as_ref() else {
17149                return Ok(false);
17150            };
17151            if distributed.staged_len() != pos as usize {
17152                return Ok(false);
17153            }
17154            if distributed.peek_append_ring(1)?.1 {
17155                return Ok(false);
17156            }
17157            let t0 = window
17158                .map(|w| (pos as usize + 1).min(w))
17159                .unwrap_or(pos as usize + 1);
17160            if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
17161                return Ok(false);
17162            }
17163        }
17164        Ok(true)
17165    }
17166
17167    /// FULL t-row attention pass for the VERIFY walk (same-session rows): rope/append +
17168    /// fa + combine + o_proj join in 3 launches/rank/layer. Row r appends at slot
17169    /// len-base+r and one last block advances len by t; the fa rows read len_back =
17170    /// t-1-r. Returns None when the fused-rope class does not hold (the walk keeps the
17171    /// per-column stash flow). Caller has passed `step35_fa_rows_precheck`.
17172    pub(crate) fn step35_verify_rope_fa_pass(
17173        &self,
17174        e: &Engine,
17175        il: usize,
17176        cache: &Cache,
17177        pos0: usize,
17178        t: usize,
17179        stage_pos: bool,
17180    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17181        use cudarc::driver::DevicePtr;
17182        if !crate::tp::fuse_rope_append_on() {
17183            return Ok(None);
17184        }
17185        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17186            return Ok(None);
17187        };
17188        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17189            return Ok(None);
17190        };
17191        let Some(attention) = tp.attention.as_ref() else {
17192            return Ok(None);
17193        };
17194        let geometry = self.step35_geom(il);
17195        let head_dim = geometry.head_dim_k as usize;
17196        if head_dim != 128 {
17197            return Ok(None);
17198        }
17199        let heads = geometry.n_head as usize;
17200        let window = geometry.window.map(|w| w as usize);
17201        let ranks = tp.runtime.devices().len();
17202        let Some(distributed) = cache.tp_kv[il].as_ref() else {
17203            return Ok(None);
17204        };
17205        if distributed.kv_dim_k() != distributed.kv_dim_v() {
17206            return Ok(None);
17207        }
17208        {
17209            let rank0 = distributed.rank(0).ok_or("verify rope pass lost rank 0")?;
17210            if rank0.base_d().is_none()
17211                && distributed.staged_len() + t > distributed.physical_capacity()
17212            {
17213                return Ok(None);
17214            }
17215        }
17216        let mut rope_freqs = Vec::with_capacity(ranks);
17217        for rank in 0..ranks {
17218            let engine = tp
17219                .runtime
17220                .rank_engine(rank)
17221                .ok_or("verify rope pass lost a rank engine")?;
17222            rope_freqs.push(if geometry.rope_factors {
17223                match self
17224                    .step35_aux
17225                    .as_ref()
17226                    .and_then(|aux| aux.rope_freqs(engine))
17227                {
17228                    Some(f) => Some(f),
17229                    None => return Ok(None),
17230                }
17231            } else {
17232                None
17233            });
17234        }
17235        let ladder = |t_kv: usize| -> usize {
17236            if t_kv <= 2048 {
17237                16
17238            } else if t_kv <= 16384 {
17239                64
17240            } else {
17241                128
17242            }
17243        };
17244        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17245        let mut max_ns = 1usize;
17246        let mut positions = Vec::with_capacity(t);
17247        for r in 0..t {
17248            positions.push((pos0 + r) as i32);
17249            let t_kv = window
17250                .map(|w| (pos0 + r + 1).min(w))
17251                .unwrap_or(pos0 + r + 1);
17252            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17253        }
17254        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17255        let mut tab_keys = vec![0u64; ranks];
17256        for rank in 0..ranks {
17257            let engine = tp
17258                .runtime
17259                .rank_engine(rank)
17260                .ok_or("verify rope pass lost a rank engine")?;
17261            let rank_cache = distributed
17262                .rank(rank)
17263                .ok_or("verify rope pass lost a KV cache rank")?;
17264            let _main = engine.gpu.enter_main()?;
17265            let s = engine.stream();
17266            let (kp, _g0) = rank_cache.k().device_ptr(&s);
17267            let (vp, _g1) = rank_cache.v().device_ptr(&s);
17268            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17269            let bp = match rank_cache.base_d() {
17270                Some(b) => {
17271                    let (p, _g) = b.device_ptr(&s);
17272                    p as u64
17273                }
17274                None => 0u64,
17275            };
17276            tab_keys[rank] = (kp as u64)
17277                .rotate_left(17)
17278                .wrapping_add(bp)
17279                .wrapping_add((il as u64) << 32)
17280                .wrapping_add(t as u64)
17281                .wrapping_add(1 << 63);
17282            for _r in 0..t {
17283                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17284            }
17285        }
17286        let ws_index = tp
17287            .runtime
17288            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17289        tp.runtime
17290            .decode_v2_rope_fa_rows(
17291                ws_index,
17292                e,
17293                &tp.o,
17294                &session_parts,
17295                &tab_keys,
17296                &positions,
17297                stage_pos,
17298                true,
17299                &attention.q_norm,
17300                &attention.k_norm,
17301                &rope_freqs,
17302                t,
17303                head_dim,
17304                geometry.n_rot as usize,
17305                window.unwrap_or(0),
17306                max_ns,
17307                geometry.attention_scale(),
17308                k_tok_bytes,
17309                v_tok_bytes,
17310                self.cfg.rms_eps,
17311                geometry.rope_base,
17312            )
17313            .map(Some)
17314    }
17315
17316    /// FULL t-row attention pass for the batched walk (rope/append + fa + combine +
17317    /// o_proj join, 3 launches/rank/layer): returns None when the fused-rope class does
17318    /// not hold — the caller falls back to the per-row stash flow. The caller has
17319    /// already passed `step35_batch_fa_rows_precheck`.
17320    #[allow(clippy::too_many_arguments)]
17321    pub(crate) fn step35_batch_rope_fa_pass(
17322        &self,
17323        e: &Engine,
17324        il: usize,
17325        caches: &[&mut Cache],
17326        row_to_cache: impl Fn(usize) -> usize,
17327        positions: &[i32],
17328        t: usize,
17329        stage_pos: bool,
17330    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17331        use cudarc::driver::DevicePtr;
17332        if !crate::tp::fuse_rope_append_on() {
17333            return Ok(None);
17334        }
17335        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17336            return Ok(None);
17337        };
17338        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17339            return Ok(None);
17340        };
17341        let Some(attention) = tp.attention.as_ref() else {
17342            return Ok(None);
17343        };
17344        let geometry = self.step35_geom(il);
17345        let head_dim = geometry.head_dim_k as usize;
17346        if head_dim != 128 {
17347            return Ok(None);
17348        }
17349        let heads = geometry.n_head as usize;
17350        let window = geometry.window.map(|w| w as usize);
17351        let ranks = tp.runtime.devices().len();
17352        // The rows kernels never arm base_d; refuse once a ring could have rebased
17353        // without an armed base (the table would read base=0 after a real rebase).
17354        for r in 0..t {
17355            let cache = &caches[row_to_cache(r)];
17356            let Some(distributed) = cache.tp_kv[il].as_ref() else {
17357                return Ok(None);
17358            };
17359            if distributed.kv_dim_k() != distributed.kv_dim_v() {
17360                return Ok(None);
17361            }
17362            let rank0 = distributed.rank(0).ok_or("rope fa pass lost rank 0")?;
17363            if rank0.base_d().is_none()
17364                && distributed.staged_len() + t > distributed.physical_capacity()
17365            {
17366                return Ok(None);
17367            }
17368        }
17369        let mut rope_freqs = Vec::with_capacity(ranks);
17370        for rank in 0..ranks {
17371            let engine = tp
17372                .runtime
17373                .rank_engine(rank)
17374                .ok_or("rope fa pass lost a rank engine")?;
17375            rope_freqs.push(if geometry.rope_factors {
17376                match self
17377                    .step35_aux
17378                    .as_ref()
17379                    .and_then(|aux| aux.rope_freqs(engine))
17380                {
17381                    Some(f) => Some(f),
17382                    None => return Ok(None),
17383                }
17384            } else {
17385                None
17386            });
17387        }
17388        let ladder = |t_kv: usize| -> usize {
17389            if t_kv <= 2048 {
17390                16
17391            } else if t_kv <= 16384 {
17392                64
17393            } else {
17394                128
17395            }
17396        };
17397        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17398        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17399        let mut tab_keys = vec![0u64; ranks];
17400        for (r, &pos) in positions.iter().enumerate().take(t) {
17401            let cache = &caches[row_to_cache(r)];
17402            let distributed = cache.tp_kv[il]
17403                .as_ref()
17404                .ok_or("rope fa pass lost a distributed KV cache")?;
17405            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17406            let t_kv = window
17407                .map(|w| (pos as usize + 1).min(w))
17408                .unwrap_or(pos as usize + 1);
17409            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17410            for rank in 0..ranks {
17411                let engine = tp
17412                    .runtime
17413                    .rank_engine(rank)
17414                    .ok_or("rope fa pass lost a rank engine")?;
17415                let rank_cache = distributed
17416                    .rank(rank)
17417                    .ok_or("rope fa pass lost a KV cache rank")?;
17418                let _main = engine.gpu.enter_main()?;
17419                let s = engine.stream();
17420                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17421                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17422                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17423                let bp = match rank_cache.base_d() {
17424                    Some(b) => {
17425                        let (p, _g) = b.device_ptr(&s);
17426                        p as u64
17427                    }
17428                    None => 0u64,
17429                };
17430                tab_keys[rank] = tab_keys[rank]
17431                    .rotate_left(9)
17432                    .wrapping_add(kp as u64)
17433                    .wrapping_add(bp)
17434                    .wrapping_add(il as u64);
17435                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17436            }
17437        }
17438        let ws_index = tp
17439            .runtime
17440            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17441        tp.runtime
17442            .decode_v2_rope_fa_rows(
17443                ws_index,
17444                e,
17445                &tp.o,
17446                &session_parts,
17447                &tab_keys,
17448                positions,
17449                stage_pos,
17450                false,
17451                &attention.q_norm,
17452                &attention.k_norm,
17453                &rope_freqs,
17454                t,
17455                head_dim,
17456                geometry.n_rot as usize,
17457                window.unwrap_or(0),
17458                max_ns,
17459                geometry.attention_scale(),
17460                k_tok_bytes,
17461                v_tok_bytes,
17462                self.cfg.rms_eps,
17463                geometry.rope_base,
17464            )
17465            .map(Some)
17466    }
17467
17468    /// Multi-session t-row fa join (batched serving): per-row table entries point at
17469    /// each row's OWN session rings/counters (len_back = 0 — every session appended
17470    /// exactly its one row). Tables stage once per (layer, rank, session-set, t).
17471    #[allow(clippy::too_many_arguments)]
17472    pub(crate) fn step35_batch_fa_rows_join(
17473        &self,
17474        e: &Engine,
17475        il: usize,
17476        caches: &[&mut Cache],
17477        row_to_cache: impl Fn(usize) -> usize,
17478        positions: &[i32],
17479        t: usize,
17480    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17481        use cudarc::driver::DevicePtr;
17482        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17483            return Err("batch fa rows join expects full attention".into());
17484        };
17485        let tp = fa
17486            .step_tp_qkv
17487            .as_ref()
17488            .ok_or("batch fa rows join lost its resident projections")?;
17489        let geometry = self.step35_geom(il);
17490        let heads = geometry.n_head as usize;
17491        let head_dim = geometry.head_dim_k as usize;
17492        let window = geometry.window.map(|w| w as usize);
17493        let ladder = |t_kv: usize| -> usize {
17494            if t_kv <= 2048 {
17495                16
17496            } else if t_kv <= 16384 {
17497                64
17498            } else {
17499                128
17500            }
17501        };
17502        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17503        for (r, &pos) in positions.iter().enumerate() {
17504            let cache = &caches[row_to_cache(r)];
17505            let distributed = cache.tp_kv[il]
17506                .as_ref()
17507                .ok_or("batch fa rows join lost a distributed KV cache")?;
17508            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17509            let t_kv = window
17510                .map(|w| (pos as usize + 1).min(w))
17511                .unwrap_or(pos as usize + 1);
17512            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17513        }
17514        // Multi-session tables also rebuild from every live K/V/len/base tuple. Keeping a
17515        // process-lifetime raw-pointer cache here omitted V and len identity and had no
17516        // allocation generation, so allocator reuse could bind one request to another.
17517        let ranks = tp.runtime.devices().len();
17518        let mut tables = Vec::with_capacity(ranks);
17519        for rank in 0..ranks {
17520            let engine = tp
17521                .runtime
17522                .rank_engine(rank)
17523                .ok_or("batch fa rows join lost a rank engine")?;
17524            let _main = engine.gpu.enter_main()?;
17525            let s = engine.stream();
17526            let mut host = Vec::with_capacity(t * 6);
17527            for r in 0..t {
17528                let cache = &caches[row_to_cache(r)];
17529                let distributed = cache.tp_kv[il]
17530                    .as_ref()
17531                    .ok_or("batch fa rows join lost a distributed KV cache")?;
17532                let rank_cache = distributed
17533                    .rank(rank)
17534                    .ok_or("batch fa rows join lost a KV cache rank")?;
17535                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17536                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17537                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17538                let bp = match rank_cache.base_d() {
17539                    Some(b) => {
17540                        let (p, _g) = b.device_ptr(&s);
17541                        p as u64
17542                    }
17543                    None => 0u64,
17544                };
17545                host.extend_from_slice(&[kp as u64, vp as u64, lp as u64, bp, 0u64, 0u64]);
17546            }
17547            tables.push(engine.stream().clone_htod(&host)?);
17548        }
17549        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17550        let ws_index = tp
17551            .runtime
17552            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17553        tp.runtime.decode_v2_fa_rows_join(
17554            ws_index,
17555            e,
17556            &tp.o,
17557            &tabs,
17558            t,
17559            head_dim,
17560            window.unwrap_or(0),
17561            max_ns,
17562            geometry.attention_scale(),
17563            k_tok_bytes,
17564            v_tok_bytes,
17565        )
17566    }
17567
17568    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
17569    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
17570    /// slab on `e`.
17571    pub(crate) fn step35_verify_spec_fa2_join(
17572        &self,
17573        e: &Engine,
17574        il: usize,
17575        cache: &Cache,
17576        pos0: usize,
17577    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17578        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17579            return Err("spec fa2 join expects full attention".into());
17580        };
17581        let tp = fa
17582            .step_tp_qkv
17583            .as_ref()
17584            .ok_or("spec fa2 join lost its resident projections")?;
17585        let geometry = self.step35_geom(il);
17586        let heads = geometry.n_head as usize;
17587        let head_dim = geometry.head_dim_k as usize;
17588        let window = geometry.window.map(|w| w as usize);
17589        // POST-append view of the second row (kernel T1 = len - lstart with len =
17590        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
17591        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
17592        let distributed = cache.tp_kv[il]
17593            .as_ref()
17594            .ok_or("spec fa2 join lost its distributed KV cache")?;
17595        let ws_index = tp
17596            .runtime
17597            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17598        tp.runtime.decode_v2_spec_fa2_join(
17599            ws_index,
17600            e,
17601            &tp.o,
17602            distributed,
17603            head_dim,
17604            window.unwrap_or(0),
17605            bucket,
17606            geometry.attention_scale(),
17607        )
17608    }
17609
17610    fn step35_tp_decode_attn_resident_v2(
17611        &self,
17612        e: &Engine,
17613        fa: &FullAttnLayer,
17614        il: usize,
17615        h: &CudaSlice<f32>,
17616        pos_d: &CudaSlice<i32>,
17617        cache: &mut Cache,
17618    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17619        let tp = fa
17620            .step_tp_qkv
17621            .as_ref()
17622            .ok_or("Step TP decode lost its resident projections")?;
17623        let attention = tp
17624            .attention
17625            .as_ref()
17626            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
17627        if !tp.runtime.native_p2p() {
17628            return Err("rank-local Step attention requires native P2P".into());
17629        }
17630        if crate::Engine::kv_fp8_on() {
17631            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
17632        }
17633
17634        let geometry = self.step35_geom(il);
17635        let window = geometry.window.map(|window| window as usize);
17636        let ranks = tp.runtime.devices().len();
17637        let head_dim = geometry.head_dim_k as usize;
17638        let heads = geometry.n_head as usize;
17639        let kv_heads = geometry.n_head_kv as usize;
17640        if heads % ranks != 0 || kv_heads % ranks != 0 {
17641            return Err(format!(
17642                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
17643            )
17644            .into());
17645        }
17646        let local_heads = heads / ranks;
17647        let local_kv_heads = kv_heads / ranks;
17648        let max_ctx = cache.max_ctx;
17649
17650        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
17651
17652        let base_len = cache.kv[il]
17653            .as_ref()
17654            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
17655            .len;
17656        {
17657            let distributed = cache.tp_kv[il]
17658                .as_ref()
17659                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
17660            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
17661                return Err(format!(
17662                    "Step TP layer {il} cache lengths diverged before decode: \
17663                     local={base_len} distributed={}/{}",
17664                    distributed.committed_len(),
17665                    distributed.staged_len()
17666                )
17667                .into());
17668            }
17669        }
17670        if pos_d.len() != 1 {
17671            return Err(format!(
17672                "rank-local Step decode requires one position, got {}",
17673                pos_d.len()
17674            )
17675            .into());
17676        }
17677
17678        let decode_input = attention
17679            .decode_input
17680            .as_ref()
17681            .ok_or("Step TP decode v2 requires the replicated decode input")?;
17682        let mut decode_input = decode_input
17683            .lock()
17684            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
17685
17686        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
17687        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
17688        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
17689        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
17690        let use_gate_shards = (attention.gate_shards.is_some()
17691            || attention.gate_shards_bf16.is_some())
17692            && crate::tp::step_tp_qkv_fused_enabled()?;
17693        let gate_raw = if use_gate_shards {
17694            None
17695        } else {
17696            let gate_weight = fa
17697                .attn_gate
17698                .as_ref()
17699                .ok_or("step35 layer is missing attn_gate.weight")?;
17700            let gate_raw = e.matmul(gate_weight, h, 1)?;
17701            if gate_raw.len() != heads {
17702                return Err(format!(
17703                    "Step TP layer {il} gate output {} != {heads}",
17704                    gate_raw.len()
17705                )
17706                .into());
17707            }
17708            Some(gate_raw)
17709        };
17710
17711        let ws_index = tp
17712            .runtime
17713            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17714        let mut ws_guard = tp
17715            .runtime
17716            .decode_v2_workspace()
17717            .lock()
17718            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
17719        let ws = ws_guard
17720            .get_mut(ws_index)
17721            .ok_or("Step TP decode v2 workspace missing after ensure")?;
17722
17723        let mut rope_freqs = Vec::with_capacity(ranks);
17724        for rank in 0..ranks {
17725            let engine = tp
17726                .runtime
17727                .rank_engine(rank)
17728                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17729            rope_freqs.push(if geometry.rope_factors {
17730                self.step35_aux
17731                    .as_ref()
17732                    .and_then(|aux| aux.rope_freqs(engine))
17733            } else {
17734                None
17735            });
17736        }
17737        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
17738        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
17739        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
17740        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
17741        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
17742        // the fused rope+append+inc launch on dcw tokens.)
17743        let staged_next = base_len + 1;
17744        let t_kv_eff = window
17745            .map(|window| staged_next.min(window))
17746            .unwrap_or(staged_next);
17747        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
17748            let (write_row, would_rebase) = cache.tp_kv[il]
17749                .as_ref()
17750                .expect("distributed cache checked above")
17751                .peek_append_ring(1)?;
17752            if !would_rebase {
17753                // Arm the base mirrors on first use: base = logical staged - physical row.
17754                let base = (base_len - write_row) as i32;
17755                let distributed = cache.tp_kv[il]
17756                    .as_mut()
17757                    .expect("distributed cache checked above");
17758                for rank in 0..ranks {
17759                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
17760                        format!("Step TP layer {il} has no engine for rank {rank}")
17761                    })?;
17762                    let _main = engine.gpu.enter_main()?;
17763                    let rank_cache = distributed
17764                        .rank_mut(rank)
17765                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17766                    if rank_cache.base_d().is_none() {
17767                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
17768                    }
17769                }
17770            }
17771            !would_rebase
17772        };
17773        let fuse_rope = dcw
17774            && crate::tp::fuse_rope_append_on()
17775            && head_dim == 128
17776            && cache.tp_kv[il]
17777                .as_ref()
17778                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
17779                .unwrap_or(false);
17780
17781        let tcol_col = crate::tp::take_verify_tcol();
17782        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
17783        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
17784        // state must advance per column) but skips the fa+gate launch; post-rope q and
17785        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
17786        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
17787        // normally and the walk consumes the real output — stash flag stays unset).
17788        let fa2_col = crate::tp::take_spec_fa2_defer();
17789        tp.runtime.decode_v2_input_qkv(
17790            ws,
17791            e,
17792            h,
17793            pos_d,
17794            gate_raw.as_ref(),
17795            if !use_gate_shards {
17796                None
17797            } else if let Some(shards) = attention.gate_shards.as_deref() {
17798                Some(crate::tp::StepTpGateShards::F32(shards))
17799            } else {
17800                attention
17801                    .gate_shards_bf16
17802                    .as_deref()
17803                    .map(crate::tp::StepTpGateShards::Bf16)
17804            },
17805            &mut decode_input,
17806            &tp.q,
17807            &tp.k,
17808            &tp.v,
17809            &attention.q_norm,
17810            &attention.k_norm,
17811            head_dim,
17812            geometry.n_rot as usize,
17813            geometry.rope_base,
17814            &rope_freqs,
17815            self.cfg.rms_eps,
17816            fuse_rope,
17817            tcol_col,
17818        )?;
17819
17820        let transaction = cache.tp_kv[il]
17821            .as_mut()
17822            .expect("distributed cache checked above")
17823            .begin_transaction()?;
17824        let append_result = tp.runtime.append_tp_kv_transaction_inner(
17825            cache.tp_kv[il]
17826                .as_mut()
17827                .expect("distributed cache checked above"),
17828            transaction,
17829            &ws.k,
17830            &ws.v_raw,
17831            1,
17832            dcw,
17833        );
17834        if let Err(error) = append_result {
17835            let _ = tp.runtime.rollback_tp_kv_transaction(
17836                cache.tp_kv[il]
17837                    .as_mut()
17838                    .expect("distributed cache checked above"),
17839                transaction,
17840            );
17841            return Err(error);
17842        }
17843
17844        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17845            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
17846            // reborrows the cache mutably per rank.
17847            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
17848                let distributed = cache.tp_kv[il]
17849                    .as_ref()
17850                    .expect("distributed cache checked above");
17851                let staged_len = distributed.staged_len();
17852                let view_start = window
17853                    .map(|window| staged_len.saturating_sub(window))
17854                    .unwrap_or(0);
17855                (
17856                    staged_len,
17857                    distributed.physical_range(view_start, staged_len)?,
17858                    distributed.k_tok_bytes(),
17859                    distributed.v_tok_bytes(),
17860                    distributed.physical_capacity(),
17861                )
17862            };
17863            let view_start = window
17864                .map(|window| staged_len.saturating_sub(window))
17865                .unwrap_or(0);
17866            let t_kv = staged_len - view_start;
17867            for rank in 0..ranks {
17868                let engine = tp
17869                    .runtime
17870                    .rank_engine(rank)
17871                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17872                let _main = engine.gpu.enter_main()?;
17873                if dcw {
17874                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
17875                    // stream visit. distributed is borrowed shared here; the planes need mut —
17876                    // reborrow through the cache Option (the closure holds cache mutably).
17877                    {
17878                        let distributed_mut = cache.tp_kv[il]
17879                            .as_mut()
17880                            .expect("distributed cache checked above");
17881                        let (kv_dim_k, kv_dim_v) =
17882                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
17883                        let (k_tok_bytes, v_tok_bytes) =
17884                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
17885                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17886                            format!("Step TP layer {il} has no KV cache rank {rank}")
17887                        })?;
17888                        let (k_plane, v_plane, len_d, base_d) =
17889                            rank_cache.planes_and_counters_mut();
17890                        if fuse_rope {
17891                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
17892                            // + last-block len inc in ONE launch. Bit-identical bodies.
17893                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
17894                            let crate::tp::StepTpDecodeV2Ws {
17895                                q_raw,
17896                                k_raw,
17897                                v_raw,
17898                                q,
17899                                k,
17900                                pos,
17901                                pos_stage,
17902                                fuse_ctr,
17903                                ..
17904                            } = &mut *ws;
17905                            // Same-device rank: the staged-copy elision leaves pos[rank]
17906                            // stale — read the e-context pos stage directly (mirrors the
17907                            // rope elision in input_qkv_rank).
17908                            let pos_ref: &CudaSlice<i32> = if same_dev {
17909                                pos_stage
17910                                    .as_ref()
17911                                    .ok_or("step TP decode v2 pos stage not armed")?
17912                            } else {
17913                                &pos[rank]
17914                            };
17915                            engine.qk_norm_rope_append_inc_dcw(
17916                                &q_raw[rank],
17917                                &k_raw[rank],
17918                                &v_raw[rank],
17919                                &attention.q_norm[rank],
17920                                &attention.k_norm[rank],
17921                                &mut q[rank],
17922                                &mut k[rank],
17923                                pos_ref,
17924                                k_plane,
17925                                v_plane,
17926                                len_d,
17927                                base_d,
17928                                &mut fuse_ctr[rank],
17929                                kv_dim_k,
17930                                kv_dim_v,
17931                                k_tok_bytes,
17932                                v_tok_bytes,
17933                                head_dim,
17934                                geometry.n_rot as usize,
17935                                local_heads,
17936                                local_kv_heads,
17937                                self.cfg.rms_eps,
17938                                geometry.rope_base,
17939                                1.0,
17940                                rope_freqs[rank],
17941                            )?;
17942                        } else {
17943                            engine.append_kv_quantized_dcw(
17944                                &ws.k[rank],
17945                                &ws.v_raw[rank],
17946                                k_plane,
17947                                v_plane,
17948                                len_d,
17949                                base_d,
17950                                kv_dim_k,
17951                                kv_dim_v,
17952                                k_tok_bytes,
17953                                v_tok_bytes,
17954                            )?;
17955                        }
17956                        if !fuse_rope {
17957                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17958                                format!("Step TP layer {il} has no KV cache rank {rank}")
17959                            })?;
17960                            engine.inc_i32(rank_cache.len_d_mut())?;
17961                        }
17962                    }
17963                    let distributed = cache.tp_kv[il]
17964                        .as_ref()
17965                        .expect("distributed cache checked above");
17966                    let rank_cache = distributed
17967                        .rank(rank)
17968                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17969                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
17970                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
17971                    if fa2_col.is_some() {
17972                        // SPEC_FA2 defer: append landed above; the fa for this column
17973                        // runs in the T=2 joined launch after the pair's second append.
17974                        continue;
17975                    }
17976                    {
17977                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
17978                        // the gated output directly (bit-identical; one launch saved).
17979                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
17980                        engine.fa_decode_dcw(
17981                            &q[rank],
17982                            &k_ring,
17983                            &v_ring,
17984                            &mut gated[rank],
17985                            head_dim,
17986                            local_heads,
17987                            local_kv_heads,
17988                            rank_cache.len_d(),
17989                            rank_cache.base_d(),
17990                            window.unwrap_or(0),
17991                            t_kv,
17992                            geometry.attention_scale(),
17993                            k_tok_bytes_c,
17994                            v_tok_bytes_c,
17995                            Some(&gate[rank]),
17996                        )?;
17997                    }
17998                    continue;
17999                }
18000                let distributed = cache.tp_kv[il]
18001                    .as_ref()
18002                    .expect("distributed cache checked above");
18003                let rank_cache = distributed
18004                    .rank(rank)
18005                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
18006                let k_view = engine.view_u8_range(
18007                    rank_cache.k(),
18008                    physical.start * k_tok_bytes_c,
18009                    physical.end * k_tok_bytes_c,
18010                );
18011                let v_view = engine.view_u8_range(
18012                    rank_cache.v(),
18013                    physical.start * v_tok_bytes_c,
18014                    physical.end * v_tok_bytes_c,
18015                );
18016                engine.fa_decode_kvmod(
18017                    &ws.q[rank],
18018                    &k_view,
18019                    &v_view,
18020                    &mut ws.attn_out[rank],
18021                    head_dim,
18022                    local_heads,
18023                    local_kv_heads,
18024                    t_kv,
18025                    geometry.attention_scale(),
18026                    k_tok_bytes_c,
18027                    v_tok_bytes_c,
18028                    false,
18029                )?;
18030                engine.attn_head_gate(
18031                    &ws.attn_out[rank],
18032                    &ws.gate[rank],
18033                    &mut ws.gated[rank],
18034                    None,
18035                    head_dim,
18036                    local_heads,
18037                    1,
18038                )?;
18039            }
18040
18041            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
18042            // column's `gated` rows and skip the per-column finish choreography entirely
18043            // (the batched b4_tcol + join runs after every column). The returned buffer
18044            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
18045            // stashed flag, never this buffer. Ineligible configs fall back to the
18046            // normal finish and the driver consumes the real `mixed` per column.
18047            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
18048                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
18049                // finish all run in the joined pass. Returned buffer is UNWRITTEN
18050                // (oproj-defer precedent — the walk reads the stash flag, never this).
18051                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
18052                crate::tp::set_spec_fa2_stashed();
18053                e.uninit(ws.o_out)?
18054            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
18055                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
18056                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
18057                    crate::tp::set_tcol_oproj_stashed();
18058                    e.uninit(ws.o_out)?
18059                } else {
18060                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
18061                }
18062            } else {
18063                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
18064            };
18065
18066            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
18067            // decode_v2_finish ordered behind the root event. Same math and cache state
18068            // transitions as v1.
18069            let local = cache.kv[il]
18070                .as_mut()
18071                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
18072            if local.len != base_len || base_len + 1 > max_ctx {
18073                return Err(format!(
18074                    "Step TP layer {il} local cache changed during decode: \
18075                     len={} base={base_len} max={max_ctx}",
18076                    local.len
18077                )
18078                .into());
18079            }
18080            if crate::tp::no_local_shadow_on() {
18081                // Lengths advance, contents stay stale (graph-door precedent: decode reads
18082                // only the distributed TP caches; local contents feed spec/MTP scratch).
18083                local.len = base_len + 1;
18084                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
18085                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
18086                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
18087                if !crate::tp::len_mirror_lazy_on() {
18088                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
18089                }
18090            } else {
18091                let retain_from = window
18092                    .map(|window| {
18093                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
18094                        let rollback_retain =
18095                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
18096                        staged_retain.min(rollback_retain)
18097                    })
18098                    .unwrap_or(0);
18099                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
18100                e.append_kv_quantized(
18101                    &ws.k_shadow,
18102                    &ws.v_shadow,
18103                    &mut local.k,
18104                    &mut local.v,
18105                    write_row,
18106                    local.kv_dim_k,
18107                    local.kv_dim_v,
18108                    local.k_tok_bytes,
18109                    local.v_tok_bytes,
18110                    false,
18111                )?;
18112                local.len = base_len + 1;
18113                e.set_i32_one(&mut local.len_d, local.len as i32)?;
18114            }
18115            Ok(output)
18116        })();
18117
18118        let output = match staged {
18119            Ok(output) => output,
18120            Err(error) => {
18121                let _ = tp.runtime.rollback_tp_kv_transaction(
18122                    cache.tp_kv[il]
18123                        .as_mut()
18124                        .expect("distributed cache checked above"),
18125                    transaction,
18126                );
18127                if let Some(local) = cache.kv[il].as_mut() {
18128                    local.len = base_len;
18129                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
18130                }
18131                return Err(error);
18132            }
18133        };
18134        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
18135        // the rank counters (same value as the absolute re-set on full accept), so commit
18136        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
18137        // keeps the absolute set (its appends do NOT inc).
18138        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
18139        if lazy_commit {
18140            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
18141                cache.tp_kv[il]
18142                    .as_mut()
18143                    .expect("distributed cache checked above"),
18144                transaction,
18145                1,
18146            ) {
18147                let _ = tp.runtime.rollback_tp_kv_transaction(
18148                    cache.tp_kv[il]
18149                        .as_mut()
18150                        .expect("distributed cache checked above"),
18151                    transaction,
18152                );
18153                let local = cache.kv[il].as_mut().expect("local cache checked above");
18154                local.len = base_len;
18155                e.set_i32_one(&mut local.len_d, base_len as i32)?;
18156                return Err(error);
18157            }
18158        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
18159            cache.tp_kv[il]
18160                .as_mut()
18161                .expect("distributed cache checked above"),
18162            transaction,
18163            1,
18164        ) {
18165            let _ = tp.runtime.rollback_tp_kv_transaction(
18166                cache.tp_kv[il]
18167                    .as_mut()
18168                    .expect("distributed cache checked above"),
18169                transaction,
18170            );
18171            let local = cache.kv[il].as_mut().expect("local cache checked above");
18172            local.len = base_len;
18173            e.set_i32_one(&mut local.len_d, base_len as i32)?;
18174            return Err(error);
18175        }
18176
18177        let committed = cache.tp_kv[il]
18178            .as_ref()
18179            .expect("distributed cache checked above")
18180            .committed_len();
18181        let local_len = cache.kv[il]
18182            .as_ref()
18183            .expect("local cache checked above")
18184            .len;
18185        if committed != local_len {
18186            return Err(format!(
18187                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
18188            )
18189            .into());
18190        }
18191        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
18192        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
18193            eprintln!(
18194                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
18195                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
18196                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
18197                 attention_tensor_parallel=true attention_scope={} \
18198                 input_path=root-device-replicated gate_tensor_parallel=false \
18199                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
18200                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
18201                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
18202                 performance_claim=false (logged once; every decode layer runs this driver)",
18203                tp.layer,
18204                tp.devices,
18205                if window.is_some() {
18206                    "rank-local-swa-ring"
18207                } else {
18208                    "rank-local-global"
18209                },
18210                tp.runtime.transport_label(),
18211                tp.runtime.bulk_p2p(),
18212            );
18213        }
18214        Ok(output)
18215    }
18216
18217    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
18218    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
18219    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
18220    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
18221    /// requiring `attn_gate`).
18222    ///
18223    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
18224    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
18225    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
18226    #[allow(clippy::too_many_arguments)]
18227    pub(crate) fn step35_decode_attn(
18228        &self,
18229        e: &Engine,
18230        fa: &FullAttnLayer,
18231        il: usize,
18232        h: &CudaSlice<f32>,
18233        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
18234        pos_d: &CudaSlice<i32>,
18235        cache: &mut Cache,
18236    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18237        if fa
18238            .step_tp_qkv
18239            .as_ref()
18240            .is_some_and(|tp| tp.attention.is_some())
18241        {
18242            if pre_q.is_some() {
18243                return Err(
18244                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
18245                     pre-quantized decode path"
18246                        .into(),
18247                );
18248            }
18249            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
18250        }
18251
18252        let geometry = self.step35_geom(il);
18253        let hd = geometry.head_dim_k as usize;
18254        let nkv = geometry.n_head_kv as usize;
18255        let nh = geometry.n_head as usize;
18256        let rbase = geometry.rope_base;
18257        let scale = geometry.attention_scale();
18258        let swa = geometry.window.is_some();
18259        let eps = self.cfg.rms_eps;
18260        let win = geometry.window.unwrap_or(0) as usize;
18261        let n_rot = geometry.n_rot as usize;
18262        let n_embd = self.cfg.n_embd as usize;
18263        let gw = fa
18264            .attn_gate
18265            .as_ref()
18266            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
18267
18268        let tp_qkv = if fa.step_tp_qkv.is_some() {
18269            if pre_q.is_some() {
18270                return Err(
18271                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
18272                     pre-quantized decode path"
18273                        .into(),
18274                );
18275            }
18276            self.step35_tp_qkv(e, fa, h, 1)?
18277        } else {
18278            None
18279        };
18280
18281        let (q0, k0, v0, gt) = match tp_qkv {
18282            Some(mut g3) => {
18283                let v = g3.pop().unwrap();
18284                let k = g3.pop().unwrap();
18285                let q = g3.pop().unwrap();
18286                let gt = e.matmul(gw, h, 1)?;
18287                (q, k, v, gt)
18288            }
18289            None => match pre_q {
18290                Some((hq, hdq)) => {
18291                    debug_assert!(
18292                        e.uses_q8_1_fast(gw),
18293                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
18294                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
18295                    );
18296                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
18297                        Some(t3) => t3,
18298                        None => (
18299                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
18300                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
18301                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
18302                        ),
18303                    };
18304                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
18305                    (a, b, c, gt)
18306                }
18307                None => {
18308                    if e.uses_q8_1_fast(&fa.wq)
18309                        && e.uses_q8_1_fast(&fa.wk)
18310                        && e.uses_q8_1_fast(&fa.wv)
18311                        && e.uses_q8_1_fast(gw)
18312                    {
18313                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
18314                        let (a, b, c) =
18315                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
18316                                Some(t3) => t3,
18317                                None => (
18318                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
18319                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
18320                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
18321                                ),
18322                            };
18323                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
18324                        (a, b, c, gt)
18325                    } else {
18326                        (
18327                            e.matmul(&fa.wq, h, 1)?,
18328                            e.matmul(&fa.wk, h, 1)?,
18329                            e.matmul(&fa.wv, h, 1)?,
18330                            e.matmul(gw, h, 1)?,
18331                        )
18332                    }
18333                }
18334            },
18335        };
18336
18337        let mut q = e.uninit(nh * hd)?;
18338        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
18339        let mut k = e.uninit(nkv * hd)?;
18340        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
18341        let ff = if swa {
18342            None
18343        } else {
18344            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
18345        };
18346        #[cfg(debug_assertions)]
18347        if let Some(ff) = ff {
18348            crate::debug_assert_tensor_stream_device(
18349                ff,
18350                &e.stream(),
18351                "step35_decode_attn.rope_freqs",
18352            );
18353        }
18354        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
18355
18356        if std::env::var("MEMRA_NOFA").is_ok() {
18357            return Err(
18358                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
18359                        cache; unset MEMRA_NOFA to use fa_decode"
18360                    .into(),
18361            );
18362        }
18363        let kvl = cache.kv[il].as_mut().unwrap();
18364        let next_len = kvl.len + 1;
18365        let (off, t_kv) = if swa && next_len > win {
18366            (next_len - win, win)
18367        } else {
18368            (0, next_len)
18369        };
18370        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
18371        e.append_kv_quantized(
18372            &k,
18373            &v0,
18374            &mut kvl.k,
18375            &mut kvl.v,
18376            write_row,
18377            kvl.kv_dim_k,
18378            kvl.kv_dim_v,
18379            kvl.k_tok_bytes,
18380            kvl.v_tok_bytes,
18381            crate::Engine::kv_fp8_on(),
18382        )?;
18383        kvl.len = next_len;
18384        let physical = kvl.physical_rows(off, off + t_kv)?;
18385        let k_view = e.view_u8_range(
18386            &kvl.k,
18387            physical.start * kvl.k_tok_bytes,
18388            physical.end * kvl.k_tok_bytes,
18389        );
18390        let v_view = e.view_u8_range(
18391            &kvl.v,
18392            physical.start * kvl.v_tok_bytes,
18393            physical.end * kvl.v_tok_bytes,
18394        );
18395        let mut attn = e.uninit(nh * hd)?;
18396        e.fa_decode_kvmod(
18397            &q,
18398            &k_view,
18399            &v_view,
18400            &mut attn,
18401            hd,
18402            nh,
18403            nkv,
18404            t_kv,
18405            scale,
18406            kvl.k_tok_bytes,
18407            kvl.v_tok_bytes,
18408            crate::Engine::kv_fp8_on(),
18409        )?;
18410
18411        let mut ag = e.uninit(nh * hd)?;
18412        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
18413        self.step35_o(e, fa, &ag, 1)
18414    }
18415}
18416
18417// ===================================================================================== //
18418//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
18419//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
18420//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
18421//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
18422//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
18423//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
18424// ===================================================================================== //
18425impl HybridModel {
18426    pub fn is_gemma4_e4b(&self) -> bool {
18427        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
18428    }
18429
18430    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
18431    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
18432    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
18433    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
18434        let g = self.cfg.gemma4.as_ref().unwrap();
18435        let swa = g.swa_pattern[il];
18436        let hd = if swa {
18437            g.key_length_swa
18438        } else {
18439            g.key_length_global
18440        } as usize;
18441        let Mixer::Full(fa) = &self.layers[il].mixer else {
18442            panic!("e4b layer {il} not full-attn")
18443        };
18444        let nh = fa.wq.out_features() / hd;
18445        let nkv = fa.wk.out_features() / hd;
18446        (
18447            hd,
18448            nkv,
18449            nh,
18450            if swa {
18451                g.rope_base_swa
18452            } else {
18453                g.rope_base_global
18454            },
18455            1.0,
18456            swa,
18457        )
18458    }
18459
18460    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
18461    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
18462        self.layers[il]
18463            .gemma4
18464            .as_ref()
18465            .and_then(|b| b.e4b.as_ref())
18466            .and_then(|e4| e4.kv_share.map(|t| t as usize))
18467    }
18468
18469    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
18470    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
18471    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
18472    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
18473    fn gemma4_e4b_inp_pl(
18474        &self,
18475        e: &Engine,
18476        tokens: &[u32],
18477        x_scaled: &CudaSlice<f32>,
18478        t: usize,
18479    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18480        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
18481        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
18482    }
18483
18484    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
18485    fn gemma4_e4b_inp_pl_dev(
18486        &self,
18487        e: &Engine,
18488        tok_d: &CudaSlice<u32>,
18489        x_scaled: &CudaSlice<f32>,
18490        t: usize,
18491    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18492        let aux = self.gemma4_aux.as_ref().unwrap();
18493        let m = aux.e4b.as_ref().unwrap();
18494        let n_embd = self.cfg.n_embd as usize;
18495        let n_layer = self.layers.len();
18496        let width = m.n_epl * n_layer;
18497        let tbl = m.tok_tbl_gpu.get_or_init(|| {
18498            e.upload_u8(&m.tok_embd_bytes)
18499                .expect("e4b per-layer token table upload")
18500        });
18501        let mut a =
18502            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
18503        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
18504        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
18505        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
18506        let mut pn = e.uninit(t * width)?;
18507        e.rms_norm(
18508            &p,
18509            m.proj_norm.float_data(),
18510            &mut pn,
18511            m.n_epl,
18512            t * n_layer,
18513            self.cfg.rms_eps,
18514        )?;
18515        let mut out = e.uninit(t * width)?;
18516        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
18517        Ok(out)
18518    }
18519
18520    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
18521    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
18522    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
18523    /// already holds this forward's rows — the target runs earlier in the stack).
18524    #[allow(clippy::too_many_arguments)]
18525    fn gemma4_e4b_attn(
18526        &self,
18527        e: &Engine,
18528        il: usize,
18529        hq: &CudaSlice<i8>,
18530        hdq: &CudaSlice<f32>,
18531        pos_d: &CudaSlice<i32>,
18532        t: usize,
18533        cache: &mut Cache,
18534        dc_bucket: Option<usize>,
18535    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18536        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
18537        let eps = self.cfg.rms_eps;
18538        let aux = self.gemma4_aux.as_ref().unwrap();
18539        let ones = aux.ones(e);
18540        #[cfg(debug_assertions)]
18541        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
18542        let Mixer::Full(fa) = &self.layers[il].mixer else {
18543            unreachable!()
18544        };
18545        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
18546        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
18547        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
18548        let h0 = e.zeros(0)?;
18549        let h = &h0;
18550
18551        let ff = if swa {
18552            None
18553        } else {
18554            Some(
18555                aux.rope_freqs(e)
18556                    .expect("e4b global rope needs rope_freqs.weight"),
18557            )
18558        };
18559        #[cfg(debug_assertions)]
18560        if let Some(ff) = ff {
18561            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
18562        }
18563        let share = self.gemma4_e4b_kv_target(il);
18564        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
18565        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
18566        let mut q;
18567        if let Some(_tgt) = share {
18568            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
18569            q = e.uninit(t * nh * hd)?;
18570            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
18571            // empty; q0 stands in for the unused k/v pointers).
18572            let mut kdummy = e.uninit(1)?;
18573            let mut vdummy = e.uninit(1)?;
18574            e.rms_norm_qkv_rope(
18575                &q0,
18576                &q0,
18577                &q0,
18578                fa.q_norm.float_data(),
18579                fa.q_norm.float_data(),
18580                ones,
18581                &mut q,
18582                &mut kdummy,
18583                &mut vdummy,
18584                hd,
18585                self.gemma4_rope_dims(il),
18586                nh * t,
18587                0,
18588                pos_d,
18589                nh,
18590                1,
18591                base,
18592                1.0,
18593                ff,
18594                eps,
18595            )?;
18596        } else {
18597            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
18598            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
18599            // q|k|v rows — the cat norm+rope twin consumes it directly.
18600            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
18601            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
18602            q = e.uninit(t * nh * hd)?;
18603            let mut k = e.uninit(t * nkv * hd)?;
18604            let mut v = e.uninit(t * nkv * hd)?;
18605            if t == 1 && cat.is_some() {
18606                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
18607                e.rms_norm_qkv_rope_cat(
18608                    &qkv0,
18609                    fa.q_norm.float_data(),
18610                    fa.k_norm.float_data(),
18611                    ones,
18612                    &mut q,
18613                    &mut k,
18614                    &mut v,
18615                    hd,
18616                    self.gemma4_rope_dims(il),
18617                    nh,
18618                    nkv,
18619                    pos_d,
18620                    nh,
18621                    nkv,
18622                    base,
18623                    1.0,
18624                    ff,
18625                    eps,
18626                )?;
18627            } else {
18628                let (q0, k0, v0) = match if t == 1 {
18629                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
18630                } else {
18631                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
18632                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
18633                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18634                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
18635                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
18636                    } else {
18637                        None
18638                    }
18639                } {
18640                    Some(triple) => triple,
18641                    None => (
18642                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
18643                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
18644                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
18645                    ), // E4B: real v (K != V)
18646                };
18647                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
18648                // the normed rows; V ones-rms, never roped).
18649                e.rms_norm_qkv_rope(
18650                    &q0,
18651                    &k0,
18652                    &v0,
18653                    fa.q_norm.float_data(),
18654                    fa.k_norm.float_data(),
18655                    ones,
18656                    &mut q,
18657                    &mut k,
18658                    &mut v,
18659                    hd,
18660                    self.gemma4_rope_dims(il),
18661                    nh * t,
18662                    nkv * t,
18663                    pos_d,
18664                    nh,
18665                    nkv,
18666                    base,
18667                    1.0,
18668                    ff,
18669                    eps,
18670                )?;
18671            }
18672            let kvl = cache.kv[il].as_mut().unwrap();
18673            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
18674            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
18675            // degenerate tok-0 stream, 2026-07-12).
18676            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18677            if dc_bucket.is_some() {
18678                // DC arm (graph serving): append at the len_d slot, advance the counter
18679                // in-stream — replay-correct, no host len in the launch args. Host mirrors
18680                // are NOT touched here (the replay loop owns them; a bump at capture-record
18681                // time would double-count the capture iteration).
18682                debug_assert!(t == 1);
18683                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
18684                e.append_kv_quantized_row_dc_inc(
18685                    &k,
18686                    &v,
18687                    &mut kvl.k,
18688                    &mut kvl.v,
18689                    &mut kvl.len_d,
18690                    kvl.kv_dim_k,
18691                    kvl.kv_dim_v,
18692                    kvl.k_tok_bytes,
18693                    kvl.v_tok_bytes,
18694                    cls,
18695                )?;
18696            } else {
18697                e.append_kv_quantized_rows(
18698                    &k,
18699                    &v,
18700                    &mut kvl.k,
18701                    &mut kvl.v,
18702                    kvl.len,
18703                    t,
18704                    kvl.kv_dim_k,
18705                    kvl.kv_dim_v,
18706                    kvl.k_tok_bytes,
18707                    kvl.v_tok_bytes,
18708                    cls,
18709                )?;
18710                kvl.len += t;
18711            }
18712            kv_f32 = Some((k, v));
18713        }
18714        // attention: per-row causal fa over the (own or target) quantized cache. The cache
18715        // already contains this forward's rows in both arms; row i attends [.., base+i].
18716        let kvl_idx = share.unwrap_or(il);
18717        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
18718        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
18719        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
18720        let mut attn = e.uninit(t * nh * hd)?;
18721        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
18722        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
18723        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
18724        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
18725        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
18726        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
18727        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
18728        //     rows (the T=K verify kernel; the target appended this forward's rows already).
18729        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
18730        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
18731        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
18732        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
18733            if let Some((kf, vf)) = &kv_f32 {
18734                if hd == 256 && t <= win {
18735                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18736                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18737                }
18738                if hd == 256 && swa && t > win {
18739                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18740                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18741                }
18742                if hd == 512 && !swa {
18743                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18744                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18745                }
18746            } else if share.is_some() {
18747                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18748                let k_view = e.view_u8(&kvl.k, kvl.k.len());
18749                let v_view = e.view_u8(&kvl.v, kvl.v.len());
18750                if hd == 256 && (!swa || t <= win) {
18751                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
18752                    e.fa_prefill_view(
18753                        &q,
18754                        &k_view,
18755                        &v_view,
18756                        &mut attn,
18757                        hd,
18758                        nh,
18759                        nkv,
18760                        t,
18761                        t,
18762                        scale,
18763                        true,
18764                        kvl.k_tok_bytes,
18765                        kvl.v_tok_bytes,
18766                        g,
18767                    )?;
18768                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18769                }
18770                // remaining shared classes (swa above the window; hd512 globals): dequant
18771                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
18772                let kv_dim = nkv * hd;
18773                let mut kf = e.uninit(t * kv_dim)?;
18774                let mut vf = e.uninit(t * kv_dim)?;
18775                e.fa_dequant_kv_view_f32(
18776                    &k_view,
18777                    &v_view,
18778                    &mut kf,
18779                    &mut vf,
18780                    kv_dim,
18781                    kv_dim,
18782                    t,
18783                    kvl.k_tok_bytes,
18784                    kvl.v_tok_bytes,
18785                    g,
18786                )?;
18787                if hd == 512 {
18788                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18789                } else {
18790                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18791                }
18792                return Ok(e.matmul(&fa.wo, &attn, t)?);
18793            }
18794        }
18795        if let Some(bucket) = dc_bucket {
18796            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
18797            // fa_decode_dc over the live counter. len_d already advanced past this token
18798            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
18799            // counter (advanced when the target ran earlier in the stack).
18800            assert!(t == 1);
18801            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
18802            // and under the window every live t_kv sits below it — cap the capture bucket
18803            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
18804            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
18805            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
18806            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
18807                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
18808            } else {
18809                bucket
18810            };
18811            let k_view = e.view_u8(&kvl.k, kvl.k.len());
18812            let v_view = e.view_u8(&kvl.v, kvl.v.len());
18813            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18814            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
18815            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
18816            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
18817            // captured into the dc graph like any other launch. Extending the cascade to
18818            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
18819            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
18820            // MEMRA_WPF=0 rollback seam.
18821            if crate::Engine::wpf_level() >= 1 {
18822                e.prefetch_weight_l2(&fa.wo)?;
18823            }
18824            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
18825            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
18826            if e.uses_q8_1_fast(&fa.wo) {
18827                let mut oq = e.alloc_i8_uninit(nh * hd)?;
18828                let mut od = e.zeros(nh * hd / 32)?;
18829                e.fa_decode_dc_q8(
18830                    &q,
18831                    &k_view,
18832                    &v_view,
18833                    &mut attn,
18834                    hd,
18835                    nh,
18836                    nkv,
18837                    &kvl.len_d,
18838                    bucket,
18839                    scale,
18840                    kvl.k_tok_bytes,
18841                    kvl.v_tok_bytes,
18842                    g,
18843                    Some((&mut oq, &mut od)),
18844                )?;
18845                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
18846            }
18847            e.fa_decode_dc(
18848                &q,
18849                &k_view,
18850                &v_view,
18851                &mut attn,
18852                hd,
18853                nh,
18854                nkv,
18855                &kvl.len_d,
18856                bucket,
18857                scale,
18858                kvl.k_tok_bytes,
18859                kvl.v_tok_bytes,
18860                g,
18861            )?;
18862            return Ok(e.matmul(&fa.wo, &attn, t)?);
18863        }
18864        for i in 0..t {
18865            let avail = base_len + i + 1;
18866            let (off_tok, t_kv) = if swa && avail > win {
18867                (avail - win, win)
18868            } else {
18869                (0, avail)
18870            };
18871            let k_view = e.view_u8_range(
18872                &kvl.k,
18873                off_tok * kvl.k_tok_bytes,
18874                (off_tok + t_kv) * kvl.k_tok_bytes,
18875            );
18876            let v_view = e.view_u8_range(
18877                &kvl.v,
18878                off_tok * kvl.v_tok_bytes,
18879                (off_tok + t_kv) * kvl.v_tok_bytes,
18880            );
18881            let qv = e.view(&q, t * nh * hd);
18882            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
18883            let mut q_one = e.uninit(nh * hd)?;
18884            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
18885            let mut a_one = e.uninit(nh * hd)?;
18886            // read class MUST match the append class (globals are e4m3 under gkv): the
18887            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
18888            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
18889            e.fa_decode_kvmod(
18890                &q_one,
18891                &k_view,
18892                &v_view,
18893                &mut a_one,
18894                hd,
18895                nh,
18896                nkv,
18897                t_kv,
18898                scale,
18899                kvl.k_tok_bytes,
18900                kvl.v_tok_bytes,
18901                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
18902            )?;
18903            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
18904        }
18905        Ok(e.matmul(&fa.wo, &attn, t)?)
18906    }
18907
18908    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
18909    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
18910    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
18911    /// layer; does NOT advance cache.pos (caller owns pos).
18912    fn gemma4_e4b_trunk(
18913        &self,
18914        e: &Engine,
18915        tokens: &[u32],
18916        pos0: usize,
18917        cache: &mut Cache,
18918        head_last: bool,
18919    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18920        let n_embd = self.cfg.n_embd as usize;
18921        let t = tokens.len();
18922        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18923        let pos_d = e.htod_i32(&pos)?;
18924        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
18925        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18926        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
18927        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
18928    }
18929
18930    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
18931    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
18932    /// eager chain by construction: SAME functions, not twins).
18933    fn gemma4_e4b_trunk_core(
18934        &self,
18935        e: &Engine,
18936        x_in: CudaSlice<f32>,
18937        inp_pl: CudaSlice<f32>,
18938        pos_d: &CudaSlice<i32>,
18939        t: usize,
18940        cache: &mut Cache,
18941        dc_bucket: Option<usize>,
18942        cap_logits: bool,
18943        head_last: bool,
18944    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18945        let n_embd = self.cfg.n_embd as usize;
18946        let eps = self.cfg.rms_eps;
18947        let n_layer = self.layers.len();
18948        let mut x = x_in;
18949        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
18950        let n_epl = aux_e4b.n_epl;
18951
18952        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
18953        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
18954        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
18955        // head rides matmul_pre too. First layer's pair comes from a standalone fused
18956        // norm+quant.
18957        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
18958        for il in 0..n_layer {
18959            let layer = &self.layers[il];
18960            let (hq, hdq) = match h_carry.take() {
18961                Some(p) => p,
18962                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
18963            };
18964            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
18965            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
18966            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
18967            let bits = layer.gemma4.as_ref().unwrap();
18968            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
18969            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
18970            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
18971            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
18972            // the fused single-phase reduction is NOT FP-order-identical to the unfused
18973            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
18974            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
18975            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
18976            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
18977            // gate dropped, decode AND verify ride the same fused chain — parity by
18978            // construction, VERIFY-GATE 0.000e0.
18979            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
18980            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
18981                e,
18982                layer,
18983                &o,
18984                &x,
18985                t,
18986                Some(layer.post_attn_norm.float_data()),
18987                fuse_exit,
18988            )?;
18989            let mut resid = e.uninit(t * n_embd)?;
18990            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
18991            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
18992            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
18993            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
18994            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
18995            let g = if fuse_exit {
18996                // sn here = RAW f0 (post_ffw deferred).
18997                let (rq, rd) = e.rms_pre_add_q8_1(
18998                    &sn,
18999                    bits.post_ffw_norm.float_data(),
19000                    &attn_out,
19001                    &mut resid,
19002                    n_embd,
19003                    t,
19004                    self.cfg.rms_eps,
19005                )?;
19006                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
19007            } else {
19008                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
19009                e.matmul(&e4b.inp_gate, &resid, t)?
19010            };
19011            let mut act = e.uninit(t * n_epl)?;
19012            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
19013                let ipv = e.view(&inp_pl, n_epl * n_layer);
19014                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
19015                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
19016                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
19017            } else {
19018                let mut inp_this = e.uninit(t * n_epl)?;
19019                e.copy_rows_strided(
19020                    &inp_pl,
19021                    &mut inp_this,
19022                    n_epl,
19023                    t,
19024                    n_epl * n_layer,
19025                    il * n_epl,
19026                )?;
19027                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
19028                e.matmul(&e4b.proj, &act, t)?
19029            };
19030            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
19031            // ONE launch (glue-fusion lane; last layer emits through output_norm).
19032            let next_norm = if il + 1 < n_layer {
19033                self.layers[il + 1].attn_norm.float_data()
19034            } else {
19035                self.output_norm.float_data()
19036            };
19037            let mut xn = e.uninit(t * n_embd)?;
19038            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
19039                &y,
19040                e4b.post_norm.float_data(),
19041                &resid,
19042                bits.layer_scale,
19043                next_norm,
19044                &mut xn,
19045                n_embd,
19046                t,
19047                eps,
19048            )?;
19049            h_carry = Some(pair);
19050            x = xn;
19051        }
19052        // the head consumes the last layer's fused (output_norm) emit. head_last callers
19053        // (prime, last_only forward) need only the final row's logits — the all-T head is
19054        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
19055        let (oq, odq) = h_carry.take().unwrap();
19056        let h0 = e.zeros(0)?;
19057        let hm = if head_last { 1 } else { t };
19058        let (hq, hd) = if head_last && t > 1 {
19059            let mut q1 = e.uninit_i8(n_embd)?;
19060            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
19061            let nb = n_embd / 32;
19062            let mut d1 = e.uninit(nb)?;
19063            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
19064            (q1, d1)
19065        } else {
19066            (oq, odq)
19067        };
19068        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
19069        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
19070        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
19071        // Logit-returning callers (host logits / spec prime) keep the capped emit.
19072        if cap_logits {
19073            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
19074            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
19075        }
19076        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
19077        Ok((ld, x))
19078    }
19079
19080    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
19081    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
19082    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
19083    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
19084    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
19085    /// covers exactly the layers that appended).
19086    pub fn gemma4_e4b_decode_step_t_am_dev(
19087        &self,
19088        e: &Engine,
19089        tok_d: &CudaSlice<u32>,
19090        t: usize,
19091        pos0: usize,
19092        cache: &mut Cache,
19093    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19094        let n_embd = self.cfg.n_embd as usize;
19095        let eps = self.cfg.rms_eps;
19096        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
19097        let pos_d = e.htod_i32(&pos)?;
19098        let embd_gpu = self
19099            .embd_gpu
19100            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
19101        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
19102        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
19103        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
19104        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
19105        let (ld, xp) =
19106            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
19107        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
19108        // emit is already capped, matching the eager chain bit-for-bit).
19109        let n_vocab = self.output.out_features();
19110        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
19111        for i in 0..t {
19112            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
19113        }
19114        let mut hn = e.uninit(t * n_embd)?;
19115        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
19116        cache.pos += t;
19117        Ok((vam, hn))
19118    }
19119
19120    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
19121    /// prime path — mirror of `gemma4_decode_step_t_h`).
19122    pub(crate) fn gemma4_e4b_decode_step_t_h(
19123        &self,
19124        e: &Engine,
19125        tokens: &[u32],
19126        pos0: usize,
19127        cache: &mut Cache,
19128    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19129        let n_embd = self.cfg.n_embd as usize;
19130        let eps = self.cfg.rms_eps;
19131        let t = tokens.len();
19132        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
19133        let mut hn = e.uninit(t * n_embd)?;
19134        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
19135        cache.pos += t;
19136        Ok((e.dtoh(&ld)?, hn))
19137    }
19138
19139    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
19140    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
19141    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
19142    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
19143    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
19144    pub fn gemma4_e4b_decode_step_dcg(
19145        &self,
19146        e: &Engine,
19147        token_d: &mut CudaSlice<u32>,
19148        pos_d: &mut CudaSlice<i32>,
19149        embd_gpu: &CudaSlice<u8>,
19150        embd_qt: i32,
19151        embd_rb: usize,
19152        cache: &mut Cache,
19153        n_vocab: usize,
19154        bucket: usize,
19155    ) -> Result<(), Box<dyn std::error::Error>> {
19156        let n_embd = self.cfg.n_embd as usize;
19157        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
19158        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
19159        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
19160        let (ld, _x) =
19161            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
19162        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
19163        e.inc_seqlen(pos_d)?;
19164        Ok(())
19165    }
19166
19167    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
19168    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
19169    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
19170    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
19171    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
19172    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
19173    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
19174    #[allow(clippy::too_many_arguments)]
19175    pub fn gemma4_e4b_decode_step_dc(
19176        &self,
19177        e: &Engine,
19178        token_d: &CudaSlice<u32>,
19179        pos_d: &mut CudaSlice<i32>,
19180        embd_gpu: &CudaSlice<u8>,
19181        embd_qt: i32,
19182        embd_rb: usize,
19183        cache: &mut Cache,
19184        n_vocab: usize,
19185    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
19186        let n_embd = self.cfg.n_embd as usize;
19187        let eps = self.cfg.rms_eps;
19188        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
19189        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
19190        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
19191        let (ld, _x) =
19192            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
19193        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
19194        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
19195        e.inc_seqlen(pos_d)?;
19196        cache.pos += 1;
19197        let _ = eps;
19198        Ok(tok_out)
19199    }
19200
19201    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
19202    /// pre-output_norm hidden). Advances cache.pos.
19203    pub(crate) fn gemma4_e4b_decode_step_h(
19204        &self,
19205        e: &Engine,
19206        token: u32,
19207        cache: &mut Cache,
19208    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19209        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
19210        let logits = e.dtoh(&ld)?;
19211        cache.pos += 1;
19212        Ok((logits, x))
19213    }
19214
19215    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
19216    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
19217    /// fast; the prefill fa arms come later.
19218    pub(crate) fn gemma4_e4b_prime(
19219        &self,
19220        e: &Engine,
19221        tokens: &[u32],
19222        cache: &mut Cache,
19223    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19224        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
19225        // process-kill as gemma4_prime — refuse per-request.
19226        if cache.pos != 0 {
19227            return Err(
19228                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
19229                        call or decode tokenwise"
19230                    .into(),
19231            );
19232        }
19233        let n_embd = self.cfg.n_embd as usize;
19234        let t = tokens.len();
19235        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
19236        cache.pos += t;
19237        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
19238        let xv = e.view(&x, t * n_embd);
19239        let row = xv.slice((t - 1) * n_embd..t * n_embd);
19240        let mut h_seed = e.uninit(n_embd)?;
19241        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
19242        Ok((last, h_seed, x))
19243    }
19244
19245    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
19246    pub(crate) fn gemma4_e4b_forward(
19247        &self,
19248        e: &Engine,
19249        tokens: &[u32],
19250        last_only: bool,
19251    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19252        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
19253        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
19254        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
19255    }
19256}
19257
19258#[cfg(test)]
19259mod prime_chunk_schedule_tests {
19260    use super::{
19261        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
19262        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
19263        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
19264        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
19265    };
19266
19267    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
19268        ranges.iter().map(|(start, end)| end - start).collect()
19269    }
19270
19271    fn auto_chunk(t: usize) -> usize {
19272        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
19273    }
19274
19275    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
19276    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
19277    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
19278    /// must land every boundary on it without changing coverage.
19279    #[test]
19280    fn auto_prime_ranges_align_to_the_gdn_grid() {
19281        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
19282        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
19283            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
19284            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
19285            for w in ranges.windows(2) {
19286                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
19287            }
19288            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
19289        };
19290
19291        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
19292        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
19293        let t = 9510usize;
19294        let fill = auto_chunk(t);
19295        let fixed = fixed_prime_chunk_ranges(t, fill);
19296        assert!(
19297            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
19298            "broken arm vanished: fixed auto boundaries all landed on-grid"
19299        );
19300        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
19301        assert!(
19302            dynamic[..dynamic.len() - 1]
19303                .iter()
19304                .any(|&(_, e)| e % c != 0),
19305            "broken arm vanished: dynamic auto boundaries all landed on-grid"
19306        );
19307
19308        for ranges in [&fixed, &dynamic] {
19309            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
19310            assert_covers(&aligned, t);
19311            for &(_, e) in &aligned[..aligned.len() - 1] {
19312                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
19313            }
19314            // boundaries only move DOWN, at most c-1 tokens.
19315            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
19316                assert!(a <= b && b - a < c);
19317            }
19318        }
19319
19320        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
19321        // empty range; the schedule survives degenerate short fills.
19322        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
19323        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
19324        assert_covers(&aligned, 200);
19325        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
19326
19327        // No-ops: single range, c=0 (grid off), already-aligned schedules.
19328        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
19329        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
19330        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
19331        assert_eq!(
19332            align_prime_ranges_to_gdn(&on_grid, 300, c),
19333            on_grid.as_slice()
19334        );
19335    }
19336
19337    #[test]
19338    fn active_matrix_prefix_scopes_reused_prime_slabs() {
19339        assert_eq!(
19340            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
19341            29 * 4096
19342        );
19343        assert_eq!(
19344            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
19345            29 * 4096
19346        );
19347        assert_eq!(
19348            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
19349            24 * 4096
19350        );
19351        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
19352        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
19353    }
19354
19355    #[test]
19356    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
19357        assert!(validate_step_prime_batch_modes(false, false).is_ok());
19358
19359        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
19360        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
19361
19362        for grouped in [false, true] {
19363            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
19364            assert!(err.contains("did not clear the live-server performance gate"));
19365            assert!(err.contains("per-session grouped prefill"));
19366        }
19367    }
19368
19369    #[test]
19370    fn step_grouped_path_is_eager_single_token_only() {
19371        assert!(step_grouped_decode_shape(false, 1));
19372        assert!(!step_grouped_decode_shape(true, 1));
19373        assert!(!step_grouped_decode_shape(false, 2));
19374        assert!(!step_grouped_decode_shape(true, 2));
19375    }
19376
19377    #[test]
19378    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
19379        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
19380        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
19381        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
19382        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
19383        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
19384        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
19385
19386        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
19387        assert!(step_grouped_prefill_shape(
19388            true,
19389            true,
19390            crate::cache::PRIME_CHUNK_MAX_TOKENS,
19391        ));
19392        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
19393        assert!(!step_grouped_prefill_shape(
19394            true,
19395            true,
19396            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
19397        ));
19398        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
19399        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
19400    }
19401
19402    #[test]
19403    fn step_tp_prefill_door_is_strict_and_default_off() {
19404        assert!(!parse_step_tp_prefill(None).unwrap());
19405        assert!(!parse_step_tp_prefill(Some("")).unwrap());
19406        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
19407        assert!(parse_step_tp_prefill(Some("1")).unwrap());
19408        assert!(parse_step_tp_prefill(Some("true")).is_err());
19409        assert!(parse_step_tp_prefill(Some("2")).is_err());
19410    }
19411
19412    #[test]
19413    fn step_tp_prefill_requires_a_qualified_even_rank_shape() {
19414        assert!(step_tp_prefill_shape(
19415            true,
19416            PRIME_MIN_T,
19417            4,
19418            true,
19419            true,
19420            false,
19421        ));
19422        assert!(!step_tp_prefill_shape(
19423            false,
19424            PRIME_MIN_T,
19425            4,
19426            true,
19427            true,
19428            false,
19429        ));
19430        assert!(!step_tp_prefill_shape(
19431            true,
19432            PRIME_MIN_T - 1,
19433            4,
19434            true,
19435            true,
19436            false,
19437        ));
19438        // TP2 admits (2026-08-25); odd/1-card placements still refuse.
19439        assert!(step_tp_prefill_shape(
19440            true,
19441            PRIME_MIN_T,
19442            2,
19443            true,
19444            true,
19445            false
19446        ));
19447        assert!(!step_tp_prefill_shape(
19448            true,
19449            PRIME_MIN_T,
19450            1,
19451            true,
19452            true,
19453            false
19454        ));
19455        assert!(!step_tp_prefill_shape(
19456            true,
19457            PRIME_MIN_T,
19458            3,
19459            true,
19460            true,
19461            false
19462        ));
19463        assert!(!step_tp_prefill_shape(
19464            true,
19465            PRIME_MIN_T,
19466            4,
19467            false,
19468            true,
19469            false,
19470        ));
19471        assert!(!step_tp_prefill_shape(
19472            true,
19473            PRIME_MIN_T,
19474            4,
19475            true,
19476            false,
19477            false,
19478        ));
19479        assert!(!step_tp_prefill_shape(
19480            true,
19481            PRIME_MIN_T,
19482            4,
19483            true,
19484            true,
19485            true,
19486        ));
19487    }
19488
19489    #[test]
19490    fn fixed_schedule_retains_measured_geometry() {
19491        assert_eq!(
19492            sizes(&fixed_prime_chunk_ranges(461, 128)),
19493            vec![128, 128, 128, 77]
19494        );
19495        assert_eq!(
19496            sizes(&fixed_prime_chunk_ranges(1833, 230)),
19497            vec![230, 230, 230, 230, 230, 230, 230, 223]
19498        );
19499        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
19500        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
19501        assert_eq!(capped, vec![4096, 4088, 16]);
19502        assert!(capped.iter().all(|&rows| rows <= 4096));
19503        assert_eq!(
19504            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
19505            vec![4100],
19506            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
19507        );
19508    }
19509
19510    #[test]
19511    fn dynamic_schedule_matches_registered_shapes() {
19512        let cases = [
19513            (461, vec![64, 141, 132, 124]),
19514            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
19515            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
19516        ];
19517        for (t, expected) in cases {
19518            let chunk = auto_chunk(t);
19519            let fixed = fixed_prime_chunk_ranges(t, chunk);
19520            assert_eq!(
19521                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
19522                expected
19523            );
19524        }
19525    }
19526
19527    #[test]
19528    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
19529        for t in 256..=8192 {
19530            let chunk = auto_chunk(t);
19531            let fixed = fixed_prime_chunk_ranges(t, chunk);
19532            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
19533            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
19534            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
19535            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
19536            for pair in dynamic.windows(2) {
19537                assert_eq!(pair[0].1, pair[1].0, "T={t}");
19538            }
19539            assert!(
19540                dynamic
19541                    .iter()
19542                    .all(|(start, end)| end - start >= PRIME_MIN_T),
19543                "T={t} sizes={:?}",
19544                sizes(&dynamic)
19545            );
19546            if dynamic.len() >= 3 {
19547                let chunk_sizes = sizes(&dynamic);
19548                assert!(
19549                    chunk_sizes[0] < chunk_sizes[1],
19550                    "T={t} sizes={chunk_sizes:?}"
19551                );
19552                assert!(
19553                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
19554                    "T={t} sizes={chunk_sizes:?}"
19555                );
19556            }
19557        }
19558    }
19559}
19560
19561#[cfg(test)]
19562mod page_prefetch_tests {
19563    use super::{
19564        grouped_worker_prefetch_position, page_prefetch_positions,
19565        page_prefetch_window_from_values, worker_prefetch_positions,
19566    };
19567
19568    #[test]
19569    fn page_prefetch_window_keeps_existing_opt_in_default() {
19570        assert_eq!(page_prefetch_window_from_values(false, None), 0);
19571        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
19572        assert_eq!(page_prefetch_window_from_values(true, None), 1);
19573        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
19574        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
19575        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
19576    }
19577
19578    #[test]
19579    fn rolling_page_prefetch_advises_each_future_expert_once() {
19580        let advised: Vec<_> = (0..7)
19581            .flat_map(|position| page_prefetch_positions(position, 7, 3))
19582            .collect();
19583        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
19584
19585        let one_ahead: Vec<_> = (0..4)
19586            .flat_map(|position| page_prefetch_positions(position, 4, 1))
19587            .collect();
19588        assert_eq!(one_ahead, vec![1, 2, 3]);
19589        assert!(page_prefetch_positions(0, 4, 0).is_empty());
19590    }
19591
19592    #[test]
19593    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
19594        assert_eq!(grouped_worker_prefetch_position(0, None), None);
19595        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
19596            .chain(
19597                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
19598            )
19599            .collect();
19600        assert_eq!(positions, vec![0, 1, 2, 3]);
19601        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
19602    }
19603
19604    #[test]
19605    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
19606        let queued: Vec<_> = (0..8)
19607            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
19608            .collect();
19609        assert_eq!(queued, (0..8).collect::<Vec<_>>());
19610
19611        let one_at_a_time: Vec<_> = (0..4)
19612            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
19613            .collect();
19614        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
19615        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
19616    }
19617}
19618
19619pub struct G4DcSlots {
19620    x: CudaSlice<f32>,
19621    xn: CudaSlice<f32>,
19622    cur: CudaSlice<f32>,
19623    hq: CudaSlice<i8>,
19624    hd_: CudaSlice<f32>,
19625    q0: CudaSlice<f32>,
19626    k0: CudaSlice<f32>,
19627    v0: CudaSlice<f32>,
19628    q: CudaSlice<f32>,
19629    k: CudaSlice<f32>,
19630    v: CudaSlice<f32>,
19631    attn: CudaSlice<f32>,
19632    o: CudaSlice<f32>,
19633    attn_out: CudaSlice<f32>,
19634    zsh: CudaSlice<f32>,
19635    zq: CudaSlice<i8>,
19636    zd: CudaSlice<f32>,
19637    gate: CudaSlice<f32>,
19638    up: CudaSlice<f32>,
19639    act: CudaSlice<f32>,
19640    actq: CudaSlice<i8>,
19641    actd: CudaSlice<f32>,
19642    f0: CudaSlice<f32>,
19643    sn: CudaSlice<f32>,
19644    hn: CudaSlice<f32>,
19645    logits: CudaSlice<f32>,
19646}
19647
19648/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
19649/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
19650/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
19651/// fixed logits stage the head writes.
19652pub struct Step35TokenGraphState {
19653    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
19654    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
19655    pub token_d: cudarc::driver::CudaSlice<u32>,
19656    pub pos_d: cudarc::driver::CudaSlice<i32>,
19657    pub logits_stage: cudarc::driver::CudaSlice<f32>,
19658    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
19659    /// launch, so an alloc made inside one captured child is not referable from another):
19660    /// the running residual, the post-attention pair, the shared-expert row, and the
19661    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
19662    pub x: cudarc::driver::CudaSlice<f32>,
19663    pub x1: cudarc::driver::CudaSlice<f32>,
19664    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
19665    pub sh_stage: cudarc::driver::CudaSlice<f32>,
19666    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
19667    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
19668    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
19669    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
19670    pub router_logits: cudarc::driver::CudaSlice<f32>,
19671    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
19672    pub shexp_up: cudarc::driver::CudaSlice<f32>,
19673    pub shexp_act: cudarc::driver::CudaSlice<f32>,
19674    pub gate_sig: cudarc::driver::CudaSlice<f32>,
19675    pub dense_z: cudarc::driver::CudaSlice<f32>,
19676    pub dense_gate: cudarc::driver::CudaSlice<f32>,
19677    pub dense_up: cudarc::driver::CudaSlice<f32>,
19678    pub dense_act: cudarc::driver::CudaSlice<f32>,
19679    pub hn: cudarc::driver::CudaSlice<f32>,
19680    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
19681    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
19682    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
19683    pub probe_x: cudarc::driver::CudaSlice<f32>,
19684    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
19685    /// the in-graph tail argmax chain; host reads the ring once per chunk.
19686    pub token_hist: cudarc::driver::CudaSlice<u32>,
19687    pub hist_idx: cudarc::driver::CudaSlice<i32>,
19688}
19689
19690impl HybridModel {
19691    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
19692    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
19693    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
19694    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
19695    /// needs a rebuild this token).
19696    ///
19697    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
19698    /// but not their contents under this door (the TP rank caches are fully maintained
19699    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
19700    /// must not run with the door on until the local-dcw twin lands.
19701    pub(crate) fn step35_token_graph_step(
19702        &self,
19703        e: &Engine,
19704        token: u32,
19705        cache: &mut Cache,
19706    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
19707        if !self.uses_sliding_gated_moe_program()
19708            || !crate::tp::step_tp_graph_enabled()?
19709            || !crate::tp::step_tp_dcw_enabled()?
19710            || !crate::tp::step_tp_qkv_fused_enabled()?
19711            || !crate::tp::step_tp_dev_router_enabled()?
19712            || !crate::tp::step_nvfp4_dev_routes_enabled()?
19713        {
19714            return Ok(None);
19715        }
19716        // GRAPH-LAUNCH HEADROOM GUARD (see spec::GRAPH_LAUNCH_MIN_FREE): the eager
19717        // token step is this route's byte-identical twin — warmup and rebase tokens
19718        // already ride it — so below the driver-free floor the token goes eager
19719        // (`Ok(None)` = the caller's eager fallback) instead of feeding cuGraphLaunch
19720        // an exhausted card (lane/graph-launch-guard-sweep-20260831).
19721        if !crate::spec::graph_launch_headroom_ok(e) {
19722            static NOTED: std::sync::Once = std::sync::Once::new();
19723            NOTED.call_once(|| crate::spec::graph_replay_suspended_note("step-tp-token"));
19724            return Ok(None);
19725        }
19726        let n_embd = self.cfg.n_embd as usize;
19727        let n_vocab = self.cfg.n_vocab as usize;
19728        let eps = self.cfg.rms_eps;
19729        let n_layers = self.layers.len();
19730        let pos = cache.pos;
19731        let staged_next = pos + 1;
19732        if staged_next < 96 {
19733            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
19734        }
19735
19736        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
19737        // eager fallback for the whole token; the host path also updates base_d there).
19738        for il in 0..n_layers {
19739            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
19740                return Ok(None); // caches not hydrated yet — eager warms them
19741            };
19742            if tp_kv.peek_append_ring(1)?.1 {
19743                return Ok(None);
19744            }
19745        }
19746
19747        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
19748        // their window and share one bucket forever after ctx > window).
19749        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
19750        if !fa_vec {
19751            return Ok(None);
19752        }
19753        let sp = crate::fa_split_keys(staged_next, 8);
19754        let bucket_max = (n_splits * sp).max(staged_next);
19755
19756        let mut state_guard = self
19757            .step35_token_graph
19758            .lock()
19759            .map_err(|_| "step35 token graph lock is poisoned")?;
19760        if state_guard.is_none() {
19761            let _main = e.gpu.enter_main()?;
19762            let n_expert = self
19763                .cfg
19764                .moe
19765                .as_ref()
19766                .map(|m| m.expert_count as usize)
19767                .unwrap_or(0);
19768            let n_ff_sh = self
19769                .layers
19770                .iter()
19771                .find_map(|l| match &l.ffn {
19772                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
19773                    _ => None,
19774                })
19775                .unwrap_or(0);
19776            let n_ff_dense = self
19777                .layers
19778                .iter()
19779                .find_map(|l| match &l.ffn {
19780                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
19781                    _ => None,
19782                })
19783                .unwrap_or(0);
19784            *state_guard = Some(Step35TokenGraphState {
19785                graphs: Vec::new(),
19786                token_d: e.stream().clone_htod(&[0u32])?,
19787                pos_d: e.htod_i32(&[pos as i32])?,
19788                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
19789                x: e.htod(&vec![0.0f32; n_embd])?,
19790                x1: e.htod(&vec![0.0f32; n_embd])?,
19791                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
19792                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
19793                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19794                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19795                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
19796                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19797                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19798                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19799                gate_sig: e.htod(&vec![1.0f32; 1])?,
19800                dense_z: e.htod(&vec![0.0f32; n_embd])?,
19801                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19802                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19803                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19804                hn: e.htod(&vec![0.0f32; n_embd])?,
19805                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
19806                probe_x: e.htod(&vec![0.0f32; n_embd])?,
19807                token_hist: e.stream().clone_htod(&[0u32; 16])?,
19808                hist_idx: e.htod_i32(&[0])?,
19809            });
19810        }
19811        let state = state_guard.as_mut().expect("state armed above");
19812        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
19813        // first use, and an alloc inside a captured section is a mem node (child graphs
19814        // reject those — the tail argmax chain needs them already resident).
19815        {
19816            let _main = e.gpu.enter_main()?;
19817            let Step35TokenGraphState {
19818                logits_stage,
19819                token_d,
19820                ..
19821            } = &mut *state;
19822            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
19823        }
19824
19825        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
19826        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
19827        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
19828        // ceiling at build so the baked pointers never move.
19829        if state.graphs.is_empty() {
19830            // Build the parent at this bucket. Capture executes nothing; correctness is
19831            // pinned at replay by the token-identity gate.
19832            self.step35_token_graph_build(e, cache, state, bucket_max)?;
19833        }
19834        {
19835            let (b, g) = state.graphs.first_mut().expect("graph built above");
19836            if *b != bucket_max {
19837                g.retarget_bucket(bucket_max)?;
19838                *b = bucket_max;
19839            }
19840        }
19841        let graph = state
19842            .graphs
19843            .first()
19844            .map(|(_, g)| g)
19845            .expect("graph built above");
19846
19847        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
19848        let t_fence = tg_timing.then(std::time::Instant::now);
19849        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
19850        // queued on the rank streams, and graph children carry no ordering edge to those
19851        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
19852        // sync is a no-op between consecutive replays.
19853        {
19854            let fa0 = match &self.layers[0].mixer {
19855                Mixer::Full(fa) => fa,
19856                _ => return Err("step35 token graph expects full-attention layers".into()),
19857            };
19858            let tp0 = fa0
19859                .step_tp_qkv
19860                .as_ref()
19861                .ok_or("step35 token graph lost its TP state")?;
19862            for rank in 0..tp0.runtime.devices().len() {
19863                let engine = tp0
19864                    .runtime
19865                    .rank_engine(rank)
19866                    .ok_or("step35 token graph lost a rank engine")?;
19867                let _main = engine.gpu.enter_main()?;
19868                engine.stream().synchronize()?;
19869            }
19870        }
19871
19872        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
19873        {
19874            let _main = e.gpu.enter_main()?;
19875            e.set_u32_one(&mut state.token_d, token)?;
19876            e.set_i32_one(&mut state.pos_d, pos as i32)?;
19877        }
19878        let t_launch = tg_timing.then(std::time::Instant::now);
19879        graph.launch(e)?;
19880        let t_book = tg_timing.then(std::time::Instant::now);
19881        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
19882        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
19883        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
19884        // replay error the counters are already advanced — acceptable: the decode aborts.
19885        for il in 0..n_layers {
19886            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
19887            let transaction = tp_kv.begin_transaction()?;
19888            let fa = match &self.layers[il].mixer {
19889                Mixer::Full(fa) => fa,
19890                _ => return Err("step35 token graph expects full-attention layers".into()),
19891            };
19892            let tp = fa
19893                .step_tp_qkv
19894                .as_ref()
19895                .ok_or("step35 token graph lost its TP state")?;
19896            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
19897            // incs own the counters). Shards unused.
19898            let empty: [CudaSlice<f32>; 0] = [];
19899            tp.runtime.append_tp_kv_transaction_inner(
19900                tp_kv,
19901                transaction,
19902                &empty,
19903                &empty,
19904                1,
19905                true,
19906            )?;
19907            tp.runtime
19908                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
19909            // Local shadow: lengths advance (v1 keeps contents stale under the door).
19910            if let Some(local) = cache.kv[il].as_mut() {
19911                local.len = pos + 1;
19912                let _main = e.gpu.enter_main()?;
19913                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
19914            }
19915        }
19916        cache.pos = pos + 1;
19917        let t_sync = tg_timing.then(std::time::Instant::now);
19918        let (logits, h_seed) = {
19919            let _main = e.gpu.enter_main()?;
19920            e.stream().synchronize()?;
19921            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
19922        };
19923        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
19924            use std::sync::atomic::{AtomicU64, Ordering};
19925            static NS: [AtomicU64; 5] = [
19926                AtomicU64::new(0),
19927                AtomicU64::new(0),
19928                AtomicU64::new(0),
19929                AtomicU64::new(0),
19930                AtomicU64::new(0),
19931            ];
19932            static CALLS: AtomicU64 = AtomicU64::new(0);
19933            let now = std::time::Instant::now();
19934            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
19935            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
19936            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
19937            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
19938            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
19939            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
19940            if calls % 100 == 0 {
19941                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
19942                eprintln!(
19943                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
19944                     syncdtoh_us={:.0} total_us={:.0}",
19945                    avg(0),
19946                    avg(1),
19947                    avg(2),
19948                    avg(3),
19949                    avg(4)
19950                );
19951            }
19952        }
19953        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
19954        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
19955            use std::io::Write;
19956            let (pm, px) = {
19957                let _main = e.gpu.enter_main()?;
19958                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
19959            };
19960            for (path, data) in [
19961                ("/root/tg-probe-mixed.bin", &pm),
19962                ("/root/tg-probe-x.bin", &px),
19963            ] {
19964                let mut fo = std::fs::OpenOptions::new()
19965                    .create(true)
19966                    .append(true)
19967                    .open(path)?;
19968                for v in data {
19969                    fo.write_all(&v.to_le_bytes())?;
19970                }
19971            }
19972        }
19973        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
19974        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
19975        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
19976            let hh = {
19977                let _main = e.gpu.enter_main()?;
19978                e.dtoh(&state.hn)?
19979            };
19980            use std::io::Write;
19981            let mut fo = std::fs::OpenOptions::new()
19982                .create(true)
19983                .append(true)
19984                .open(path)?;
19985            for v in &hh {
19986                fo.write_all(&v.to_le_bytes())?;
19987            }
19988        }
19989        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
19990        // per rank per token; diagnostics only.
19991        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
19992            for il in [0usize, 1, 44] {
19993                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
19994                let host_len = tp_kv.staged_len();
19995                let fa = match &self.layers[il].mixer {
19996                    Mixer::Full(fa) => fa,
19997                    _ => continue,
19998                };
19999                let tp = fa
20000                    .step_tp_qkv
20001                    .as_ref()
20002                    .ok_or("step35 token graph lost its TP state")?;
20003                for rank in 0..tp.runtime.devices().len() {
20004                    let engine = tp
20005                        .runtime
20006                        .rank_engine(rank)
20007                        .ok_or("step35 token graph lost a rank engine")?;
20008                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
20009                    let _main = engine.gpu.enter_main()?;
20010                    engine.stream().synchronize()?;
20011                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
20012                    let base_d = match rank_cache.base_d() {
20013                        Some(b) => engine.dtoh_i32_one(b)?,
20014                        None => -1,
20015                    };
20016                    eprintln!(
20017                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
20018                         len_d={len_d} base_d={base_d}"
20019                    );
20020                }
20021            }
20022        }
20023        Ok(Some((logits, h_seed)))
20024    }
20025
20026    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
20027    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
20028    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
20029    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
20030    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
20031    pub(crate) fn head_split_matvec(
20032        &self,
20033        e: &Engine,
20034        hn: &CudaSlice<f32>,
20035    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
20036        if self.head_split_fill_device(e, hn)?.is_none() {
20037            return Ok(None);
20038        }
20039        let guard = HEAD_SPLIT_WS
20040            .lock()
20041            .map_err(|_| "head split lock is poisoned")?;
20042        let ws = guard.as_ref().expect("filled above");
20043        let _main = e.gpu.enter_main()?;
20044        Ok(Some(e.dtoh(&ws.logits_e)?))
20045    }
20046
20047    /// Compute body of the split head: arms the replica + staging on first use, then fills
20048    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
20049    /// push) and orders e's stream behind it. None = ineligible.
20050    fn head_split_fill_device(
20051        &self,
20052        e: &Engine,
20053        hn: &CudaSlice<f32>,
20054    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
20055        use cudarc::driver::DevicePtr;
20056        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
20057            return Ok(None);
20058        };
20059        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
20060            Mixer::Full(fa) => fa
20061                .step_tp_qkv
20062                .as_ref()
20063                .and_then(|tp| tp.runtime.rank_engine(1)),
20064            _ => None,
20065        }) else {
20066            return Ok(None);
20067        };
20068        let n_embd = self.cfg.n_embd as usize;
20069        let n_vocab = self.cfg.n_vocab as usize;
20070        let half = n_vocab / 2;
20071        let mut guard = HEAD_SPLIT_WS
20072            .lock()
20073            .map_err(|_| "head split lock is poisoned")?;
20074        let pin = {
20075            let _main = e.gpu.enter_main()?;
20076            let stream = e.stream();
20077            let (ptr, _g) = head.device_ptr(&stream);
20078            ptr as u64
20079        };
20080        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
20081            // One-time: upload rank1's row half + persistent staging.
20082            let hi_rows = n_vocab - half;
20083            let (w1, hn1, y1, ev_done) = {
20084                let _r1 = rank1.gpu.enter_main()?;
20085                (
20086                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
20087                    rank1.htod(&vec![0.0f32; n_embd])?,
20088                    rank1.htod(&vec![0.0f32; hi_rows])?,
20089                    rank1.ctx().new_event(None)?,
20090                )
20091            };
20092            {
20093                use cudarc::driver::sys;
20094                let src = pin + (half * n_embd * 2) as u64;
20095                let dst = {
20096                    let _r1 = rank1.gpu.enter_main()?;
20097                    let rstream = rank1.stream();
20098                    let (d, _g) = w1.device_ptr(&rstream);
20099                    d as u64
20100                };
20101                let _r1 = rank1.gpu.enter_main()?;
20102                let r = unsafe {
20103                    sys::cuMemcpyAsync(
20104                        dst as sys::CUdeviceptr,
20105                        src as sys::CUdeviceptr,
20106                        hi_rows * n_embd * 2,
20107                        rank1.stream().cu_stream() as sys::CUstream,
20108                    )
20109                };
20110                if r != sys::CUresult::CUDA_SUCCESS {
20111                    return Err(format!("head split replica upload: {r:?}").into());
20112                }
20113                rank1.stream().synchronize()?;
20114            }
20115            let (logits_e, ev_hn) = {
20116                let _main = e.gpu.enter_main()?;
20117                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
20118            };
20119            let (raw_hn1, raw_y1) = {
20120                let _r1 = rank1.gpu.enter_main()?;
20121                let rstream = rank1.stream();
20122                let (a, _g0) = hn1.device_ptr(&rstream);
20123                let (b, _g1) = y1.device_ptr(&rstream);
20124                (a as u64, b as u64)
20125            };
20126            let raw_logits_hi = {
20127                let _main = e.gpu.enter_main()?;
20128                let stream = e.stream();
20129                let (l, _g) = logits_e.device_ptr(&stream);
20130                l as u64 + (half * 4) as u64
20131            };
20132            *guard = Some(HeadSplit {
20133                pin,
20134                w1,
20135                hn1,
20136                y1,
20137                logits_e,
20138                ev_hn,
20139                ev_done,
20140                raw_hn1,
20141                raw_y1,
20142                raw_logits_hi,
20143                samp: None,
20144            });
20145        }
20146        let ws = guard.as_mut().expect("armed above");
20147        let hi_rows = n_vocab - half;
20148        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
20149        let raw_hn = {
20150            let _main = e.gpu.enter_main()?;
20151            let stream = e.stream();
20152            let (h, _g) = hn.device_ptr(&stream);
20153            ws.ev_hn.record(&stream)?;
20154            h as u64
20155        };
20156        {
20157            let _r1 = rank1.gpu.enter_main()?;
20158            rank1.stream().wait(&ws.ev_hn)?;
20159            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
20160            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
20161            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
20162            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
20163            ws.ev_done.record(&rank1.stream())?;
20164        }
20165        {
20166            let _main = e.gpu.enter_main()?;
20167            let head_lo = head.slice(0..half * n_embd * 2);
20168            let HeadSplit { logits_e, .. } = &mut *ws;
20169            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
20170            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
20171            e.stream().wait(&ws.ev_done)?;
20172            Ok(Some(()))
20173        }
20174    }
20175
20176    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
20177    /// row exactly like the host variant (identical halves, identical concat) and runs the
20178    /// device argmax into `token_d` — NO host readback. Returns false when the split is
20179    /// ineligible (caller falls back to the plain matmul head).
20180    pub(crate) fn head_split_argmax_device(
20181        &self,
20182        e: &Engine,
20183        hn: &CudaSlice<f32>,
20184        token_d: &mut CudaSlice<u32>,
20185    ) -> Result<bool, Box<dyn std::error::Error>> {
20186        if self.head_split_fill_device(e, hn)?.is_none() {
20187            return Ok(false);
20188        }
20189        let n_vocab = self.cfg.n_vocab as usize;
20190        let guard = HEAD_SPLIT_WS
20191            .lock()
20192            .map_err(|_| "head split lock is poisoned")?;
20193        let ws = guard.as_ref().expect("filled above");
20194        let _main = e.gpu.enter_main()?;
20195        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
20196        Ok(true)
20197    }
20198
20199    /// SAMPLED twin of `head_split_argmax_device`. The split head already materializes the
20200    /// full concatenated row in `ws.logits_e`, so sampling does NOT have to give up HEAD_SPLIT
20201    /// — it draws from that row on device (filter thresholds, Gumbel perturbation, argmax)
20202    /// exactly as the serve tick does. Worth ~0.2 ms/token: the post-W8 census had the
20203    /// unsplit q8 head at ~364 us against ~82 us per half.
20204    pub(crate) fn head_split_sample_device(
20205        &self,
20206        e: &Engine,
20207        hn: &CudaSlice<f32>,
20208        token_d: &mut CudaSlice<u32>,
20209        samp: &crate::decode_batch::DevSamp,
20210        ctr: u32,
20211    ) -> Result<bool, Box<dyn std::error::Error>> {
20212        if self.head_split_fill_device(e, hn)?.is_none() {
20213            return Ok(false);
20214        }
20215        let n_vocab = self.cfg.n_vocab as usize;
20216        let guard = HEAD_SPLIT_WS
20217            .lock()
20218            .map_err(|_| "head split lock is poisoned")?;
20219        let mut guard = guard;
20220        let ws = guard.as_mut().expect("filled above");
20221        let _main = e.gpu.enter_main()?;
20222        if ws.samp.is_none() {
20223            ws.samp = Some(SampScratch {
20224                pb: e.zeros(n_vocab)?,
20225                th: e.zeros(1)?,
20226                z: e.zeros(1)?,
20227                mx: e.zeros(1)?,
20228                rows: e.htod_i32(&[0i32])?,
20229            });
20230        }
20231        let filtered = samp.top_k > 0 || samp.top_p < 1.0 || samp.min_p > 0.0;
20232        let HeadSplit {
20233            logits_e,
20234            samp: scratch,
20235            ..
20236        } = &mut *ws;
20237        let sc = scratch.as_mut().expect("armed above");
20238        if filtered {
20239            e.filter_stats(
20240                logits_e, n_vocab, &sc.rows, &mut sc.th, &mut sc.z, &mut sc.mx, n_vocab, 1,
20241                samp.temp, samp.top_k, samp.top_p, samp.min_p,
20242            )?;
20243            let SampScratch { pb, th, mx, .. } = sc;
20244            e.gumbel_perturb_filtered_col(
20245                logits_e, 0, pb, n_vocab, samp.seed, ctr, samp.temp, mx, th, 0,
20246            )?;
20247        } else {
20248            e.gumbel_perturb_col(logits_e, 0, &mut sc.pb, n_vocab, samp.seed, ctr, samp.temp)?;
20249        }
20250        e.argmax_token_device_col(&sc.pb, 0, n_vocab, token_d, 0)?;
20251        Ok(true)
20252    }
20253
20254    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
20255    /// token's row).
20256    pub(crate) fn head_split_logits_dtoh(
20257        &self,
20258        e: &Engine,
20259    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
20260        let guard = HEAD_SPLIT_WS
20261            .lock()
20262            .map_err(|_| "head split lock is poisoned")?;
20263        let ws = guard.as_ref().ok_or("head split logits not armed")?;
20264        let _main = e.gpu.enter_main()?;
20265        Ok(e.dtoh(&ws.logits_e)?)
20266    }
20267
20268    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
20269    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
20270    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
20271    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
20272    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
20273    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
20274    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
20275    /// own loop re-derive hist[k-1] from the returned row.
20276    pub fn step35_token_graph_chunk(
20277        &self,
20278        e: &Engine,
20279        token: u32,
20280        k_target: usize,
20281        cache: &mut Cache,
20282    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
20283        if !self.uses_sliding_gated_moe_program()
20284            || !crate::tp::step_tp_graph_enabled()?
20285            || !crate::tp::step_tp_dcw_enabled()?
20286            || !crate::tp::step_tp_qkv_fused_enabled()?
20287            || !crate::tp::step_tp_dev_router_enabled()?
20288            || !crate::tp::step_nvfp4_dev_routes_enabled()?
20289        {
20290            return Ok(None);
20291        }
20292        // GRAPH-LAUNCH HEADROOM GUARD: same guard, same eager twin as
20293        // `step35_token_graph_step` (the chunk is that step replayed k times).
20294        if !crate::spec::graph_launch_headroom_ok(e) {
20295            static NOTED: std::sync::Once = std::sync::Once::new();
20296            NOTED.call_once(|| crate::spec::graph_replay_suspended_note("step-tp-token"));
20297            return Ok(None);
20298        }
20299        let n_layers = self.layers.len();
20300        let pos = cache.pos;
20301        let staged_next = pos + 1;
20302        if staged_next < 96 {
20303            return Ok(None);
20304        }
20305        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
20306        // exec's n_splits ladder must match eager per depth).
20307        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20308        if !fa_vec {
20309            return Ok(None);
20310        }
20311        let sp = crate::fa_split_keys(staged_next, 8);
20312        let bucket_max = (n_splits * sp).max(staged_next);
20313        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
20314        let mut k = k_target.min(to_boundary).min(16);
20315        if k < 2 {
20316            return Ok(None);
20317        }
20318        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
20319        for il in 0..n_layers {
20320            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20321                return Ok(None);
20322            };
20323            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
20324                k -= 1;
20325            }
20326            if k < 2 {
20327                return Ok(None);
20328            }
20329        }
20330
20331        let mut state_guard = self
20332            .step35_token_graph
20333            .lock()
20334            .map_err(|_| "step35 token graph lock is poisoned")?;
20335        let Some(state) = state_guard.as_mut() else {
20336            return Ok(None); // per-token path arms the state + stages first
20337        };
20338        if state.graphs.is_empty() {
20339            return Ok(None);
20340        }
20341        {
20342            let (b, g) = state.graphs.first_mut().expect("checked above");
20343            if *b != bucket_max {
20344                g.retarget_bucket(bucket_max)?;
20345                *b = bucket_max;
20346            }
20347        }
20348        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
20349
20350        // Rank-stream fence (eager stragglers; see the per-token path).
20351        {
20352            let fa0 = match &self.layers[0].mixer {
20353                Mixer::Full(fa) => fa,
20354                _ => return Err("step35 token graph expects full-attention layers".into()),
20355            };
20356            let tp0 = fa0
20357                .step_tp_qkv
20358                .as_ref()
20359                .ok_or("step35 token graph lost its TP state")?;
20360            for rank in 0..tp0.runtime.devices().len() {
20361                let engine = tp0
20362                    .runtime
20363                    .rank_engine(rank)
20364                    .ok_or("step35 token graph lost a rank engine")?;
20365                let _main = engine.gpu.enter_main()?;
20366                engine.stream().synchronize()?;
20367            }
20368        }
20369
20370        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
20371        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
20372        {
20373            let _main = e.gpu.enter_main()?;
20374            e.set_u32_one(&mut state.token_d, token)?;
20375            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20376            e.set_i32_one(&mut state.hist_idx, 0)?;
20377        }
20378        for _ in 0..k {
20379            graph.launch(e)?;
20380        }
20381        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
20382        for il in 0..n_layers {
20383            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20384            let transaction = tp_kv.begin_transaction()?;
20385            let fa = match &self.layers[il].mixer {
20386                Mixer::Full(fa) => fa,
20387                _ => return Err("step35 token graph expects full-attention layers".into()),
20388            };
20389            let tp = fa
20390                .step_tp_qkv
20391                .as_ref()
20392                .ok_or("step35 token graph lost its TP state")?;
20393            let empty: [CudaSlice<f32>; 0] = [];
20394            tp.runtime.append_tp_kv_transaction_inner(
20395                tp_kv,
20396                transaction,
20397                &empty,
20398                &empty,
20399                k,
20400                true,
20401            )?;
20402            tp.runtime
20403                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
20404            if let Some(local) = cache.kv[il].as_mut() {
20405                local.len = pos + k;
20406                let _main = e.gpu.enter_main()?;
20407                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
20408            }
20409        }
20410        cache.pos = pos + k;
20411        let (hist, logits) = {
20412            let _main = e.gpu.enter_main()?;
20413            e.stream().synchronize()?;
20414            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
20415        };
20416        Ok(Some((hist[..k].to_vec(), logits)))
20417    }
20418}
20419
20420impl HybridModel {
20421    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
20422    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
20423    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
20424    /// of each phase fork in parallel and merge into the following root section.
20425    #[allow(clippy::too_many_arguments)]
20426    fn step35_token_graph_build(
20427        &self,
20428        e: &Engine,
20429        cache: &mut Cache,
20430        state: &mut Step35TokenGraphState,
20431        bucket_max: usize,
20432    ) -> Result<(), Box<dyn std::error::Error>> {
20433        use cudarc::driver::DevicePtr;
20434        let n_embd = self.cfg.n_embd as usize;
20435        let eps = self.cfg.rms_eps;
20436        let n_layers = self.layers.len();
20437        let started = std::time::Instant::now();
20438        if !crate::router_kernel_on() {
20439            return Err(
20440                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
20441            );
20442        }
20443        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
20444            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
20445        }
20446
20447        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
20448        let embd_gpu = self
20449            .embd_gpu_try(e)
20450            .ok_or("step35 token graph could not upload the device embed table")?;
20451        let embd_qtype = match self.embd.ggml_type {
20452            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
20453            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
20454            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
20455        };
20456        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
20457
20458        // Fixed-stage pointers the sections reference.
20459        let (p_mixed, p_kshadow, p_vshadow) = {
20460            let _main = e.gpu.enter_main()?;
20461            let stream = e.stream();
20462            let (a, _g) = state.mixed_stage.device_ptr(&stream);
20463            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
20464            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
20465            (a as u64, b as u64, c as u64)
20466        };
20467
20468        crate::tp::token_graph_build_begin()?;
20469        let mut group_id: u32 = 0;
20470        for il in 0..n_layers {
20471            let layer = &self.layers[il];
20472            let fa = match &layer.mixer {
20473                Mixer::Full(fa) => fa,
20474                _ => return Err("step35 token graph expects full-attention layers".into()),
20475            };
20476            let tp = fa
20477                .step_tp_qkv
20478                .as_ref()
20479                .ok_or("step35 token graph lost its TP state")?;
20480            let attention = tp
20481                .attention
20482                .as_ref()
20483                .ok_or("step35 token graph lost its attention aux")?;
20484            let geometry = self.step35_geom(il);
20485            let window = geometry.window.map(|w| w as usize);
20486            let head_dim = geometry.head_dim_k as usize;
20487            let heads = geometry.n_head as usize;
20488            let kv_heads = geometry.n_head_kv as usize;
20489            let ranks = tp.runtime.devices().len();
20490            let local_heads = heads / ranks;
20491            let local_kv_heads = kv_heads / ranks;
20492            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
20493            let use_gate_shards =
20494                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
20495            if !use_gate_shards {
20496                return Err("step35 token graph requires the fused gate shards".into());
20497            }
20498
20499            let ws_index = tp
20500                .runtime
20501                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
20502            let ws_mutex = tp.runtime.decode_v2_workspace();
20503            let mut ws_guard = ws_mutex
20504                .lock()
20505                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
20506            let ws = ws_guard
20507                .get_mut(ws_index)
20508                .ok_or("step TP decode v2 workspace missing after ensure")?;
20509            tp.runtime
20510                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
20511            let mut rope_freqs = Vec::with_capacity(ranks);
20512            for rank in 0..ranks {
20513                let engine = tp
20514                    .runtime
20515                    .rank_engine(rank)
20516                    .ok_or("step35 token graph lost a rank engine")?;
20517                rope_freqs.push(if geometry.rope_factors {
20518                    self.step35_aux
20519                        .as_ref()
20520                        .and_then(|aux| aux.rope_freqs(engine))
20521                } else {
20522                    None
20523                });
20524            }
20525            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
20526                Some(crate::tp::StepTpGateShards::F32(shards))
20527            } else {
20528                attention
20529                    .gate_shards_bf16
20530                    .as_deref()
20531                    .map(crate::tp::StepTpGateShards::Bf16)
20532            };
20533
20534            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
20535            let decode_input = attention
20536                .decode_input
20537                .as_ref()
20538                .ok_or("step35 token graph requires the replicated decode input")?;
20539            let mut decode_input = decode_input
20540                .lock()
20541                .map_err(|_| "replicated decode input lock is poisoned")?;
20542            // Stage arming happens through the eager stage flow once; require it here.
20543            if ws.h_stage.is_none() {
20544                return Err(
20545                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
20546                );
20547            }
20548            {
20549                let state_x = &mut state.x;
20550                let token_d = &state.token_d;
20551                let pos_d = &state.pos_d;
20552                crate::tp::graph_section(e, None, || {
20553                    let _main = e.gpu.enter_main()?;
20554                    if il == 0 {
20555                        e.embed_gather_device_into(
20556                            embd_gpu,
20557                            token_d,
20558                            state_x,
20559                            n_embd,
20560                            embd_qtype,
20561                            embd_row_bytes,
20562                        )?;
20563                    }
20564                    {
20565                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
20566                        e.rms_norm(
20567                            state_x,
20568                            layer.attn_norm.float_data(),
20569                            h_stage,
20570                            n_embd,
20571                            1,
20572                            eps,
20573                        )?;
20574                    }
20575                    {
20576                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
20577                        let mut dst = pos_stage.slice_mut(0..1);
20578                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
20579                    }
20580                    Ok(())
20581                })?;
20582            }
20583
20584            // ---- R0/R1 (parallel): projections + dcw attention interior ----
20585            group_id += 1;
20586            for rank in 0..ranks {
20587                let engine = tp
20588                    .runtime
20589                    .rank_engine(rank)
20590                    .ok_or("step35 token graph lost a rank engine")?;
20591                {
20592                    // fa partial pool must reach the RUN CEILING before capture — an
20593                    // in-capture grow is a mem node (child graphs reject those), and the
20594                    // retarget path (increment C) widens the baked memsets up to the ceiling
20595                    // without moving the pool pointers. Two ensures cover both sp rungs.
20596                    let ceiling = window
20597                        .map(|w| cache.max_ctx.min(w))
20598                        .unwrap_or(cache.max_ctx);
20599                    let _main = engine.gpu.enter_main()?;
20600                    engine.fa_dcw_pool_ensure(
20601                        head_dim,
20602                        local_heads,
20603                        local_kv_heads,
20604                        ceiling.min(2048),
20605                    )?;
20606                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
20607                    engine.fa_dcw_pool_ensure(
20608                        head_dim,
20609                        local_heads,
20610                        local_kv_heads,
20611                        layer_bucket,
20612                    )?;
20613                }
20614                let runtime = &tp.runtime;
20615                let q_norm = &attention.q_norm;
20616                let k_norm = &attention.k_norm;
20617                let gate_ref = gate_shards_arg.as_ref();
20618                crate::tp::graph_section(engine, Some(group_id), || {
20619                    runtime.decode_v2_input_qkv_rank(
20620                        ws,
20621                        &state.pos_d,
20622                        &mut decode_input,
20623                        &tp.q,
20624                        &tp.k,
20625                        &tp.v,
20626                        q_norm,
20627                        k_norm,
20628                        head_dim,
20629                        geometry.n_rot as usize,
20630                        geometry.rope_base,
20631                        &rope_freqs,
20632                        eps,
20633                        gate_ref,
20634                        true,
20635                        false,
20636                        rank,
20637                        None,
20638                    )?;
20639                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
20640                    // replayed values track the live counters).
20641                    let distributed = cache.tp_kv[il]
20642                        .as_mut()
20643                        .ok_or("step35 token graph lost a TP cache")?;
20644                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
20645                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
20646                    let capacity = distributed.physical_capacity();
20647                    {
20648                        let rank_cache = distributed
20649                            .rank_mut(rank)
20650                            .ok_or("step35 token graph lost a rank cache")?;
20651                        let (k_plane, v_plane, len_d, base_d) =
20652                            rank_cache.planes_and_counters_mut();
20653                        engine.append_kv_quantized_dcw(
20654                            &ws.k[rank],
20655                            &ws.v_raw[rank],
20656                            k_plane,
20657                            v_plane,
20658                            len_d,
20659                            base_d,
20660                            kv_dim_k,
20661                            kv_dim_v,
20662                            ktb,
20663                            vtb,
20664                        )?;
20665                    }
20666                    {
20667                        let rank_cache = distributed
20668                            .rank_mut(rank)
20669                            .ok_or("step35 token graph lost a rank cache")?;
20670                        engine.inc_i32(rank_cache.len_d_mut())?;
20671                    }
20672                    let rank_cache = distributed
20673                        .rank(rank)
20674                        .ok_or("step35 token graph lost a rank cache")?;
20675                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
20676                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
20677                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
20678                    // retarget addresses combine's nsp at arg slot 6, and the fused
20679                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
20680                    // only the eager arm takes FUSION #2d.
20681                    engine.fa_decode_dcw(
20682                        &ws.q[rank],
20683                        &k_ring,
20684                        &v_ring,
20685                        &mut ws.attn_out[rank],
20686                        head_dim,
20687                        local_heads,
20688                        local_kv_heads,
20689                        rank_cache.len_d(),
20690                        rank_cache.base_d(),
20691                        window.unwrap_or(0),
20692                        layer_bucket,
20693                        geometry.attention_scale(),
20694                        ktb,
20695                        vtb,
20696                        None,
20697                    )?;
20698                    engine.attn_head_gate(
20699                        &ws.attn_out[rank],
20700                        &ws.gate[rank],
20701                        &mut ws.gated[rank],
20702                        None,
20703                        head_dim,
20704                        local_heads,
20705                        1,
20706                    )?;
20707                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
20708                    Ok(())
20709                })?;
20710            }
20711
20712            // ---- ROOT: combine + shadows + e-mirrors ----
20713            {
20714                let root = tp
20715                    .runtime
20716                    .rank_engine(0)
20717                    .ok_or("step35 token graph lost the root engine")?;
20718                let runtime = &tp.runtime;
20719                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
20720            }
20721            drop(ws_guard);
20722            drop(decode_input);
20723
20724            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
20725                .ok()
20726                .and_then(|v| v.parse().ok());
20727            if probe_layer == Some(il) {
20728                let Step35TokenGraphState {
20729                    mixed_stage,
20730                    probe_mixed,
20731                    ..
20732                } = &mut *state;
20733                crate::tp::graph_section(e, None, || {
20734                    let _main = e.gpu.enter_main()?;
20735                    let mut dst = probe_mixed.slice_mut(0..n_embd);
20736                    e.stream()
20737                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
20738                    Ok(())
20739                })?;
20740            }
20741
20742            // ---- FFN half ----
20743            match &layer.ffn {
20744                crate::hybrid::Ffn::Dense {
20745                    ffn_gate,
20746                    ffn_up,
20747                    ffn_down,
20748                } => {
20749                    let n_ff = ffn_gate.out_features();
20750                    let lim = self.cfg.clamp_shexp_at(il as u32);
20751                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
20752                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
20753                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
20754                    if lim.is_some() {
20755                        return Err("step35 token graph dense FFN with clamp unsupported".into());
20756                    }
20757                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
20758                        (
20759                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
20760                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
20761                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
20762                        ) => (wg, wu, wd),
20763                        _ => {
20764                            return Err(
20765                                "step35 token graph dense FFN requires bf16-resident weights"
20766                                    .into(),
20767                            );
20768                        }
20769                    };
20770                    crate::tp::graph_section(e, None, || {
20771                        let _main = e.gpu.enter_main()?;
20772                        let Step35TokenGraphState {
20773                            x,
20774                            x1,
20775                            mixed_stage,
20776                            dense_z,
20777                            dense_gate,
20778                            dense_up,
20779                            dense_act,
20780                            sh_stage,
20781                            ..
20782                        } = &mut *state;
20783                        e.add_rms_norm(
20784                            x,
20785                            mixed_stage,
20786                            layer.post_attn_norm.float_data(),
20787                            x1,
20788                            dense_z,
20789                            n_embd,
20790                            1,
20791                            eps,
20792                        )?;
20793                        // TWO SINGLE matvecs, not the dual: eager dense rides two
20794                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
20795                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
20796                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
20797                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
20798                        Self::ffn_act_lim(
20799                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
20800                        )?;
20801                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
20802                        e.add(x1, sh_stage, x, n_embd)?;
20803                        Ok(())
20804                    })?;
20805                }
20806                crate::hybrid::Ffn::Moe(m) => {
20807                    let moe = self
20808                        .cfg
20809                        .moe
20810                        .as_ref()
20811                        .ok_or("step35 token graph needs moe cfg")?;
20812                    let n_expert = moe.expert_count as usize;
20813                    let n_used = moe.expert_used_count as usize;
20814                    let sigmoid = self
20815                        .cfg
20816                        .sigmoid_router()
20817                        .ok_or("step35 token graph needs the sigmoid router")?;
20818                    let step_tp = m
20819                        .step_tp
20820                        .as_ref()
20821                        .ok_or("step35 token graph needs TP experts")?;
20822                    let bank = match &step_tp.experts {
20823                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
20824                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
20825                    };
20826                    let routes_ws_mutex = bank.device_workspace_handle();
20827                    let mut routes_guard = routes_ws_mutex
20828                        .lock()
20829                        .map_err(|_| "routes workspace lock is poisoned")?;
20830                    let routes_ws = routes_guard
20831                        .as_mut()
20832                        .ok_or("step35 token graph requires the routes workspace warmed")?;
20833                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
20834                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
20835                    let p_z = {
20836                        let root = step_tp
20837                            .runtime
20838                            .rank_engine(0)
20839                            .ok_or("routes root engine missing")?;
20840                        let _main = root.gpu.enter_main()?;
20841                        let stream = root.stream();
20842                        let in_stage = routes_ws
20843                            .in_stage_handle()
20844                            .ok_or("routes in stage not armed")?;
20845                        let (a, _g) = in_stage.device_ptr(&stream);
20846                        a as u64
20847                    };
20848                    let local_out = bank.expert_width / ranks;
20849
20850                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
20851                    crate::tp::graph_section(e, None, || {
20852                        let _main = e.gpu.enter_main()?;
20853                        {
20854                            let in_stage = routes_ws
20855                                .in_stage_mut()
20856                                .ok_or("routes in stage not armed")?;
20857                            let Step35TokenGraphState {
20858                                x, x1, mixed_stage, ..
20859                            } = &mut *state;
20860                            e.add_rms_norm(
20861                                x,
20862                                mixed_stage,
20863                                layer.post_attn_norm.float_data(),
20864                                x1,
20865                                in_stage,
20866                                n_embd,
20867                                1,
20868                                eps,
20869                            )?;
20870                        }
20871                        {
20872                            let z_ref = routes_ws
20873                                .in_stage_handle()
20874                                .ok_or("routes in stage not armed")?;
20875                            e.router_gemv_into(
20876                                m.gate_inp.float_data(),
20877                                z_ref,
20878                                &mut state.router_logits,
20879                                n_embd,
20880                                n_expert,
20881                                1,
20882                            )?;
20883                        }
20884                        let (sel_e, w_e) = routes_ws
20885                            .dev_route_e_mut()
20886                            .ok_or("routes staging not armed")?;
20887                        e.moe_router_sigmoid_topk_into(
20888                            &state.router_logits,
20889                            1,
20890                            n_expert,
20891                            n_used,
20892                            m.active_count(),
20893                            &m.exp_probs_b_dev,
20894                            &m.active_experts_dev,
20895                            sigmoid.0,
20896                            sigmoid.1,
20897                            sel_e,
20898                            w_e,
20899                        )?;
20900                        Ok(())
20901                    })?;
20902
20903                    // ---- R0r/R1r (parallel): routes sweeps ----
20904                    group_id += 1;
20905                    for rank in 0..ranks {
20906                        let engine = step_tp
20907                            .runtime
20908                            .rank_engine(rank)
20909                            .ok_or("routes rank engine missing")?;
20910                        let runtime = &step_tp.runtime;
20911                        crate::tp::graph_section(engine, Some(group_id), || {
20912                            runtime.routes_rank_section(
20913                                bank,
20914                                routes_ws,
20915                                p_z,
20916                                local_out,
20917                                n_used,
20918                                step_tp.activation_limit,
20919                                rank,
20920                            )
20921                        })?;
20922                    }
20923
20924                    // ---- ROOTr: combine into the out stage ----
20925                    {
20926                        let root = step_tp
20927                            .runtime
20928                            .rank_engine(0)
20929                            .ok_or("routes root engine missing")?;
20930                        let runtime = &step_tp.runtime;
20931                        crate::tp::graph_section(root, None, || {
20932                            runtime.routes_root_section(bank, routes_ws)
20933                        })?;
20934                    }
20935
20936                    // ---- E3: shexp + add_shared onto the out stage + residual ----
20937                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
20938                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
20939                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
20940                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
20941                        (
20942                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
20943                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
20944                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
20945                        ) => (wg, wu, wd),
20946                        _ => {
20947                            return Err(
20948                                "step35 token graph shexp requires bf16-resident weights".into()
20949                            );
20950                        }
20951                    };
20952                    let n_ff_sh = m
20953                        .gate_shexp
20954                        .as_ref()
20955                        .expect("matched Some above")
20956                        .out_features();
20957                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
20958                    // init, reproducing eager's ones vector without a launch.
20959                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
20960                    crate::tp::graph_section(e, None, || {
20961                        let _main = e.gpu.enter_main()?;
20962                        let (z_ref, out_stage) = routes_ws
20963                            .in_and_out_stages_mut()
20964                            .ok_or("routes stages not armed")?;
20965                        let Step35TokenGraphState {
20966                            x,
20967                            x1,
20968                            sh_stage,
20969                            shexp_gate,
20970                            shexp_up,
20971                            shexp_act,
20972                            gate_sig,
20973                            ..
20974                        } = &mut *state;
20975                        e.matvec_bf16_dual_into(
20976                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
20977                        )?;
20978                        Self::ffn_act_lim(
20979                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
20980                            n_ff_sh,
20981                        )?;
20982                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
20983                        if let Some(gate_w) = gate_inp_shexp {
20984                            e.sigmoid_dot_rows_into(
20985                                z_ref,
20986                                gate_w.float_data(),
20987                                gate_sig,
20988                                n_embd,
20989                                1,
20990                            )?;
20991                        }
20992                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
20993                        e.add(x1, out_stage, x, n_embd)?;
20994                        Ok(())
20995                    })?;
20996                }
20997            }
20998            if probe_layer == Some(il) {
20999                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
21000                crate::tp::graph_section(e, None, || {
21001                    let _main = e.gpu.enter_main()?;
21002                    let mut dst = probe_x.slice_mut(0..n_embd);
21003                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
21004                    Ok(())
21005                })?;
21006            }
21007        }
21008
21009        // ---- Tail: output norm + head into the logits stage ----
21010        let head = match &self.output {
21011            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
21012            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
21013        };
21014        crate::tp::graph_section(e, None, || {
21015            let _main = e.gpu.enter_main()?;
21016            let Step35TokenGraphState {
21017                x,
21018                hn,
21019                logits_stage,
21020                token_d,
21021                pos_d,
21022                token_hist,
21023                hist_idx,
21024                ..
21025            } = &mut *state;
21026            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
21027            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
21028            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
21029            // argmax_gate-validated), the id lands in the history ring, and pos advances on
21030            // device — consecutive launches chain with NO host sync. Single-token mode
21031            // overwrites token_d/pos_d from the host before each launch, so these nodes are
21032            // harmless there.
21033            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
21034            e.u32_hist_append(token_d, token_hist, hist_idx)?;
21035            e.inc_i32(pos_d)?;
21036            Ok(())
21037        })?;
21038
21039        let graph = crate::tp::token_graph_build_finish()?;
21040        state.graphs.push((bucket_max, graph));
21041        eprintln!(
21042            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
21043             build_ms={:.0} performance_claim=false",
21044            started.elapsed().as_secs_f64() * 1e3
21045        );
21046        Ok(())
21047    }
21048}