Skip to main content

memra_engine/
hybrid_forward.rs

1//! Hybrid forward pass (Stage-1, f32, prefill, single sequence). Per layer dispatches to a
2//! linear-attention (Gated DeltaNet) or full-attention mixer, then SwiGLU FFN. Matches
3//! llama.cpp src/models/qwen35.cpp node-for-node.
4
5use crate::Engine;
6use crate::cache::Cache;
7use cudarc::driver::CudaSlice;
8use memra_gguf::config::ModelConfig;
9
10/// Resident trunk transients for the eager prime (piecewise-graph foundation; see
11/// HybridModel::prime_slabs). Every live buffer prefix is fully overwritten before use per prime;
12/// capacity beyond the current token count must never cross a shape-sensitive boundary.
13pub struct PrimeSlabs {
14    pub t_cap: usize,
15    pub h: CudaSlice<f32>,
16    pub x1: CudaSlice<f32>,
17    pub z: CudaSlice<f32>,
18    pub act: CudaSlice<f32>,
19    pub xa: CudaSlice<f32>,
20    pub xb: CudaSlice<f32>,
21    pub h16: CudaSlice<u8>,
22    pub z16: CudaSlice<u8>,
23    /// piecewise boundary slabs (increment 2): GEMM outputs land here so the
24    /// downstream captured segments see fixed addresses.
25    pub gate: CudaSlice<f32>, // t * n_ff_max
26    pub up: CudaSlice<f32>,      // t * n_ff_max
27    pub ffn_out: CudaSlice<f32>, // t * n_embd
28    /// piecewise increment 3: per-layer S-glue segment graphs (down-add + next
29    /// attn-norm, ALL-slab IO, zero in-graph allocations -> keeperless capture is
30    /// clean). Baked at this t_cap; replay only when t == t_cap. seg_glue[il] fires
31    /// between layer il and il+1 (ping-pong parity is deterministic per il).
32    pub seg_glue: Vec<Option<cudarc::driver::CudaGraph>>,
33    /// increment 5 (core-split edition): the mixer out-GEMM writes _into_ `mixed`
34    /// directly (no staging copy — the increment-4 copy route was refuted), making
35    /// S-mid [add + post-norm] all-slab and capturable.
36    pub mixed: CudaSlice<f32>,
37    pub seg_mid: Vec<Option<cudarc::driver::CudaGraph>>,
38    pub seg_t: usize,
39}
40
41// Split prime ranges cannot enter the full-range segment-graph arm, and every slab access
42// is serialized by its device mutex after binding that device's CUDA context on the thread.
43unsafe impl Send for PrimeSlabs {}
44
45/// Shared-expert gate+up at t==1: NVFP4 fused2 (the ornith15/qwen35moe NVFP4 mints keep
46/// gate/up_shexp uniformly NVFP4, so the Q8-only fused2 never fired there and the pair fell
47/// to two mr2 singles + two re-quantizes of the same z — 2 of the 8 unfused launches/layer
48/// the orndecode B=1 census ranked at 17.1%), else the Q8_0 fused2 (the Q8 35B mint), else
49/// two singles. ONE helper for all three shexp dispatch sites — the MEMRA_GDN_MMA
50/// three-read-sites defect is the precedent for not inlining this thrice. Fusion law
51/// everywhere: per (tensor,row) the fused seg body is verbatim, so fused == singles
52/// bit-identically, and the shared (zq, zd) is the same quantize each single recomputes.
53fn shexp_gate_up_t1(
54    e: &Engine,
55    gate_shexp: &crate::model::GpuTensor,
56    up_shexp: &crate::model::GpuTensor,
57    z: &CudaSlice<f32>,
58    zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
59) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
60    let is_nvfp4 = |w: &crate::model::GpuTensor| matches!(w, crate::model::GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4);
61    if is_nvfp4(gate_shexp) && is_nvfp4(up_shexp) {
62        // Reuse the caller's t==1 z-quantize when one exists (the zq8 seam the dev arm
63        // already consumes) — the helper's own quantize is the identical kernel on the
64        // identical input, so this drops one launch per MoE layer without moving a byte.
65        let pair = match zq8 {
66            Some((zq, zd)) => e.matmul_nvfp4_fused2(gate_shexp, up_shexp, zq, zd, 1)?,
67            None => {
68                let (zq, zd) = e.quantize_q8_1(z, 1, gate_shexp.in_features())?;
69                e.matmul_nvfp4_fused2(gate_shexp, up_shexp, &zq, &zd, 1)?
70            }
71        };
72        if let Some(pair) = pair {
73            return Ok(pair);
74        }
75    }
76    match e.matmul_q8_fused2_x(gate_shexp, up_shexp, z)? {
77        Some(pair) => Ok(pair),
78        None => Ok((e.matmul(gate_shexp, z, 1)?, e.matmul(up_shexp, z, 1)?)),
79    }
80}
81
82fn active_matrix_values(
83    available: usize,
84    rows: usize,
85    columns: usize,
86    label: &str,
87) -> Result<usize, String> {
88    let required = rows
89        .checked_mul(columns)
90        .ok_or_else(|| format!("{label} shape overflows: {rows}x{columns}"))?;
91    if available < required {
92        return Err(format!(
93            "{label} has {available} values, fewer than the active {rows}x{columns} ({required})"
94        ));
95    }
96    Ok(required)
97}
98
99fn step_grouped_decode_shape(prefill: bool, tokens: usize) -> bool {
100    !prefill && tokens == 1
101}
102
103fn parse_step_ep_grouped_prefill(value: Option<&str>) -> Result<bool, String> {
104    match value {
105        None | Some("") | Some("0") => Ok(false),
106        Some("1") => Ok(true),
107        Some(value) => Err(format!(
108            "MEMRA_STEP_EP_GROUPED_PREFILL={value:?} is invalid; expected 0 or 1"
109        )),
110    }
111}
112
113fn step_ep_grouped_prefill_enabled() -> Result<bool, String> {
114    parse_step_ep_grouped_prefill(
115        std::env::var("MEMRA_STEP_EP_GROUPED_PREFILL")
116            .ok()
117            .as_deref(),
118    )
119}
120
121fn step_grouped_prefill_shape(enabled: bool, prefill: bool, tokens: usize) -> bool {
122    enabled && prefill && (PRIME_MIN_T..=crate::cache::PRIME_CHUNK_MAX_TOKENS).contains(&tokens)
123}
124
125fn parse_step_tp_prefill(value: Option<&str>) -> Result<bool, String> {
126    match value {
127        None | Some("") | Some("0") => Ok(false),
128        Some("1") => Ok(true),
129        Some(value) => Err(format!(
130            "MEMRA_STEP_TP_PREFILL={value:?} is invalid; expected 0 or 1"
131        )),
132    }
133}
134
135fn step_tp_prefill_enabled() -> Result<bool, String> {
136    parse_step_tp_prefill(std::env::var("MEMRA_STEP_TP_PREFILL").ok().as_deref())
137}
138
139fn validate_step_prime_batch_modes(tp_prefill: bool, grouped_prefill: bool) -> Result<(), String> {
140    if grouped_prefill && !tp_prefill {
141        return Err("MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into());
142    }
143    if tp_prefill {
144        return Err(
145            "Step TP4 cross-request prime batching did not clear the live-server performance \
146             gate; use per-session grouped prefill"
147                .into(),
148        );
149    }
150    Ok(())
151}
152
153fn step_tp_prefill_shape(
154    enabled: bool,
155    tokens: usize,
156    ranks: usize,
157    native_p2p: bool,
158    has_rank_local_attention: bool,
159    fp8_kv: bool,
160) -> bool {
161    // TP2 admitted 2026-08-25 behind the same off-by-default door. The prefill body is
162    // rank-count-generic (every geometry check divides by `ranks`); only this shape ever
163    // named 4. TP4's high-context NO-GO was a TRANSPORT verdict — token-row column
164    // gathers plus remote O blocks issue ~61,440 peer copies per 4K attention layer on
165    // that placement — which is not evidence about a 2-card native-P2P placement that
166    // reduces O rank-locally. TP2 is UNQUALIFIED until its own prefill argmax + TTFT
167    // receipts land; the door stays off by default.
168    enabled
169        && tokens >= PRIME_MIN_T
170        && matches!(ranks, 2 | 4)
171        && native_p2p
172        && has_rank_local_attention
173        && !fp8_kv
174}
175
176fn empty_cache_layers<T>(n: usize) -> Vec<Option<T>> {
177    std::iter::repeat_with(|| None).take(n).collect()
178}
179
180/// Temporarily move a PP-2 cache's layer state into two independently-owned cache shells.
181/// The stage walkers then receive disjoint `&mut Cache` values and can run on separate host
182/// threads without aliasing. GPU buffers are moved, not copied; Drop restores every layer
183/// and publishes the last position completed by both stages.
184struct PrimeCacheStages<'a> {
185    parent: &'a mut Cache,
186    cut: usize,
187    stage0: Cache,
188    stage1: Cache,
189}
190
191impl<'a> PrimeCacheStages<'a> {
192    fn new(parent: &'a mut Cache, cut: usize) -> Self {
193        let n = parent.kv.len();
194        assert_eq!(parent.recur.len(), n, "cache layer vectors disagree");
195        assert!(cut <= n, "PP-2 cache cut {cut} exceeds {n} layers");
196        let mut kv0 = empty_cache_layers(n);
197        let mut kv1 = empty_cache_layers(n);
198        let mut tp_kv0 = empty_cache_layers(n);
199        let mut tp_kv1 = empty_cache_layers(n);
200        let mut recur0 = empty_cache_layers(n);
201        let mut recur1 = empty_cache_layers(n);
202        for i in 0..cut {
203            kv0[i] = parent.kv[i].take();
204            tp_kv0[i] = parent.tp_kv[i].take();
205            recur0[i] = parent.recur[i].take();
206        }
207        for i in cut..n {
208            kv1[i] = parent.kv[i].take();
209            tp_kv1[i] = parent.tp_kv[i].take();
210            recur1[i] = parent.recur[i].take();
211        }
212        let pos = parent.pos;
213        let max_ctx = parent.max_ctx;
214        Self {
215            parent,
216            cut,
217            stage0: Cache {
218                kv: kv0,
219                tp_kv: tp_kv0,
220                recur: recur0,
221                pos,
222                max_ctx,
223                last_logits_dev: None,
224                dflash_taps: None,
225            },
226            stage1: Cache {
227                kv: kv1,
228                tp_kv: tp_kv1,
229                recur: recur1,
230                pos,
231                max_ctx,
232                last_logits_dev: None,
233                dflash_taps: None,
234            },
235        }
236    }
237
238    fn parts(&mut self) -> (&mut Cache, &mut Cache) {
239        (&mut self.stage0, &mut self.stage1)
240    }
241}
242
243impl Drop for PrimeCacheStages<'_> {
244    fn drop(&mut self) {
245        let n = self.parent.kv.len();
246        for i in 0..n {
247            let source = if i < self.cut {
248                &mut self.stage0
249            } else {
250                &mut self.stage1
251            };
252            debug_assert!(self.parent.kv[i].is_none());
253            debug_assert!(self.parent.tp_kv[i].is_none());
254            debug_assert!(self.parent.recur[i].is_none());
255            self.parent.kv[i] = source.kv[i].take();
256            self.parent.tp_kv[i] = source.tp_kv[i].take();
257            self.parent.recur[i] = source.recur[i].take();
258        }
259        self.parent.pos = self.stage0.pos.min(self.stage1.pos);
260    }
261}
262
263/// task #18 (attn side): one sequence's pre-attention outputs (post-rope q/k, v, out-gate).
264pub(crate) struct AttnPre {
265    pub q: cudarc::driver::CudaSlice<f32>,
266    pub k: cudarc::driver::CudaSlice<f32>,
267    pub v: cudarc::driver::CudaSlice<f32>,
268    pub gate: Option<cudarc::driver::CudaSlice<f32>>,
269}
270
271/// task #18: one sequence's GDN prep outputs (the scan inputs).
272pub(crate) struct GdnPrep {
273    pub hk: usize,
274    pub q_l2: cudarc::driver::CudaSlice<f32>,
275    pub k_l2: cudarc::driver::CudaSlice<f32>,
276    pub v_g: cudarc::driver::CudaSlice<f32>,
277    pub beta: cudarc::driver::CudaSlice<f32>,
278    pub g_log: cudarc::driver::CudaSlice<f32>,
279    pub kb16: Option<cudarc::driver::CudaSlice<u8>>,
280    pub qb16: Option<cudarc::driver::CudaSlice<u8>>,
281}
282
283/// Device scratch for the burst verify stream (see `verify_stream_scratch`).
284pub(crate) struct VerifyStreamScratch {
285    pub pos_d: CudaSlice<i32>,
286    pub row_ctrs: Vec<CudaSlice<i32>>,
287}
288use crate::hybrid::{FullAttnLayer, HybridModel, LinearAttnLayer, Mixer, MoeWeights};
289
290struct MoeInputTraceWriter {
291    dir: std::path::PathBuf,
292    index: std::fs::File,
293    payloads: std::collections::HashMap<u16, (std::fs::File, u64)>,
294}
295
296static MOE_INPUT_TRACE_WRITER: std::sync::OnceLock<std::sync::Mutex<Option<MoeInputTraceWriter>>> =
297    std::sync::OnceLock::new();
298
299/// STAGE-2 GROUPED DECODE gate (MEMRA_MOE_GDEC, default ON; `=0` restores the sequential
300/// per-expert launch chain). See `moe_gdec_token`.
301fn gdec_enabled() -> bool {
302    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
303    *E.get_or_init(|| {
304        std::env::var("MEMRA_MOE_GDEC")
305            .map(|v| v != "0")
306            .unwrap_or(true)
307    })
308}
309
310/// SLAB-LOCAL RESIDENT ARM gate (lane/pp-leverb 2026-08-08, MEMRA_MOE_SLAB, default ON;
311/// `=0` restores the SLRU dispatch even when resident slabs exist). Read PER CALL, never
312/// memoized — probes A/B the two provenances in one process (the MEMRA_PRIME_PP pattern).
313/// See `moe_ffn_sequential_zq8`'s slab_local arm: the sigmoid-router archs (step35/M3/Hy3)
314/// are denied every `dev_exps` consumer (pairs/dev route softmax), so before this arm the
315/// fits-VRAM resident slabs were UPLOADED for them but never READ — the SLRU kept staging
316/// the same bytes beside a dead copy (37 GB H2D per pp4096 prime on the Step SKU, anatomy
317/// receipt). The arm reads the SAME bytes through the SAME kernels; only the pointer
318/// PROVENANCE changes (slab base + ex*stride vs SLRU slot address) — the bit-identity class
319/// `moe_ffn_dev`'s resident arm already documents against its SLRU arm.
320fn moe_slab_enabled() -> bool {
321    std::env::var("MEMRA_MOE_SLAB").as_deref() != Ok("0")
322}
323
324/// Expert-grouped dispatch remains opt-in after the local 5090 transfer gate rejected the
325/// default flip. `=0` selects the established path, while any other explicit value enables the
326/// grouped research arm for the current call.
327fn moe_grouped_enabled(_cfg: &ModelConfig, _prefill: bool) -> bool {
328    std::env::var("MEMRA_MOE_GROUPED")
329        .map(|value| value != "0")
330        .unwrap_or(false)
331}
332
333/// Deterministic in-token expert prefetch. `MEMRA_MOE_PREFETCH=1` overlaps memory-source H2D on the
334/// copy stream; selecting the opt-in worker spill backend enables the same known-next hook for disk.
335fn moe_prefetch_enabled() -> bool {
336    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
337    *E.get_or_init(|| {
338        std::env::var("MEMRA_MOE_PREFETCH").as_deref() == Ok("1")
339            || crate::spill_pread::worker_enabled()
340    })
341}
342
343/// Best-effort OS page-cache prefetch distance for mmap-backed expert ranges. Independent of the
344/// H2D copy-stream experiment so storage->RAM and RAM->HBM overlap can be measured separately.
345/// The opt-in default stays one expert to preserve the original experiment; spill rigs can widen
346/// it with `MEMRA_MOE_PAGE_PREFETCH_WINDOW` to cover NVMe latency.
347fn moe_page_prefetch_window() -> usize {
348    static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
349    *W.get_or_init(|| {
350        page_prefetch_window_from_values(
351            std::env::var("MEMRA_MOE_PAGE_PREFETCH").as_deref() == Ok("1"),
352            std::env::var("MEMRA_MOE_PAGE_PREFETCH_WINDOW")
353                .ok()
354                .as_deref(),
355        )
356    })
357}
358
359fn page_prefetch_window_from_values(enabled: bool, raw_window: Option<&str>) -> usize {
360    if !enabled {
361        return 0;
362    }
363    raw_window.and_then(|value| value.parse().ok()).unwrap_or(1)
364}
365
366/// Return only the newly exposed positions in a rolling lookahead window. Position zero seeds the
367/// full window; each later position adds one expert at the far edge. Thus widening the window does
368/// not repeatedly issue `MADV_WILLNEED` for the same range.
369fn page_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
370    if window == 0 || position >= len {
371        return len..len;
372    }
373    let (start, count) = if position == 0 {
374        (1, window)
375    } else {
376        (position.saturating_add(window), 1)
377    };
378    let start = start.min(len);
379    start..start.saturating_add(count).min(len)
380}
381
382/// Grouped worker-I/O schedule: prime the first active expert before the loop, then queue exactly
383/// one known-next expert at each iteration. Returning positions keeps expert ordering authoritative.
384fn grouped_worker_prefetch_position(order_len: usize, current: Option<usize>) -> Option<usize> {
385    let position = current.map_or(0, |position| position.saturating_add(1));
386    (position < order_len).then_some(position)
387}
388
389/// Fill the worker ring with complete experts, retaining one pinned buffer for an unexpected
390/// demand miss. Each expert has gate/up/down extents, so depth 16 admits a rolling five-expert
391/// window. Position zero primes the current expert too: its three independent reads can run in
392/// parallel instead of demand-serializing gate, up, and down before any useful GPU work exists.
393fn worker_prefetch_window() -> usize {
394    static WINDOW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
395    *WINDOW.get_or_init(|| {
396        let automatic = crate::spill_pread::configured_depth().saturating_sub(1) / 3;
397        std::env::var("MEMRA_SPILL_WORKER_EXPERT_WINDOW")
398            .ok()
399            .and_then(|value| value.parse::<usize>().ok())
400            .unwrap_or(automatic.max(1))
401    })
402}
403
404/// Return only positions newly exposed by a rolling worker-I/O window. Unlike mmap page advice,
405/// this includes the current expert when the window is seeded so all three current projections
406/// enter the CPU pool together.
407fn worker_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
408    if window == 0 || position >= len {
409        return len..len;
410    }
411    let (start, count) = if position == 0 {
412        (0, window)
413    } else {
414        (position.saturating_add(window).saturating_sub(1), 1)
415    };
416    let start = start.min(len);
417    start..start.saturating_add(count).min(len)
418}
419
420/// LAUNCH-STRUCTURE STAGE 3 gate (MEMRA_MOE_DEV, default ON; `=0` restores host routing). The
421/// zero-DtoH device-dispatch path for fully-resident layers: router top-k output stays on device,
422/// expert weight pointers come from the per-layer device table. Requires the fused router (the
423/// dev path consumes the device sel/w directly), so MEMRA_FUSED_ROUTER=0 also disables it.
424fn moe_dev_enabled() -> bool {
425    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
426    *E.get_or_init(|| {
427        std::env::var("MEMRA_MOE_DEV")
428            .map(|v| v != "0")
429            .unwrap_or(true)
430            && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0"))
431    })
432}
433
434/// Device sigmoid top-k is the default for Step-3.7 / M3 / Hy3 / GLM-DSA. `MEMRA_SIG_ROUTER=0` restores
435/// the full-logit DtoH plus `moe_route_sigmoid_host` oracle without changing expert dispatch.
436fn sigmoid_router_enabled() -> bool {
437    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
438    *E.get_or_init(|| {
439        std::env::var("MEMRA_SIG_ROUTER")
440            .map(|v| v != "0")
441            .unwrap_or(true)
442    })
443}
444
445/// MoE EXPERT dp4a gate (MEMRA_MOE_Q8, default ON; `=0` restores the Stage-A f32-dequant expert
446/// kernels). Applies when gate/up/down expert qtypes are all in the dp4a body set (IQ3_S/IQ4_XS).
447/// FP-order differs from Stage-A (int dp4a + warp tree) — argmax/run-gen/stream-identity gates
448/// arbitrate; the sequential and fused q8 paths ship as a matched pair (MEMRA_MOE_GATE contract).
449fn moe_q8_enabled() -> bool {
450    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
451    *E.get_or_init(|| {
452        std::env::var("MEMRA_MOE_Q8")
453            .map(|v| v != "0")
454            .unwrap_or(true)
455    })
456}
457
458/// gemma4 fast-arm gate: qtypes with an `expert_dot_g` dp4a body (superset used by the gelu
459/// dev arm; the qwen q8 arms keep their own battery-gated q8_expert_supported policy).
460fn expert_dp4a_supported(qt: i32) -> bool {
461    qt == crate::QT_Q4_0
462        || qt == crate::QT_IQ3_S
463        || qt == crate::QT_IQ4_XS
464        || qt == crate::QT_Q3_K
465        || qt == crate::QT_Q4_K
466        || qt == crate::QT_Q6_K
467}
468
469fn q8_expert_supported(qt: i32) -> bool {
470    // k-quant arms added 2026-07-06 (Q3_K/Q4_K/Q6_K bodies for the UD tail layers). Briefly
471    // default-excluded the same day when they appeared to break 35B real-prompt spec — the
472    // ACTUAL culprit was the MoE router's cuBLASLt n-dependence (d994271); with the router
473    // decode-exact at verify t, the k-quant arms pass the full spec battery (p1/p2/p3 + raw
474    // K=1..8) and are DEFAULT ON again (+9 tok/s: 148.9 -> 157.9). MEMRA_MOE_Q8_KQ=0 excludes.
475    static KQ: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
476    let kq = *KQ.get_or_init(|| {
477        std::env::var("MEMRA_MOE_Q8_KQ")
478            .map(|v| v != "0")
479            .unwrap_or(true)
480    });
481    // NVFP4 experts: DEFAULT ON (2026-07-17). The M3-era "decode-vs-verify MISMATCH 3.4e1"
482    // that had this excluded was the missing per-expert macro-scale fold, fixed in the
483    // dev-kernel epilogues + moe_w_scale_by_expert; the 35B ct-NVFP4 artifact now runs the
484    // q8 arm at parity with the IQ4_XS daily (174-178 tok/s, spec K=1..8 exact). M3/Hy3
485    // never reach the q8 arms regardless (sigmoid-router cfg gates on pairs/dev/gdec).
486    // MEMRA_MOE_Q8_NVFP4=0 restores the f32 arm.
487    let nvfp4_q8 = std::env::var("MEMRA_MOE_Q8_NVFP4")
488        .map(|v| v != "0")
489        .unwrap_or(true);
490    qt == crate::QT_IQ3_S
491        || qt == crate::QT_IQ4_XS
492        || (nvfp4_q8 && qt == crate::QT_NVFP4)
493        || (kq && (qt == crate::QT_Q3_K || qt == crate::QT_Q4_K || qt == crate::QT_Q6_K))
494}
495
496/// The decode-once (_dec) and IQ-MMA expert kernels dequant via IQ-specific extractors —
497/// k-quant tensors must fall to the _em dot path instead.
498fn q8_expert_dec_supported(qt: i32) -> bool {
499    qt == crate::QT_IQ3_S || qt == crate::QT_IQ4_XS || qt == crate::QT_Q4_0
500}
501
502/// Grouped-f16 door (MEMRA_MOE_F16G) per-projection admission: the qtype has a dequant-to-f16
503/// kernel in cu/moe_f16_grouped.cu AND the projection's k dimension tiles its block size.
504/// Round 49 widened coverage to q35's UD mix (gate/up IQ3_S x39 + Q3_K x1 + IQ4_XS x1; down
505/// IQ4_XS x37 + Q6_K x3 + Q4_K x1) — the round-47 IQ4_XS/Q4_0-only table admitted ~1 of 41
506/// q35 layers, which is why that cell measured FLAT.
507fn f16g_proj_ok(qt: i32, in_f: usize) -> bool {
508    match qt {
509        crate::QT_Q4_0 => in_f % 32 == 0,
510        crate::QT_IQ4_XS | crate::QT_IQ3_S | crate::QT_Q3_K | crate::QT_Q4_K | crate::QT_Q6_K => {
511            in_f % 256 == 0
512        }
513        // NVFP4 (block 64) added lane/moebatch-q35moe 2026-08-21: the ornith15 expert bank is
514        // uniform NVFP4, which passed the pairs q8 gate but missed BOTH batched doors
515        // (use_mma's dec set and this table), so 14.7k-token prefill rode the per-pair _em
516        // fallback — 88.6% of the prime wall (prime-anatomy receipt).
517        crate::QT_NVFP4 => in_f % 64 == 0,
518        _ => false,
519    }
520}
521
522/// STAGE 3 prewarm gate (MEMRA_MOE_PREWARM, default ON; `=0` leaves residency organic). One-shot
523/// per layer: force-admit every block while FREE slots cover the whole layer (never evicts).
524fn moe_prewarm_enabled() -> bool {
525    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
526    *E.get_or_init(|| {
527        std::env::var("MEMRA_MOE_PREWARM")
528            .map(|v| v != "0")
529            .unwrap_or(true)
530    })
531}
532
533/// During a discarded fixed-residency profile, admit CPU-routed misses after their current-token
534/// CPU result is complete. The current result and numeric path are unchanged; later warmup tokens
535/// can then vote for and exercise those experts on GPU before the cache is frozen.
536fn cpu_expert_profile_admit_enabled() -> bool {
537    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
538    *E.get_or_init(|| std::env::var("MEMRA_CPU_EXPERT_FREEZE_PROFILE_ADMIT").as_deref() == Ok("1"))
539}
540
541/// Minimum prompt length for the BATCHED cache prime (`prime_cache`). Below this the tokenwise
542/// decode loop wins anyway (the batched path's GEMM dispatch needs m>=16, and the stateful conv
543/// kernel needs T >= d_conv-1). Callers: generate / generate_spec.
544pub const PRIME_MIN_T: usize = 16;
545
546/// Widest tick the MoE DEV per-token program serves (lane/orndecode-20260822). PRIME_MIN_T
547/// doubled as the dev-arm's upper bound on the assumption that t==16 only ever meant real
548/// prefill; the exact-16 decode tier broke that assumption — at B=16 the MoE stage crossed
549/// onto the t>=MMA_T grouped/kq GEMM program (m_e ~1.6 rows/expert: 52.6% of the tick at
550/// ~104 us/launch) or the `_em` per-pair fallback (67.7 us), both catastrophically slower
551/// than the dev q8 kernels that serve B<=8 (8.8 us gate_up covering a token's whole expert
552/// set). Decode widths 2..=16 now ride dev; the grouped/pairs prefill programs start at 17.
553/// gate2/gate3 byte batteries at B=12/16 are the qualification (bit-checked vs isolated).
554const MOE_DEV_MAX_T: usize = 16;
555const PRIME_PIPE_MICROBATCHES: usize = 8;
556const PRIME_PIPE_MIN_CHUNK: usize = 128;
557const PRIME_PIPE_EDGE_MIN_CHUNK: usize = 64;
558const PRIME_PIPE_LINEAR_WORK: usize = 8;
559
560fn prime_pp2_auto_geometry(n_layers: usize) -> bool {
561    crate::pp::prime_pp_on()
562        && !crate::pp::pp2_streams_off()
563        && crate::pp::pp_cuts(n_layers).is_some_and(|cuts| cuts.len() == 3)
564}
565
566/// Effective internal prime chunk. An explicit MEMRA_PRIME_CHUNK is authoritative.
567/// Naked PP-2 primes use the measured pipeline geometry: up to eight microchunks, never
568/// below 128 tokens, while the legacy 4096-token cap remains the long-context bound.
569pub fn prime_chunk_tokens(t: usize, n_layers: usize) -> usize {
570    if let Ok(value) = std::env::var("MEMRA_PRIME_CHUNK") {
571        let parsed = value
572            .parse::<usize>()
573            .unwrap_or(crate::cache::PRIME_CHUNK_MAX_TOKENS);
574        return if crate::cache::swa_ring_on() {
575            if parsed == 0 {
576                crate::cache::PRIME_CHUNK_MAX_TOKENS
577            } else {
578                parsed.min(crate::cache::PRIME_CHUNK_MAX_TOKENS)
579            }
580        } else {
581            parsed
582        };
583    }
584    let chunk = crate::cache::PRIME_CHUNK_MAX_TOKENS;
585    if prime_pp2_auto_geometry(n_layers) && t >= 2 * PRIME_PIPE_MIN_CHUNK {
586        chunk.min(
587            t.div_ceil(PRIME_PIPE_MICROBATCHES)
588                .max(PRIME_PIPE_MIN_CHUNK),
589        )
590    } else {
591        chunk
592    }
593}
594
595fn fixed_prime_chunk_ranges(t: usize, chunk: usize) -> Vec<(usize, usize)> {
596    fixed_prime_chunk_ranges_for_ring(t, chunk, crate::cache::swa_ring_on())
597}
598
599fn fixed_prime_chunk_ranges_for_ring(t: usize, chunk: usize, ring_on: bool) -> Vec<(usize, usize)> {
600    if chunk == 0 || t <= chunk {
601        return vec![(0, t)];
602    }
603    let mut ranges = Vec::with_capacity(t.div_ceil(chunk));
604    let mut start = 0usize;
605    while start < t {
606        let mut end = (start + chunk).min(t);
607        if t - end > 0 && t - end < PRIME_MIN_T {
608            if ring_on {
609                let shifted = t - PRIME_MIN_T;
610                end = if shifted > start { shifted } else { t };
611            } else {
612                end = t;
613            }
614        }
615        ranges.push((start, end));
616        start = end;
617    }
618    ranges
619}
620
621fn prime_chunk_work(prefix: usize, total: usize) -> u128 {
622    let prefix = prefix as u128;
623    prefix * (prefix + (PRIME_PIPE_LINEAR_WORK as u128) * (total as u128))
624}
625
626fn dynamic_prime_chunk_ranges(
627    t: usize,
628    fixed_chunk: usize,
629    fixed: &[(usize, usize)],
630) -> Vec<(usize, usize)> {
631    let n = fixed.len();
632    if n < 3 {
633        return fixed.to_vec();
634    }
635
636    let max_first = t - (n - 1) * PRIME_MIN_T;
637    let first = fixed_chunk
638        .div_ceil(2)
639        .max(PRIME_PIPE_EDGE_MIN_CHUNK)
640        .min(max_first);
641    let mut ranges = Vec::with_capacity(n);
642    ranges.push((0, first));
643
644    let first_work = prime_chunk_work(first, t);
645    let work_span = prime_chunk_work(t, t) - first_work;
646    let denominator = (n - 1) as u128;
647    let mut previous = first;
648    for boundary in 1..n - 1 {
649        let target = first_work * denominator + work_span * (boundary as u128);
650        let remaining = n - 1 - boundary;
651        let mut low = previous + PRIME_MIN_T;
652        let mut high = t - remaining * PRIME_MIN_T;
653        while low < high {
654            let mid = low + (high - low) / 2;
655            if prime_chunk_work(mid, t) * denominator >= target {
656                high = mid;
657            } else {
658                low = mid + 1;
659            }
660        }
661        ranges.push((previous, low));
662        previous = low;
663    }
664    ranges.push((previous, t));
665    ranges
666}
667
668/// Internal prime ranges. The naked PP-2 pipeline defaults to a short-fill,
669/// equal-modeled-time schedule; MEMRA_PRIME_CHUNK_SCHED=fixed restores the measured
670/// equal-token ranges. An explicit MEMRA_PRIME_CHUNK always retains fixed semantics.
671///
672/// `gdn_grid`: the model runs the chunked GDN WY scan (`HybridModel::gdn_prime_grid_on`) —
673/// AUTO-scheduled internal boundaries are then snapped down to the WY-chunk grid
674/// (`align_prime_ranges_to_gdn`; the spec-longctx grid law, extended from serve splits to
675/// the PP prime microchunks). Explicit MEMRA_PRIME_CHUNK keeps its operator-authoritative
676/// (fixed, unaligned) semantics — the FLAGS caveat documents that identity contract.
677pub fn prime_chunk_ranges(t: usize, n_layers: usize, gdn_grid: bool) -> Vec<(usize, usize)> {
678    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
679    let chunk = prime_chunk_tokens(t, n_layers);
680    let fixed = fixed_prime_chunk_ranges(t, chunk);
681    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
682        Ok(value) => value == "dynamic",
683        Err(_) => true,
684    };
685    if explicit_chunk {
686        return fixed;
687    }
688    let ranges = if !dynamic || !prime_pp2_auto_geometry(n_layers) {
689        fixed
690    } else {
691        dynamic_prime_chunk_ranges(t, chunk, &fixed)
692    };
693    // MEMRA_PRIME_GRID_ALIGN=0 is the shared rollback seam of the grid law (same env the
694    // worker's serve-boundary alignment honors, read per call so gates can flip it
695    // in-process): the legacy off-grid auto schedule — the toothed cell's broken arm.
696    if gdn_grid && std::env::var("MEMRA_PRIME_GRID_ALIGN").as_deref() != Ok("0") {
697        align_prime_ranges_to_gdn(&ranges, t, Engine::gdn_chunk_size())
698    } else {
699        ranges
700    }
701}
702
703/// Snap AUTO prime-range internal boundaries DOWN to the GDN WY-chunk grid (lane/
704/// hermes-perf-fixes, 2026-08-23 — the missing helper the PP-auto-ranges finding names).
705///
706/// THE LAW THIS EXTENDS (measured, research/multiturn-cache-20260821/
707/// LONGCTX-EXACTNESS-20260821.md; the serve-split half already ships as the worker's
708/// `grid_align_boundary`): under the chunked WY scan a prompt primed as two calls split at
709/// L is bit-identical to the monolithic prime iff `L % gdn_chunk_size() == 0` — an off-grid
710/// call start shifts the fold grid and materializes recurrent state at a point the
711/// monolithic program never computes. The prime loop walks these ranges as separate
712/// `prime_layers` calls, so INTERNAL microchunk boundaries are the same seam: the PP-2
713/// auto geometry (`t.div_ceil(8).max(128)` fills, and every dynamic short-fill boundary)
714/// lands off the 32-token grid for most prompt lengths, which is exactly the
715/// chunk-value bit-identity the GDN lane falsified (FLAGS PRIME_CHUNK/SCHED caveat).
716///
717/// Boundaries only move DOWN (earlier is always semantically safe — same argument as the
718/// worker's alignment); a boundary that collapses onto its predecessor is dropped (ranges
719/// merge). The final range always ends at `t`. Aligning down only GROWS the tail
720/// remainder, so the fixed-schedule tail-merge rule is never re-violated. Cost bound: at
721/// most `c-1` tokens shift per boundary.
722pub fn align_prime_ranges_to_gdn(
723    ranges: &[(usize, usize)],
724    t: usize,
725    c: usize,
726) -> Vec<(usize, usize)> {
727    if c == 0 || ranges.len() < 2 {
728        return ranges.to_vec();
729    }
730    let mut out: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
731    let mut start = 0usize;
732    for (i, &(_, end)) in ranges.iter().enumerate() {
733        let e = if i + 1 == ranges.len() {
734            t
735        } else {
736            end / c * c
737        };
738        if e > start {
739            out.push((start, e));
740            start = e;
741        } // else: boundary collapsed onto its predecessor — merge into the next range
742    }
743    debug_assert_eq!(out.last().map(|&(_, e)| e), Some(t));
744    out
745}
746
747struct HeadSplit {
748    pin: u64,
749    w1: CudaSlice<u8>,
750    hn1: CudaSlice<f32>,
751    y1: CudaSlice<f32>,
752    logits_e: CudaSlice<f32>,
753    ev_hn: cudarc::driver::CudaEvent,
754    ev_done: cudarc::driver::CudaEvent,
755    raw_hn1: u64,
756    raw_y1: u64,
757    raw_logits_hi: u64,
758    /// SAMPLED-TAIL scratch (perturbed row + the filter's threshold/z/max slots + the row
759    /// index). Allocating these per token cost more than the split head saved: the first
760    /// sampled-split measurement came in at 78.25 tok/s against 78.96 for the unsplit head,
761    /// which is five allocations per token, not arithmetic.
762    samp: Option<SampScratch>,
763}
764
765struct SampScratch {
766    pb: CudaSlice<f32>,
767    th: CudaSlice<f32>,
768    z: CudaSlice<f32>,
769    mx: CudaSlice<f32>,
770    rows: CudaSlice<i32>,
771}
772/// HEAD-SPLIT workspace (host + device twins share it).
773static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
774
775/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
776/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
777/// input bits — rank1's local selection is bit-equal to the root's.
778#[allow(clippy::type_complexity)]
779static DEV1_ROUTER_REPS: std::sync::Mutex<
780    Option<(
781        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
782        Option<CudaSlice<f32>>,
783    )>,
784> = std::sync::Mutex::new(None);
785
786/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
787/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
788/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
789#[allow(clippy::type_complexity)]
790static SHEXP_D1_REPS: std::sync::Mutex<
791    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
792> = std::sync::Mutex::new(None);
793#[allow(clippy::type_complexity)]
794static SHEXP_D1_WS: std::sync::Mutex<
795    Option<(
796        (usize, usize),
797        CudaSlice<f32>,
798        CudaSlice<f32>,
799        CudaSlice<f32>,
800        cudarc::driver::CudaEvent,
801        cudarc::driver::CudaEvent,
802    )>,
803> = std::sync::Mutex::new(None);
804
805/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
806static SHEXP_OV_WS: std::sync::Mutex<
807    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
808> = std::sync::Mutex::new(None);
809
810impl HybridModel {
811    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
812    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
813    /// an off-grid prime-call boundary shifts the WY fold grid (see
814    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
815    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
816    pub fn gdn_prime_grid_on(&self) -> bool {
817        Engine::gdn_chunked_enabled()
818            && self
819                .layers
820                .iter()
821                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
822    }
823
824    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
825    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
826    /// (the device buffers must be addressable on both sides — the TP registry builds its
827    /// own Engine per rank, so this is a real seam, not a formality).
828    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
829        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
830    }
831
832    fn step35_tp_qkv(
833        &self,
834        e: &Engine,
835        fa: &FullAttnLayer,
836        h: &CudaSlice<f32>,
837        t: usize,
838    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
839        let Some(tp) = fa.step_tp_qkv.as_ref() else {
840            return Ok(None);
841        };
842        let values = active_matrix_values(
843            h.len(),
844            t,
845            self.cfg.n_embd as usize,
846            "Step TP QKV activation",
847        )?;
848        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
849        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
850        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
851        // round-trip on every execute that the peer transport exists to remove. The
852        // device twins are byte-identical by construction (the same bytes travel dtod
853        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
854        // The host arm below remains the transport for !native_p2p (host staging IS that
855        // transport) and for a root context this engine cannot address.
856        if Self::step35_tp_device_resident(e, tp) {
857            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
858            // on theirs (same context, different streams).
859            e.stream().synchronize()?;
860            let q = tp
861                .runtime
862                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
863            let k = tp
864                .runtime
865                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
866            let v = tp
867                .runtime
868                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
869            Self::step35_tp_log_once(tp, "qkv", "device-resident");
870            return Ok(Some(vec![q, k, v]));
871        }
872        let host = e.dtoh_view(&h.slice(0..values))?;
873        let q = if tp.runtime.native_p2p() {
874            tp.runtime
875                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
876        } else {
877            tp.runtime
878                .bf16_column_parallel_resident(&tp.q, &host, t)?
879                .gathered
880        };
881        let k = if tp.runtime.native_p2p() {
882            tp.runtime
883                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
884        } else {
885            tp.runtime
886                .bf16_column_parallel_resident(&tp.k, &host, t)?
887                .gathered
888        };
889        let v = if tp.runtime.native_p2p() {
890            tp.runtime
891                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
892        } else {
893            tp.runtime
894                .bf16_column_parallel_resident(&tp.v, &host, t)?
895                .gathered
896        };
897        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
898        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
899    }
900
901    /// One transport banner per (projection, transport) — the old per-call eprintln fired
902    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
903    /// unbouncing (the sibling grouped-EP path already learned this).
904    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
905        use std::sync::atomic::{AtomicBool, Ordering};
906        static LOGGED: [AtomicBool; 4] = [
907            AtomicBool::new(false),
908            AtomicBool::new(false),
909            AtomicBool::new(false),
910            AtomicBool::new(false),
911        ];
912        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
913        if LOGGED[idx].swap(true, Ordering::Relaxed) {
914            return;
915        }
916        eprintln!(
917            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
918             tensor_parallel=true attention_local=true kv_local=true transport={} \
919             native_p2p={} bulk_p2p={} activation={activation} \
920             output={} performance_claim=false (logged once per transport)",
921            tp.layer,
922            tp.devices,
923            tp.runtime.transport_label(),
924            tp.runtime.native_p2p(),
925            tp.runtime.bulk_p2p(),
926            if activation == "device-resident" {
927                "root-resident"
928            } else {
929                "root-readback"
930            },
931        );
932    }
933
934    fn step35_tp_o(
935        &self,
936        e: &Engine,
937        fa: &FullAttnLayer,
938        activation: &CudaSlice<f32>,
939        tokens: usize,
940    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
941        let Some(tp) = fa.step_tp_qkv.as_ref() else {
942            return Ok(None);
943        };
944        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
945        // of the attention output, no host O staging, root-resident reduction consumed in
946        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
947        if Self::step35_tp_device_resident(e, tp) {
948            e.stream().synchronize()?; // producer fence, as the QKV half
949            let output = tp
950                .runtime
951                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
952            Self::step35_tp_log_once(tp, "o", "device-resident");
953            return Ok(Some(output));
954        }
955        let host = e.dtoh(activation)?;
956        let output = if tp.runtime.native_p2p() {
957            tp.runtime
958                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
959        } else {
960            tp.runtime
961                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
962        };
963        Self::step35_tp_log_once(tp, "o", "host-canonical");
964        Ok(Some(e.htod(&output)?))
965    }
966
967    fn step35_o(
968        &self,
969        e: &Engine,
970        fa: &FullAttnLayer,
971        activation: &CudaSlice<f32>,
972        tokens: usize,
973    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
974        match self.step35_tp_o(e, fa, activation, tokens)? {
975            Some(output) => Ok(output),
976            None => e.matmul(&fa.wo, activation, tokens),
977        }
978    }
979
980    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
981    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
982    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
983    /// (it forces a dtoh + host hash per layer).
984    fn prime_trace_path() -> Option<&'static str> {
985        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
986        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
987            .as_deref()
988    }
989
990    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
991    /// each prime_layers stage and accumulates wall time per stage class, printed after
992    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
993    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
994    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
995    fn prime_anatomy_on() -> bool {
996        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
997        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
998    }
999
1000    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
1001        static S: [std::sync::atomic::AtomicU64; 5] = [
1002            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
1003            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
1004            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
1005            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
1006            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
1007        ];
1008        &S
1009    }
1010
1011    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
1012    pub fn forward(
1013        &self,
1014        e: &Engine,
1015        tokens: &[u32],
1016    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1017        if self.is_gemma4_e4b() {
1018            return self.gemma4_e4b_forward(e, tokens, false);
1019        }
1020        if self.uses_gemma_program() {
1021            return self.gemma4_forward(e, tokens, false);
1022        }
1023        let cfg = &self.cfg;
1024        let n_embd = cfg.n_embd as usize;
1025        let t = tokens.len();
1026        let eps = cfg.rms_eps;
1027        let pos: Vec<i32> = (0..t as i32).collect();
1028        let pos_d = e.htod_i32(&pos)?;
1029
1030        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1031
1032        for (il, layer) in self.layers.iter().enumerate() {
1033            // attn_norm
1034            let mut h = e.uninit(t * n_embd)?;
1035            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1036
1037            let mixed = match &layer.mixer {
1038                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1039                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1040                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1041            };
1042
1043            // residual 1
1044            let mut x1 = e.uninit(t * n_embd)?;
1045            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1046
1047            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1048            let mut z = e.uninit(t * n_embd)?;
1049            e.rms_norm(
1050                &x1,
1051                layer.post_attn_norm.float_data(),
1052                &mut z,
1053                n_embd,
1054                t,
1055                eps,
1056            )?;
1057            let ffn_out = match &layer.ffn {
1058                crate::hybrid::Ffn::Dense {
1059                    ffn_gate,
1060                    ffn_up,
1061                    ffn_down,
1062                } => {
1063                    let n_ff = ffn_gate.out_features();
1064                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1065                    let up = g2.pop().unwrap();
1066                    let gate = g2.pop().unwrap();
1067                    let mut act = e.uninit(t * n_ff)?;
1068                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1069                    // both the dense MLP and the shared expert, and its limit is
1070                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1071                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1072                    Self::ffn_act_lim(
1073                        e,
1074                        &self.cfg,
1075                        &gate,
1076                        &up,
1077                        1.0,
1078                        1.0,
1079                        self.cfg.clamp_shexp_at(il as u32),
1080                        &mut act,
1081                        t * n_ff,
1082                    )?;
1083                    e.matmul(ffn_down, &act, t)?
1084                }
1085                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1086            };
1087            let mut x2 = e.uninit(t * n_embd)?;
1088            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1089            x = x2;
1090        }
1091
1092        let mut hn = e.uninit(t * n_embd)?;
1093        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1094        let logits = e.matmul(&self.output, &hn, t)?;
1095        Ok(e.dtoh(&logits)?)
1096    }
1097
1098    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1099    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1100    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1101    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1102    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1103    pub fn forward_last(
1104        &self,
1105        e: &Engine,
1106        tokens: &[u32],
1107    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1108        if self.uses_gemma_program() {
1109            return self.gemma4_forward(e, tokens, true);
1110        }
1111        let cfg = &self.cfg;
1112        let n_embd = cfg.n_embd as usize;
1113        let t = tokens.len();
1114        let eps = cfg.rms_eps;
1115        let pos: Vec<i32> = (0..t as i32).collect();
1116        let pos_d = e.htod_i32(&pos)?;
1117
1118        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1119        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1120        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1121        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1122        let anat = Self::prime_anatomy_on();
1123        let mut anat_last = if anat {
1124            e.stream().synchronize()?;
1125            Some(std::time::Instant::now())
1126        } else {
1127            None
1128        };
1129        macro_rules! anat_mark {
1130            ($slot:expr) => {
1131                if let Some(ts) = anat_last.as_mut() {
1132                    e.stream().synchronize()?;
1133                    Self::prime_anatomy_slots()[$slot].fetch_add(
1134                        ts.elapsed().as_nanos() as u64,
1135                        std::sync::atomic::Ordering::Relaxed,
1136                    );
1137                    *ts = std::time::Instant::now();
1138                }
1139            };
1140        }
1141        for (il, layer) in self.layers.iter().enumerate() {
1142            let mut h = e.uninit(t * n_embd)?;
1143            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1144            if probe {
1145                e.stream().synchronize()?;
1146                eprintln!("[probe] L{il} norm ok");
1147            }
1148            anat_mark!(4);
1149            let mixed = match &layer.mixer {
1150                Mixer::Full(fa) => {
1151                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1152                    anat_mark!(0);
1153                    y
1154                }
1155                Mixer::Linear(la) => {
1156                    let y = self.linear_attn(e, la, &h, t)?;
1157                    anat_mark!(1);
1158                    y
1159                }
1160                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1161            };
1162            if probe {
1163                e.stream().synchronize()?;
1164                eprintln!("[probe] L{il} mixer ok");
1165            }
1166            let mut x1 = e.uninit(t * n_embd)?;
1167            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1168            let mut z = e.uninit(t * n_embd)?;
1169            e.rms_norm(
1170                &x1,
1171                layer.post_attn_norm.float_data(),
1172                &mut z,
1173                n_embd,
1174                t,
1175                eps,
1176            )?;
1177            anat_mark!(4);
1178            let ffn_out = match &layer.ffn {
1179                crate::hybrid::Ffn::Dense {
1180                    ffn_gate,
1181                    ffn_up,
1182                    ffn_down,
1183                } => {
1184                    let n_ff = ffn_gate.out_features();
1185                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1186                    let up = g2.pop().unwrap();
1187                    let gate = g2.pop().unwrap();
1188                    let mut act = e.uninit(t * n_ff)?;
1189                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1190                    Self::ffn_act_lim(
1191                        e,
1192                        &self.cfg,
1193                        &gate,
1194                        &up,
1195                        1.0,
1196                        1.0,
1197                        self.cfg.clamp_shexp_at(il as u32),
1198                        &mut act,
1199                        t * n_ff,
1200                    )?;
1201                    let y = e.matmul(ffn_down, &act, t)?;
1202                    anat_mark!(3);
1203                    y
1204                }
1205                crate::hybrid::Ffn::Moe(m) => {
1206                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1207                    anat_mark!(2);
1208                    y
1209                }
1210            };
1211            if probe {
1212                e.stream().synchronize()?;
1213                eprintln!("[probe] L{il} ffn ok");
1214            }
1215            let mut x2 = e.uninit(t * n_embd)?;
1216            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1217            x = x2;
1218        }
1219        if anat {
1220            let s = Self::prime_anatomy_slots();
1221            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1222            eprintln!(
1223                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1224                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1225                ms(0),
1226                ms(1),
1227                ms(2),
1228                ms(3),
1229                ms(4)
1230            );
1231        }
1232        // norm over all T, then slice the LAST row and run lm_head on that single row.
1233        let mut hn = e.uninit(t * n_embd)?;
1234        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1235        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1236        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1237        let mut hlast = e.uninit(n_embd)?;
1238        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1239        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1240        Ok(e.dtoh(&logits)?)
1241    }
1242
1243    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1244    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1245    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1246    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1247    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1248    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1249    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1250    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1251    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1252    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1253    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1254    ///   (c) `cache.pos`/KV len/len_d advance by T.
1255    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1256    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1257    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1258    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1259    ///
1260    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1261    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1262    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1263    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1264    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1265    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1266    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1267    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1268    /// differently under load — research/tick-seg-20260807, receipt in
1269    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1270    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1271    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1272    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1273    /// caller that SPLITS one request across calls passes the remainder.
1274    pub fn prime_cache(
1275        &self,
1276        e: &Engine,
1277        tokens: &[u32],
1278        cache: &mut Cache,
1279        queued_after: usize,
1280    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1281        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1282    }
1283
1284    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1285    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1286    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1287    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1288    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1289    pub fn prime_cache_overlaid(
1290        &self,
1291        e: &Engine,
1292        tokens: &[u32],
1293        cache: &mut Cache,
1294        queued_after: usize,
1295        overlay: Option<&crate::vision::EmbedOverlay>,
1296    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1297        let n_embd = self.cfg.n_embd as usize;
1298        let t = tokens.len();
1299        // MEMRA_PRIME_TROWS=1: prefill through the same-session t-row walk (per-row t=1
1300        // program = the tokenwise-prime ORACLE class) — replaces the host-canonical
1301        // per-token step-TP prime. Text-only fresh primes; anything else falls through.
1302        if overlay.is_none() {
1303            if let Some(out) = self.step35_prime_trows(e, tokens, cache)? {
1304                return Ok(out);
1305            }
1306        }
1307        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1308        // session cache — every chunk (including the first) takes the continuation arm
1309        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1310        assert!(
1311            t >= PRIME_MIN_T,
1312            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1313        );
1314        assert!(
1315            cache.pos + t <= cache.max_ctx,
1316            "prime_cache: prompt exceeds cache max_ctx"
1317        );
1318
1319        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1320        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1321        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1322        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1323        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1324        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1325        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1326        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1327        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1328        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1329        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1330        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1331            if self.is_gemma4_e4b() {
1332                if overlay.is_some() {
1333                    return Err(
1334                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1335                    );
1336                }
1337                return self.gemma4_e4b_prime(e, tokens, cache);
1338            }
1339            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1340            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1341            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1342            // spans become bidirectional attention islands (lane/gemma-vision).
1343            return self.gemma4_prime(e, tokens, cache, overlay);
1344        }
1345        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1346        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1347        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1348        // the prefill's ARITHMETIC, so two rigs with different values produced different
1349        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1350        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1351        // (VERDICT.md) — and it is NOT what docs originally said:
1352        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1353        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1354        //     output head), so growing a chunk cannot move an existing row's value.
1355        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1356        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1357        //     not describe our leak.
1358        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1359        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1360        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1361        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1362        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1363        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1364        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1365        // the source — every row is in one numeric class, so the chunk size no longer steers
1366        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1367        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1368        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1369        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1370        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1371        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1372        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1373        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1374        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1375        // across calls, the request still ends at the same absolute position, whatever the tick
1376        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1377        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1378        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1379        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1380        // on in a measured default run.
1381        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1382        let seq_end = if legacy_calllocal {
1383            cache.pos + t
1384        } else {
1385            cache.pos + t + queued_after
1386        };
1387        if ranges.len() == 1 {
1388            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1389        }
1390        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1391        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1392        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1393        // this lane owns the balanced two-stage schedule only.
1394        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1395            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1396                if overlay.is_some() {
1397                    return Err(
1398                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1399                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1400                            .into(),
1401                    );
1402                }
1403                if crate::pp::pp_multi_stream_same_device() {
1404                    return Err(
1405                        "prime chunk pipeline refused with 2 stage streams on one device — \
1406                         that concurrent-stream placement remains quarantined by the deferred \
1407                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1408                         the serial split."
1409                            .into(),
1410                    );
1411                }
1412                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1413            }
1414        }
1415        let mut hiddens = e.uninit(t * n_embd)?;
1416        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1417        for &(start, end) in &ranges {
1418            // chunked prime writes tap rows at the chunk's absolute offset
1419            if let Some(taps) = cache.dflash_taps.as_mut() {
1420                taps.base = start;
1421            }
1422            let (l, hs, x) =
1423                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1424            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1425            last = Some((l, hs));
1426        }
1427        let (logits, h_seed) = last.unwrap();
1428        Ok((logits, h_seed, hiddens))
1429    }
1430
1431    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1432    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1433    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1434    /// norm, lm head, and caller hidden-stack copy as the serial split.
1435    fn prime_cache_pp2_pipelined(
1436        &self,
1437        e: &Engine,
1438        tokens: &[u32],
1439        cache: &mut Cache,
1440        seq_end: usize,
1441        ranges: &[(usize, usize)],
1442        fence: &[usize],
1443    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1444        debug_assert_eq!(fence.len(), 3);
1445        debug_assert!(ranges.len() >= 2);
1446        let rt = crate::pp::PpNRt::get(e)?;
1447        assert_eq!(
1448            rt.n_stages(),
1449            2,
1450            "prime pipeline requires exactly two PP stages"
1451        );
1452        let n_embd = self.cfg.n_embd as usize;
1453        let t = tokens.len();
1454        let initial_base = cache.pos;
1455        let caller_stream = e.stream();
1456
1457        // #87 reverse publication before any new stage allocation, then prewarm both
1458        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1459        // after stage 1(N) is queued would synchronize that stream and erase the first
1460        // overlap on a two-chunk prompt.
1461        rt.fence_stages_behind(&caller_stream)?;
1462        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1463        rt.prepare_overlap_slots(0, max_payload)?;
1464
1465        let mut hiddens = e.uninit(t * n_embd)?;
1466        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1467        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1468        let (cache0, cache1) = stage_caches.parts();
1469        let (first_start, first_end) = ranges[0];
1470        let mut slot = self.prime_pp2_stage0_enqueue(
1471            e,
1472            rt,
1473            &tokens[first_start..first_end],
1474            cache0,
1475            seq_end,
1476            fence,
1477            initial_base + first_start,
1478            true,
1479        )?;
1480        cache0.pos = initial_base + first_end;
1481
1482        for (i, &(start, end)) in ranges.iter().enumerate() {
1483            let base = initial_base + start;
1484            debug_assert_eq!(
1485                cache1.pos, base,
1486                "stage 1 must drain chunks in original position order"
1487            );
1488            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1489                let next_base = initial_base + next_start;
1490                debug_assert_eq!(
1491                    cache0.pos, next_base,
1492                    "stage 0 must issue chunks in original position order"
1493                );
1494                let cache0_stage = &mut *cache0;
1495                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1496                // on one host thread therefore serialize even if the calls are ordered as
1497                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1498                // stage 1 consumes slot N while stage 0 produces slot N+1.
1499                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1500                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1501                        let next = self
1502                            .prime_pp2_stage0_enqueue(
1503                                e,
1504                                rt,
1505                                &tokens[next_start..next_end],
1506                                cache0_stage,
1507                                seq_end,
1508                                fence,
1509                                next_base,
1510                                true,
1511                            )
1512                            .map_err(|err| err.to_string())?;
1513                        cache0_stage.pos = initial_base + next_end;
1514                        Ok(next)
1515                    });
1516                    let x = self.prime_pp2_stage1_enqueue(
1517                        e,
1518                        rt,
1519                        slot,
1520                        end - start,
1521                        cache1,
1522                        seq_end,
1523                        fence,
1524                        base,
1525                        true,
1526                    )?;
1527                    let out = {
1528                        rt.bind_stage(1)?;
1529                        let _st1 = rt.enter(1);
1530                        let e1 = rt.engine(1, e);
1531                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1532                    };
1533                    let next = stage0
1534                        .join()
1535                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1536                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1537                    Ok((out, Some(next)))
1538                })?
1539            } else {
1540                let x = self.prime_pp2_stage1_enqueue(
1541                    e,
1542                    rt,
1543                    slot,
1544                    end - start,
1545                    cache1,
1546                    seq_end,
1547                    fence,
1548                    base,
1549                    true,
1550                )?;
1551                let out = {
1552                    rt.bind_stage(1)?;
1553                    let _st1 = rt.enter(1);
1554                    let e1 = rt.engine(1, e);
1555                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1556                };
1557                (out, None)
1558            };
1559
1560            rt.publish_to(1, &caller_stream)?;
1561            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1562            last = Some((out.0, out.1));
1563            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1564
1565            if let Some(next) = next_slot {
1566                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1567                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1568                // Stage 0(N+1) is already queued before this wait is appended, so its
1569                // overlap with stage 1(N) is preserved.
1570                rt.fence_stages_behind(&caller_stream)?;
1571                slot = next;
1572            }
1573        }
1574
1575        debug_assert_eq!(cache0.pos, initial_base + t);
1576        debug_assert_eq!(cache1.pos, initial_base + t);
1577        let (logits, h_seed) = last.unwrap();
1578        Ok((logits, h_seed, hiddens))
1579    }
1580
1581    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1582    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1583    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1584    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1585    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1586    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1587    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1588        if Engine::gdn_db_on()
1589            && Engine::gdn_chunked_enabled()
1590            && t >= 16
1591            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1592            && num_k * 2 == num_v
1593        {
1594            num_k
1595        } else {
1596            num_v
1597        }
1598    }
1599
1600    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1601    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1602    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1603    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1604    fn f16out_on(e: &Engine, t: usize) -> bool {
1605        crate::f16_ffi::pp_f16_enabled()
1606            && t >= 16
1607            && !e.verify_exact_on()
1608            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1609    }
1610
1611    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1612    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1613    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1614    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1615    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1616    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1617    /// see one entry, byte-identical behavior.
1618    pub fn prime_slabs_get(
1619        &self,
1620        e: &Engine,
1621        t: usize,
1622        n_embd: usize,
1623        n_ff_max: usize,
1624    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1625        let mut slabs = self.prime_slabs.lock().unwrap();
1626        let dev = e.ctx().ordinal();
1627        let need_new = match slabs.get(&dev) {
1628            None => true,
1629            Some(sl) => sl.lock().unwrap().t_cap < t,
1630        };
1631        if need_new {
1632            slabs.insert(
1633                dev,
1634                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1635                    t_cap: t,
1636                    h: e.uninit(t * n_embd)?,
1637                    x1: e.uninit(t * n_embd)?,
1638                    z: e.uninit(t * n_embd)?,
1639                    act: e.uninit(t * n_ff_max)?,
1640                    xa: e.uninit(t * n_embd)?,
1641                    xb: e.uninit(t * n_embd)?,
1642                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1643                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1644                    gate: e.uninit(t * n_ff_max)?,
1645                    up: e.uninit(t * n_ff_max)?,
1646                    ffn_out: e.uninit(t * n_embd)?,
1647                    seg_glue: Vec::new(),
1648                    mixed: e.uninit(t * n_embd)?,
1649                    seg_mid: Vec::new(),
1650                    seg_t: 0,
1651                })),
1652            );
1653        }
1654        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1655    }
1656
1657    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1658    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1659    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1660    fn prime_chunk(
1661        &self,
1662        e: &Engine,
1663        tokens: &[u32],
1664        cache: &mut Cache,
1665        seq_end: usize,
1666        chunk_off: usize,
1667        overlay: Option<&crate::vision::EmbedOverlay>,
1668    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1669        if crate::pp::pp_host_bounce_active()
1670            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1671        {
1672            return Err(
1673                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1674                 has no active prime stage split and would peer-read remote weights; keep \
1675                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1676                    .into(),
1677            );
1678        }
1679        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1680        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1681        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1682        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1683        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1684        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1685        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1686        // loader is off and there is nothing remote to split for.
1687        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1688            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1689                if overlay.is_some() {
1690                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1691                         run single-device or MEMRA_PRIME_PP=0"
1692                        .into());
1693                }
1694                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1695            }
1696        }
1697        if crate::pp::pp_host_bounce_active() {
1698            return Err(
1699                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1700                 refusing an unsplit remote-weight walk"
1701                    .into(),
1702            );
1703        }
1704        let t = tokens.len();
1705        let base = cache.pos;
1706        debug_assert!(
1707            seq_end >= base + t,
1708            "prime_chunk: seq_end must cover this chunk"
1709        );
1710        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1711        let pos_d = e.htod_i32(&pos)?;
1712
1713        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1714        if let Some(ov) = overlay {
1715            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1716            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1717            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1718            let n_embd = self.cfg.n_embd as usize;
1719            for &(pos, row_off, n_rows) in &ov.spans {
1720                let lo = pos.max(chunk_off);
1721                let hi = (pos + n_rows).min(chunk_off + t);
1722                if lo < hi {
1723                    let src_row = row_off + (lo - pos);
1724                    let view = ov
1725                        .rows
1726                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1727                    e.copy_view_into(
1728                        &mut x_embed,
1729                        (lo - chunk_off) * n_embd,
1730                        &view,
1731                        (hi - lo) * n_embd,
1732                    )?;
1733                }
1734            }
1735        }
1736        let x = self.prime_layers(
1737            e,
1738            x_embed,
1739            0,
1740            self.layers.len(),
1741            &pos_d,
1742            t,
1743            base,
1744            cache,
1745            seq_end,
1746        )?;
1747        self.prime_chunk_epilogue(e, x, t, cache)
1748    }
1749
1750    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1751    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1752    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1753    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1754    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1755    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1756    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1757    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1758    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1759    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1760    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1761    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1762    ///     each stage walks through its own resident transients;
1763    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1764    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1765    #[allow(clippy::too_many_arguments)]
1766    fn prime_layers(
1767        &self,
1768        e: &Engine,
1769        x_in: CudaSlice<f32>,
1770        lo: usize,
1771        hi: usize,
1772        pos_d: &CudaSlice<i32>,
1773        t: usize,
1774        base: usize,
1775        cache: &mut Cache,
1776        seq_end: usize,
1777    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1778        let cfg = &self.cfg;
1779        let n_embd = cfg.n_embd as usize;
1780        let eps = cfg.rms_eps;
1781        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1782        // standalone convert launches). Only when the f16 lane serves and T reaches the
1783        // GEMM tier; bit-identical either way.
1784        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1785        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1786        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1787        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1788        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1789        // capacity tail must stay behind checked views. The hidden-stack return clones the
1790        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1791        let n_ff_max = self
1792            .layers
1793            .iter()
1794            .map(|l| match &l.ffn {
1795                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1796                _ => n_embd,
1797            })
1798            .max()
1799            .unwrap_or(n_embd)
1800            .max(n_embd);
1801        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1802        let slab = if use_slabs {
1803            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1804        } else {
1805            None
1806        };
1807        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1808        let mut x_own; // fallback storage when slabs are off
1809        type SlabRefs<'a> = (
1810            &'a mut CudaSlice<f32>,
1811            &'a mut CudaSlice<f32>,
1812            &'a mut CudaSlice<f32>,
1813            &'a mut CudaSlice<f32>,
1814            &'a mut CudaSlice<u8>,
1815            &'a mut CudaSlice<u8>,
1816            &'a mut CudaSlice<f32>,
1817            &'a mut CudaSlice<f32>,
1818            &'a mut CudaSlice<f32>,
1819        );
1820        let (mut x_cur, mut x_nxt, sl): (
1821            &mut CudaSlice<f32>,
1822            &mut CudaSlice<f32>,
1823            Option<SlabRefs>,
1824        );
1825        let mut seg: Option<(
1826            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1827            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1828            &mut CudaSlice<f32>,
1829            &mut usize,
1830        )> = None;
1831        let mut x_own2;
1832        match slab_guard.as_mut() {
1833            Some(g) => {
1834                let slabs = &mut **g;
1835                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1836                let PrimeSlabs {
1837                    xa,
1838                    xb,
1839                    h,
1840                    x1,
1841                    z,
1842                    act,
1843                    h16,
1844                    z16,
1845                    gate,
1846                    up,
1847                    ffn_out,
1848                    seg_glue,
1849                    mixed,
1850                    seg_mid,
1851                    seg_t,
1852                    ..
1853                } = slabs;
1854                x_cur = xa;
1855                x_nxt = xb;
1856                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1857                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1858            }
1859            None => {
1860                x_own = x_in;
1861                x_own2 = e.uninit(t * n_embd)?;
1862                x_cur = &mut x_own;
1863                x_nxt = &mut x_own2;
1864                sl = None;
1865            }
1866        }
1867        let mut alloc_h;
1868        let mut alloc_x1;
1869        let mut alloc_z;
1870        let mut alloc_act;
1871        let mut alloc_h16;
1872        let mut alloc_z16;
1873        let mut alloc_gate;
1874        let mut alloc_up;
1875        let mut alloc_fo;
1876        let (h, x1, z, act): (
1877            &mut CudaSlice<f32>,
1878            &mut CudaSlice<f32>,
1879            &mut CudaSlice<f32>,
1880            &mut CudaSlice<f32>,
1881        );
1882        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
1883        let (sl_gate, sl_up, sl_fo): (
1884            &mut CudaSlice<f32>,
1885            &mut CudaSlice<f32>,
1886            &mut CudaSlice<f32>,
1887        );
1888        match sl {
1889            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
1890                h = a;
1891                x1 = b;
1892                z = c;
1893                act = d;
1894                h16 = e16;
1895                z16 = f16b;
1896                sl_gate = g;
1897                sl_up = u;
1898                sl_fo = fo;
1899            }
1900            None => {
1901                alloc_h = e.uninit(t * n_embd)?;
1902                alloc_x1 = e.uninit(t * n_embd)?;
1903                alloc_z = e.uninit(t * n_embd)?;
1904                alloc_act = e.uninit(t * n_ff_max)?;
1905                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1906                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1907                alloc_gate = e.uninit(t * n_ff_max)?;
1908                alloc_up = e.uninit(t * n_ff_max)?;
1909                alloc_fo = e.uninit(t * n_embd)?;
1910                h = &mut alloc_h;
1911                x1 = &mut alloc_x1;
1912                z = &mut alloc_z;
1913                act = &mut alloc_act;
1914                h16 = &mut alloc_h16;
1915                z16 = &mut alloc_z16;
1916                sl_gate = &mut alloc_gate;
1917                sl_up = &mut alloc_up;
1918                sl_fo = &mut alloc_fo;
1919            }
1920        }
1921        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
1922        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
1923        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
1924        // first prime at this t (capture does not execute -> launch right after).
1925        let n_layers = self.layers.len();
1926        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
1927        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
1928        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
1929        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
1930        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
1931        // machinery stays (byte-identical) as their foundation.
1932        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
1933        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
1934        // step35 rides its own mixer through the normal per-layer arm below.
1935        let use_seg = f16fuse
1936            && seg.is_some()
1937            && !self.uses_sliding_gated_moe_program()
1938            && lo == 0
1939            && hi == n_layers
1940            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
1941        if let Some((sg, sm, _, st)) = seg.as_mut() {
1942            if **st != t {
1943                sg.clear();
1944                sg.extend((0..n_layers).map(|_| None));
1945                sm.clear();
1946                sm.extend((0..n_layers).map(|_| None));
1947                **st = t;
1948            }
1949        }
1950        {
1951            let layer_lo = &self.layers[lo];
1952            if f16fuse {
1953                e.rms_norm_f16out(
1954                    x_cur,
1955                    layer_lo.attn_norm.float_data(),
1956                    h,
1957                    h16,
1958                    n_embd,
1959                    t,
1960                    eps,
1961                )?;
1962            } else {
1963                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
1964            }
1965        }
1966        let anat = Self::prime_anatomy_on();
1967        let mut anat_last = if anat {
1968            e.stream().synchronize()?;
1969            Some(std::time::Instant::now())
1970        } else {
1971            None
1972        };
1973        // Closes the region that just ENDED into `slot`, restarting the clock.
1974        macro_rules! anat_mark {
1975            ($slot:expr) => {
1976                if let Some(ts) = anat_last.as_mut() {
1977                    e.stream().synchronize()?;
1978                    Self::prime_anatomy_slots()[$slot].fetch_add(
1979                        ts.elapsed().as_nanos() as u64,
1980                        std::sync::atomic::Ordering::Relaxed,
1981                    );
1982                    *ts = std::time::Instant::now();
1983                }
1984            };
1985        }
1986        for il in lo..hi {
1987            let layer = &self.layers[il];
1988            let hx16 = if f16fuse { Some(&*h16) } else { None };
1989            if use_seg {
1990                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
1991                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
1992                let (pre, pre16, w_out) = match &layer.mixer {
1993                    Mixer::Full(fa) => {
1994                        let g3 = match hx16 {
1995                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
1996                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
1997                        };
1998                        let (pre, pre16) =
1999                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
2000                        (pre, pre16, &fa.wo)
2001                    }
2002                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2003                    Mixer::Linear(la) => {
2004                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2005                        let g4 = match hx16 {
2006                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
2007                            None => e.matmul_group(&ws, h, t)?,
2008                        };
2009                        let (pre, pre16) =
2010                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
2011                        (pre, pre16, &la.ssm_out)
2012                    }
2013                };
2014                {
2015                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
2016                    let pre_n = pre.len() / t;
2017                    let xh_pre = match pre16 {
2018                        Some(x) => x,
2019                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
2020                    };
2021                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
2022                        let y = e.matmul(w_out, &pre, t)?;
2023                        e.copy_into(mslab, 0, &y, t * n_embd)?;
2024                    }
2025                    if sm[il].is_none() {
2026                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2027                        let w_post = layer.post_attn_norm.float_data();
2028                        e.stream().synchronize()?;
2029                        e.stream()
2030                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2031                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2032                            e.add(x_cur, mslab, x1, t * n_embd)?;
2033                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2034                            Ok(())
2035                        })();
2036                        let g = e.stream().end_capture(
2037                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2038                        r?;
2039                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2040                    }
2041                    sm[il].as_ref().unwrap().launch()?;
2042                }
2043            } else {
2044                let mixed = match &layer.mixer {
2045                    Mixer::Full(fa) => {
2046                        let y =
2047                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2048                        anat_mark!(0);
2049                        y
2050                    }
2051                    Mixer::Linear(la) => {
2052                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2053                        anat_mark!(1);
2054                        y
2055                    }
2056                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2057                };
2058                if f16fuse {
2059                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2060                    // bit-identical) — the standalone add pass disappears.
2061                    e.add_rms_norm_f16out(
2062                        x_cur,
2063                        &mixed,
2064                        layer.post_attn_norm.float_data(),
2065                        x1,
2066                        z,
2067                        z16,
2068                        n_embd,
2069                        t,
2070                        eps,
2071                    )?;
2072                } else {
2073                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2074                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2075                }
2076                anat_mark!(4);
2077            }
2078            let zx16 = if f16fuse { Some(&*z16) } else { None };
2079            match &layer.ffn {
2080                crate::hybrid::Ffn::Dense {
2081                    ffn_gate,
2082                    ffn_up,
2083                    ffn_down,
2084                } => {
2085                    let n_ff = ffn_gate.out_features();
2086                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2087                    // the allocating group + copy when a mirror is missing.
2088                    let mut into_ok = false;
2089                    if let Some(xh) = zx16 {
2090                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2091                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2092                    }
2093                    if !into_ok {
2094                        let mut g2 = match zx16 {
2095                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2096                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2097                        };
2098                        let up_y = g2.pop().unwrap();
2099                        let gate_y = g2.pop().unwrap();
2100                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2101                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2102                    }
2103                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2104                    // operand in-epilogue; non-silu activations keep the standalone convert.
2105                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2106                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2107                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2108                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2109                    {
2110                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2111                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2112                        Some(a16)
2113                    } else {
2114                        Self::ffn_act_lim(
2115                            e,
2116                            &self.cfg,
2117                            sl_gate,
2118                            sl_up,
2119                            1.0,
2120                            1.0,
2121                            d_lim,
2122                            act,
2123                            t * n_ff,
2124                        )?;
2125                        None
2126                    };
2127                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2128                    let xh_act = match act16 {
2129                        Some(x) => x,
2130                        None => e.f16_act(act, t * n_ff, n_ff)?,
2131                    };
2132                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2133                        let y = e.matmul(ffn_down, &*act, t)?;
2134                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2135                    }
2136                }
2137                crate::hybrid::Ffn::Moe(m) => {
2138                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2139                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2140                    anat_mark!(2);
2141                }
2142            }
2143            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2144                anat_mark!(3);
2145            }
2146            if use_seg && il + 1 < hi {
2147                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2148                let w_next = self.layers[il + 1].attn_norm.float_data();
2149                let (sg, _, _, _) = seg.as_mut().unwrap();
2150                if sg[il].is_none() {
2151                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2152                    e.stream().synchronize()?;
2153                    e.stream()
2154                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2155                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2156                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2157                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2158                        Ok(())
2159                    })();
2160                    let g = e.stream().end_capture(
2161                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2162                    );
2163                    r?;
2164                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2165                }
2166                sg[il].as_ref().unwrap().launch()?;
2167            } else {
2168                if il + 1 < hi {
2169                    let w_next = self.layers[il + 1].attn_norm.float_data();
2170                    if f16fuse {
2171                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2172                    } else {
2173                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2174                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2175                    }
2176                } else {
2177                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2178                }
2179            }
2180            anat_mark!(4);
2181            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2182            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2183            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2184            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2185            // unset (the default) costs one OnceLock read per layer.
2186            if let Some(path) = Self::prime_trace_path() {
2187                let row = (base + t - 1) as usize;
2188                let host = e.dtoh(x_nxt)?;
2189                let last = &host[(t - 1) * n_embd..t * n_embd];
2190                use std::io::Write as _;
2191                let mut f = std::fs::OpenOptions::new()
2192                    .create(true)
2193                    .append(true)
2194                    .open(path)?;
2195                let mut h64: u64 = 0xcbf29ce484222325;
2196                for v in last {
2197                    h64 ^= v.to_bits() as u64;
2198                    h64 = h64.wrapping_mul(0x100000001b3);
2199                }
2200                writeln!(
2201                    f,
2202                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2203                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2204                    last[0], last[1], last[2]
2205                )?;
2206            }
2207            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2208            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2209            self.dflash_tap(e, cache, il, x_nxt, t)?;
2210            std::mem::swap(&mut x_cur, &mut x_nxt);
2211        }
2212        if anat {
2213            let s = Self::prime_anatomy_slots();
2214            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2215            eprintln!(
2216                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2217                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2218                ms(0),
2219                ms(1),
2220                ms(2),
2221                ms(3),
2222                ms(4)
2223            );
2224        }
2225        // hidden-stack return: clone the final x out of the slab
2226        let mut x = e.uninit(t * n_embd)?;
2227        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2228        drop(slab_guard);
2229        Ok(x)
2230    }
2231
2232    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2233    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2234    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2235    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2236    fn prime_chunk_epilogue(
2237        &self,
2238        e: &Engine,
2239        x: CudaSlice<f32>,
2240        t: usize,
2241        cache: &mut Cache,
2242    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2243        let n_embd = self.cfg.n_embd as usize;
2244        let eps = self.cfg.rms_eps;
2245        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2246        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2247        // the post-norm copy happens after hn exists).
2248        let mut h_seed = e.uninit(n_embd)?;
2249        if !crate::spec::spec_hpost() {
2250            e.copy_view_into(
2251                &mut h_seed,
2252                0,
2253                &x.slice((t - 1) * n_embd..t * n_embd),
2254                n_embd,
2255            )?;
2256        }
2257        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2258        let mut hn = e.uninit(t * n_embd)?;
2259        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2260        if crate::spec::spec_hpost() {
2261            e.copy_view_into(
2262                &mut h_seed,
2263                0,
2264                &hn.slice((t - 1) * n_embd..t * n_embd),
2265                n_embd,
2266            )?;
2267        }
2268        let last = e.view(&hn, t * n_embd);
2269        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2270        let mut hlast = e.uninit(n_embd)?;
2271        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2272        let logits = e.matmul(&self.output, &hlast, 1)?;
2273        cache.pos += t;
2274        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2275        // post-norm stack hn (MEMRA_SPEC_HPOST).
2276        Ok((
2277            e.dtoh(&logits)?,
2278            h_seed,
2279            if crate::spec::spec_hpost() { hn } else { x },
2280        ))
2281    }
2282
2283    /// Post-final-norm hidden state of one row of a prime-returned hidden stack — the
2284    /// embedding-pooling read (lane/embed-serve). `hiddens` is `prime_cache`'s third
2285    /// return: the pre-norm stack by default, but ALREADY post-norm under
2286    /// MEMRA_SPEC_HPOST (see `prime_chunk_epilogue`), so the norm is applied only in
2287    /// the default shape. Returns the host f32 row (`n_embd` wide).
2288    pub fn hidden_postnorm_row(
2289        &self,
2290        e: &Engine,
2291        hiddens: &CudaSlice<f32>,
2292        row: usize,
2293    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2294        let n_embd = self.cfg.n_embd as usize;
2295        let mut x1 = e.uninit(n_embd)?;
2296        e.copy_view_into(
2297            &mut x1,
2298            0,
2299            &hiddens.slice(row * n_embd..(row + 1) * n_embd),
2300            n_embd,
2301        )?;
2302        if crate::spec::spec_hpost() {
2303            return Ok(e.dtoh(&x1)?);
2304        }
2305        let mut hn = e.uninit(n_embd)?;
2306        e.rms_norm(
2307            &x1,
2308            self.output_norm.float_data(),
2309            &mut hn,
2310            n_embd,
2311            1,
2312            self.cfg.rms_eps,
2313        )?;
2314        Ok(e.dtoh(&hn)?)
2315    }
2316
2317    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2318    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2319    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2320    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2321    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2322    /// prefill kernels. Structure mirrors the verify split exactly:
2323    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2324    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2325    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2326    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2327    ///                  there via the sharded loader) → `publish_to`
2328    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2329    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2330    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2331    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2332    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2333    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2334    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2335    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2336    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2337    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2338    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2339    /// and its liveness counter is bumped here — the gate goes green with this function.
2340    fn prime_chunk_ppn(
2341        &self,
2342        e: &Engine,
2343        tokens: &[u32],
2344        cache: &mut Cache,
2345        seq_end: usize,
2346        fence: &[usize],
2347    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2348        let rt = crate::pp::PpNRt::get(e)?;
2349        let n_st = fence.len() - 1;
2350        assert_eq!(
2351            rt.n_stages(),
2352            n_st,
2353            "PpNRt stage count {} != fence stages {n_st}",
2354            rt.n_stages()
2355        );
2356        let n_embd = self.cfg.n_embd as usize;
2357        let t = tokens.len();
2358        let base = cache.pos;
2359        debug_assert!(
2360            seq_end >= base + t,
2361            "prime_chunk_ppn: seq_end must cover this chunk"
2362        );
2363        let payload = t * n_embd;
2364        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2365        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2366        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2367        let caller_stream = e.stream();
2368        rt.fence_stages_behind(&caller_stream)?;
2369
2370        if n_st == 2 {
2371            let slot =
2372                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2373            let x =
2374                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2375            let out = {
2376                rt.bind_stage(1)?;
2377                let _st1 = rt.enter(1);
2378                let e1 = rt.engine(1, e);
2379                self.prime_chunk_epilogue(e1, x, t, cache)?
2380            };
2381            rt.publish_to(1, &caller_stream)?;
2382            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2383            return Ok(out);
2384        }
2385
2386        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2387
2388        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2389        let mut slot = {
2390            let _st0 = rt.enter(0);
2391            let e0 = rt.engine(0, e);
2392            let pos_d = e0.htod_i32(&pos)?;
2393            let x = self.embed(e0, tokens)?;
2394            let x =
2395                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2396            rt.tx(0, &x, payload)?
2397            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2398        };
2399
2400        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2401        for s in 1..n_st - 1 {
2402            let _st = rt.enter(s);
2403            let es = rt.engine(s, e);
2404            let pos_d = es.htod_i32(&pos)?;
2405            let x = rt.rx(s - 1, slot, payload)?;
2406            let x = self.prime_layers(
2407                es,
2408                x,
2409                fence[s],
2410                fence[s + 1],
2411                &pos_d,
2412                t,
2413                base,
2414                cache,
2415                seq_end,
2416            )?;
2417            slot = rt.tx(s, &x, payload)?;
2418        }
2419
2420        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2421        let _stl = rt.enter(n_st - 1);
2422        let el = rt.engine(n_st - 1, e);
2423        let pos_d = el.htod_i32(&pos)?;
2424        let x = rt.rx(n_st - 2, slot, payload)?;
2425        let x = self.prime_layers(
2426            el,
2427            x,
2428            fence[n_st - 1],
2429            fence[n_st],
2430            &pos_d,
2431            t,
2432            base,
2433            cache,
2434            seq_end,
2435        )?;
2436        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2437        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2438        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2439        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2440        // stage stream host-side, but the law is stated in events, not in a dtoh side
2441        // effect a later deferred form would remove.
2442        rt.publish_to(n_st - 1, &caller_stream)?;
2443        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2444        Ok(out)
2445    }
2446
2447    fn prime_pp2_stage0_enqueue(
2448        &self,
2449        e: &Engine,
2450        rt: &crate::pp::PpNRt,
2451        tokens: &[u32],
2452        cache: &mut Cache,
2453        seq_end: usize,
2454        fence: &[usize],
2455        base: usize,
2456        pipelined: bool,
2457    ) -> Result<usize, Box<dyn std::error::Error>> {
2458        let t = tokens.len();
2459        let n_embd = self.cfg.n_embd as usize;
2460        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2461        rt.bind_stage(0)?;
2462        let _st0 = rt.enter(0);
2463        let e0 = rt.engine(0, e);
2464        let pos_d = e0.htod_i32(&pos)?;
2465        let x = self.embed(e0, tokens)?;
2466        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2467        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2468        if pipelined {
2469            rt.tx_pipelined(0, &x, t * n_embd)
2470        } else {
2471            rt.tx(0, &x, t * n_embd)
2472        }
2473    }
2474
2475    fn prime_pp2_stage1_enqueue(
2476        &self,
2477        e: &Engine,
2478        rt: &crate::pp::PpNRt,
2479        slot: usize,
2480        t: usize,
2481        cache: &mut Cache,
2482        seq_end: usize,
2483        fence: &[usize],
2484        base: usize,
2485        pipelined: bool,
2486    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2487        let n_embd = self.cfg.n_embd as usize;
2488        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2489        rt.bind_stage(1)?;
2490        let _st1 = rt.enter(1);
2491        let e1 = rt.engine(1, e);
2492        let pos_d = e1.htod_i32(&pos)?;
2493        let x = rt.rx(0, slot, t * n_embd)?;
2494        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2495        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2496    }
2497
2498    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2499    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2500    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2501    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2502    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2503    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2504    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2505    /// bookkeeping still runs on the host per call — the real replay path moves the write
2506    /// slot to the len_d device counter (increment 3).
2507    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2508    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2509    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2510    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2511    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2512    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2513    pub fn prime_chunk_captured(
2514        &self,
2515        e: &Engine,
2516        x_in: &CudaSlice<f32>,
2517        pos_d: &CudaSlice<i32>,
2518        t: usize,
2519        cache: &mut Cache,
2520        len_d: &CudaSlice<i32>,
2521        logits_out: &mut CudaSlice<f32>,
2522        h_seed_out: &mut CudaSlice<f32>,
2523    ) -> Result<(), Box<dyn std::error::Error>> {
2524        let cfg = &self.cfg;
2525        let n_embd = cfg.n_embd as usize;
2526        let eps = cfg.rms_eps;
2527        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2528        let mut x = e.uninit(t * n_embd)?;
2529        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2530        for (il, layer) in self.layers.iter().enumerate() {
2531            let mut h = e.uninit(t * n_embd)?;
2532            let mut hx16: Option<CudaSlice<u8>> = None;
2533            if f16fuse {
2534                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2535                e.rms_norm_f16out(
2536                    &x,
2537                    layer.attn_norm.float_data(),
2538                    &mut h,
2539                    &mut b16,
2540                    n_embd,
2541                    t,
2542                    eps,
2543                )?;
2544                hx16 = Some(b16);
2545            } else {
2546                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2547            }
2548            let mixed = match &layer.mixer {
2549                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2550                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2551                // come from the caller (see step35_attn_pre_wo's doc note).
2552                Mixer::Full(fa) => {
2553                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2554                }
2555                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2556                Mixer::Linear(la) => {
2557                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2558                    let g4 = match hx16.as_ref() {
2559                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2560                        None => e.matmul_group(&ws, &h, t)?,
2561                    };
2562                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2563                }
2564            };
2565            let mut x1 = e.uninit(t * n_embd)?;
2566            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2567            let mut z = e.uninit(t * n_embd)?;
2568            let mut zx16: Option<CudaSlice<u8>> = None;
2569            if f16fuse {
2570                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2571                e.rms_norm_f16out(
2572                    &x1,
2573                    layer.post_attn_norm.float_data(),
2574                    &mut z,
2575                    &mut b16,
2576                    n_embd,
2577                    t,
2578                    eps,
2579                )?;
2580                zx16 = Some(b16);
2581            } else {
2582                e.rms_norm(
2583                    &x1,
2584                    layer.post_attn_norm.float_data(),
2585                    &mut z,
2586                    n_embd,
2587                    t,
2588                    eps,
2589                )?;
2590            }
2591            let ffn_out = match &layer.ffn {
2592                crate::hybrid::Ffn::Dense {
2593                    ffn_gate,
2594                    ffn_up,
2595                    ffn_down,
2596                } => {
2597                    let n_ff = ffn_gate.out_features();
2598                    let mut g2 = match &zx16 {
2599                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2600                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2601                    };
2602                    let up = g2.pop().unwrap();
2603                    let gate = g2.pop().unwrap();
2604                    let mut act = e.uninit(t * n_ff)?;
2605                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2606                    Self::ffn_act_lim(
2607                        e,
2608                        &self.cfg,
2609                        &gate,
2610                        &up,
2611                        1.0,
2612                        1.0,
2613                        self.cfg.clamp_shexp_at(il as u32),
2614                        &mut act,
2615                        t * n_ff,
2616                    )?;
2617                    e.matmul(ffn_down, &act, t)?
2618                }
2619                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2620            };
2621            let mut x2 = e.uninit(t * n_embd)?;
2622            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2623            x = x2;
2624        }
2625        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2626        if !crate::spec::spec_hpost() {
2627            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2628        }
2629        let mut hn = e.uninit(t * n_embd)?;
2630        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2631        if crate::spec::spec_hpost() {
2632            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2633        }
2634        let mut hlast = e.uninit(n_embd)?;
2635        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2636        let logits = e.matmul(&self.output, &hlast, 1)?;
2637        let nv = logits.len();
2638        e.copy_into(logits_out, 0, &logits, nv)?;
2639        Ok(())
2640    }
2641
2642    fn step35_prime_batch_on() -> bool {
2643        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2644    }
2645
2646    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2647    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2648    #[allow(clippy::too_many_arguments)]
2649    fn step35_prime_batch_layers(
2650        &self,
2651        e: &Engine,
2652        mut x: CudaSlice<f32>,
2653        lo: usize,
2654        hi: usize,
2655        ts: &[usize],
2656        offs: &[usize],
2657        pos_ds: &[CudaSlice<i32>],
2658        caches: &mut [&mut Cache],
2659    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2660        let cfg = &self.cfg;
2661        let n_embd = cfg.n_embd as usize;
2662        let eps = cfg.rms_eps;
2663        let b = ts.len();
2664        let total: usize = ts.iter().sum();
2665        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2666
2667        let split = |e: &Engine,
2668                     y: &CudaSlice<f32>,
2669                     dim: usize|
2670         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2671            let mut out = Vec::with_capacity(b);
2672            for s in 0..b {
2673                let mut ys = e.uninit(ts[s] * dim)?;
2674                e.copy_view_into(
2675                    &mut ys,
2676                    0,
2677                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2678                    ts[s] * dim,
2679                )?;
2680                out.push(ys);
2681            }
2682            Ok(out)
2683        };
2684
2685        for il in lo..hi {
2686            let layer = &self.layers[il];
2687            let Mixer::Full(fa) = &layer.mixer else {
2688                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2689            };
2690
2691            let mut h = e.uninit(total * n_embd)?;
2692            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2693            if f16fuse {
2694                e.rms_norm_f16out(
2695                    &x,
2696                    layer.attn_norm.float_data(),
2697                    &mut h,
2698                    &mut hx16,
2699                    n_embd,
2700                    total,
2701                    eps,
2702                )?;
2703            } else {
2704                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2705            }
2706
2707            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2708            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2709            // application stay verbatim.
2710            let gate_w = fa
2711                .attn_gate
2712                .as_ref()
2713                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2714            let mut g4 = if f16fuse {
2715                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2716            } else {
2717                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2718            };
2719            let gate = g4.pop().unwrap();
2720            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2721                (0..b).map(|_| Vec::with_capacity(3)).collect();
2722            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2723                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2724                    parts[s].push(ys);
2725                }
2726            }
2727            let gates = split(e, &gate, gate_w.out_features())?;
2728            let geometry = self.step35_geom(il);
2729            let hd = geometry.head_dim_k as usize;
2730            let nh = geometry.n_head as usize;
2731            let mut ag_cat = e.uninit(total * nh * hd)?;
2732            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2733                let ag = self.step35_attn_pre_wo(
2734                    e,
2735                    fa,
2736                    g3s,
2737                    None,
2738                    Some(&gate),
2739                    &pos_ds[s],
2740                    ts[s],
2741                    Some(&mut *caches[s]),
2742                    il,
2743                    ts[s],
2744                )?;
2745                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2746            }
2747            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2748
2749            let mut x1 = e.uninit(total * n_embd)?;
2750            let mut z = e.uninit(total * n_embd)?;
2751            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2752            if f16fuse {
2753                e.add_rms_norm_f16out(
2754                    &x,
2755                    &mixed,
2756                    layer.post_attn_norm.float_data(),
2757                    &mut x1,
2758                    &mut z,
2759                    &mut zx16,
2760                    n_embd,
2761                    total,
2762                    eps,
2763                )?;
2764            } else {
2765                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2766                e.rms_norm(
2767                    &x1,
2768                    layer.post_attn_norm.float_data(),
2769                    &mut z,
2770                    n_embd,
2771                    total,
2772                    eps,
2773                )?;
2774            }
2775
2776            let ffn_out = match &layer.ffn {
2777                crate::hybrid::Ffn::Dense {
2778                    ffn_gate,
2779                    ffn_up,
2780                    ffn_down,
2781                } => {
2782                    let n_ff = ffn_gate.out_features();
2783                    let mut g2 = if f16fuse {
2784                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2785                    } else {
2786                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2787                    };
2788                    let up = g2.pop().unwrap();
2789                    let gate = g2.pop().unwrap();
2790                    let mut act = e.uninit(total * n_ff)?;
2791                    let d_lim = cfg.clamp_shexp_at(il as u32);
2792                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2793                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2794                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2795                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2796                            Some(y) => y,
2797                            None => e.matmul(ffn_down, &act, total)?,
2798                        }
2799                    } else {
2800                        Self::ffn_act_lim(
2801                            e,
2802                            cfg,
2803                            &gate,
2804                            &up,
2805                            1.0,
2806                            1.0,
2807                            d_lim,
2808                            &mut act,
2809                            total * n_ff,
2810                        )?;
2811                        e.matmul(ffn_down, &act, total)?
2812                    }
2813                }
2814                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2815            };
2816            let mut x2 = e.uninit(total * n_embd)?;
2817            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2818            x = x2;
2819        }
2820        Ok(x)
2821    }
2822
2823    fn step35_prime_batch_epilogue(
2824        &self,
2825        e: &Engine,
2826        x: CudaSlice<f32>,
2827        ts: &[usize],
2828        offs: &[usize],
2829        caches: &mut [&mut Cache],
2830    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2831        let n_embd = self.cfg.n_embd as usize;
2832        let total: usize = ts.iter().sum();
2833        let mut hn = e.uninit(total * n_embd)?;
2834        e.rms_norm(
2835            &x,
2836            self.output_norm.float_data(),
2837            &mut hn,
2838            n_embd,
2839            total,
2840            self.cfg.rms_eps,
2841        )?;
2842
2843        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2844        let mut out = Vec::with_capacity(ts.len());
2845        for s in 0..ts.len() {
2846            let mut hidden = e.uninit(ts[s] * n_embd)?;
2847            e.copy_view_into(
2848                &mut hidden,
2849                0,
2850                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
2851                ts[s] * n_embd,
2852            )?;
2853            let last0 = (offs[s] + ts[s] - 1) * n_embd;
2854            let mut h_seed = e.uninit(n_embd)?;
2855            e.copy_view_into(
2856                &mut h_seed,
2857                0,
2858                &hidden_src.slice(last0..last0 + n_embd),
2859                n_embd,
2860            )?;
2861            // Exactness-first: the serial reference runs the output head at m=1.
2862            let mut hlast = e.uninit(n_embd)?;
2863            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
2864            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
2865            caches[s].pos += ts[s];
2866            out.push((logits, h_seed, hidden));
2867        }
2868        Ok(out)
2869    }
2870
2871    fn step35_prime_cache_batch(
2872        &self,
2873        e: &Engine,
2874        prompts: &[&[u32]],
2875        caches: &mut [&mut Cache],
2876    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2877        validate_step_prime_batch_modes(
2878            step_tp_prefill_enabled()?,
2879            step_ep_grouped_prefill_enabled()?,
2880        )?;
2881        if crate::pp::pp_host_bounce_active()
2882            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
2883        {
2884            return Err(
2885                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
2886                 stage split; refusing an unsplit remote-weight walk"
2887                    .into(),
2888            );
2889        }
2890        if !Self::step35_prime_batch_on() {
2891            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
2892        }
2893        if caches.iter().any(|c| c.pos != 0) {
2894            return Err(
2895                "step35 batched prime currently supports complete fresh prompts only; \
2896                 continuation/tick chunks require per-request queued_after"
2897                    .into(),
2898            );
2899        }
2900
2901        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2902        for &t in &ts {
2903            assert!(
2904                t >= PRIME_MIN_T,
2905                "step35 batched prime needs T >= {PRIME_MIN_T}"
2906            );
2907        }
2908        for (s, c) in caches.iter().enumerate() {
2909            assert!(
2910                ts[s] <= c.max_ctx,
2911                "step35 batched prime exceeds cache max_ctx"
2912            );
2913        }
2914        let offs: Vec<usize> = ts
2915            .iter()
2916            .scan(0usize, |a, &t| {
2917                let o = *a;
2918                *a += t;
2919                Some(o)
2920            })
2921            .collect();
2922        let total: usize = ts.iter().sum();
2923        let payload = total * self.cfg.n_embd as usize;
2924        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2925        let positions: Vec<Vec<i32>> = ts.iter().map(|&t| (0..t as i32).collect()).collect();
2926        let upload_positions =
2927            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
2928                positions
2929                    .iter()
2930                    .map(|p| e.htod_i32(p))
2931                    .collect::<Result<_, _>>()
2932            };
2933
2934        static ONCE: std::sync::Once = std::sync::Once::new();
2935        ONCE.call_once(|| {
2936            eprintln!(
2937                "[step35-prime-batch] first concat prime: B={} tokens={total}",
2938                prompts.len()
2939            );
2940        });
2941
2942        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
2943            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
2944                let rt = crate::pp::PpNRt::get(e)?;
2945                let n_st = fence.len() - 1;
2946                assert_eq!(
2947                    rt.n_stages(),
2948                    n_st,
2949                    "step35 prime batch stage count mismatch"
2950                );
2951                let caller_stream = e.stream();
2952                rt.fence_stages_behind(&caller_stream)?;
2953
2954                let mut slot = {
2955                    let _st0 = rt.enter(0);
2956                    let e0 = rt.engine(0, e);
2957                    let pos_ds = upload_positions(e0)?;
2958                    let x = self.embed(e0, &cat_tokens)?;
2959                    let x = self.step35_prime_batch_layers(
2960                        e0, x, fence[0], fence[1], &ts, &offs, &pos_ds, caches,
2961                    )?;
2962                    rt.tx(0, &x, payload)?
2963                };
2964                for s in 1..n_st - 1 {
2965                    let _st = rt.enter(s);
2966                    let es = rt.engine(s, e);
2967                    let pos_ds = upload_positions(es)?;
2968                    let x = rt.rx(s - 1, slot, payload)?;
2969                    let x = self.step35_prime_batch_layers(
2970                        es,
2971                        x,
2972                        fence[s],
2973                        fence[s + 1],
2974                        &ts,
2975                        &offs,
2976                        &pos_ds,
2977                        caches,
2978                    )?;
2979                    slot = rt.tx(s, &x, payload)?;
2980                }
2981
2982                let _stl = rt.enter(n_st - 1);
2983                let el = rt.engine(n_st - 1, e);
2984                let pos_ds = upload_positions(el)?;
2985                let x = rt.rx(n_st - 2, slot, payload)?;
2986                let x = self.step35_prime_batch_layers(
2987                    el,
2988                    x,
2989                    fence[n_st - 1],
2990                    fence[n_st],
2991                    &ts,
2992                    &offs,
2993                    &pos_ds,
2994                    caches,
2995                )?;
2996                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
2997                rt.publish_to(n_st - 1, &caller_stream)?;
2998                crate::pp::STEP35_PRIME_BATCH_SPLITS
2999                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3000                out
3001            } else {
3002                let pos_ds = upload_positions(e)?;
3003                let x = self.embed(e, &cat_tokens)?;
3004                let x = self.step35_prime_batch_layers(
3005                    e,
3006                    x,
3007                    0,
3008                    self.layers.len(),
3009                    &ts,
3010                    &offs,
3011                    &pos_ds,
3012                    caches,
3013                )?;
3014                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3015            }
3016        } else {
3017            let pos_ds = upload_positions(e)?;
3018            let x = self.embed(e, &cat_tokens)?;
3019            let x = self.step35_prime_batch_layers(
3020                e,
3021                x,
3022                0,
3023                self.layers.len(),
3024                &ts,
3025                &offs,
3026                &pos_ds,
3027                caches,
3028            )?;
3029            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3030        };
3031        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3032        Ok(out)
3033    }
3034
3035    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
3036    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
3037    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
3038    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
3039    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
3040    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
3041    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
3042    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
3043    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
3044    /// over the quantized past; Linear: the stateful pad_view twin — the same state
3045    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
3046    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
3047    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
3048    /// back to single-chunk serving).
3049    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
3050    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
3051    pub fn prime_cache_batch(
3052        &self,
3053        e: &Engine,
3054        prompts: &[&[u32]],
3055        caches: &mut [&mut Cache],
3056    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3057        if crate::pp::pp_cuts(self.layers.len()).is_some()
3058            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
3059        {
3060            return Err("pipeline rewrite is not qualified for batched prime".into());
3061        }
3062        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3063            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3064                return Err("neither batched-prime nor eager rewrite is qualified".into());
3065            }
3066            if prompts.len() != caches.len() {
3067                return Err("prime fallback prompt/cache shape mismatch".into());
3068            }
3069            static ONCE: std::sync::Once = std::sync::Once::new();
3070            ONCE.call_once(|| {
3071                eprintln!(
3072                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3073                );
3074            });
3075            return prompts
3076                .iter()
3077                .copied()
3078                .zip(caches.iter_mut())
3079                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3080                .collect();
3081        }
3082        let cfg = &self.cfg;
3083        let n_embd = cfg.n_embd as usize;
3084        let eps = cfg.rms_eps;
3085        let b = prompts.len();
3086        assert!(b >= 1 && b == caches.len());
3087        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3088        let carried = pos0s.iter().any(|&p| p > 0);
3089        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3090        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3091        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3092        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3093        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3094        if self.uses_gemma_program() {
3095            return Err(
3096                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3097                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3098                    .into(),
3099            );
3100        }
3101        // Step35 has a dedicated concat walk: the generic core below cannot express its
3102        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3103        if self.uses_sliding_gated_moe_program() {
3104            return self.step35_prime_cache_batch(e, prompts, caches);
3105        }
3106        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3107        for &t in &ts {
3108            assert!(
3109                t >= PRIME_MIN_T,
3110                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3111            );
3112        }
3113        for (s, c) in caches.iter().enumerate() {
3114            assert!(
3115                c.pos + ts[s] <= c.max_ctx,
3116                "prime_cache_batch: prompt exceeds cache max_ctx"
3117            );
3118        }
3119        let total: usize = ts.iter().sum();
3120        let offs: Vec<usize> = ts
3121            .iter()
3122            .scan(0usize, |a, &t| {
3123                let o = *a;
3124                *a += t;
3125                Some(o)
3126            })
3127            .collect();
3128        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3129        let pos_ds: Vec<CudaSlice<i32>> = ts
3130            .iter()
3131            .zip(&pos0s)
3132            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3133            .collect::<Result<_, _>>()?;
3134        // split a concat [total, dim] buffer into per-seq copies
3135        let split = |e: &Engine,
3136                     y: &CudaSlice<f32>,
3137                     dim: usize|
3138         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3139            let mut out = Vec::with_capacity(b);
3140            for s in 0..b {
3141                let mut ys = e.uninit(ts[s] * dim)?;
3142                e.copy_view_into(
3143                    &mut ys,
3144                    0,
3145                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3146                    ts[s] * dim,
3147                )?;
3148                out.push(ys);
3149            }
3150            Ok(out)
3151        };
3152
3153        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3154        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3155        for (il, layer) in self.layers.iter().enumerate() {
3156            let mut h = e.uninit(total * n_embd)?;
3157            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3158            e.rms_norm_f16out(
3159                &x,
3160                layer.attn_norm.float_data(),
3161                &mut h,
3162                &mut hx16,
3163                n_embd,
3164                total,
3165                eps,
3166            )?;
3167            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3168            let mut mixed = e.uninit(total * n_embd)?;
3169            match &layer.mixer {
3170                Mixer::Full(fa) => {
3171                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3172                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3173                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3174                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3175                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3176                    // back to the per-seq dispatch.
3177                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3178                    let (n_head, n_head_kv, head_dim) = (
3179                        geometry.n_head as usize,
3180                        geometry.n_head_kv as usize,
3181                        geometry.head_dim_k as usize,
3182                    );
3183                    let fa_scale = geometry.attention_scale();
3184                    let use_favl = !carried
3185                        && (2..=8).contains(&b)
3186                        && (head_dim == 256 || head_dim == 128)
3187                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3188                        && std::env::var("MEMRA_NOFA").is_err()
3189                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3190                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3191                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3192                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3193                    if use_favl {
3194                        let (qf_w, kf_w, vf_w) = (
3195                            fa.wq.out_features(),
3196                            fa.wk.out_features(),
3197                            fa.wv.out_features(),
3198                        );
3199                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3200                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3201                        // cannot check its own extents; `qf_w` is the wq out-features that set
3202                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3203                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3204                        struct APre {
3205                            q: CudaSlice<f32>,
3206                            gate: Option<CudaSlice<f32>>,
3207                            qn: CudaSlice<f32>,
3208                            kn: CudaSlice<f32>,
3209                        }
3210                        let mut aps = Vec::with_capacity(b);
3211                        for &t in ts.iter().take(b) {
3212                            aps.push(APre {
3213                                q: e.uninit(t * n_head * head_dim)?,
3214                                gate: Some(e.uninit(t * n_head * head_dim)?),
3215                                qn: e.uninit(t * n_head * head_dim)?,
3216                                kn: e.uninit(t * n_head_kv * head_dim)?,
3217                            });
3218                        }
3219                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3220                            let kvl = caches[0].kv[il].as_ref().unwrap();
3221                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3222                        };
3223                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3224                            .map(|s| {
3225                                let (o, t) = (offs[s], ts[s]);
3226                                let kvl = caches[s].kv[il].as_ref().unwrap();
3227                                assert!(
3228                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3229                                    "prime_cache_batch attn vl: fresh + capacity"
3230                                );
3231                                crate::AttnPreVl {
3232                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3233                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3234                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3235                                    q: e.addr_f32(&aps[s].q),
3236                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3237                                    qn: e.addr_f32(&aps[s].qn),
3238                                    kn: e.addr_f32(&aps[s].kn),
3239                                    kc: e.addr_u8(&kvl.k),
3240                                    vc: e.addr_u8(&kvl.v),
3241                                    t: t as i32,
3242                                    pad: 0,
3243                                }
3244                            })
3245                            .collect();
3246                        e.attn_pre_vl8(
3247                            &pargs,
3248                            fa.q_norm.float_data(),
3249                            fa.k_norm.float_data(),
3250                            head_dim,
3251                            geometry.n_rot as usize,
3252                            n_head,
3253                            n_head_kv,
3254                            self.cfg.rms_eps,
3255                            geometry.rope_base,
3256                            1.0,
3257                            kv_dim_k,
3258                            kv_dim_v,
3259                            ktb,
3260                            vtb,
3261                        )?;
3262                        for s in 0..b {
3263                            let kvl = caches[s].kv[il].as_mut().unwrap();
3264                            kvl.len += ts[s];
3265                            let new_len = kvl.len as i32;
3266                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3267                        }
3268                        let mut attns = Vec::with_capacity(b);
3269                        let mut mirrors = Vec::with_capacity(b);
3270                        for &t in ts.iter().take(b) {
3271                            attns.push(e.uninit(t * n_head * head_dim)?);
3272                            let n = t * n_head_kv * head_dim;
3273                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3274                        }
3275                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3276                        // promoted single-seq config is on; else the mma favl.
3277                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3278                            Ok("0") => false,
3279                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3280                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3281                            // portable build.
3282                            Ok("1") => {
3283                                crate::refuse_portable_force(
3284                                    "MEMRA_FA3=1",
3285                                    "the sm_90a fa3/bf16 kernels",
3286                                );
3287                                true
3288                            }
3289                            _ => cfg!(memra_hopper_mma),
3290                        };
3291                        if fa3_on {
3292                            let mut q16s = Vec::with_capacity(b);
3293                            let mut v16s = Vec::with_capacity(b);
3294                            for s in 0..b {
3295                                let t = ts[s];
3296                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3297                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3298                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3299                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3300                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3301                                e.f32_to_bf16_v(
3302                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3303                                    &mut v16,
3304                                    t * n_head_kv * head_dim,
3305                                )?;
3306                                q16s.push(q16);
3307                                v16s.push((k16, v16));
3308                            }
3309                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3310                            let mut kp = qp;
3311                            let mut vp = qp;
3312                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3313                            let mut tsv = [0i32; 8];
3314                            for s in 0..b {
3315                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3316                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3317                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3318                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3319                                tsv[s] = ts[s] as i32;
3320                            }
3321                            let rc = unsafe {
3322                                crate::fa3_vl_raw(
3323                                    qp.as_ptr(),
3324                                    kp.as_ptr(),
3325                                    vp.as_ptr(),
3326                                    op.as_ptr(),
3327                                    tsv.as_ptr(),
3328                                    b as i32,
3329                                    n_head as i32,
3330                                    n_head_kv as i32,
3331                                    head_dim as i32,
3332                                    fa_scale,
3333                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3334                                )
3335                            };
3336                            if rc != 0 {
3337                                return Err(format!("memra_fa3_vl rc={rc}").into());
3338                            }
3339                        } else {
3340                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3341                                .map(|s| crate::FaSeqVl {
3342                                    q: e.addr_f32(&aps[s].qn),
3343                                    k16: e.addr_u8(&mirrors[s].0),
3344                                    v16: e.addr_u8(&mirrors[s].1),
3345                                    o: e.addr_f32(&attns[s]),
3346                                    kf: e.addr_f32(&aps[s].kn),
3347                                    vf: e.addr_f32v(
3348                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3349                                    ),
3350                                    t: ts[s] as i32,
3351                                    pad: 0,
3352                                })
3353                                .collect();
3354                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3355                        }
3356                        for (s, attn) in attns.into_iter().enumerate() {
3357                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3358                                e,
3359                                attn,
3360                                &aps[s].gate,
3361                                ts[s],
3362                                n_head,
3363                                head_dim,
3364                            )?;
3365                            let mut done = false;
3366                            if let Some(xh) = &ag16 {
3367                                done = e.try_f16_gemm_pre_into_off(
3368                                    &fa.wo,
3369                                    xh,
3370                                    ts[s],
3371                                    &mut mixed,
3372                                    offs[s] * n_embd,
3373                                )?;
3374                            }
3375                            if !done {
3376                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3377                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3378                            }
3379                        }
3380                    } else {
3381                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3382                            (0..b).map(|_| Vec::new()).collect();
3383                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3384                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3385                                parts[s].push(ys);
3386                            }
3387                        }
3388                        for (s, g3s) in parts.into_iter().enumerate() {
3389                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3390                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3391                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3392                            )?;
3393                            let mut done = false;
3394                            if let Some(xh) = &ag16 {
3395                                done = e.try_f16_gemm_pre_into_off(
3396                                    &fa.wo,
3397                                    xh,
3398                                    ts[s],
3399                                    &mut mixed,
3400                                    offs[s] * n_embd,
3401                                )?;
3402                            }
3403                            if !done {
3404                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3405                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3406                            }
3407                        }
3408                    }
3409                }
3410                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3411                Mixer::Linear(la) => {
3412                    // task #16: NO split copies (cores read row-offset views of the concat
3413                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3414                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3415                    // varlen K5 launch for all sequences.
3416                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3417                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3418                    let outs =
3419                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3420                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3421                        let (o, t) = (offs[s], ts[s]);
3422                        let mut done = false;
3423                        if let Some(xh) = &gn16 {
3424                            done = e.try_f16_gemm_pre_into_off(
3425                                &la.ssm_out,
3426                                xh,
3427                                t,
3428                                &mut mixed,
3429                                o * n_embd,
3430                            )?;
3431                        }
3432                        if !done {
3433                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3434                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3435                        }
3436                    }
3437                }
3438            }
3439            let mut x1 = e.uninit(total * n_embd)?;
3440            let mut z = e.uninit(total * n_embd)?;
3441            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3442            e.add_rms_norm_f16out(
3443                &x,
3444                &mixed,
3445                layer.post_attn_norm.float_data(),
3446                &mut x1,
3447                &mut z,
3448                &mut zx16,
3449                n_embd,
3450                total,
3451                eps,
3452            )?;
3453            let ffn_out = match &layer.ffn {
3454                crate::hybrid::Ffn::Dense {
3455                    ffn_gate,
3456                    ffn_up,
3457                    ffn_down,
3458                } => {
3459                    let n_ff = ffn_gate.out_features();
3460                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3461                    let up = g2.pop().unwrap();
3462                    let gate = g2.pop().unwrap();
3463                    let mut act = e.uninit(total * n_ff)?;
3464                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3465                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3466                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3467                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3468                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3469                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3470                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3471                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3472                            Some(y) => y,
3473                            None => e.matmul(ffn_down, &act, total)?,
3474                        }
3475                    } else {
3476                        Self::ffn_act_lim(
3477                            e,
3478                            &self.cfg,
3479                            &gate,
3480                            &up,
3481                            1.0,
3482                            1.0,
3483                            d_lim,
3484                            &mut act,
3485                            total * n_ff,
3486                        )?;
3487                        e.matmul(ffn_down, &act, total)?
3488                    }
3489                }
3490                crate::hybrid::Ffn::Moe(m) => {
3491                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3492                }
3493            };
3494            let mut x2 = e.uninit(total * n_embd)?;
3495            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3496            x = x2;
3497        }
3498        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3499        let mut hn = e.uninit(total * n_embd)?;
3500        e.rms_norm(
3501            &x,
3502            self.output_norm.float_data(),
3503            &mut hn,
3504            n_embd,
3505            total,
3506            eps,
3507        )?;
3508        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3509        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3510        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3511        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3512        // argmax battery arbitrates, same as every other prefill GEMM change.
3513        let mut hcat = e.uninit(b * n_embd)?;
3514        for s in 0..b {
3515            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3516            e.copy_view_into(
3517                &mut hcat,
3518                s * n_embd,
3519                &hn.slice(last0..last0 + n_embd),
3520                n_embd,
3521            )?;
3522        }
3523        let logits_cat = if b >= 2 {
3524            e.try_f16_gemm(&self.output, &hcat, b)?
3525        } else {
3526            None
3527        };
3528        let logits_host: Option<Vec<f32>> = match &logits_cat {
3529            Some(lc) => Some(e.dtoh(lc)?),
3530            None => None,
3531        };
3532        let n_vocab = self.output.out_features();
3533        let mut hidden_all = if crate::spec::spec_hpost() {
3534            split(e, &hn, n_embd)?
3535        } else {
3536            split(e, &x, n_embd)?
3537        };
3538        let mut out = Vec::with_capacity(b);
3539        for s in 0..b {
3540            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3541            let mut h_seed = e.uninit(n_embd)?;
3542            if !crate::spec::spec_hpost() {
3543                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3544            } else {
3545                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3546            }
3547            let logits = match &logits_host {
3548                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3549                None => {
3550                    let mut hlast = e.uninit(n_embd)?;
3551                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3552                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3553                }
3554            };
3555            caches[s].pos += ts[s];
3556            out.push((logits, h_seed, hidden_all.remove(0)));
3557        }
3558        Ok(out)
3559    }
3560
3561    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3562    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3563    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3564    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3565    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3566    ///
3567    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3568    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3569    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3570    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3571    #[allow(clippy::too_many_arguments)]
3572    fn full_attn_prime(
3573        &self,
3574        e: &Engine,
3575        fa: &FullAttnLayer,
3576        h: &CudaSlice<f32>,
3577        hx: Option<&CudaSlice<u8>>,
3578        pos_d: &CudaSlice<i32>,
3579        t: usize,
3580        cache: &mut Cache,
3581        il: usize,
3582        seq_end: usize,
3583    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3584        if self.uses_sliding_gated_moe_program() {
3585            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3586        }
3587        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3588        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3589        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3590        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3591        let g3 = match hx {
3592            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3593            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3594        };
3595        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3596    }
3597
3598    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3599    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3600    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3601    fn full_attn_prime_core(
3602        &self,
3603        e: &Engine,
3604        fa: &FullAttnLayer,
3605        g3: Vec<CudaSlice<f32>>,
3606        pos_d: &CudaSlice<i32>,
3607        t: usize,
3608        cache: &mut Cache,
3609        il: usize,
3610    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3611        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3612        if let Some(xh) = &ag16 {
3613            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3614                return Ok(y);
3615            }
3616        }
3617        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3618    }
3619
3620    fn full_attn_prime_core_inner(
3621        &self,
3622        e: &Engine,
3623        fa: &FullAttnLayer,
3624        g3: Vec<CudaSlice<f32>>,
3625        pos_d: &CudaSlice<i32>,
3626        t: usize,
3627        cache: &mut Cache,
3628        il: usize,
3629    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3630        let cfg = &self.cfg;
3631        let geometry = cfg.full_attention_geometry_at(il as u32);
3632        let n_head = geometry.n_head as usize;
3633        let n_head_kv = geometry.n_head_kv as usize;
3634        let head_dim = geometry.head_dim_k as usize;
3635        let scale = geometry.attention_scale();
3636        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3637        let AttnPre { q, k, v, gate } = pre;
3638        let mut attn = e.uninit(t * n_head * head_dim)?;
3639        self.full_attn_prime_fa_dispatch(
3640            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3641        )?;
3642        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3643    }
3644
3645    /// task #18 (attn side): projections tail through KV append — everything before the
3646    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3647    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3648    #[allow(clippy::type_complexity)]
3649    fn full_attn_prime_pre_fa(
3650        &self,
3651        e: &Engine,
3652        fa: &FullAttnLayer,
3653        mut g3: Vec<CudaSlice<f32>>,
3654        pos_d: &CudaSlice<i32>,
3655        t: usize,
3656        cache: &mut Cache,
3657        il: usize,
3658    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3659        let cfg = &self.cfg;
3660        let geometry = cfg.full_attention_geometry_at(il as u32);
3661        let n_head = geometry.n_head as usize;
3662        let n_head_kv = geometry.n_head_kv as usize;
3663        let head_dim = geometry.head_dim_k as usize;
3664        let eps = cfg.rms_eps;
3665
3666        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3667        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3668        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3669        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3670        let v = g3.pop().unwrap();
3671        let mut k = g3.pop().unwrap();
3672        let qf = g3.pop().unwrap();
3673        let (mut q, gate) = if gated {
3674            let mut q = e.uninit(t * n_head * head_dim)?;
3675            let mut gate = e.uninit(t * n_head * head_dim)?;
3676            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3677            (q, Some(gate))
3678        } else {
3679            (qf, None)
3680        };
3681
3682        let mut qn = e.uninit(t * n_head * head_dim)?;
3683        e.rms_norm(
3684            &q,
3685            fa.q_norm.float_data(),
3686            &mut qn,
3687            head_dim,
3688            n_head * t,
3689            eps,
3690        )?;
3691        q = qn;
3692        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3693        e.rms_norm(
3694            &k,
3695            fa.k_norm.float_data(),
3696            &mut kn,
3697            head_dim,
3698            n_head_kv * t,
3699            eps,
3700        )?;
3701        k = kn;
3702        let rope_dims = geometry.n_rot as usize;
3703        e.rope_neox(
3704            &mut q,
3705            pos_d,
3706            head_dim,
3707            rope_dims,
3708            n_head,
3709            t,
3710            geometry.rope_base,
3711            1.0,
3712        )?;
3713        e.rope_neox(
3714            &mut k,
3715            pos_d,
3716            head_dim,
3717            rope_dims,
3718            n_head_kv,
3719            t,
3720            geometry.rope_base,
3721            1.0,
3722        )?;
3723
3724        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3725        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3726        {
3727            let kvl = cache.kv[il].as_mut().unwrap();
3728            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3729            e.append_kv_quantized_rows(
3730                &k,
3731                &v,
3732                &mut kvl.k,
3733                &mut kvl.v,
3734                kvl.len,
3735                t,
3736                kvl.kv_dim_k,
3737                kvl.kv_dim_v,
3738                kvl.k_tok_bytes,
3739                kvl.v_tok_bytes,
3740                crate::Engine::kv_fp8_on(),
3741            )?;
3742            kvl.len += t;
3743            let new_len = kvl.len as i32;
3744            e.set_i32_one(&mut kvl.len_d, new_len)?;
3745        }
3746
3747        let base_len = {
3748            let kvl = cache.kv[il].as_ref().unwrap();
3749            kvl.len - t // KV rows present BEFORE this chunk's append above
3750        };
3751        Ok((AttnPre { q, k, v, gate }, base_len))
3752    }
3753
3754    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3755    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3756    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3757    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3758    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3759    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3760    #[allow(clippy::too_many_arguments)]
3761    fn full_attn_prime_fa_dispatch(
3762        &self,
3763        e: &Engine,
3764        q: &CudaSlice<f32>,
3765        k: &CudaSlice<f32>,
3766        v: &CudaSlice<f32>,
3767        attn: &mut CudaSlice<f32>,
3768        base_len: usize,
3769        t: usize,
3770        cache: &mut Cache,
3771        il: usize,
3772        head_dim: usize,
3773        n_head: usize,
3774        n_head_kv: usize,
3775        scale: f32,
3776    ) -> Result<(), Box<dyn std::error::Error>> {
3777        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3778        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3779        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3780        // attend through the quantized cache exactly like every later chunk (quantize-then-
3781        // attend). One numeric class for every row => the chunk size cannot decide where a
3782        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3783        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
3784        // pin-the-boundary approach).
3785        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
3786        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
3787        // with the fix unconditional, only re-introducing the class edge can prove the gate
3788        // still detects the mechanism. Never on in a measured default run.
3789        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
3790            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3791                e.sdpa_naive(
3792                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3793                )?;
3794            } else {
3795                e.fa_prefill(
3796                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3797                )?;
3798            }
3799            return Ok(());
3800        }
3801        let kvl = cache.kv[il].as_ref().unwrap();
3802        let t_kv = base_len + t;
3803        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
3804        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
3805        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
3806        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
3807        // same numeric class, so the uniform contract holds on the fallback too.
3808        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3809            e.sdpa_naive_quantized_view(
3810                q,
3811                &k_view,
3812                &v_view,
3813                attn,
3814                head_dim,
3815                n_head,
3816                n_head_kv,
3817                t,
3818                t_kv,
3819                scale,
3820                true,
3821                kvl.k_tok_bytes,
3822                kvl.v_tok_bytes,
3823            )?;
3824            return Ok(());
3825        }
3826        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
3827        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
3828        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
3829        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
3830        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
3831        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
3832        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
3833        let deqw = std::env::var("MEMRA_PRIME_DEQW")
3834            .map(|v| v != "0")
3835            .unwrap_or(true);
3836        if deqw {
3837            e.fa_prefill_view_ws(
3838                q,
3839                &k_view,
3840                &v_view,
3841                attn,
3842                head_dim,
3843                n_head,
3844                n_head_kv,
3845                t,
3846                t_kv,
3847                scale,
3848                true,
3849                kvl.k_tok_bytes,
3850                kvl.v_tok_bytes,
3851                crate::Engine::kv_fp8_on(),
3852            )?;
3853        } else {
3854            e.fa_prefill_view(
3855                q,
3856                &k_view,
3857                &v_view,
3858                attn,
3859                head_dim,
3860                n_head,
3861                n_head_kv,
3862                t,
3863                t_kv,
3864                scale,
3865                true,
3866                kvl.k_tok_bytes,
3867                kvl.v_tok_bytes,
3868                crate::Engine::kv_fp8_on(),
3869            )?;
3870        }
3871        Ok(())
3872    }
3873
3874    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
3875    /// (bit-identical composition) and hands wo its fp16 operand directly.
3876    fn full_attn_prime_post_fa(
3877        &self,
3878        e: &Engine,
3879        attn: CudaSlice<f32>,
3880        gate: &Option<CudaSlice<f32>>,
3881        t: usize,
3882        n_head: usize,
3883        head_dim: usize,
3884    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3885        let (attn_g, ag16) = match gate {
3886            Some(gate) => {
3887                let n = t * n_head * head_dim;
3888                let mut ag = e.uninit(n)?;
3889                if Self::f16out_on(e, t) {
3890                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
3891                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
3892                    (ag, Some(a16))
3893                } else {
3894                    let mut gsig = e.uninit(n)?;
3895                    e.sigmoid(gate, &mut gsig, n)?;
3896                    e.mul(&attn, &gsig, &mut ag, n)?;
3897                    (ag, None)
3898                }
3899            }
3900            None => (attn, None),
3901        };
3902        Ok((attn_g, ag16))
3903    }
3904
3905    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
3906    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
3907    /// carried THROUGH the cache like the spec verify does: carried-ring conv
3908    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
3909    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
3910    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
3911    fn linear_attn_prime(
3912        &self,
3913        e: &Engine,
3914        la: &LinearAttnLayer,
3915        h: &CudaSlice<f32>,
3916        hx: Option<&CudaSlice<u8>>,
3917        t: usize,
3918        cache: &mut Cache,
3919        il: usize,
3920    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3921        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
3922        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3923        let g4 = match hx {
3924            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
3925            None => e.matmul_group(&ws, h, t)?,
3926        };
3927        self.linear_attn_prime_core(e, la, g4, t, cache, il)
3928    }
3929
3930    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
3931    fn linear_attn_prime_core(
3932        &self,
3933        e: &Engine,
3934        la: &LinearAttnLayer,
3935        mut g4: Vec<CudaSlice<f32>>,
3936        t: usize,
3937        cache: &mut Cache,
3938        il: usize,
3939    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3940        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
3941    }
3942
3943    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
3944    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
3945    /// conv ring writes back from the true tail. None = classic path, byte-identical.
3946    #[allow(clippy::too_many_arguments)]
3947    fn linear_attn_prime_core_pad_inner(
3948        &self,
3949        e: &Engine,
3950        la: &LinearAttnLayer,
3951        mut g4: Vec<CudaSlice<f32>>,
3952        t: usize,
3953        cache: &mut Cache,
3954        il: usize,
3955        pad_len: Option<&CudaSlice<i32>>,
3956    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3957        // shim over the view twin (task #16): full-range views of the owned buffers.
3958        let geometry = la.geometry;
3959        let d_state = geometry.key_head_dim as usize;
3960        let num_k = geometry.key_heads as usize;
3961        let num_v = geometry.value_heads as usize;
3962        let key_dim = d_state * num_k;
3963        let value_dim = geometry.value_head_dim as usize * num_v;
3964        let conv_dim = key_dim * 2 + value_dim;
3965        let alpha = g4.pop().unwrap(); // [T, num_v]
3966        let beta_raw = g4.pop().unwrap(); // [T, num_v]
3967        let z = g4.pop().unwrap(); // [T, value_dim]
3968        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
3969        self.linear_attn_prime_core_pad_view(
3970            e,
3971            la,
3972            &qkv_mixed.slice(0..t * conv_dim),
3973            &z.slice(0..t * value_dim),
3974            &beta_raw.slice(0..t * num_v),
3975            &alpha.slice(0..t * num_v),
3976            t,
3977            cache,
3978            il,
3979            pad_len,
3980        )
3981    }
3982
3983    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
3984    /// shared verbatim by the per-seq scan path and the varlen batched path.
3985    #[allow(clippy::too_many_arguments)]
3986    fn linear_attn_gdn_prep(
3987        &self,
3988        e: &Engine,
3989        la: &LinearAttnLayer,
3990        qkv_mixed: &cudarc::driver::CudaView<f32>,
3991        beta_raw: &cudarc::driver::CudaView<f32>,
3992        alpha: &cudarc::driver::CudaView<f32>,
3993        t: usize,
3994        cache: &mut Cache,
3995        il: usize,
3996        pad_len: Option<&CudaSlice<i32>>,
3997    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
3998        let cfg = &self.cfg;
3999        let geometry = la.geometry;
4000        let d_state = geometry.key_head_dim as usize;
4001        let num_k = geometry.key_heads as usize;
4002        let num_v = geometry.value_heads as usize;
4003        let d_conv = geometry.conv_kernel as usize;
4004        let key_dim = d_state * num_k; // 2048
4005        let value_dim = geometry.value_head_dim as usize * num_v;
4006        let conv_dim = key_dim * 2 + value_dim; // 8192
4007        let eps = cfg.rms_eps;
4008        debug_assert!(
4009            t >= d_conv - 1,
4010            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
4011        );
4012
4013        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
4014        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
4015        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
4016        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
4017        let rl = cache.recur[il].as_mut().unwrap();
4018        let hk = Self::gdn_hk(e, t, num_v, num_k);
4019        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
4020        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
4021        let mut q_g = e.uninit(d_state * hk * t)?;
4022        let mut k_g = e.uninit(d_state * hk * t)?;
4023        let mut v_g = e.uninit(d_state * num_v * t)?;
4024        if conv_fuse {
4025            e.ssm_conv1d_gdn_state_pad(
4026                qkv_mixed,
4027                &mut rl.conv_state,
4028                la.ssm_conv1d.float_data(),
4029                &mut q_g,
4030                &mut k_g,
4031                &mut v_g,
4032                conv_dim,
4033                t,
4034                d_conv,
4035                d_state,
4036                num_v,
4037                num_k,
4038                key_dim,
4039                hk,
4040                pad_len,
4041            )?;
4042        } else {
4043            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
4044            e.ssm_conv1d_tm_state_pad_v(
4045                qkv_mixed,
4046                &mut rl.conv_state,
4047                la.ssm_conv1d.float_data(),
4048                &mut conv_out,
4049                conv_dim,
4050                t,
4051                d_conv,
4052                pad_len,
4053            )?;
4054            e.qkv_to_gdn_repack(
4055                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
4056            )?;
4057        }
4058        let mut q_l2 = e.uninit(d_state * hk * t)?;
4059        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
4060        // Emitted only where a consumer exists (the wgmma config) — on other arches the
4061        // alloc + epilogue stores would be pure waste.
4062        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4063            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4064            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4065            Some(qb)
4066        } else {
4067            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4068            None
4069        };
4070        let mut k_l2 = e.uninit(d_state * hk * t)?;
4071        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4072        let kb16 = if Engine::l2_v2_on(d_state) {
4073            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4074            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4075            Some(kb)
4076        } else {
4077            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4078            None
4079        };
4080        let mut beta = e.uninit(t * num_v)?;
4081        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4082        let mut g_log = e.uninit(t * num_v)?;
4083        e.gdn_glog_v(
4084            alpha,
4085            la.ssm_dt.float_data(),
4086            la.ssm_a.float_data(),
4087            &mut g_log,
4088            num_v,
4089            t,
4090        )?;
4091        if let Some(len_d) = pad_len {
4092            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4093        }
4094        Ok(GdnPrep {
4095            hk,
4096            q_l2,
4097            k_l2,
4098            v_g,
4099            beta,
4100            g_log,
4101            kb16,
4102            qb16,
4103        })
4104    }
4105
4106    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4107    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4108    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4109    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4110    #[allow(clippy::too_many_arguments)]
4111    fn linear_attn_prime_core_batch(
4112        &self,
4113        e: &Engine,
4114        la: &LinearAttnLayer,
4115        g4: &[CudaSlice<f32>],
4116        offs: &[usize],
4117        ts: &[usize],
4118        caches: &mut [&mut Cache],
4119        il: usize,
4120    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4121        let geometry = la.geometry;
4122        let d_state = geometry.key_head_dim as usize;
4123        let num_k = geometry.key_heads as usize;
4124        let num_v = geometry.value_heads as usize;
4125        let d_conv = geometry.conv_kernel as usize;
4126        let key_dim = d_state * num_k;
4127        let value_dim = geometry.value_head_dim as usize * num_v;
4128        let conv_dim = key_dim * 2 + value_dim;
4129        let eps = self.cfg.rms_eps;
4130        let scale = 1.0 / (d_state as f32).sqrt();
4131        let b = ts.len();
4132        let c = Engine::gdn_chunk_size();
4133        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4134        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4135        let carried = caches.iter().any(|c| c.pos > 0);
4136        let use_vl = !carried
4137            && (2..=8).contains(&b)
4138            && Engine::gdn_chunked_enabled()
4139            && ts.iter().all(|&t| t >= 16)
4140            && e.gdn_mma_enabled(c)
4141            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4142        if !use_vl {
4143            return (0..b)
4144                .map(|s| {
4145                    let (o, t) = (offs[s], ts[s]);
4146                    self.linear_attn_prime_core_pad_view(
4147                        e,
4148                        la,
4149                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4150                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4151                        &g4[2].slice(o * num_v..(o + t) * num_v),
4152                        &g4[3].slice(o * num_v..(o + t) * num_v),
4153                        t,
4154                        caches[s],
4155                        il,
4156                        None,
4157                    )
4158                })
4159                .collect();
4160        }
4161        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4162        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4163        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4164        struct SeqBufs {
4165            conv_out: CudaSlice<f32>,
4166            q_g: CudaSlice<f32>,
4167            k_g: CudaSlice<f32>,
4168            v_g: CudaSlice<f32>,
4169            q_l2: CudaSlice<f32>,
4170            k_l2: CudaSlice<f32>,
4171            beta: CudaSlice<f32>,
4172            g_log: CudaSlice<f32>,
4173            gn: CudaSlice<f32>,
4174            gn16: CudaSlice<u8>,
4175        }
4176        let f16o = Self::f16out_on(e, 16);
4177        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4178        let mut sb = Vec::with_capacity(b);
4179        let mut pres = Vec::with_capacity(b);
4180        for &t in ts.iter().take(b) {
4181            sb.push(SeqBufs {
4182                conv_out: e.uninit(conv_dim * t)?,
4183                q_g: e.uninit(d_state * hk * t)?,
4184                k_g: e.uninit(d_state * hk * t)?,
4185                v_g: e.uninit(d_state * num_v * t)?,
4186                q_l2: e.uninit(d_state * hk * t)?,
4187                k_l2: e.uninit(d_state * hk * t)?,
4188                beta: e.uninit(t * num_v)?,
4189                g_log: e.uninit(t * num_v)?,
4190                gn: e.uninit(d_state * num_v * t)?,
4191                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4192            });
4193            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4194        }
4195        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4196            .map(|s| {
4197                let (o, t) = (offs[s], ts[s]);
4198                let rl = caches[s].recur[il].as_ref().unwrap();
4199                crate::GdnPrepVl {
4200                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4201                    conv_state: e.addr_f32(&rl.conv_state),
4202                    conv_out: e.addr_f32(&sb[s].conv_out),
4203                    q_g: e.addr_f32(&sb[s].q_g),
4204                    k_g: e.addr_f32(&sb[s].k_g),
4205                    v_g: e.addr_f32(&sb[s].v_g),
4206                    q_l2: e.addr_f32(&sb[s].q_l2),
4207                    k_l2: e.addr_f32(&sb[s].k_l2),
4208                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4209                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4210                    beta: e.addr_f32(&sb[s].beta),
4211                    g_log: e.addr_f32(&sb[s].g_log),
4212                    o: e.addr_f32(&pres[s].o),
4213                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4214                    gn: e.addr_f32(&sb[s].gn),
4215                    gn16: e.addr_u8(&sb[s].gn16),
4216                    kb16: if Engine::l2_v2_on(d_state) {
4217                        e.addr_u8(&pres[s].kb16)
4218                    } else {
4219                        0
4220                    },
4221                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4222                        e.addr_u8(&pres[s].qb16)
4223                    } else {
4224                        0
4225                    },
4226                    t: t as i32,
4227                    pad: 0,
4228                }
4229            })
4230            .collect();
4231        let args: Vec<crate::GdnSeqVl> = (0..b)
4232            .map(|s| {
4233                let rl = caches[s].recur[il].as_ref().unwrap();
4234                crate::GdnSeqVl {
4235                    kb16: e.addr_u8(&pres[s].kb16),
4236                    gcum: e.addr_f32(&pres[s].gcum),
4237                    beta: e.addr_f32(&sb[s].beta),
4238                    u: e.addr_f32(&pres[s].u),
4239                    wb16: e.addr_u8(&pres[s].wb16),
4240                    y: e.addr_u8(&pres[s].y16),
4241                    ssnap: e.addr_u8(&pres[s].ssnap16),
4242                    state_in: e.addr_f32(&rl.ssm_state),
4243                    state_out: e.addr_f32(&rl.ssm_state_alt),
4244                    q: e.addr_f32(&sb[s].q_l2),
4245                    p: e.addr_f32(&pres[s].p),
4246                    o: e.addr_f32(&pres[s].o),
4247                    k: e.addr_f32(&sb[s].k_l2),
4248                    v: e.addr_f32(&sb[s].v_g),
4249                    g: e.addr_f32(&sb[s].g_log),
4250                    a: e.addr_f32(&pres[s].a),
4251                    w: e.addr_f32(&pres[s].w),
4252                    t: ts[s] as i32,
4253                    nc: pres[s].nc as i32,
4254                }
4255            })
4256            .collect();
4257        e.gdn_prep_vl8(
4258            &prep_args,
4259            la.ssm_conv1d.float_data(),
4260            la.ssm_dt.float_data(),
4261            la.ssm_a.float_data(),
4262            conv_dim,
4263            d_conv,
4264            d_state,
4265            num_v,
4266            num_k,
4267            key_dim,
4268            hk,
4269            eps,
4270        )?;
4271        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4272        // both standalone mirror launches vanish on the default config.
4273        if !Engine::l2_v2_on(d_state) {
4274            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4275        }
4276        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4277        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4278            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4279            if !Engine::l2_v2_on(d_state) {
4280                for s in 0..b {
4281                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4282                }
4283            }
4284            let mut wa = [crate::GdnWVl::default(); 8];
4285            for s in 0..b {
4286                wa[s] = crate::GdnWVl {
4287                    qb16: e.addr_u8(&pres[s].qb16),
4288                    pb16: e.addr_u8(&pres[s].pb16),
4289                };
4290            }
4291            Some(crate::GdnWVl8(wa))
4292        } else {
4293            None
4294        };
4295        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4296        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4297        if f16o {
4298            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4299        }
4300        // per-seq state swap (+ non-f16out tail fallback)
4301        let mut out = Vec::with_capacity(b);
4302        for (s, bufs) in sb.into_iter().enumerate() {
4303            let rl = caches[s].recur[il].as_mut().unwrap();
4304            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4305            let (o, t) = (offs[s], ts[s]);
4306            let SeqBufs { mut gn, gn16, .. } = bufs;
4307            if f16o {
4308                out.push((gn, Some(gn16)));
4309            } else {
4310                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4311                e.gated_rmsnorm_zv(
4312                    &pres[s].o,
4313                    la.ssm_norm.float_data(),
4314                    &z_v,
4315                    &mut gn,
4316                    d_state,
4317                    num_v * t,
4318                    eps,
4319                )?;
4320                out.push((gn, None));
4321            }
4322        }
4323        Ok(out)
4324    }
4325
4326    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4327    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4328    /// Same kernels, same values, byte-identical to the Vec shim above.
4329    #[allow(clippy::too_many_arguments)]
4330    fn linear_attn_prime_core_pad_view(
4331        &self,
4332        e: &Engine,
4333        la: &LinearAttnLayer,
4334        qkv_mixed: &cudarc::driver::CudaView<f32>,
4335        z: &cudarc::driver::CudaView<f32>,
4336        beta_raw: &cudarc::driver::CudaView<f32>,
4337        alpha: &cudarc::driver::CudaView<f32>,
4338        t: usize,
4339        cache: &mut Cache,
4340        il: usize,
4341        pad_len: Option<&CudaSlice<i32>>,
4342    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4343        let cfg = &self.cfg;
4344        let geometry = la.geometry;
4345        let d_state = geometry.key_head_dim as usize;
4346        let num_v = geometry.value_heads as usize;
4347        let eps = cfg.rms_eps;
4348        let scale = 1.0 / (d_state as f32).sqrt();
4349
4350        let prep =
4351            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4352
4353        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4354        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4355        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4356        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4357        // verify keep the sequential kernel).
4358        let mut o = e.uninit(d_state * num_v * t)?;
4359        let rl = cache.recur[il].as_mut().unwrap();
4360        {
4361            let crate::cache::RecurLayer {
4362                ssm_state,
4363                ssm_state_alt,
4364                ..
4365            } = rl;
4366            e.gdn_scan_prefill(
4367                &prep.q_l2,
4368                &prep.k_l2,
4369                &prep.v_g,
4370                &prep.g_log,
4371                &prep.beta,
4372                prep.kb16.as_ref(),
4373                prep.qb16.as_ref(),
4374                ssm_state,
4375                ssm_state_alt,
4376                &mut o,
4377                num_v,
4378                t,
4379                scale,
4380                prep.hk,
4381            )?;
4382        }
4383        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4384
4385        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4386        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4387        let mut gn = e.uninit(d_state * num_v * t)?;
4388        let gn16 = if Self::f16out_on(e, t) {
4389            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4390            e.gated_rmsnorm_f16out_zv(
4391                &o,
4392                la.ssm_norm.float_data(),
4393                z,
4394                &mut gn,
4395                &mut g16,
4396                d_state,
4397                num_v * t,
4398                eps,
4399            )?;
4400            Some(g16)
4401        } else {
4402            e.gated_rmsnorm_zv(
4403                &o,
4404                la.ssm_norm.float_data(),
4405                z,
4406                &mut gn,
4407                d_state,
4408                num_v * t,
4409                eps,
4410            )?;
4411            None
4412        };
4413        Ok((gn, gn16))
4414    }
4415
4416    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4417    #[allow(clippy::too_many_arguments)]
4418    fn linear_attn_prime_core_pad(
4419        &self,
4420        e: &Engine,
4421        la: &LinearAttnLayer,
4422        g4: Vec<CudaSlice<f32>>,
4423        t: usize,
4424        cache: &mut Cache,
4425        il: usize,
4426        pad_len: Option<&CudaSlice<i32>>,
4427    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4428        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4429        if let Some(xh) = &gn16 {
4430            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4431                return Ok(y);
4432            }
4433        }
4434        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4435    }
4436
4437    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4438    ///
4439    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4440    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4441    pub fn full_attn(
4442        &self,
4443        e: &Engine,
4444        fa: &FullAttnLayer,
4445        h: &CudaSlice<f32>,
4446        pos_d: &CudaSlice<i32>,
4447        t: usize,
4448        il: usize,
4449    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4450        if self.uses_sliding_gated_moe_program() {
4451            return self.step35_attn(e, fa, h, pos_d, t, il);
4452        }
4453        let cfg = &self.cfg;
4454        let _n_embd = cfg.n_embd as usize;
4455        let geometry = cfg.full_attention_geometry_at(il as u32);
4456        let n_head = geometry.n_head as usize;
4457        let n_head_kv = geometry.n_head_kv as usize;
4458        let head_dim = geometry.head_dim_k as usize;
4459        let eps = cfg.rms_eps;
4460        let scale = geometry.attention_scale();
4461
4462        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4463        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4464        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4465        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4466        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4467        let v = g3.pop().unwrap();
4468        let mut k = g3.pop().unwrap();
4469        let qf = g3.pop().unwrap();
4470        let (mut q, gate) = if gated {
4471            let mut q = e.uninit(t * n_head * head_dim)?;
4472            let mut gate = e.uninit(t * n_head * head_dim)?;
4473            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4474            (q, Some(gate))
4475        } else {
4476            (qf, None)
4477        };
4478
4479        // QK-norm (per head_dim row), then partial RoPE.
4480        let mut qn = e.uninit(t * n_head * head_dim)?;
4481        e.rms_norm(
4482            &q,
4483            fa.q_norm.float_data(),
4484            &mut qn,
4485            head_dim,
4486            n_head * t,
4487            eps,
4488        )?;
4489        q = qn;
4490        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4491        e.rms_norm(
4492            &k,
4493            fa.k_norm.float_data(),
4494            &mut kn,
4495            head_dim,
4496            n_head_kv * t,
4497            eps,
4498        )?;
4499        k = kn;
4500        let rope_dims = geometry.n_rot as usize;
4501        e.rope_neox(
4502            &mut q,
4503            pos_d,
4504            head_dim,
4505            rope_dims,
4506            n_head,
4507            t,
4508            geometry.rope_base,
4509            1.0,
4510        )?;
4511        e.rope_neox(
4512            &mut k,
4513            pos_d,
4514            head_dim,
4515            rope_dims,
4516            n_head_kv,
4517            t,
4518            geometry.rope_base,
4519            1.0,
4520        )?;
4521
4522        // SDPA
4523        let mut attn = e.uninit(t * n_head * head_dim)?;
4524        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4525        // falls back to naive sdpa.
4526        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4527            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4528            e.sdpa_naive(
4529                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4530            )?;
4531        } else {
4532            e.fa_prefill(
4533                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4534            )?;
4535        }
4536
4537        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4538        let attn_g = match &gate {
4539            Some(gate) => {
4540                let mut gsig = e.uninit(t * n_head * head_dim)?;
4541                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4542                let mut ag = e.uninit(t * n_head * head_dim)?;
4543                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4544                ag
4545            }
4546            None => attn,
4547        };
4548
4549        // o projection
4550        let o = e.matmul(&fa.wo, &attn_g, t)?;
4551        Ok(o)
4552    }
4553
4554    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4555    pub fn linear_attn(
4556        &self,
4557        e: &Engine,
4558        la: &LinearAttnLayer,
4559        h: &CudaSlice<f32>,
4560        t: usize,
4561    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4562        let cfg = &self.cfg;
4563        let _n_embd = cfg.n_embd as usize;
4564        let geometry = la.geometry;
4565        let d_state = geometry.key_head_dim as usize;
4566        let num_k = geometry.key_heads as usize;
4567        let num_v = geometry.value_heads as usize;
4568        let d_conv = geometry.conv_kernel as usize;
4569        let head_k = d_state;
4570        let head_v = geometry.value_head_dim as usize;
4571        let key_dim = head_k * num_k; // 2048
4572        let value_dim = head_v * num_v; // 4096
4573        let conv_dim = key_dim * 2 + value_dim; // 8192
4574        let eps = cfg.rms_eps;
4575        let scale = 1.0 / (d_state as f32).sqrt();
4576
4577        // projections
4578        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4579        let mut g4 = e.matmul_group(
4580            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4581            h,
4582            t,
4583        )?;
4584        let alpha = g4.pop().unwrap(); // [T, num_v]
4585        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4586        let z = g4.pop().unwrap(); // [T, value_dim]
4587        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4588
4589        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4590        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4591        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4592        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4593        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4594        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4595        let _ = (head_k, head_v);
4596        let mut q_g = e.uninit(d_state * num_v * t)?;
4597        let mut k_g = e.uninit(d_state * num_v * t)?;
4598        let mut v_g = e.uninit(d_state * num_v * t)?;
4599        e.ssm_conv1d_gdn(
4600            &qkv_mixed,
4601            la.ssm_conv1d.float_data(),
4602            &mut q_g,
4603            &mut k_g,
4604            &mut v_g,
4605            conv_dim,
4606            t,
4607            d_conv,
4608            d_state,
4609            num_v,
4610            num_k,
4611            key_dim,
4612        )?;
4613        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4614        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4615        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4616        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4617        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4618        let v_gd = v_g;
4619
4620        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4621        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4622        let mut beta = e.uninit(t * num_v)?;
4623        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4624        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4625        let mut g_log = e.uninit(t * num_v)?;
4626        e.gdn_glog(
4627            &alpha,
4628            la.ssm_dt.float_data(),
4629            la.ssm_a.float_data(),
4630            &mut g_log,
4631            num_v,
4632            t,
4633        )?;
4634
4635        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4636        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4637        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4638        let mut o = e.uninit(d_state * num_v * t)?;
4639        e.gdn_scan_prefill(
4640            &q_l2,
4641            &k_l2,
4642            &v_gd,
4643            &g_log,
4644            &beta,
4645            None,
4646            None,
4647            &state_in,
4648            &mut state_out,
4649            &mut o,
4650            num_v,
4651            t,
4652            scale,
4653            num_v,
4654        )?;
4655
4656        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4657        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4658        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4659        // o rows are (t*num_v+vh) too. Good.
4660        let mut gn = e.uninit(d_state * num_v * t)?;
4661        e.gated_rmsnorm(
4662            &o,
4663            la.ssm_norm.float_data(),
4664            &z,
4665            &mut gn,
4666            d_state,
4667            num_v * t,
4668            eps,
4669        )?;
4670
4671        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4672        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4673        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4674        let out = e.matmul(&la.ssm_out, &gn, t)?;
4675        Ok(out)
4676    }
4677}
4678
4679impl HybridModel {
4680    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4681    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4682    ///
4683    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4684    /// different 860160-byte block than the same expert of layer 7).
4685    ///
4686    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4687    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4688    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4689    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4690    pub fn moe_ffn_il(
4691        &self,
4692        e: &Engine,
4693        m: &MoeWeights,
4694        z: &CudaSlice<f32>,
4695        t: usize,
4696        il: u16,
4697    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4698        Self::moe_ffn_inner(
4699            e,
4700            m,
4701            z,
4702            None,
4703            t,
4704            &self.cfg,
4705            il,
4706            self.max_moe_block(),
4707            false,
4708            None,
4709            self.uses_sliding_gated_moe_program(),
4710        )
4711    }
4712
4713    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4714    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4715    pub fn moe_ffn_il_prefill(
4716        &self,
4717        e: &Engine,
4718        m: &MoeWeights,
4719        z: &CudaSlice<f32>,
4720        t: usize,
4721        il: u16,
4722    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4723        Self::moe_ffn_inner(
4724            e,
4725            m,
4726            z,
4727            None,
4728            t,
4729            &self.cfg,
4730            il,
4731            self.max_moe_block(),
4732            true,
4733            Some(&self.step_grouped_prefill),
4734            self.uses_sliding_gated_moe_program(),
4735        )
4736    }
4737
4738    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4739    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4740    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4741    pub fn moe_ffn_il_zq8(
4742        &self,
4743        e: &Engine,
4744        m: &MoeWeights,
4745        z: &CudaSlice<f32>,
4746        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4747        t: usize,
4748        il: u16,
4749    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4750        Self::moe_ffn_inner(
4751            e,
4752            m,
4753            z,
4754            zq8,
4755            t,
4756            &self.cfg,
4757            il,
4758            self.max_moe_block(),
4759            false,
4760            None,
4761            self.uses_sliding_gated_moe_program(),
4762        )
4763    }
4764
4765    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4766    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4767    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4768    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4769    ///
4770    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4771    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4772    pub(crate) fn moe_ffn(
4773        e: &Engine,
4774        m: &MoeWeights,
4775        z: &CudaSlice<f32>,
4776        t: usize,
4777        cfg: &ModelConfig,
4778        il: u16,
4779        max_block: usize,
4780    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4781        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4782    }
4783
4784    #[allow(clippy::too_many_arguments)]
4785    pub(crate) fn moe_ffn_inner(
4786        e: &Engine,
4787        m: &MoeWeights,
4788        z: &CudaSlice<f32>,
4789        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4790        t: usize,
4791        cfg: &ModelConfig,
4792        il: u16,
4793        max_block: usize,
4794        prefill: bool,
4795        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
4796        sliding_gated_moe: bool,
4797    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4798        let worker_io = crate::spill_pread::worker_enabled();
4799        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
4800        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
4801            e.with_moe_cache(max_block, |cache, _| {
4802                cache.begin_forward_epoch(il, t);
4803                if worker_io {
4804                    cache.begin_worker_scope();
4805                }
4806                Ok(())
4807            })?;
4808        }
4809        if m.step_ep.is_some() || m.step_tp.is_some() {
4810            let moe = cfg
4811                .moe
4812                .as_ref()
4813                .ok_or("Step distributed execution requires MoE model metadata")?;
4814            let n_embd = cfg.n_embd as usize;
4815            let n_expert = moe.expert_count as usize;
4816            let n_used = moe.expert_used_count as usize;
4817            let sigmoid = cfg
4818                .sigmoid_router()
4819                .ok_or("Step distributed execution requires the Step sigmoid router")?;
4820            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
4821            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
4822            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
4823            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
4824                return Err(
4825                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
4826                );
4827            }
4828            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
4829                return Err(format!(
4830                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
4831                    PRIME_MIN_T,
4832                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
4833                )
4834                .into());
4835            }
4836            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
4837            let grouped_prefill_shape =
4838                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
4839            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
4840                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
4841            }) {
4842                let (selected, route_weights) =
4843                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
4844                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
4845                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
4846                Self::trace_moe_input(e, il, t, n_embd, z)?;
4847                let selected = selected
4848                    .iter()
4849                    .map(|&expert| expert as usize)
4850                    .collect::<Vec<_>>();
4851
4852                // The narrow route readback above orders the owning-stage producer. The grouped
4853                // runtime then copies the resident root activation into its persistent rank inputs.
4854                e.stream().synchronize()?;
4855                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
4856                    state.projection.set_activation_limit(ep.activation_limit)?;
4857                    ep.runtime
4858                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
4859                            ep.experts.e4m3()?,
4860                            &mut state.projection,
4861                            z,
4862                            t,
4863                            &selected,
4864                        )?;
4865                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
4866                        &state.projection,
4867                        &mut state.combine,
4868                        &route_weights,
4869                    )?;
4870                    ep.runtime.execute_step_grouped_expert_parallel_gate(
4871                        ep.experts.e4m3()?,
4872                        &mut state.projection,
4873                    )?;
4874                    ep.runtime.execute_step_grouped_expert_parallel_combine(
4875                        &state.projection,
4876                        &mut state.combine,
4877                    )?;
4878                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
4879                        &state.projection,
4880                        &state.combine,
4881                        e,
4882                    )?;
4883                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
4884                    if prefill {
4885                        // A shared plan may be reused by the next layer on a different runtime
4886                        // stream. Complete the owning-stage copy before its source is overwritten.
4887                        e.stream().synchronize()?;
4888                    }
4889                    eprintln!(
4890                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
4891                         attention_layout=tensor-parallel expert_layout=expert-parallel \
4892                         expert_transport={} native_p2p=true route_control=host-narrow \
4893                         input=root-device projection_workspaces=persistent \
4894                         combine=root-device output=owning-stage-device \
4895                         prefill={prefill} batched_decode=false capacity={} \
4896                         performance_claim=false",
4897                        ep.devices,
4898                        ep.runtime.transport_label(),
4899                        state.projection.max_tokens(),
4900                    );
4901                    Ok::<_, Box<dyn std::error::Error>>(output)
4902                };
4903
4904                if grouped_prefill_shape {
4905                    let grouped_prefill = grouped_prefill
4906                        .ok_or("Step grouped prefill has no model-scoped executor")?;
4907                    let mut shared = grouped_prefill
4908                        .lock()
4909                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
4910                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
4911                        state.devices != ep.devices
4912                            || state.grouped.projection.max_tokens() < t
4913                            || state.grouped.projection.input_width() != n_embd
4914                            || state.grouped.projection.expert_width()
4915                                != moe.expert_ff_length as usize
4916                    });
4917                    if needs_prepare {
4918                        let seed_input = vec![0.0f32; n_embd];
4919                        let seed_selected = &selected[..n_used];
4920                        let seed_weights = &route_weights[..n_used];
4921                        let projection = ep
4922                            .runtime
4923                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
4924                                ep.experts.e4m3()?,
4925                                &seed_input,
4926                                1,
4927                                seed_selected,
4928                                ep.activation_limit,
4929                                t,
4930                            )?;
4931                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
4932                            &projection,
4933                            seed_weights,
4934                        )?;
4935                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
4936                            devices: ep.devices.clone(),
4937                            grouped: crate::hybrid::StepEpGroupedDecode {
4938                                projection,
4939                                combine,
4940                            },
4941                        });
4942                        eprintln!(
4943                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
4944                             shared_across_layers=true performance_claim=false",
4945                            ep.devices,
4946                        );
4947                    }
4948                    return execute(
4949                        &mut shared
4950                            .state
4951                            .as_mut()
4952                            .expect("Step grouped prefill state prepared above")
4953                            .grouped,
4954                    );
4955                }
4956
4957                let mut grouped = ep
4958                    .grouped_decode
4959                    .as_ref()
4960                    .expect("grouped decode presence checked above")
4961                    .lock()
4962                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
4963                return execute(&mut grouped);
4964            }
4965            if grouped_prefill_shape {
4966                return Err(
4967                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
4968                        .into(),
4969                );
4970            }
4971            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
4972            // expert program — the per-layer host logits readback (the last per-layer host
4973            // sync) disappears. Selection tie-breaking may differ from the host router:
4974            // numeric-class door, run-gen argmax gate + boot battery.
4975            if t == 1
4976                && crate::tp::step_nvfp4_dev_routes_enabled()?
4977                && crate::tp::step_tp_dev_router_enabled()?
4978            {
4979                if let Some(tp) = &m.step_tp {
4980                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
4981                        let (sf, route_norm) = sigmoid;
4982                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
4983                        // before the router — the rank streams overlap the gemv+topk.
4984                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
4985                        // from its own z copy (replicated deterministic router — identical
4986                        // bits in, identical sel/w out) and starts its sweep without
4987                        // waiting the root's sel broadcast.
4988                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4989                        let d1_router = *D1_ROUTER.get_or_init(|| {
4990                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
4991                        });
4992                        if d1_router {
4993                            let (sf_h, rn_h) = sigmoid;
4994                            let n_ex = m.gate_exps.n_expert;
4995                            let act_ct = m.active_count();
4996                            let _ = tp.runtime.nvfp4_routes_prestage_with(
4997                                bank,
4998                                e,
4999                                z,
5000                                |rank1, in1, sel1, w1| {
5001                                    let mut guard = DEV1_ROUTER_REPS
5002                                        .lock()
5003                                        .map_err(|_| "dev1 router replica lock")?;
5004                                    let (reps, scratch) =
5005                                        guard.get_or_insert_with(|| (Default::default(), None));
5006                                    if !reps.contains_key(&il) {
5007                                        use cudarc::driver::DevicePtr;
5008                                        let (g1, p1, a1) = (
5009                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
5010                                            rank1.htod(&vec![0.0f32; n_ex])?,
5011                                            rank1.alloc_u8_uninit(n_ex)?,
5012                                        );
5013                                        for (src, dst_len, dst) in [
5014                                            (
5015                                                {
5016                                                    let s = e.stream();
5017                                                    let (p, _g) =
5018                                                        m.gate_inp.float_data().device_ptr(&s);
5019                                                    p as u64
5020                                                },
5021                                                n_ex * n_embd * 4,
5022                                                {
5023                                                    let s = rank1.stream();
5024                                                    let (p, _g) = g1.device_ptr(&s);
5025                                                    p as u64
5026                                                },
5027                                            ),
5028                                            (
5029                                                {
5030                                                    let s = e.stream();
5031                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
5032                                                    p as u64
5033                                                },
5034                                                n_ex * 4,
5035                                                {
5036                                                    let s = rank1.stream();
5037                                                    let (p, _g) = p1.device_ptr(&s);
5038                                                    p as u64
5039                                                },
5040                                            ),
5041                                            (
5042                                                {
5043                                                    let s = e.stream();
5044                                                    let (p, _g) =
5045                                                        m.active_experts_dev.device_ptr(&s);
5046                                                    p as u64
5047                                                },
5048                                                n_ex,
5049                                                {
5050                                                    let s = rank1.stream();
5051                                                    let (p, _g) = a1.device_ptr(&s);
5052                                                    p as u64
5053                                                },
5054                                            ),
5055                                        ] {
5056                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
5057                                        }
5058                                        rank1.stream().synchronize()?;
5059                                        reps.insert(il, (g1, p1, a1));
5060                                    }
5061                                    if scratch.is_none() {
5062                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5063                                    }
5064                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5065                                    let logits1 = scratch.as_mut().expect("armed above");
5066                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5067                                    rank1.moe_router_sigmoid_topk_into(
5068                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5069                                        w1,
5070                                    )?;
5071                                    Ok(true)
5072                                },
5073                            )?;
5074                        } else {
5075                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5076                        }
5077                        // Persistent selection buffers: the allocating topk built two fresh
5078                        // slices per layer; sel/w land in process-static rows instead
5079                        // (host-op diet — same kernel, same bytes).
5080                        static SELW: std::sync::Mutex<
5081                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5082                        > = std::sync::Mutex::new(None);
5083                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5084                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5085                            *selw = Some((
5086                                e.ctx().ordinal(),
5087                                e.htod_i32(&vec![0i32; n_used])?,
5088                                e.htod(&vec![0.0f32; n_used])?,
5089                            ));
5090                        }
5091                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5092                        e.moe_router_sigmoid_topk_into(
5093                            &logits,
5094                            t,
5095                            n_expert,
5096                            n_used,
5097                            m.active_count(),
5098                            &m.exp_probs_b_dev,
5099                            &m.active_experts_dev,
5100                            sf,
5101                            route_norm,
5102                            sel_d,
5103                            w_d,
5104                        )?;
5105                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5106                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5107                        // PREJOIN hook so it executes while the peer rank drains its sweep
5108                        // (fills dev0's join wait); apply adds the identical values after.
5109                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5110                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5111                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5112                        });
5113                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5114                        // expert runs on rank1 — the idle device — same kernels, same
5115                        // split program, down row root-resident: bit-identical.
5116                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5117                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5118                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5119                        }) && tp.runtime.rank_engine(1).is_some();
5120                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5121                        // overlap ws + ones row and hand their RAW pointers to the routed
5122                        // run — the join add folds the shexp apply into one launch.
5123                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5124                        let tail3 = *TAIL3
5125                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5126                        let mut ov_issued = false;
5127                        let mut d1_issued = false;
5128                        let mut tail_folded = false;
5129                        let mut output = if shexp_d1 {
5130                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5131                            tp.runtime
5132                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5133                                    bank,
5134                                    e,
5135                                    z,
5136                                    &sel_d,
5137                                    &w_d,
5138                                    n_used,
5139                                    tp.activation_limit,
5140                                    || {
5141                                        d1_issued = Self::shexp_dev1_issue(
5142                                            e, rank1, m, z, cfg, il, n_embd,
5143                                        )?;
5144                                        Ok(())
5145                                    },
5146                                )?
5147                        } else if shexp_ov {
5148                            // Raw sh/ones pointers for the fused tail (persistent statics;
5149                            // pointers stable, no lock held across the routed call). The
5150                            // sh CONTENT is written by the prejoin-issued kernels earlier
5151                            // on e's stream — stream order covers the fused add.
5152                            let post_add = if tail3 {
5153                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5154                            } else {
5155                                None
5156                            };
5157                            let used_post = post_add.is_some();
5158                            let out = tp
5159                                .runtime
5160                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5161                                    bank,
5162                                    e,
5163                                    z,
5164                                    &sel_d,
5165                                    &w_d,
5166                                    n_used,
5167                                    tp.activation_limit,
5168                                    || {
5169                                        ov_issued =
5170                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5171                                        Ok(())
5172                                    },
5173                                    post_add,
5174                                )?;
5175                            // ov_issued false with post_add armed = an early-return arm
5176                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5177                            // fall through to the normal shexp add (battery v22 receipt:
5178                            // the strict error here failed every graph-door boot).
5179                            if used_post && ov_issued {
5180                                tail_folded = true; // apply folded into the join add
5181                            }
5182                            out
5183                        } else {
5184                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5185                                bank,
5186                                e,
5187                                z,
5188                                &sel_d,
5189                                &w_d,
5190                                n_used,
5191                                tp.activation_limit,
5192                            )?
5193                        };
5194                        if output.len() != t * n_embd {
5195                            return Err(format!(
5196                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5197                                output.len()
5198                            )
5199                            .into());
5200                        }
5201                        if tail_folded {
5202                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5203                        } else if d1_issued {
5204                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5205                        } else if ov_issued {
5206                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5207                        } else {
5208                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5209                        }
5210                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5211                            std::sync::atomic::AtomicU64::new(0);
5212                        let layer_bit = 1u64 << (il as u64 % 64);
5213                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5214                            & layer_bit
5215                            == 0
5216                        {
5217                            eprintln!(
5218                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5219                                 expert_transport={} native_p2p={} router=device \
5220                                 activation=host-canonical accumulation=host-canonical \
5221                                 output=e-device io=device performance_claim=false \
5222                                 (logged once per layer)",
5223                                tp.devices,
5224                                tp.runtime.transport_label(),
5225                                tp.runtime.native_p2p(),
5226                            );
5227                        }
5228                        return Ok(output);
5229                    }
5230                }
5231            }
5232            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5233            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5234            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5235            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5236            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5237            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5238            let route_started = route_timing.then(std::time::Instant::now);
5239            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5240                e,
5241                &logits,
5242                z,
5243                t,
5244                n_embd,
5245                n_expert,
5246                n_used,
5247                m.exp_probs_b.as_deref(),
5248                sigmoid,
5249                m.active_experts.as_deref(),
5250            )?;
5251            if let Some(started) = route_started {
5252                use std::sync::atomic::Ordering;
5253                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5254                    + started.elapsed().as_nanos() as u64;
5255                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5256                if calls % 430 == 0 {
5257                    eprintln!(
5258                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5259                        ns as f64 / 1.0e6,
5260                        ns as f64 / calls as f64 / 1.0e3,
5261                    );
5262                }
5263            }
5264            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5265            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5266            Self::trace_moe_input(e, il, t, n_embd, z)?;
5267            let selected = selected
5268                .iter()
5269                .map(|&expert| expert as usize)
5270                .collect::<Vec<_>>();
5271            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5272            // combined output comes back as an e-context row — no host round-trip, no host
5273            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5274            // both preserve f32 bits), gated by greedy token identity.
5275            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5276                if let Some(tp) = &m.step_tp {
5277                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5278                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5279                            bank,
5280                            e,
5281                            z,
5282                            &selected,
5283                            &route_weights,
5284                            n_used,
5285                            tp.activation_limit,
5286                        )?;
5287                        if output.len() != t * n_embd {
5288                            return Err(format!(
5289                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5290                                output.len()
5291                            )
5292                            .into());
5293                        }
5294                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5295                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5296                            std::sync::atomic::AtomicU64::new(0);
5297                        let layer_bit = 1u64 << (il as u64 % 64);
5298                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5299                            & layer_bit
5300                            == 0
5301                        {
5302                            eprintln!(
5303                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5304                                 expert_transport={} native_p2p={} activation=host-canonical \
5305                                 accumulation=host-canonical output=e-device io=device \
5306                                 performance_claim=false (logged once per layer)",
5307                                tp.devices,
5308                                tp.runtime.transport_label(),
5309                                tp.runtime.native_p2p(),
5310                            );
5311                        }
5312                        return Ok(output);
5313                    }
5314                }
5315            }
5316            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5317                (
5318                    match &tp.experts {
5319                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5320                            tp.runtime.run_tensor_parallel_routes(
5321                                bank,
5322                                &input,
5323                                t,
5324                                &selected,
5325                                &route_weights,
5326                                n_used,
5327                            )?
5328                        }
5329                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5330                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5331                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5332                                    bank,
5333                                    &input,
5334                                    &selected,
5335                                    &route_weights,
5336                                    n_used,
5337                                    tp.activation_limit,
5338                                )?
5339                            } else {
5340                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5341                                    bank,
5342                                    &input,
5343                                    t,
5344                                    &selected,
5345                                    &route_weights,
5346                                    n_used,
5347                                    tp.activation_limit,
5348                                )?
5349                            }
5350                        }
5351                    },
5352                    "tp",
5353                    &tp.devices,
5354                    tp.runtime.transport_label(),
5355                    tp.runtime.native_p2p(),
5356                )
5357            } else {
5358                let ep = m
5359                    .step_ep
5360                    .as_ref()
5361                    .ok_or("Step distributed runtime has no EP or TP state")?;
5362                (
5363                    match &ep.experts {
5364                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5365                            ep.runtime.run_routed_experts(
5366                                bank,
5367                                &input,
5368                                t,
5369                                &selected,
5370                                &route_weights,
5371                                n_used,
5372                                ep.activation_limit,
5373                            )?
5374                        }
5375                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5376                            ep.runtime.run_routed_experts_nvfp4(
5377                                bank,
5378                                &input,
5379                                t,
5380                                &selected,
5381                                &route_weights,
5382                                n_used,
5383                                ep.activation_limit,
5384                            )?
5385                        }
5386                    },
5387                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5388                    &ep.devices,
5389                    ep.runtime.transport_label(),
5390                    ep.runtime.native_p2p(),
5391                )
5392            };
5393            if routed.len() != t * n_embd {
5394                return Err(format!(
5395                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5396                    routed.len()
5397                )
5398                .into());
5399            }
5400            let mut output = e.htod(&routed)?;
5401            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5402            // Once per layer per process: the topology contract line is a boot receipt, not a
5403            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5404            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5405            let layer_bit = 1u64 << (il as u64 % 64);
5406            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5407                == 0
5408            {
5409                eprintln!(
5410                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5411                     expert_transport={transport} native_p2p={native_p2p} \
5412                     activation={} accumulation={} output={} \
5413                     performance_claim=false (logged once per layer)",
5414                    if let Some(ep) = &m.step_ep {
5415                        ep.runtime.expert_activation_label()
5416                    } else {
5417                        "host-canonical"
5418                    },
5419                    if let Some(ep) = &m.step_ep {
5420                        ep.runtime.expert_accumulation_label()
5421                    } else {
5422                        "host-canonical"
5423                    },
5424                    if let Some(ep) = &m.step_ep {
5425                        ep.runtime.expert_output_label()
5426                    } else {
5427                        "host-accumulated"
5428                    },
5429                );
5430                if let Some(ep) = &m.step_ep {
5431                    if let Some(limit) = ep.activation_limit {
5432                        eprintln!(
5433                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5434                             formula=min-silu-times-clamped-up performance_claim=false"
5435                        );
5436                    }
5437                }
5438            }
5439            return Ok(output);
5440        }
5441        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5442            let moe = cfg.moe.as_ref().unwrap();
5443            let n_expert = moe.expert_count as usize;
5444            let n_used = moe.expert_used_count as usize;
5445            let sigmoid = cfg.sigmoid_router().unwrap();
5446            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5447            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5448            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5449        }
5450        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5451        // current caller into this research arm; the naked default stays on the established path.
5452        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5453            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5454            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5455            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5456            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5457            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5458            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5459                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5460                let g_host = e.dtoh(&grouped_out)?;
5461                let s_host = e.dtoh(&seq_out)?;
5462                let g_bytes: &[u8] = unsafe {
5463                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5464                };
5465                let s_bytes: &[u8] = unsafe {
5466                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5467                };
5468                if g_bytes == s_bytes {
5469                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5470                } else {
5471                    let diffs = g_host
5472                        .iter()
5473                        .zip(s_host.iter())
5474                        .enumerate()
5475                        .filter(|(_, (a, b))| a != b)
5476                        .count();
5477                    let maxdiff = g_host
5478                        .iter()
5479                        .zip(s_host.iter())
5480                        .map(|(a, b)| (a - b).abs())
5481                        .fold(0.0f32, f32::max);
5482                    panic!(
5483                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5484                        g_host.len()
5485                    );
5486                }
5487            }
5488            return Ok(grouped_out);
5489        }
5490        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5491    }
5492
5493    fn sigmoid_resident_dev_eligible(
5494        e: &Engine,
5495        m: &MoeWeights,
5496        cfg: &ModelConfig,
5497        sliding_gated_moe: bool,
5498    ) -> bool {
5499        let Some(moe) = cfg.moe.as_ref() else {
5500            return false;
5501        };
5502        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5503        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5504        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5505        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5506            std::env::var("MEMRA_MOE_STATS").is_ok()
5507                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5508                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5509                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5510                || std::env::var("MEMRA_MOE_GATE").is_ok()
5511        });
5512        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5513            if dev.dev != e.ctx().ordinal() {
5514                return false;
5515            }
5516            let q8 = moe_q8_enabled()
5517                && q8_expert_supported(m.gate_exps.qtype)
5518                && q8_expert_supported(m.up_exps.qtype)
5519                && q8_expert_supported(m.down_exps.qtype);
5520            let fp8 = dev.fp8_blk.is_some()
5521                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5522                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5523                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5524            q8 || fp8
5525        });
5526        sliding_gated_moe
5527            && sigmoid_router_enabled()
5528            && moe_dev_enabled()
5529            && moe_slab_enabled()
5530            && !observation_mode
5531            && moe.expert_used_count <= 8
5532            && m.has_uniform_expert_layout()
5533            && m.gate_exps.macros.is_none()
5534            && m.up_exps.macros.is_none()
5535            && m.down_exps.macros.is_none()
5536            && !m.has_macros
5537            && resident_layout_supported
5538    }
5539
5540    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5541    pub(crate) fn moe_ffn_sequential(
5542        e: &Engine,
5543        m: &MoeWeights,
5544        z: &CudaSlice<f32>,
5545        t: usize,
5546        cfg: &ModelConfig,
5547        il: u16,
5548        max_block: usize,
5549    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5550        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5551    }
5552
5553    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5554    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5555    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5556    fn moe_router_logits(
5557        e: &Engine,
5558        m: &MoeWeights,
5559        z: &CudaSlice<f32>,
5560        t: usize,
5561        cfg: &ModelConfig,
5562    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5563        if t < PRIME_MIN_T {
5564            // Decode and speculative verify use one fixed per-row reduction program.
5565            if crate::router_kernel_on() {
5566                e.router_gemv(
5567                    m.gate_inp.float_data(),
5568                    z,
5569                    cfg.n_embd as usize,
5570                    m.gate_exps.n_expert,
5571                    t,
5572                )
5573            } else {
5574                e.matmul_decode_exact(&m.gate_inp, z, t)
5575            }
5576        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5577            e.router_gemv(
5578                m.gate_inp.float_data(),
5579                z,
5580                cfg.n_embd as usize,
5581                m.gate_exps.n_expert,
5582                t,
5583            )
5584        } else {
5585            e.matmul(&m.gate_inp, z, t)
5586        }
5587    }
5588
5589    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5590    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5591    /// trace is independent of the dispatch optimization selected for the forward.
5592    fn trace_moe_routes(
5593        il: u16,
5594        t: usize,
5595        sel_all: &[u32],
5596        weights: &[f32],
5597    ) -> Result<(), Box<dyn std::error::Error>> {
5598        use std::io::Write as _;
5599        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5600            let mut f = std::fs::OpenOptions::new()
5601                .create(true)
5602                .append(true)
5603                .open(path)?;
5604            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5605            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5606        }
5607        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5608            let mut f = std::fs::OpenOptions::new()
5609                .create(true)
5610                .append(true)
5611                .open(path)?;
5612            let pairs: Vec<String> = sel_all
5613                .iter()
5614                .zip(weights)
5615                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5616                .collect();
5617            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5618        }
5619        Ok(())
5620    }
5621
5622    #[allow(clippy::too_many_arguments)]
5623    fn trace_sigmoid_router_logits(
5624        e: &Engine,
5625        il: u16,
5626        t: usize,
5627        n_expert: usize,
5628        n_used: usize,
5629        logits: &CudaSlice<f32>,
5630        m: &MoeWeights,
5631        (scaling_factor, route_norm): (f32, bool),
5632    ) -> Result<(), Box<dyn std::error::Error>> {
5633        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5634            return Ok(());
5635        }
5636        let logits = e.dtoh(logits)?;
5637        let active: Vec<u8> = m
5638            .active_experts
5639            .as_ref()
5640            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5641            .unwrap_or_else(|| vec![1; n_expert]);
5642        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5643        crate::sigrouter_contract::capture_served_logits(
5644            il as u32,
5645            t,
5646            n_expert,
5647            n_used,
5648            scaling_factor,
5649            route_norm,
5650            &active,
5651            &bias,
5652            &logits,
5653        )?;
5654        Ok(())
5655    }
5656
5657    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
5658    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
5659    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
5660    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
5661    fn trace_moe_input(
5662        e: &Engine,
5663        il: u16,
5664        t: usize,
5665        n_embd: usize,
5666        z: &CudaSlice<f32>,
5667    ) -> Result<(), Box<dyn std::error::Error>> {
5668        use std::io::Write as _;
5669        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
5670            return Ok(());
5671        };
5672        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
5673        let host = e.dtoh_view(&z.slice(0..values))?;
5674        let bytes = unsafe {
5675            std::slice::from_raw_parts(
5676                host.as_ptr().cast::<u8>(),
5677                host.len() * std::mem::size_of::<f32>(),
5678            )
5679        };
5680        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
5681        let mut state = state
5682            .lock()
5683            .map_err(|_| "MoE input trace writer lock is poisoned")?;
5684        if state.is_none() {
5685            let dir = std::path::PathBuf::from(&dir);
5686            std::fs::create_dir_all(&dir)?;
5687            let index = std::fs::OpenOptions::new()
5688                .create(true)
5689                .append(true)
5690                .open(dir.join("index.jsonl"))?;
5691            *state = Some(MoeInputTraceWriter {
5692                dir,
5693                index,
5694                payloads: std::collections::HashMap::new(),
5695            });
5696        }
5697        let writer = state.as_mut().unwrap();
5698        if writer.dir != std::path::Path::new(&dir) {
5699            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
5700        }
5701        let file_name = format!("layer-{il:03}.f32");
5702        if !writer.payloads.contains_key(&il) {
5703            let payload = std::fs::OpenOptions::new()
5704                .create(true)
5705                .append(true)
5706                .open(writer.dir.join(&file_name))?;
5707            let offset = payload.metadata()?.len();
5708            writer.payloads.insert(il, (payload, offset));
5709        }
5710        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
5711        let row_offset = *offset;
5712        payload.write_all(bytes)?;
5713        *offset += bytes.len() as u64;
5714        writeln!(
5715            writer.index,
5716            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
5717             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
5718             \"payload_bytes\":{}}}",
5719            bytes.len()
5720        )?;
5721        Ok(())
5722    }
5723
5724    #[allow(clippy::too_many_arguments)]
5725    pub(crate) fn moe_ffn_sequential_zq8(
5726        e: &Engine,
5727        m: &MoeWeights,
5728        z: &CudaSlice<f32>,
5729        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5730        t: usize,
5731        cfg: &ModelConfig,
5732        il: u16,
5733        max_block: usize,
5734    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5735        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
5736        let moe = cfg.moe.as_ref().unwrap();
5737        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
5738        let n_expert = moe.expert_count as usize; // 256
5739        let n_used = moe.expert_used_count as usize; // 8
5740        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
5741
5742        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
5743        debug_assert_eq!(m.gate_exps.in_f, n_embd);
5744        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
5745        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
5746        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
5747        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
5748
5749        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
5750        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
5751        let lim_exp = cfg.clamp_exp_at(il as u32);
5752        let lim_shexp = cfg.clamp_shexp_at(il as u32);
5753        let use_cache = Engine::moe_cache_enabled();
5754        let uniform_experts = m.has_uniform_expert_layout();
5755        let moe_q8 = uniform_experts
5756            && moe_q8_enabled()
5757            && q8_expert_supported(m.gate_exps.qtype)
5758            && q8_expert_supported(m.up_exps.qtype)
5759            && q8_expert_supported(m.down_exps.qtype);
5760        // Experimental secondary backend: complete experts already resident in the SLRU stay on
5761        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
5762        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
5763        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
5764        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
5765        // commands and CI have no llama.cpp or OpenMP dependency.
5766        let cpu_expert_requested = crate::cpu_experts::configured();
5767        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
5768            return Err(std::io::Error::other(
5769                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
5770            )
5771            .into());
5772        }
5773        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
5774        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
5775        // Those backends are each deterministic but are different numeric configurations, so a
5776        // later prefill eviction can change greedy output. Freeze after the first real prefill;
5777        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
5778        // staging below and cannot change backend assignment.
5779        let freeze_cpu_residency = cpu_expert_requested
5780            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
5781        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
5782            .ok()
5783            .and_then(|value| value.parse::<usize>().ok())
5784            .is_some_and(|tokens| tokens > 0);
5785        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
5786            e.freeze_moe_cache();
5787        }
5788        let cache_frozen = use_cache && e.moe_cache_frozen();
5789        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
5790
5791        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
5792        // cannot change logits, selected expert ids, or routing weights.
5793        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5794        if let Some(sig) = cfg.sigmoid_router() {
5795            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
5796        }
5797
5798        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
5799        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
5800        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
5801        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
5802        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
5803        // per-token host stall that dominated the 35B decode wall after stages 1+2.
5804        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
5805        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
5806        // only difference is where sel/w/pointers are READ from (device instead of params).
5807        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
5808        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
5809        // Any non-resident layer falls through to host routing + the gdec/sequential path.
5810        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
5811        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
5812        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
5813        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
5814        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
5815        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
5816        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
5817        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
5818        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
5819        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
5820        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
5821        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
5822        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
5823        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
5824        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
5825        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
5826        // now rides the dev loop below (same kernels per token as decode); pairs serves real
5827        // prefill (t >= 16, where spec never verifies).
5828        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
5829        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
5830        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
5831        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
5832        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
5833        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
5834        // ride the macro-aware sequential/staged paths below or every expert output is off by
5835        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
5836        let no_exp_macros = m.gate_exps.macros.is_none()
5837            && m.up_exps.macros.is_none()
5838            && m.down_exps.macros.is_none();
5839        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
5840        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
5841        // so it cannot even see the per-layer limit.
5842        if cfg.sigmoid_router().is_none()
5843            && cfg.m3.is_none()
5844            && cfg.hy3.is_none()
5845            && !cfg.swiglu_clamped_at(il as u32)
5846            && no_exp_macros
5847            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
5848            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
5849            // pairs serves real prefill from 17 up.
5850            && t > MOE_DEV_MAX_T
5851            && m.dev_exps.is_some()
5852            && moe_q8_enabled()
5853            && q8_expert_supported(m.gate_exps.qtype)
5854            && q8_expert_supported(m.up_exps.qtype)
5855            && q8_expert_supported(m.down_exps.qtype)
5856            && std::env::var("MEMRA_MOE_PAIRS")
5857                .map(|v| v != "0")
5858                .unwrap_or(true)
5859            && std::env::var("MEMRA_MOE_STATS").is_err()
5860        {
5861            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
5862        }
5863
5864        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
5865        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
5866        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
5867        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
5868        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
5869        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
5870        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
5871        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
5872        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
5873        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
5874        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
5875        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
5876        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
5877        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
5878        // Keyed off sigmoid_router() so arch #4 is denied by construction.
5879        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
5880        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
5881        let dev_ok = uniform_experts
5882            && cfg.sigmoid_router().is_none()
5883            && cfg.m3.is_none()
5884            && cfg.hy3.is_none()
5885            && !cfg.swiglu_clamped_at(il as u32);
5886        // Observation modes must route through the host-visible selection below. Otherwise a fully
5887        // resident layer returns through device dispatch before its trace/stats row is recorded,
5888        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
5889        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
5890            || std::env::var("MEMRA_MOE_TRACE").is_ok()
5891            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5892            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
5893        if dev_ok
5894            && t <= MOE_DEV_MAX_T
5895            && m.dev_exps.is_some()
5896            && n_used <= 8
5897            && moe_dev_enabled()
5898            && !observe_routes
5899        {
5900            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5901        }
5902        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
5903            let row_ok = e.with_moe_cache(max_block, |c, eng| {
5904                if moe_prewarm_enabled() {
5905                    c.prewarm_layer(il, m, eng)?;
5906                }
5907                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
5908            })?;
5909            if row_ok {
5910                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5911            }
5912        }
5913
5914        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
5915        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
5916            if cpu_hybrid {
5917                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
5918                    e,
5919                    &logits,
5920                    z,
5921                    t,
5922                    n_embd,
5923                    n_expert,
5924                    n_used,
5925                    m.exp_probs_b.as_deref(),
5926                    sig,
5927                    m.active_experts.as_deref(),
5928                )?;
5929                (sel, w, Some(input))
5930            } else {
5931                let (sel, w) =
5932                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
5933                (sel, w, None)
5934            }
5935        } else {
5936            let (sel, w) =
5937                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
5938            (sel, w, None)
5939        };
5940        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
5941
5942        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
5943        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
5944        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
5945        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
5946        Self::trace_moe_input(e, il, t, n_embd, z)?;
5947
5948        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
5949        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
5950        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
5951        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
5952        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
5953        // wait for each pending block, so later copies can overlap the earlier expert kernels while
5954        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
5955        // T=1; batched forwards can have token-local consumers still in flight between selections.
5956        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
5957        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
5958        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
5959        let worker_disk_prefetch =
5960            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
5961        let promote_worker_h2d =
5962            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
5963        if promote_worker_h2d {
5964            let mut selected_blocks = Vec::with_capacity(n_used * 3);
5965            for &ex in sel_all.iter().take(n_used) {
5966                let ex = ex as u16;
5967                selected_blocks.extend([
5968                    BlockId::new(il, PROJ_GATE, ex),
5969                    BlockId::new(il, PROJ_UP, ex),
5970                    BlockId::new(il, PROJ_DOWN, ex),
5971                ]);
5972            }
5973            for &ex in sel_all.iter().take(n_used) {
5974                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
5975            }
5976            e.with_moe_cache(max_block, |cache, eng| {
5977                cache.promote_worker_reads_at_safe_boundary(
5978                    &selected_blocks,
5979                    &selected_blocks,
5980                    eng,
5981                )?;
5982                Ok(())
5983            })?;
5984        }
5985
5986        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
5987        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
5988        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
5989            let mut cnt = vec![0u32; n_expert];
5990            for &s in sel_all.iter() {
5991                cnt[s as usize] += 1;
5992            }
5993            let total = sel_all.len() as f64;
5994            let mut h = 0.0f64;
5995            let mut active = 0usize;
5996            for &c in &cnt {
5997                if c > 0 {
5998                    active += 1;
5999                    let p = c as f64 / total;
6000                    h -= p * p.log2();
6001                }
6002            }
6003            let maxc = cnt.iter().copied().max().unwrap_or(0);
6004            println!(
6005                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
6006                il,
6007                t,
6008                sel_all.len(),
6009                active,
6010                n_expert,
6011                h,
6012                (n_expert as f64).log2(),
6013                total / active.max(1) as f64,
6014                maxc
6015            );
6016        }
6017
6018        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
6019        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
6020        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
6021        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
6022        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
6023        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
6024        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
6025        // zeroed-then-accumulated exactly as before (fallback).
6026        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
6027        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
6028        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
6029        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
6030        let gdec_may_fire = uniform_experts
6031            && use_cache
6032            && n_used <= 8
6033            && gdec_enabled()
6034            && !cfg.swiglu_clamped_at(il as u32);
6035        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
6036        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
6037        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
6038        // archs the slabs were uploaded but never read, and every expert went through the
6039        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
6040        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
6041        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
6042        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
6043        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
6044        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
6045        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
6046        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
6047        // strictly worse than staging); under PP-2 without the prime walker this admits
6048        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
6049        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
6050        let slab_local = m
6051            .dev_exps
6052            .as_ref()
6053            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
6054        let slab_bases = slab_local.map(|d| {
6055            use cudarc::driver::DevicePtr;
6056            let s = e.stream();
6057            let (pg, _g0) = d.gate.device_ptr(&s);
6058            let (pu, _g1) = d.up.device_ptr(&s);
6059            let (pd, _g2) = d.down.device_ptr(&s);
6060            (pg as u64, pu as u64, pd as u64)
6061        });
6062        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6063        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6064        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6065        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6066        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6067        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6068        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6069        // all-resident tokens, staged loop for misses), which is a dispatch-class
6070        // comparison, not a provenance one.
6071        let slab_fused_may_fire = slab_bases.is_some()
6072            && n_used <= 8
6073            && gdec_enabled()
6074            && !cfg.swiglu_clamped_at(il as u32)
6075            && cfg.m3.is_none()
6076            && no_exp_macros
6077            && moe_q8;
6078        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6079        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6080        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6081            e.uninit(t * n_embd)?
6082        } else {
6083            e.zeros(t * n_embd)?
6084        };
6085        // The router readback above already established a host boundary. Copy each small-t hidden
6086        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6087        let cpu_input = if cpu_hybrid {
6088            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6089        } else {
6090            None
6091        };
6092
6093        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6094        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6095        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6096        // measured ~123 memsets/token of the decode wall).
6097        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6098        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6099        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6100        let mut scratch_g: Option<CudaSlice<u8>> = None;
6101        let mut scratch_u: Option<CudaSlice<u8>> = None;
6102        let mut scratch_d: Option<CudaSlice<u8>> = None;
6103        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6104        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6105
6106        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6107        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6108        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6109        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6110        let page_window = moe_page_prefetch_window();
6111
6112        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6113        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6114        for tok in 0..t {
6115            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6116            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6117            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6118            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6119
6120            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6121            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6122            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6123            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6124            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6125            // miss falls through to the sequential loop below, which admits as before. In steady
6126            // state on a fully-resident rig every token-layer takes the grouped path.
6127            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6128            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6129            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6130            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6131            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6132            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6133            let no_macros = m.gate_exps.macros.is_none()
6134                && m.up_exps.macros.is_none()
6135                && m.down_exps.macros.is_none();
6136            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6137            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6138            // with pointers computed from the resident slab base + ex*stride instead of
6139            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6140            // slab holds every expert by construction, so this arm never falls through
6141            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6142            // staging both die). Bit-identity class: pointer provenance only, the same
6143            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6144            // slab exists it is strictly better (no lock, no miss).
6145            if slab_fused_may_fire {
6146                let (pg, pu, pd) = slab_bases.unwrap();
6147                let mut gp = [0u64; 8];
6148                let mut up = [0u64; 8];
6149                let mut dp = [0u64; 8];
6150                for (j, &ex) in sel.iter().enumerate() {
6151                    let ex = ex as usize;
6152                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6153                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6154                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6155                }
6156                let mut wv = [0f32; 8];
6157                wv[..n_used].copy_from_slice(w);
6158                if tok_q8.is_none() {
6159                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6160                }
6161                let (zq, zd) = tok_q8.as_ref().unwrap();
6162                let act = e.moe_gate_up_silu8_q8(
6163                    crate::WPtr8(gp),
6164                    crate::WPtr8(up),
6165                    zq,
6166                    zd,
6167                    n_embd,
6168                    n_ff_exp,
6169                    n_used,
6170                    m.gate_exps.qtype,
6171                    m.up_exps.qtype,
6172                    m.gate_exps.row_bytes,
6173                    m.up_exps.row_bytes,
6174                )?;
6175                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6176                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6177                e.moe_down8_fma_q8(
6178                    crate::WPtr8(dp),
6179                    crate::F32x8(wv),
6180                    &aq2,
6181                    &ad2,
6182                    &mut dst,
6183                    n_ff_exp,
6184                    n_embd,
6185                    n_used,
6186                    m.down_exps.qtype,
6187                    m.down_exps.row_bytes,
6188                )?;
6189                continue;
6190            }
6191            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6192                if tok_q8.is_none() {
6193                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6194                }
6195                let (zq, zd) = tok_q8.as_ref().unwrap();
6196                if Self::moe_gdec_token_q8(
6197                    e,
6198                    m,
6199                    il,
6200                    max_block,
6201                    zq,
6202                    zd,
6203                    sel,
6204                    w,
6205                    &mut moe_out,
6206                    tok,
6207                    n_embd,
6208                    n_ff_exp,
6209                    n_used,
6210                )? {
6211                    continue;
6212                }
6213            } else if gdec_may_fire
6214                && cfg.m3.is_none()
6215                && no_macros
6216                && Self::moe_gdec_token(
6217                    e,
6218                    m,
6219                    il,
6220                    max_block,
6221                    &zt,
6222                    sel,
6223                    w,
6224                    &mut moe_out,
6225                    tok,
6226                    n_embd,
6227                    n_ff_exp,
6228                    n_used,
6229                )?
6230            {
6231                continue;
6232            }
6233
6234            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6235            // slab pair could fire. This token fell through to a sequential axpy loop, which
6236            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6237            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6238            // has no fallible predicate), included for the allocation invariant's symmetry.
6239            if gdec_may_fire || slab_fused_may_fire {
6240                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6241                e.memset_zeros_view(&mut row)?;
6242            }
6243
6244            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6245            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6246            // stall this path exists to remove, while mixing projections would require another
6247            // activation round-trip. Weight addresses remain valid until this worker is joined at
6248            // the bottom of the token scope.
6249            let mut cpu_mask = vec![false; sel.len()];
6250            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6251                let gpu_resident = if use_cache {
6252                    e.with_moe_cache(max_block, |cache, _| {
6253                        Ok(sel
6254                            .iter()
6255                            .map(|&expert| {
6256                                let expert = expert as u16;
6257                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6258                                    .into_iter()
6259                                    .filter(|&projection| {
6260                                        cache
6261                                            .resident(BlockId::new(il, projection, expert))
6262                                            .is_some()
6263                                    })
6264                                    .count()
6265                            })
6266                            .collect::<Vec<_>>())
6267                    })?
6268                } else {
6269                    vec![0; sel.len()]
6270                };
6271                let mut cpu_selected = Vec::new();
6272                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6273                    if gpu_resident[index] != 3 {
6274                        cpu_mask[index] = true;
6275                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6276                        let expert = expert as usize;
6277                        cpu_selected.push((expert, route_weight));
6278                    }
6279                }
6280                if crate::cpu_experts::predictor_enabled() {
6281                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6282                    // from this layer's MoE input and prefetches predicted-and-missing
6283                    // experts into the companion RAM cache. Never blocks this thread.
6284                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6285                    crate::cpu_experts::predictor_submit(il, row);
6286                }
6287                if cpu_selected.is_empty() {
6288                    None
6289                } else {
6290                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6291                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6292                        .map_err(std::io::Error::other)?;
6293                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6294                }
6295            } else {
6296                None
6297            };
6298
6299            let worker_window = worker_disk_prefetch
6300                .then(worker_prefetch_window)
6301                .unwrap_or(0);
6302            for (j, &ex) in sel.iter().enumerate() {
6303                if cpu_mask[j] {
6304                    continue;
6305                }
6306                let ex = ex as usize;
6307                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6308                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6309                // fused form) and macro-carrying artifacts — still have their bytes in the
6310                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6311                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6312                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6313                if let Some(d) = slab_local {
6314                    let gl = m.gate_exps.expert_layout(ex);
6315                    let ul = m.up_exps.expert_layout(ex);
6316                    let dl = m.down_exps.expert_layout(ex);
6317                    let (g0, u0, d0) = (
6318                        ex * m.gate_exps.expert_stride,
6319                        ex * m.up_exps.expert_stride,
6320                        ex * m.down_exps.expert_stride,
6321                    );
6322                    let (gate, up) = if moe_q8 {
6323                        if tok_q8.is_none() {
6324                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6325                        }
6326                        let (zq, zd) = tok_q8.as_ref().unwrap();
6327                        (
6328                            e.qmatvec_expert_q8(
6329                                &d.gate,
6330                                g0..g0 + gl.len,
6331                                zq,
6332                                zd,
6333                                1,
6334                                m.gate_exps.in_f,
6335                                m.gate_exps.out_f,
6336                                gl.qtype,
6337                                gl.row_bytes,
6338                            )?,
6339                            e.qmatvec_expert_q8(
6340                                &d.up,
6341                                u0..u0 + ul.len,
6342                                zq,
6343                                zd,
6344                                1,
6345                                m.up_exps.in_f,
6346                                m.up_exps.out_f,
6347                                ul.qtype,
6348                                ul.row_bytes,
6349                            )?,
6350                        )
6351                    } else {
6352                        (
6353                            e.qmatvec_view(
6354                                &d.gate,
6355                                g0..g0 + gl.len,
6356                                &zt,
6357                                1,
6358                                m.gate_exps.in_f,
6359                                m.gate_exps.out_f,
6360                                gl.qtype,
6361                                gl.row_bytes,
6362                            )?,
6363                            e.qmatvec_view(
6364                                &d.up,
6365                                u0..u0 + ul.len,
6366                                &zt,
6367                                1,
6368                                m.up_exps.in_f,
6369                                m.up_exps.out_f,
6370                                ul.qtype,
6371                                ul.row_bytes,
6372                            )?,
6373                        )
6374                    };
6375                    let mut act = e.uninit(n_ff_exp)?;
6376                    Self::ffn_act_lim(
6377                        e,
6378                        cfg,
6379                        &gate,
6380                        &up,
6381                        m.gate_exps.macro_scale(ex),
6382                        m.up_exps.macro_scale(ex),
6383                        lim_exp,
6384                        &mut act,
6385                        n_ff_exp,
6386                    )?;
6387                    let y = if moe_q8 {
6388                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6389                        e.qmatvec_expert_q8(
6390                            &d.down,
6391                            d0..d0 + dl.len,
6392                            &aq2,
6393                            &ad2,
6394                            1,
6395                            m.down_exps.in_f,
6396                            m.down_exps.out_f,
6397                            dl.qtype,
6398                            dl.row_bytes,
6399                        )?
6400                    } else {
6401                        let actv = act.slice(0..n_ff_exp);
6402                        e.qmatvec_view(
6403                            &d.down,
6404                            d0..d0 + dl.len,
6405                            &actv,
6406                            1,
6407                            m.down_exps.in_f,
6408                            m.down_exps.out_f,
6409                            dl.qtype,
6410                            dl.row_bytes,
6411                        )?
6412                    };
6413                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6414                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6415                    continue;
6416                }
6417                for next in page_prefetch_positions(j, sel.len(), page_window) {
6418                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6419                }
6420                let keep = [
6421                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6422                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6423                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6424                ];
6425                if worker_disk_prefetch && worker_window > 0 {
6426                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6427                        Self::moe_prefetch_disk_expert(
6428                            e,
6429                            il,
6430                            sel[next] as usize,
6431                            m,
6432                            max_block,
6433                            &keep,
6434                        )?;
6435                    }
6436                } else if cache_dispatch
6437                    && !cpu_hybrid
6438                    && moe_prefetch_enabled()
6439                    && j + 1 < sel.len()
6440                {
6441                    let next = sel[j + 1] as usize;
6442                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6443                }
6444                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6445                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6446                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6447                    // layouts stay on the metadata-aware f32 path.
6448                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6449                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6450                    }
6451                    let gate = if gate_q8 {
6452                        let (zq, zd) = tok_q8.as_ref().unwrap();
6453                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6454                    } else {
6455                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6456                    };
6457                    let up = if up_q8 {
6458                        let (zq, zd) = tok_q8.as_ref().unwrap();
6459                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6460                    } else {
6461                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6462                    };
6463                    let mut act = e.uninit(n_ff_exp)?;
6464                    Self::ffn_act_lim(
6465                        e,
6466                        cfg,
6467                        &gate,
6468                        &up,
6469                        m.gate_exps.macro_scale(ex),
6470                        m.up_exps.macro_scale(ex),
6471                        lim_exp,
6472                        &mut act,
6473                        n_ff_exp,
6474                    )?;
6475                    let y = if down_q8 {
6476                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6477                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6478                    } else {
6479                        let actv = act.slice(0..n_ff_exp);
6480                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6481                    };
6482                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6483                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6484                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6485                } else if cache_dispatch {
6486                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6487                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6488                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6489                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6490                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6491                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6492                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6493                    Self::ffn_act_lim(
6494                        e,
6495                        cfg,
6496                        &gate,
6497                        &up,
6498                        m.gate_exps.macro_scale(ex),
6499                        m.up_exps.macro_scale(ex),
6500                        lim_exp,
6501                        &mut act,
6502                        n_ff_exp,
6503                    )?;
6504                    let actv = act.slice(0..n_ff_exp);
6505                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6506                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6507                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6508                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6509                } else if cache_frozen {
6510                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6511                    // first prime. Reuse every fixed resident projection directly and stage only a
6512                    // true miss through the ordinary scratch slot. This preserves the established
6513                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6514                    let gate = Self::moe_frozen_gemm(
6515                        e,
6516                        il,
6517                        PROJ_GATE,
6518                        ex,
6519                        m,
6520                        max_block,
6521                        &zt,
6522                        &mut scratch_g,
6523                        g_len,
6524                    )?;
6525                    let up = Self::moe_frozen_gemm(
6526                        e,
6527                        il,
6528                        PROJ_UP,
6529                        ex,
6530                        m,
6531                        max_block,
6532                        &zt,
6533                        &mut scratch_u,
6534                        u_len,
6535                    )?;
6536                    let mut act = e.uninit(n_ff_exp)?;
6537                    Self::ffn_act_lim(
6538                        e,
6539                        cfg,
6540                        &gate,
6541                        &up,
6542                        m.gate_exps.macro_scale(ex),
6543                        m.up_exps.macro_scale(ex),
6544                        lim_exp,
6545                        &mut act,
6546                        n_ff_exp,
6547                    )?;
6548                    let actv = act.slice(0..n_ff_exp);
6549                    let y = Self::moe_frozen_gemm(
6550                        e,
6551                        il,
6552                        PROJ_DOWN,
6553                        ex,
6554                        m,
6555                        max_block,
6556                        &actv,
6557                        &mut scratch_d,
6558                        d_len,
6559                    )?;
6560                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6561                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6562                } else {
6563                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6564                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6565                    // fully overwrites the byte range the GEMM reads).
6566                    if scratch_g.is_none() {
6567                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6568                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6569                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6570                    }
6571                    let (sg, su, sd) = (
6572                        scratch_g.as_mut().unwrap(),
6573                        scratch_u.as_mut().unwrap(),
6574                        scratch_d.as_mut().unwrap(),
6575                    );
6576                    let gl = m.gate_exps.expert_layout(ex);
6577                    let ul = m.up_exps.expert_layout(ex);
6578                    let dl = m.down_exps.expert_layout(ex);
6579                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6580                    let gate = e.qmatvec_view(
6581                        sg,
6582                        0..gl.len,
6583                        &zt,
6584                        1,
6585                        m.gate_exps.in_f,
6586                        m.gate_exps.out_f,
6587                        gl.qtype,
6588                        gl.row_bytes,
6589                    )?;
6590
6591                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6592                    let up = e.qmatvec_view(
6593                        su,
6594                        0..ul.len,
6595                        &zt,
6596                        1,
6597                        m.up_exps.in_f,
6598                        m.up_exps.out_f,
6599                        ul.qtype,
6600                        ul.row_bytes,
6601                    )?;
6602
6603                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6604                    Self::ffn_act_lim(
6605                        e,
6606                        cfg,
6607                        &gate,
6608                        &up,
6609                        m.gate_exps.macro_scale(ex),
6610                        m.up_exps.macro_scale(ex),
6611                        lim_exp,
6612                        &mut act,
6613                        n_ff_exp,
6614                    )?;
6615
6616                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6617                    let actv = act.slice(0..n_ff_exp);
6618                    let y = e.qmatvec_view(
6619                        sd,
6620                        0..dl.len,
6621                        &actv,
6622                        1,
6623                        m.down_exps.in_f,
6624                        m.down_exps.out_f,
6625                        dl.qtype,
6626                        dl.row_bytes,
6627                    )?;
6628
6629                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6630                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6631                }
6632            }
6633            if let Some(worker) = cpu_worker {
6634                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6635                let cpu_output = e.htod(&cpu_output)?;
6636                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6637                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6638            }
6639            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6640                for (j, &ex) in sel.iter().enumerate() {
6641                    if cpu_mask[j] {
6642                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6643                    }
6644                }
6645            }
6646        }
6647
6648        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6649        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6650        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6651        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6652        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6653            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
6654        {
6655            let n_ff_sh = gate_shexp.out_features(); // 512
6656            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
6657            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
6658            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
6659            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
6660            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
6661            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
6662            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
6663            let verify_t = t > 1 && t < PRIME_MIN_T;
6664            let (sg_gate, sg_up) = if t == 1 {
6665                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
6666            } else if verify_t {
6667                (
6668                    e.matmul_decode_exact(gate_shexp, z, t)?,
6669                    e.matmul_decode_exact(up_shexp, z, t)?,
6670                )
6671            } else {
6672                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
6673            };
6674            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
6675            Self::ffn_act_lim(
6676                e,
6677                cfg,
6678                &sg_gate,
6679                &sg_up,
6680                1.0,
6681                1.0,
6682                lim_shexp,
6683                &mut sa,
6684                t * n_ff_sh,
6685            )?;
6686            let sh = if verify_t {
6687                e.matmul_decode_exact(down_shexp, &sa, t)?
6688            } else {
6689                e.matmul(down_shexp, &sa, t)?
6690            }; // [T, n_embd]
6691
6692            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
6693            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
6694            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
6695            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
6696            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
6697            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
6698            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
6699            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
6700            // expert's contribution into every token's residual, so under cross-request
6701            // concat prefill a session's hidden state depended on its co-arrivals' token
6702            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
6703            let g = match &m.gate_inp_shexp {
6704                Some(gate_inp_shexp) => {
6705                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
6706                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
6707                    } else {
6708                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
6709                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
6710                        e.sigmoid(&gs, &mut g, t)?;
6711                        g
6712                    }
6713                }
6714                None => e.htod(&vec![1.0f32; t])?,
6715            };
6716            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
6717            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
6718        }
6719
6720        Ok(moe_out)
6721    }
6722
6723    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
6724    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
6725    pub fn stage1_h2d_per_token(&self) -> u64 {
6726        use crate::hybrid::Ffn;
6727        let n_used = self
6728            .cfg
6729            .moe
6730            .as_ref()
6731            .map(|m| m.expert_used_count as u64)
6732            .unwrap_or(0);
6733        let mut bytes = 0u64;
6734        for l in self.layers.iter() {
6735            if let Ffn::Moe(m) = &l.ffn {
6736                bytes += n_used
6737                    * (m.gate_exps.max_expert_bytes()
6738                        + m.up_exps.max_expert_bytes()
6739                        + m.down_exps.max_expert_bytes()) as u64;
6740            }
6741        }
6742        bytes
6743    }
6744
6745    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
6746    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
6747    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
6748    pub(crate) fn max_moe_block(&self) -> usize {
6749        use crate::hybrid::Ffn;
6750        let mut mx = 0usize;
6751        let mut scan = |ffn: &Ffn| {
6752            if let Ffn::Moe(m) = ffn {
6753                mx = mx
6754                    .max(m.gate_exps.max_expert_bytes())
6755                    .max(m.up_exps.max_expert_bytes())
6756                    .max(m.down_exps.max_expert_bytes());
6757            }
6758        };
6759        for l in self.layers.iter() {
6760            scan(&l.ffn);
6761        }
6762        if let Some(mtp) = self.mtp.as_ref() {
6763            scan(&mtp.ffn);
6764        }
6765        mx
6766    }
6767
6768    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
6769    /// but have no bytes and therefore consume no residency slot.
6770    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
6771        use crate::hybrid::Ffn;
6772        let mut sizes = Vec::new();
6773        let mut scan = |ffn: &Ffn| {
6774            let Ffn::Moe(m) = ffn else { return };
6775            for ex in 0..m.gate_exps.n_expert {
6776                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
6777                    continue;
6778                }
6779                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
6780                    let len = exps.expert_layout(ex).len;
6781                    if len > 0 {
6782                        sizes.push(len);
6783                    }
6784                }
6785            }
6786        };
6787        for layer in &self.layers {
6788            scan(&layer.ffn);
6789        }
6790        if let Some(mtp) = &self.mtp {
6791            scan(&mtp.ffn);
6792        }
6793        sizes
6794    }
6795
6796    /// Persist the frozen residency set so a later process can restage it directly and skip
6797    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
6798    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
6799    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
6800    /// post-freeze argmax gate still validates the serving assignment.
6801    pub fn save_cpu_expert_residency_profile(
6802        &self,
6803        e: &Engine,
6804        path: &std::path::Path,
6805    ) -> Result<(), Box<dyn std::error::Error>> {
6806        let Some(ids) = e.export_moe_residency() else {
6807            return Err("no MoE residency cache to persist".into());
6808        };
6809        let mut body = format!(
6810            "memra-freeze-profile v1 max_block={} blocks={}\n",
6811            self.max_moe_block(),
6812            ids.len()
6813        );
6814        for (layer, proj, ex) in &ids {
6815            body.push_str(&format!("{layer} {proj} {ex}\n"));
6816        }
6817        let tmp = path.with_extension("tmp");
6818        std::fs::write(&tmp, body)?;
6819        std::fs::rename(&tmp, path)?;
6820        println!(
6821            "[moe-cache] freeze profile saved: {} blocks -> {}",
6822            ids.len(),
6823            path.display()
6824        );
6825        Ok(())
6826    }
6827
6828    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
6829    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
6830    /// missing or its header does not match this model's slot geometry.
6831    pub fn restore_cpu_expert_residency_profile(
6832        &self,
6833        e: &Engine,
6834        path: &std::path::Path,
6835    ) -> Result<bool, Box<dyn std::error::Error>> {
6836        use crate::hybrid::Ffn;
6837        use crate::moe_cache::BlockId;
6838        let Ok(content) = std::fs::read_to_string(path) else {
6839            return Ok(false);
6840        };
6841        let mut lines = content.lines();
6842        let Some(header) = lines.next() else {
6843            return Ok(false);
6844        };
6845        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
6846        if !header.starts_with(&expected) {
6847            println!(
6848                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
6849                path.display()
6850            );
6851            return Ok(false);
6852        }
6853        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
6854            std::collections::HashMap::new();
6855        for line in lines {
6856            let mut fields = line.split_whitespace();
6857            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
6858            else {
6859                continue;
6860            };
6861            let (Ok(layer), Ok(proj), Ok(ex)) =
6862                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
6863            else {
6864                continue;
6865            };
6866            by_layer
6867                .entry(layer)
6868                .or_default()
6869                .push(BlockId::new(layer, proj, ex));
6870        }
6871        let requested: usize = by_layer.values().map(Vec::len).sum();
6872        if requested == 0 {
6873            return Ok(false);
6874        }
6875        let max_block = self.max_moe_block();
6876        let mut restaged = 0usize;
6877        let mut stage_layer =
6878            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
6879                let Ffn::Moe(m) = ffn else { return Ok(()) };
6880                let Some(ids) = by_layer.get(&layer_index) else {
6881                    return Ok(());
6882                };
6883                e.with_moe_cache(max_block, |cache, eng| {
6884                    for id in ids {
6885                        if cache.restage_block(*id, m, eng)? {
6886                            restaged += 1;
6887                        }
6888                    }
6889                    Ok(())
6890                })
6891            };
6892        for (index, layer) in self.layers.iter().enumerate() {
6893            stage_layer(index as u16, &layer.ffn)?;
6894        }
6895        if let Some(mtp) = self.mtp.as_ref() {
6896            stage_layer(u16::MAX, &mtp.ffn)?;
6897        }
6898        e.freeze_moe_cache();
6899        println!(
6900            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
6901            path.display()
6902        );
6903        Ok(true)
6904    }
6905
6906    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
6907    pub fn freeze_cpu_expert_residency(
6908        &self,
6909        e: &Engine,
6910    ) -> Result<(), Box<dyn std::error::Error>> {
6911        e.freeze_moe_cache();
6912        Ok(())
6913    }
6914
6915    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
6916    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
6917    /// the model's activation exactly.
6918    ///
6919    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
6920    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
6921    /// form for anything that can land on a clamped layer.
6922    pub fn ffn_act(
6923        e: &Engine,
6924        cfg: &ModelConfig,
6925        gate: &CudaSlice<f32>,
6926        up: &CudaSlice<f32>,
6927        act: &mut CudaSlice<f32>,
6928        n: usize,
6929    ) -> Result<(), Box<dyn std::error::Error>> {
6930        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
6931    }
6932
6933    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
6934    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
6935    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
6936    #[allow(clippy::too_many_arguments)]
6937    pub(crate) fn ffn_act_scaled(
6938        e: &Engine,
6939        cfg: &ModelConfig,
6940        gate: &CudaSlice<f32>,
6941        up: &CudaSlice<f32>,
6942        gs: f32,
6943        us: f32,
6944        act: &mut CudaSlice<f32>,
6945        n: usize,
6946    ) -> Result<(), Box<dyn std::error::Error>> {
6947        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
6948    }
6949
6950    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
6951    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
6952    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
6953    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
6954    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
6955    ///                 arrays are SEPARATE and a layer can have one without the other.
6956    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
6957    /// already known live.
6958    #[allow(clippy::too_many_arguments)]
6959    pub(crate) fn ffn_act_lim(
6960        e: &Engine,
6961        cfg: &ModelConfig,
6962        gate: &CudaSlice<f32>,
6963        up: &CudaSlice<f32>,
6964        gs: f32,
6965        us: f32,
6966        limit: Option<f32>,
6967        act: &mut CudaSlice<f32>,
6968        n: usize,
6969    ) -> Result<(), Box<dyn std::error::Error>> {
6970        if let Some(m3) = cfg.m3.as_ref() {
6971            debug_assert!(
6972                limit.is_none(),
6973                "m3 swigluoai and step35 clamp are different archs"
6974            );
6975            return e.swigluoai_mul_scaled(
6976                gate,
6977                up,
6978                gs,
6979                us,
6980                m3.swiglu_alpha,
6981                m3.swiglu_limit,
6982                act,
6983                n,
6984            );
6985        }
6986        if let Some(l) = limit {
6987            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
6988        }
6989        if gs == 1.0 && us == 1.0 {
6990            return e.silu_mul(gate, up, act, n);
6991        }
6992        e.silu_mul_scaled(gate, up, gs, us, act, n)
6993    }
6994
6995    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
6996    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
6997    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
6998    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
6999    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
7000    fn moe_route(
7001        e: &Engine,
7002        logits: &CudaSlice<f32>,
7003        t: usize,
7004        n_expert: usize,
7005        n_used: usize,
7006    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7007        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
7008    }
7009
7010    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
7011    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
7012    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
7013    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
7014    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
7015    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
7016    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
7017    #[allow(clippy::too_many_arguments)]
7018    fn moe_route_sigmoid_cfg(
7019        e: &Engine,
7020        logits: &CudaSlice<f32>,
7021        t: usize,
7022        n_expert: usize,
7023        n_used: usize,
7024        m: &MoeWeights,
7025        (sf, route_norm): (f32, bool),
7026    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7027        if sigmoid_router_enabled() {
7028            return e.moe_router_sigmoid_topk_host(
7029                logits,
7030                t,
7031                n_expert,
7032                n_used,
7033                m.active_count(),
7034                &m.exp_probs_b_dev,
7035                &m.active_experts_dev,
7036                sf,
7037                route_norm,
7038            );
7039        }
7040        let lg = e.dtoh(logits)?;
7041        Self::moe_route_sigmoid_host(
7042            &lg,
7043            t,
7044            n_expert,
7045            n_used,
7046            m.exp_probs_b.as_deref(),
7047            sf,
7048            route_norm,
7049            m.active_experts.as_deref(),
7050        )
7051    }
7052
7053    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
7054    /// the existing softmax device kernel has no mask input.
7055    fn moe_route_cfg(
7056        e: &Engine,
7057        logits: &CudaSlice<f32>,
7058        t: usize,
7059        n_expert: usize,
7060        n_used: usize,
7061        active: Option<&[bool]>,
7062    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7063        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7064        // rollback) via the single-sync pinned readback — softmax arch only.
7065        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7066            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7067        }
7068        // Host oracle (the §D bit-identity reference).
7069        let lg = e.dtoh(logits)?; // [T*n_expert] host
7070        let mut sel = vec![0u32; t * n_used];
7071        let mut w_out = vec![0f32; t * n_used];
7072        for tok in 0..t {
7073            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7074            // softmax over ALL n_expert (stable: subtract max)
7075            let maxl = row
7076                .iter()
7077                .enumerate()
7078                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7079                .map(|(_, &x)| x)
7080                .fold(f32::NEG_INFINITY, f32::max);
7081            let mut probs = vec![0f32; n_expert];
7082            let mut den = 0f32;
7083            for i in 0..n_expert {
7084                if active.is_some_and(|mask| !mask[i]) {
7085                    continue;
7086                }
7087                let x = (row[i] - maxl).exp();
7088                probs[i] = x;
7089                den += x;
7090            }
7091            for p in probs.iter_mut() {
7092                *p /= den;
7093            }
7094            // stable DESC sort: prob DESC, ascending-index tiebreak.
7095            let mut idx: Vec<usize> = (0..n_expert)
7096                .filter(|&i| active.is_none_or(|mask| mask[i]))
7097                .collect();
7098            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7099            let sl = &idx[..n_used];
7100            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7101            let mut ws: f32 = wv.iter().sum();
7102            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7103            for x in wv.iter_mut() {
7104                *x /= ws;
7105            }
7106            for j in 0..n_used {
7107                sel[tok * n_used + j] = sl[j] as u32;
7108                w_out[tok * n_used + j] = wv[j];
7109            }
7110        }
7111        Ok((sel, w_out))
7112    }
7113
7114    #[allow(clippy::too_many_arguments)]
7115    fn moe_route_sigmoid_with_input(
7116        e: &Engine,
7117        logits: &CudaSlice<f32>,
7118        input: &CudaSlice<f32>,
7119        t: usize,
7120        in_features: usize,
7121        n_expert: usize,
7122        n_used: usize,
7123        bias: Option<&[f32]>,
7124        (sf, route_norm): (f32, bool),
7125        active: Option<&[bool]>,
7126    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7127        let logit_values =
7128            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7129        let input_values =
7130            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7131        let (lg, input) = e.dtoh_pair_views(
7132            &logits.slice(0..logit_values),
7133            &input.slice(0..input_values),
7134        )?;
7135        let (sel, w) =
7136            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7137        Ok((sel, w, input))
7138    }
7139
7140    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7141    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7142    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7143    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7144    pub fn start_moe_prefetch_predictor(
7145        &self,
7146        e: &Engine,
7147        cfg: &ModelConfig,
7148    ) -> Result<(), Box<dyn std::error::Error>> {
7149        use crate::hybrid::Ffn;
7150        let Some(sig) = cfg.sigmoid_router() else {
7151            return Err("prefetch predictor requires a sigmoid-router arch".into());
7152        };
7153        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7154            .export_moe_residency()
7155            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7156            .into_iter()
7157            .collect();
7158        let mut layers = Vec::new();
7159        for (index, layer) in self.layers.iter().enumerate() {
7160            let Ffn::Moe(m) = &layer.ffn else { continue };
7161            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7162                continue;
7163            };
7164            let router = e.dtoh(data)?;
7165            let n_expert = m.gate_exps.n_expert;
7166            let n_embd = m.gate_exps.in_f;
7167            if router.len() != n_embd * n_expert {
7168                continue;
7169            }
7170            let build = |exps: &crate::model::HostExps| {
7171                (0..n_expert)
7172                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7173                    .collect::<Vec<_>>()
7174            };
7175            layers.push((
7176                index as u16,
7177                crate::cpu_experts::PredictLayerInit {
7178                    router,
7179                    bias: m.exp_probs_b.clone(),
7180                    active: m.active_experts.clone(),
7181                    n_embd,
7182                    n_used: cfg
7183                        .moe
7184                        .as_ref()
7185                        .map(|moe| moe.expert_used_count as usize)
7186                        .ok_or("prefetch predictor requires MoE config")?,
7187                    sig,
7188                    weights_n_expert: n_expert,
7189                    gate: build(&m.gate_exps),
7190                    up: build(&m.up_exps),
7191                    down: build(&m.down_exps),
7192                },
7193            ));
7194        }
7195        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7196    }
7197
7198    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7199    /// selection math to the rollback runtime, applied to host-computed logits.
7200    #[allow(clippy::too_many_arguments)]
7201    pub fn moe_route_sigmoid_host_public(
7202        logits: &[f32],
7203        t: usize,
7204        n_expert: usize,
7205        n_used: usize,
7206        bias: Option<&[f32]>,
7207        sf: f32,
7208        route_norm: bool,
7209        active: Option<&[bool]>,
7210    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7211        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7212    }
7213
7214    #[allow(clippy::too_many_arguments)]
7215    fn moe_route_sigmoid_host(
7216        lg: &[f32],
7217        t: usize,
7218        n_expert: usize,
7219        n_used: usize,
7220        bias: Option<&[f32]>,
7221        sf: f32,
7222        route_norm: bool,
7223        active: Option<&[bool]>,
7224    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7225        let active_count = active
7226            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7227            .unwrap_or(n_expert);
7228        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7229        if lg.len() != t * n_expert {
7230            return Err(format!(
7231                "sigmoid router logits length mismatch: got {}, expected {}",
7232                lg.len(),
7233                t * n_expert,
7234            )
7235            .into());
7236        }
7237        let mut sel = vec![0u32; t * n_used];
7238        let mut w_out = vec![0f32; t * n_used];
7239        for tok in 0..t {
7240            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7241            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7242            // selection score = sigmoid + bias; weight = plain sigmoid.
7243            let selsc: Vec<f32> = match bias {
7244                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7245                None => scores.clone(),
7246            };
7247            let mut idx: Vec<usize> = (0..n_expert)
7248                .filter(|&i| active.is_none_or(|mask| mask[i]))
7249                .collect();
7250            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7251            let sl = &idx[..n_used];
7252            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7253            if route_norm {
7254                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7255                for x in wv.iter_mut() {
7256                    *x = *x / ws * sf;
7257                }
7258            } else {
7259                for x in wv.iter_mut() {
7260                    *x *= sf;
7261                }
7262            }
7263            for j in 0..n_used {
7264                sel[tok * n_used + j] = sl[j] as u32;
7265                w_out[tok * n_used + j] = wv[j];
7266            }
7267        }
7268        Ok((sel, w_out))
7269    }
7270
7271    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7272    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7273    /// macro-scaled experts, and observation modes are denied by the caller.
7274    #[allow(clippy::too_many_arguments)]
7275    fn moe_ffn_sigmoid_dev(
7276        e: &Engine,
7277        m: &MoeWeights,
7278        z: &CudaSlice<f32>,
7279        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7280        logits: &CudaSlice<f32>,
7281        t: usize,
7282        cfg: &ModelConfig,
7283        il: u16,
7284        (scaling_factor, route_norm): (f32, bool),
7285    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7286        let moe = cfg.moe.as_ref().unwrap();
7287        let n_embd = cfg.n_embd as usize;
7288        let n_expert = moe.expert_count as usize;
7289        let n_used = moe.expert_used_count as usize;
7290        let n_ff_exp = moe.expert_ff_length as usize;
7291        let dev = m.dev_exps.as_ref().unwrap();
7292        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7293        debug_assert!(m.has_uniform_expert_layout());
7294        debug_assert!(!m.has_macros);
7295
7296        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7297            logits,
7298            t,
7299            n_expert,
7300            n_used,
7301            m.active_count(),
7302            &m.exp_probs_b_dev,
7303            &m.active_experts_dev,
7304            scaling_factor,
7305            route_norm,
7306        )?;
7307        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7308        if let Some(fp8) = dev.fp8_blk.as_ref() {
7309            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7310            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7311            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7312            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7313            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7314            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7315
7316            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7317            // activations with block-128 E4M3 weights. This deliberately
7318            // simple resident reference is the correctness oracle for later
7319            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7320            // load-time Q8 diagnostic representation, so one process never
7321            // crosses between numerical programs.
7322            let selected = e.dtoh_i32(&sel_d)?;
7323            let route_weights = e.dtoh(&w_d)?;
7324            let mut moe_out = e.zeros(t * n_embd)?;
7325            for tok in 0..t {
7326                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7327                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7328                for j in 0..n_used {
7329                    let pair = tok * n_used + j;
7330                    let expert = selected[pair] as usize;
7331                    let gate = Self::moe_resident_fp8_e4m3(
7332                        e,
7333                        &m.gate_exps,
7334                        &dev.gate,
7335                        &fp8.gate,
7336                        expert,
7337                        &zt,
7338                        1,
7339                    )?;
7340                    let up = Self::moe_resident_fp8_e4m3(
7341                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7342                    )?;
7343                    let mut act = e.uninit(n_ff_exp)?;
7344                    Self::ffn_act_lim(
7345                        e,
7346                        cfg,
7347                        &gate,
7348                        &up,
7349                        1.0,
7350                        1.0,
7351                        cfg.clamp_exp_at(il as u32),
7352                        &mut act,
7353                        n_ff_exp,
7354                    )?;
7355                    let act = act.slice(0..n_ff_exp);
7356                    let down = Self::moe_resident_fp8_e4m3(
7357                        e,
7358                        &m.down_exps,
7359                        &dev.down,
7360                        &fp8.down,
7361                        expert,
7362                        &act,
7363                        1,
7364                    )?;
7365                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7366                }
7367            }
7368            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7369                eprintln!(
7370                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7371                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7372                    cfg.clamp_exp_at(il as u32).is_some(),
7373                );
7374            }
7375            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7376            return Ok(moe_out);
7377        }
7378        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7379            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7380            (combined, combined)
7381        } else {
7382            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7383        };
7384        let (zq, zd) = match (t, zq8) {
7385            (1, Some((q, d))) => (q.clone(), d.clone()),
7386            _ => e.quantize_q8_1(z, t, n_embd)?,
7387        };
7388        let n_pairs = t * n_used;
7389        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7390            // The final Step layers retain the established separate gate/up -> clamp -> down
7391            // arithmetic. Pair rows are derived from token position; selected expert ids and
7392            // routing weights remain the device router's buffers throughout.
7393            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7394            let pair_tok_d = e.htod_i32(&pair_tok)?;
7395            let gate = e.moe_pairs_matvec_q8(
7396                &dev.ptr_row,
7397                0,
7398                &pair_tok_d,
7399                &sel_d,
7400                &zq,
7401                &zd,
7402                n_embd,
7403                n_ff_exp,
7404                n_expert,
7405                n_pairs,
7406                m.gate_exps.qtype,
7407                gate_row_bytes,
7408            )?;
7409            let up = e.moe_pairs_matvec_q8(
7410                &dev.ptr_row,
7411                1,
7412                &pair_tok_d,
7413                &sel_d,
7414                &zq,
7415                &zd,
7416                n_embd,
7417                n_ff_exp,
7418                n_expert,
7419                n_pairs,
7420                m.up_exps.qtype,
7421                up_row_bytes,
7422            )?;
7423            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7424            Self::ffn_act_lim(
7425                e,
7426                cfg,
7427                &gate,
7428                &up,
7429                1.0,
7430                1.0,
7431                cfg.clamp_exp_at(il as u32),
7432                &mut act,
7433                n_pairs * n_ff_exp,
7434            )?;
7435            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7436            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7437            let pair_self_d = e.htod_i32(&pair_self)?;
7438            let down = e.moe_pairs_matvec_q8(
7439                &dev.ptr_row,
7440                2,
7441                &pair_self_d,
7442                &sel_d,
7443                &aq2,
7444                &ad2,
7445                n_ff_exp,
7446                n_embd,
7447                n_expert,
7448                n_pairs,
7449                m.down_exps.qtype,
7450                m.down_exps.row_bytes,
7451            )?;
7452            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7453            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7454            let tok_off_d = e.htod_i32(&tok_off)?;
7455            let tok_ids_d = e.htod_i32(&tok_ids)?;
7456            let mut output = e.uninit(t * n_embd)?;
7457            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7458            output
7459        } else {
7460            let act = e.moe_gate_up_silu8_dev_q8_rows(
7461                &dev.ptr_row,
7462                &sel_d,
7463                &zq,
7464                &zd,
7465                t,
7466                n_embd,
7467                n_ff_exp,
7468                n_used,
7469                n_expert,
7470                m.gate_exps.qtype,
7471                m.up_exps.qtype,
7472                gate_row_bytes,
7473                up_row_bytes,
7474                &m.dev_macros,
7475            )?;
7476            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7477            let mut output = e.uninit(t * n_embd)?;
7478            e.moe_down8_fma_dev_q8_rows_g(
7479                &dev.ptr_row,
7480                &sel_d,
7481                &w_d,
7482                &aq2,
7483                &ad2,
7484                &mut output,
7485                t,
7486                n_ff_exp,
7487                n_embd,
7488                n_used,
7489                n_expert,
7490                m.down_exps.qtype,
7491                m.down_exps.row_bytes,
7492            )?;
7493            output
7494        };
7495
7496        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7497            eprintln!(
7498                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7499                cfg.clamp_exp_at(il as u32).is_some(),
7500                dev.gu_il,
7501            );
7502        }
7503        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7504        Ok(moe_out)
7505    }
7506
7507    #[allow(clippy::too_many_arguments)]
7508    fn moe_resident_fp8_e4m3(
7509        e: &Engine,
7510        exps: &crate::model::HostExps,
7511        bytes: &CudaSlice<u8>,
7512        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7513        expert: usize,
7514        x: &cudarc::driver::CudaView<f32>,
7515        m: usize,
7516    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7517        let layout = exps.expert_layout(expert);
7518        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7519        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7520        let byte_start = expert * exps.expert_stride;
7521        let scale_start = expert * scales.expert_stride;
7522        let weight = bytes.slice(byte_start..byte_start + layout.len);
7523        let scale = scales
7524            .scales
7525            .slice(scale_start..scale_start + scales.expert_stride);
7526        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7527    }
7528
7529    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7530    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7531    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7532    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7533    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7534    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7535    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7536    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7537    fn moe_ffn_pairs(
7538        e: &Engine,
7539        m: &MoeWeights,
7540        z: &CudaSlice<f32>,
7541        logits: &CudaSlice<f32>,
7542        t: usize,
7543        cfg: &ModelConfig,
7544    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7545        let moe = cfg.moe.as_ref().unwrap();
7546        let n_embd = cfg.n_embd as usize;
7547        let n_expert = moe.expert_count as usize;
7548        let n_used = moe.expert_used_count as usize;
7549        let n_ff_exp = moe.expert_ff_length as usize;
7550        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7551        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7552        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7553        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7554        debug_assert!(
7555            !cfg.swiglu_clamped_anywhere(),
7556            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7557        );
7558        let dev = m.dev_exps.as_ref().unwrap();
7559        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7560        let (rbg_d, rbu_d) = if dev.gu_il {
7561            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7562            (sxx, sxx)
7563        } else {
7564            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7565        };
7566
7567        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7568        let n_pairs = t * n_used;
7569        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7570        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7571        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7572        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7573        let pair_w: Vec<f32> = w_all.clone();
7574        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7575        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7576        let pt = e.htod_i32(&pair_tok)?;
7577        let px = e.htod_i32(&pair_ex)?;
7578        let pw = e.htod(&pair_w)?;
7579        let toff = e.htod_i32(&tok_off)?;
7580        let tids = e.htod_i32(&tok_ids)?;
7581
7582        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7583        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7584        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7585        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7586        for p in 0..n_pairs {
7587            by_ex[pair_ex[p] as usize].push(p as i32);
7588        }
7589        let mut ex_ids: Vec<i32> = Vec::new();
7590        let mut ex_off: Vec<i32> = vec![0];
7591        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7592        for (ex, list) in by_ex.iter().enumerate() {
7593            if list.is_empty() {
7594                continue;
7595            }
7596            ex_ids.push(ex as i32);
7597            ex_pairs.extend_from_slice(list);
7598            ex_off.push(ex_pairs.len() as i32);
7599        }
7600        let n_active = ex_ids.len();
7601        let exi = e.htod_i32(&ex_ids)?;
7602        let exo = e.htod_i32(&ex_off)?;
7603        let exp_d = e.htod_i32(&ex_pairs)?;
7604        let _ = &px; // pair-major twin keeps it; em path uses CSR
7605
7606        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7607        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7608        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7609        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7610        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7611        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7612        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7613        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7614        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7615        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7616        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7617        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7618        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7619        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7620        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7621        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7622        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7623        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7624        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7625        let mma_t = *MMA_T.get_or_init(|| {
7626            std::env::var("MEMRA_MOE_MMA_T")
7627                .ok()
7628                .and_then(|v| v.parse().ok())
7629                .unwrap_or(16)
7630        });
7631        let use_mma = std::env::var("MEMRA_MOE_MMA")
7632            .map(|v| v != "0")
7633            .unwrap_or(true)
7634            && t >= mma_t
7635            && q8_expert_dec_supported(m.gate_exps.qtype)
7636            && q8_expert_dec_supported(m.up_exps.qtype)
7637            && q8_expert_dec_supported(m.down_exps.qtype)
7638            && n_embd % 256 == 0
7639            && n_ff_exp % 256 == 0;
7640        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7641        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7642        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7643        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7644        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7645        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7646        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7647        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7648        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7649        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7650        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7651        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7652        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7653        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
7654        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
7655        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
7656            && q8_expert_dec_supported(m.up_exps.qtype)
7657            && q8_expert_dec_supported(m.down_exps.qtype)
7658            && n_embd % 256 == 0
7659            && n_ff_exp % 256 == 0;
7660        let f16g_mode = crate::moe_f16g_mode();
7661        let f16g = f16g_mode != 0
7662            && t >= mma_t
7663            && (f16g_mode != 3 || !mma_capable)
7664            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
7665            && f16g_proj_ok(m.up_exps.qtype, n_embd)
7666            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
7667        if use_mma || f16g {
7668            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
7669            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
7670            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
7671            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
7672            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
7673            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
7674            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
7675            let y_down = if f16g {
7676                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
7677                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
7678                // permute at the very end back to pair-id order for the scatter.
7679                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
7680                let csr_tok_d = e.htod_i32(&csr_tok)?;
7681                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
7682                let g_csr = e.moe_f16_grouped(
7683                    &dev.ptr_row,
7684                    0,
7685                    n_expert,
7686                    &exi,
7687                    &ex_off,
7688                    &exo,
7689                    &z_f16,
7690                    &z_s,
7691                    n_embd,
7692                    n_ff_exp,
7693                    n_active,
7694                    n_pairs,
7695                    m.gate_exps.qtype,
7696                    rbg_d,
7697                )?;
7698                let u_csr = e.moe_f16_grouped(
7699                    &dev.ptr_row,
7700                    1,
7701                    n_expert,
7702                    &exi,
7703                    &ex_off,
7704                    &exo,
7705                    &z_f16,
7706                    &z_s,
7707                    n_embd,
7708                    n_ff_exp,
7709                    n_active,
7710                    n_pairs,
7711                    m.up_exps.qtype,
7712                    rbu_d,
7713                )?;
7714                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
7715                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
7716                let d_csr = e.moe_f16_grouped(
7717                    &dev.ptr_row,
7718                    2,
7719                    n_expert,
7720                    &exi,
7721                    &ex_off,
7722                    &exo,
7723                    &a_f16,
7724                    &a_s,
7725                    n_ff_exp,
7726                    n_embd,
7727                    n_active,
7728                    n_pairs,
7729                    m.down_exps.qtype,
7730                    m.down_exps.row_bytes,
7731                )?;
7732                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
7733            } else {
7734                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
7735                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
7736                let gate = e.mmq_iq_experts(
7737                    &dev.ptr_row,
7738                    0,
7739                    n_expert,
7740                    &exi,
7741                    &exo,
7742                    &exp_d,
7743                    &pt,
7744                    &z_scr,
7745                    n_embd,
7746                    n_ff_exp,
7747                    n_active,
7748                    n_pairs,
7749                    t,
7750                    m.gate_exps.qtype,
7751                    rbg_d,
7752                )?;
7753                let up = e.mmq_iq_experts(
7754                    &dev.ptr_row,
7755                    1,
7756                    n_expert,
7757                    &exi,
7758                    &exo,
7759                    &exp_d,
7760                    &pt,
7761                    &z_scr,
7762                    n_embd,
7763                    n_ff_exp,
7764                    n_active,
7765                    n_pairs,
7766                    t,
7767                    m.up_exps.qtype,
7768                    rbu_d,
7769                )?;
7770                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
7771                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
7772                // registers and writes ONLY the quantized scratch — the two-pass chain
7773                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
7774                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
7775                let a_scr = if crate::moe_fuse_actq_on() {
7776                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
7777                } else {
7778                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7779                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
7780                };
7781                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7782                let pself = e.htod_i32(&pair_self)?;
7783                e.mmq_iq_experts(
7784                    &dev.ptr_row,
7785                    2,
7786                    n_expert,
7787                    &exi,
7788                    &exo,
7789                    &exp_d,
7790                    &pself,
7791                    &a_scr,
7792                    n_ff_exp,
7793                    n_embd,
7794                    n_active,
7795                    n_pairs,
7796                    n_pairs,
7797                    m.down_exps.qtype,
7798                    m.down_exps.row_bytes,
7799                )?
7800            };
7801            let mut moe_out = e.uninit(t * n_embd)?;
7802            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7803            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7804                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7805            {
7806                let n_ff_sh = gate_shexp.out_features();
7807                let sg_gate = e.matmul(gate_shexp, z, t)?;
7808                let sg_up = e.matmul(up_shexp, z, t)?;
7809                let mut sa = e.uninit(t * n_ff_sh)?;
7810                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7811                let sh = e.matmul(down_shexp, &sa, t)?;
7812                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7813                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
7814                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
7815                // i.e. the one real prefill actually takes on a resident-expert MoE model,
7816                // so the concat-prime isolation fix has to land here as well.
7817                let g = match &m.gate_inp_shexp {
7818                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7819                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7820                    }
7821                    Some(gate_inp_shexp) => {
7822                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7823                        let mut g = e.uninit(t)?;
7824                        e.sigmoid(&gs, &mut g, t)?;
7825                        g
7826                    }
7827                    None => e.htod(&vec![1.0f32; t])?,
7828                };
7829                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7830            }
7831            return Ok(moe_out);
7832        }
7833
7834        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
7835        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
7836        let dec = std::env::var("MEMRA_MOE_DEC")
7837            .map(|v| v != "0")
7838            .unwrap_or(true);
7839        let matvec = |proj,
7840                      exi: &_,
7841                      exo: &_,
7842                      exp_d: &_,
7843                      pt: &_,
7844                      aq: &_,
7845                      ad: &_,
7846                      inf,
7847                      outf,
7848                      qtype,
7849                      rb|
7850         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7851            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
7852            let dec = dec && q8_expert_dec_supported(qtype);
7853            if dec {
7854                e.moe_pairs_matvec_q8_dec(
7855                    &dev.ptr_row,
7856                    proj,
7857                    exi,
7858                    exo,
7859                    exp_d,
7860                    pt,
7861                    aq,
7862                    ad,
7863                    inf,
7864                    outf,
7865                    n_expert,
7866                    n_active,
7867                    n_pairs,
7868                    qtype,
7869                    rb,
7870                )
7871            } else {
7872                e.moe_pairs_matvec_q8_em(
7873                    &dev.ptr_row,
7874                    proj,
7875                    exi,
7876                    exo,
7877                    exp_d,
7878                    pt,
7879                    aq,
7880                    ad,
7881                    inf,
7882                    outf,
7883                    n_expert,
7884                    n_active,
7885                    n_pairs,
7886                    qtype,
7887                    rb,
7888                )
7889            }
7890        };
7891        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7892        let gate = matvec(
7893            0,
7894            &exi,
7895            &exo,
7896            &exp_d,
7897            &pt,
7898            &zq,
7899            &zd,
7900            n_embd,
7901            n_ff_exp,
7902            m.gate_exps.qtype,
7903            rbg_d,
7904        )?;
7905        let up = matvec(
7906            1,
7907            &exi,
7908            &exo,
7909            &exp_d,
7910            &pt,
7911            &zq,
7912            &zd,
7913            n_embd,
7914            n_ff_exp,
7915            m.up_exps.qtype,
7916            rbu_d,
7917        )?;
7918        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7919        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7920        // down consumes PAIR-major activation rows: pair_tok = identity.
7921        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7922        let pself = e.htod_i32(&pair_self)?;
7923        let y_down = matvec(
7924            2,
7925            &exi,
7926            &exo,
7927            &exp_d,
7928            &pself,
7929            &aq2,
7930            &ad2,
7931            n_ff_exp,
7932            n_embd,
7933            m.down_exps.qtype,
7934            m.down_exps.row_bytes,
7935        )?;
7936        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
7937        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7938
7939        // SHARED EXPERT epilogue — same as the other paths.
7940        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7941        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7942        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7943            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7944        {
7945            let n_ff_sh = gate_shexp.out_features();
7946            // These decode-exact forms are required by the new Step resident arm. Keep the
7947            // established grouped shared-expert program for every other architecture: widening
7948            // this to Gemma changed its speculative acceptance despite green argmax gates.
7949            let step_exact = true;
7950            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
7951            let (sg_gate, sg_up) = if step_exact && t == 1 {
7952                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
7953            } else if verify_t {
7954                let mut fused = None;
7955                if crate::spec::spec_fused_t()
7956                    && (2..=4).contains(&t)
7957                    && e.uses_q8_1_fast(gate_shexp)
7958                    && e.uses_q8_1_fast(up_shexp)
7959                {
7960                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7961                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
7962                }
7963                match fused {
7964                    Some(pair) => pair,
7965                    None => (
7966                        e.matmul_decode_exact(gate_shexp, z, t)?,
7967                        e.matmul_decode_exact(up_shexp, z, t)?,
7968                    ),
7969                }
7970            } else {
7971                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
7972            };
7973            let mut sa = e.uninit(t * n_ff_sh)?;
7974            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7975            let sh = if verify_t {
7976                e.matmul_decode_exact(down_shexp, &sa, t)?
7977            } else {
7978                e.matmul(down_shexp, &sa, t)?
7979            };
7980            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7981            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
7982            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
7983            // dispatch choice cannot change bits.
7984            let g = match &m.gate_inp_shexp {
7985                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7986                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7987                }
7988                Some(gate_inp_shexp) => {
7989                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7990                    let mut g = e.uninit(t)?;
7991                    e.sigmoid(&gs, &mut g, t)?;
7992                    g
7993                }
7994                None => e.htod(&vec![1.0f32; t])?,
7995            };
7996            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7997        }
7998        Ok(moe_out)
7999    }
8000
8001    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
8002    #[allow(clippy::too_many_arguments)]
8003    #[allow(clippy::too_many_arguments)]
8004    fn moe_ffn_dev(
8005        e: &Engine,
8006        m: &MoeWeights,
8007        z: &CudaSlice<f32>,
8008        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
8009        logits: &CudaSlice<f32>,
8010        t: usize,
8011        cfg: &ModelConfig,
8012        il: u16,
8013        max_block: usize,
8014    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8015        let moe = cfg.moe.as_ref().unwrap();
8016        let n_embd = cfg.n_embd as usize;
8017        let n_expert = moe.expert_count as usize;
8018        let n_used = moe.expert_used_count as usize;
8019        let n_ff_exp = moe.expert_ff_length as usize;
8020        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
8021        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
8022        // clamped layers; assert both so a future caller that skips the gate fails loudly.
8023        debug_assert!(
8024            cfg.sigmoid_router().is_none(),
8025            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
8026        );
8027        debug_assert!(
8028            !cfg.swiglu_clamped_at(il as u32),
8029            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
8030        );
8031
8032        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
8033        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
8034        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
8035        // skipped entirely for macro-free experts (every k-quant GGUF).
8036        if m.has_macros {
8037            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
8038        }
8039
8040        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
8041        let mut moe_out = e.uninit(t * n_embd)?;
8042
8043        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
8044        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
8045        if let Some(dev) = m.dev_exps.as_ref() {
8046            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
8047            // the combined stride; up's base is offset in the ptr table. Down unchanged.
8048            let (rbg_d, rbu_d) = if dev.gu_il {
8049                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8050                (sxx, sxx)
8051            } else {
8052                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8053            };
8054            let q8 = moe_q8_enabled()
8055                && q8_expert_supported(m.gate_exps.qtype)
8056                && q8_expert_supported(m.up_exps.qtype)
8057                && q8_expert_supported(m.down_exps.qtype);
8058            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
8059            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
8060            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
8061            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8062            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8063            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8064            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8065            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8066            let rows_arm = q8
8067                && t > 1
8068                && crate::spec::spec_m2()
8069                && n_ff_exp == 512
8070                && n_used <= 8
8071                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8072                    .map(|v| v.is_empty() || v == "v")
8073                    .unwrap_or(true)
8074                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8075                    .map(|v| v.is_empty() || v == "w8h2v")
8076                    .unwrap_or(true);
8077            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8078            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8079            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8080            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8081            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8082            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8083            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8084            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8085            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8086                .ok()
8087                .and_then(|v| v.parse::<i32>().ok())
8088                .unwrap_or(1);
8089            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8090            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8091            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8092            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8093            // axis. Three chain-pinning attempts did not close it (receipts,
8094            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8095            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8096            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8097            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8098            // never decode-batch-gate at B=8 on the MoE model itself.
8099            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8100            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8101            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8102            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8103            // de-admission verdict above stands until those gates are GREEN on the MoE
8104            // artifact; this door must never default on.
8105            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8106            let csr_qt = |qt: i32| {
8107                qt == crate::QT_IQ4_XS
8108                    || qt == crate::QT_IQ3_S
8109                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8110            };
8111            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8112            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8113            let csr_arm = rows_arm
8114                && csr_mode > 0
8115                && t <= csr_t_max
8116                && csr_uniform
8117                && csr_qt(m.gate_exps.qtype)
8118                && csr_qt(m.up_exps.qtype)
8119                && csr_qt(m.down_exps.qtype);
8120            if csr_arm {
8121                if csr_mode == 2 {
8122                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8123                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8124                }
8125                let n_pairs = t * n_used;
8126                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8127                let act = e.moe_gate_up_silu8_dev_q8_csr(
8128                    &dev.ptr_row,
8129                    &sel_d,
8130                    &zq,
8131                    &zd,
8132                    n_pairs,
8133                    n_embd,
8134                    n_ff_exp,
8135                    n_used,
8136                    n_expert,
8137                    m.gate_exps.qtype,
8138                    m.up_exps.qtype,
8139                    rbg_d,
8140                    rbu_d,
8141                )?;
8142                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8143                // down stays on the _rows twin — BOTH CSR down variants measured negative
8144                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8145                // 16-group rows have too little decode to amortize any dedup structure.
8146                e.moe_down8_fma_dev_q8_rows(
8147                    &dev.ptr_row,
8148                    &sel_d,
8149                    &w_d,
8150                    &aq2,
8151                    &ad2,
8152                    &mut moe_out,
8153                    t,
8154                    n_ff_exp,
8155                    n_embd,
8156                    n_used,
8157                    n_expert,
8158                    m.down_exps.qtype,
8159                    m.down_exps.row_bytes,
8160                )?;
8161                if csr_mode == 2 {
8162                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8163                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8164                        &dev.ptr_row,
8165                        &sel_d,
8166                        &zq,
8167                        &zd,
8168                        t,
8169                        n_embd,
8170                        n_ff_exp,
8171                        n_used,
8172                        n_expert,
8173                        m.gate_exps.qtype,
8174                        m.up_exps.qtype,
8175                        rbg_d,
8176                        rbu_d,
8177                        &m.dev_macros,
8178                    )?;
8179                    let mut out_r = e.uninit(t * n_embd)?;
8180                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8181                    e.moe_down8_fma_dev_q8_rows(
8182                        &dev.ptr_row,
8183                        &sel_d,
8184                        &w_d,
8185                        &aq2r,
8186                        &ad2r,
8187                        &mut out_r,
8188                        t,
8189                        n_ff_exp,
8190                        n_embd,
8191                        n_used,
8192                        n_expert,
8193                        m.down_exps.qtype,
8194                        m.down_exps.row_bytes,
8195                    )?;
8196                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8197                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8198                    let ba = a1
8199                        .iter()
8200                        .zip(&a2)
8201                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8202                        .count();
8203                    let bo = o1
8204                        .iter()
8205                        .zip(&o2)
8206                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8207                        .count();
8208                    if ba + bo > 0 {
8209                        eprintln!(
8210                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8211                            a1.len(),
8212                            o1.len()
8213                        );
8214                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8215                        let sel_h = e.dtoh_i32(&sel_d)?;
8216                        let mut shown = 0;
8217                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8218                            if x.to_bits() != y.to_bits() && shown < 4 {
8219                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8220                                let ex = sel_h[p];
8221                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8222                                eprintln!(
8223                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8224                                );
8225                                shown += 1;
8226                            }
8227                        }
8228                        std::process::exit(3);
8229                    }
8230                }
8231            } else if rows_arm {
8232                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8233                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8234                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8235                    use std::sync::atomic::{AtomicU64, Ordering};
8236                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8237                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8238                    static CALLS: AtomicU64 = AtomicU64::new(0);
8239                    let sel_h = e.dtoh_i32(&sel_d)?;
8240                    let mut u: Vec<i32> = sel_h.clone();
8241                    u.sort_unstable();
8242                    u.dedup();
8243                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8244                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8245                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8246                    if c % 480 == 0 {
8247                        let p = PAIRS.load(Ordering::Relaxed);
8248                        let q = UNIQ.load(Ordering::Relaxed);
8249                        eprintln!(
8250                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8251                            q as f64 / p as f64
8252                        );
8253                    }
8254                }
8255                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8256                let act = e.moe_gate_up_silu8_dev_q8_rows(
8257                    &dev.ptr_row,
8258                    &sel_d,
8259                    &zq,
8260                    &zd,
8261                    t,
8262                    n_embd,
8263                    n_ff_exp,
8264                    n_used,
8265                    n_expert,
8266                    m.gate_exps.qtype,
8267                    m.up_exps.qtype,
8268                    rbg_d,
8269                    rbu_d,
8270                    &m.dev_macros,
8271                )?;
8272                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8273                e.moe_down8_fma_dev_q8_rows(
8274                    &dev.ptr_row,
8275                    &sel_d,
8276                    &w_d,
8277                    &aq2,
8278                    &ad2,
8279                    &mut moe_out,
8280                    t,
8281                    n_ff_exp,
8282                    n_embd,
8283                    n_used,
8284                    n_expert,
8285                    m.down_exps.qtype,
8286                    m.down_exps.row_bytes,
8287                )?;
8288            } else {
8289                for tok in 0..t {
8290                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8291                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8292                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8293                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8294                    if q8 {
8295                        let (zq, zd) = match (t, zq8) {
8296                            (1, Some((q, d))) => (q.clone(), d.clone()),
8297                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8298                        };
8299                        let act = e.moe_gate_up_silu8_dev_q8(
8300                            &dev.ptr_row,
8301                            &selt,
8302                            &zq,
8303                            &zd,
8304                            n_embd,
8305                            n_ff_exp,
8306                            n_used,
8307                            n_expert,
8308                            m.gate_exps.qtype,
8309                            m.up_exps.qtype,
8310                            rbg_d,
8311                            rbu_d,
8312                            &m.dev_macros,
8313                        )?;
8314                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8315                        e.moe_down8_fma_dev_q8(
8316                            &dev.ptr_row,
8317                            &selt,
8318                            &wt,
8319                            &aq2,
8320                            &ad2,
8321                            &mut dst,
8322                            n_ff_exp,
8323                            n_embd,
8324                            n_used,
8325                            n_expert,
8326                            m.down_exps.qtype,
8327                            m.down_exps.row_bytes,
8328                        )?;
8329                    } else {
8330                        let act = e.moe_gate_up_silu8_dev(
8331                            &dev.ptr_row,
8332                            &selt,
8333                            &zt,
8334                            n_embd,
8335                            n_ff_exp,
8336                            n_used,
8337                            n_expert,
8338                            m.gate_exps.qtype,
8339                            m.up_exps.qtype,
8340                            rbg_d,
8341                            rbu_d,
8342                            &m.dev_macros,
8343                        )?;
8344                        e.moe_down8_fma_dev(
8345                            &dev.ptr_row,
8346                            &selt,
8347                            &wt,
8348                            &act,
8349                            &mut dst,
8350                            n_ff_exp,
8351                            n_embd,
8352                            n_used,
8353                            n_expert,
8354                            m.down_exps.qtype,
8355                            m.down_exps.row_bytes,
8356                        )?;
8357                    }
8358                }
8359            }
8360        } else {
8361            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8362            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8363            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8364            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8365            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8366            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8367            let q8 = moe_q8_enabled()
8368                && q8_expert_supported(m.gate_exps.qtype)
8369                && q8_expert_supported(m.up_exps.qtype)
8370                && q8_expert_supported(m.down_exps.qtype);
8371            e.with_moe_cache(max_block, |c, eng| {
8372                let row = c
8373                    .layer_dev_row(il, n_expert, eng)?
8374                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8375                for tok in 0..t {
8376                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8377                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8378                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8379                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8380                    if q8 {
8381                        let (zq, zd) = match (t, zq8) {
8382                            (1, Some((q, d))) => (q.clone(), d.clone()),
8383                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8384                        };
8385                        let act = eng.moe_gate_up_silu8_dev_q8(
8386                            row,
8387                            &selt,
8388                            &zq,
8389                            &zd,
8390                            n_embd,
8391                            n_ff_exp,
8392                            n_used,
8393                            n_expert,
8394                            m.gate_exps.qtype,
8395                            m.up_exps.qtype,
8396                            m.gate_exps.row_bytes,
8397                            m.up_exps.row_bytes,
8398                            &m.dev_macros,
8399                        )?;
8400                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8401                        eng.moe_down8_fma_dev_q8(
8402                            row,
8403                            &selt,
8404                            &wt,
8405                            &aq2,
8406                            &ad2,
8407                            &mut dst,
8408                            n_ff_exp,
8409                            n_embd,
8410                            n_used,
8411                            n_expert,
8412                            m.down_exps.qtype,
8413                            m.down_exps.row_bytes,
8414                        )?;
8415                    } else {
8416                        let act = eng.moe_gate_up_silu8_dev(
8417                            row,
8418                            &selt,
8419                            &zt,
8420                            n_embd,
8421                            n_ff_exp,
8422                            n_used,
8423                            n_expert,
8424                            m.gate_exps.qtype,
8425                            m.up_exps.qtype,
8426                            m.gate_exps.row_bytes,
8427                            m.up_exps.row_bytes,
8428                            &m.dev_macros,
8429                        )?;
8430                        eng.moe_down8_fma_dev(
8431                            row,
8432                            &selt,
8433                            &wt,
8434                            &act,
8435                            &mut dst,
8436                            n_ff_exp,
8437                            n_embd,
8438                            n_used,
8439                            n_expert,
8440                            m.down_exps.qtype,
8441                            m.down_exps.row_bytes,
8442                        )?;
8443                    }
8444                }
8445                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8446                c.hits += (t * 3 * n_used) as u64;
8447                Ok(())
8448            })?;
8449        }
8450
8451        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8452        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8453        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8454        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8455        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8456            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8457        {
8458            let n_ff_sh = gate_shexp.out_features();
8459            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8460            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8461            let verify_t = t > 1 && t < PRIME_MIN_T;
8462            let (sg_gate, sg_up) = if t == 1 {
8463                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8464            } else if verify_t {
8465                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8466                // rides one shared quantize + one fused2 batched launch instead of two
8467                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8468                let mut fused = None;
8469                if crate::spec::spec_fused_t()
8470                    && (2..=4).contains(&t)
8471                    && e.uses_q8_1_fast(gate_shexp)
8472                    && e.uses_q8_1_fast(up_shexp)
8473                {
8474                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8475                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8476                }
8477                match fused {
8478                    Some(pair) => pair,
8479                    None => (
8480                        e.matmul_decode_exact(gate_shexp, z, t)?,
8481                        e.matmul_decode_exact(up_shexp, z, t)?,
8482                    ),
8483                }
8484            } else {
8485                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8486            };
8487            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8488            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8489            let sh = if verify_t {
8490                e.matmul_decode_exact(down_shexp, &sa, t)?
8491            } else {
8492                e.matmul(down_shexp, &sa, t)?
8493            };
8494            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8495            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8496            // between the two arms; prefill keeps the batched cuBLASLt linear).
8497            let g = match &m.gate_inp_shexp {
8498                Some(gate_inp_shexp) => {
8499                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8500                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8501                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8502                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8503                    } else {
8504                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8505                        let mut g = e.uninit(t)?;
8506                        e.sigmoid(&gs, &mut g, t)?;
8507                        g
8508                    }
8509                }
8510                None => e.htod(&vec![1.0f32; t])?,
8511            };
8512            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8513        }
8514
8515        Ok(moe_out)
8516    }
8517
8518    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8519    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8520    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8521    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8522    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8523    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8524    /// the collected raw pointers cannot move between collection and launch (single-threaded
8525    /// decode; the lock is held only for collection, launches are stream-ordered after any
8526    /// prior same-stream staging writes).
8527    #[allow(clippy::too_many_arguments)]
8528    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8529    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8530    #[allow(clippy::too_many_arguments)]
8531    fn moe_gdec_token_q8(
8532        e: &Engine,
8533        m: &MoeWeights,
8534        il: u16,
8535        max_block: usize,
8536        zq: &CudaSlice<i8>,
8537        zd: &CudaSlice<f32>,
8538        sel: &[u32],
8539        w: &[f32],
8540        moe_out: &mut CudaSlice<f32>,
8541        tok: usize,
8542        n_embd: usize,
8543        n_ff_exp: usize,
8544        n_used: usize,
8545    ) -> Result<bool, Box<dyn std::error::Error>> {
8546        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8547        use cudarc::driver::DevicePtr;
8548        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8549            let mut g = [0u64; 8];
8550            let mut u = [0u64; 8];
8551            let mut d = [0u64; 8];
8552            for (j, &ex) in sel.iter().enumerate() {
8553                let ex = ex as u16;
8554                let (Some(sg), Some(su), Some(sd)) = (
8555                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8556                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8557                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8558                ) else {
8559                    return Ok(None);
8560                };
8561                let __s = eng.stream();
8562                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8563                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8564                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8565                g[j] = pg as u64;
8566                u[j] = pu as u64;
8567                d[j] = pd as u64;
8568            }
8569            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8570                for &ex in sel {
8571                    let ex = ex as u16;
8572                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8573                        c.note_profile_hit(BlockId::new(il, proj, ex));
8574                    }
8575                }
8576            }
8577            c.hits += (3 * n_used) as u64;
8578            Ok(Some((g, u, d)))
8579        })?;
8580        let Some((g, u, d)) = ptrs else {
8581            return Ok(false);
8582        };
8583        let mut wv = [0f32; 8];
8584        wv[..n_used].copy_from_slice(w);
8585        let act = e.moe_gate_up_silu8_q8(
8586            crate::WPtr8(g),
8587            crate::WPtr8(u),
8588            zq,
8589            zd,
8590            n_embd,
8591            n_ff_exp,
8592            n_used,
8593            m.gate_exps.qtype,
8594            m.up_exps.qtype,
8595            m.gate_exps.row_bytes,
8596            m.up_exps.row_bytes,
8597        )?;
8598        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8599        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8600        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8601        e.moe_down8_fma_q8(
8602            crate::WPtr8(d),
8603            crate::F32x8(wv),
8604            &aq2,
8605            &ad2,
8606            &mut dst,
8607            n_ff_exp,
8608            n_embd,
8609            n_used,
8610            m.down_exps.qtype,
8611            m.down_exps.row_bytes,
8612        )?;
8613        Ok(true)
8614    }
8615
8616    fn moe_gdec_token(
8617        e: &Engine,
8618        m: &MoeWeights,
8619        il: u16,
8620        max_block: usize,
8621        zt: &cudarc::driver::CudaView<f32>,
8622        sel: &[u32],
8623        w: &[f32],
8624        moe_out: &mut CudaSlice<f32>,
8625        tok: usize,
8626        n_embd: usize,
8627        n_ff_exp: usize,
8628        n_used: usize,
8629    ) -> Result<bool, Box<dyn std::error::Error>> {
8630        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8631        use cudarc::driver::DevicePtr;
8632        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8633        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8634            let mut g = [0u64; 8];
8635            let mut u = [0u64; 8];
8636            let mut d = [0u64; 8];
8637            for (j, &ex) in sel.iter().enumerate() {
8638                let ex = ex as u16;
8639                let (Some(sg), Some(su), Some(sd)) = (
8640                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8641                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8642                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8643                ) else {
8644                    return Ok(None);
8645                };
8646                let __s = eng.stream();
8647                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8648                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8649                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8650                g[j] = pg as u64;
8651                u[j] = pu as u64;
8652                d[j] = pd as u64;
8653            }
8654            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8655                for &ex in sel {
8656                    let ex = ex as u16;
8657                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8658                        c.note_profile_hit(BlockId::new(il, proj, ex));
8659                    }
8660                }
8661            }
8662            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
8663            Ok(Some((g, u, d)))
8664        })?;
8665        let Some((g, u, d)) = ptrs else {
8666            return Ok(false);
8667        };
8668        let mut wv = [0f32; 8];
8669        wv[..n_used].copy_from_slice(w);
8670        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
8671        let act = e.moe_gate_up_silu8(
8672            crate::WPtr8(g),
8673            crate::WPtr8(u),
8674            zt,
8675            n_embd,
8676            n_ff_exp,
8677            n_used,
8678            m.gate_exps.qtype,
8679            m.up_exps.qtype,
8680            m.gate_exps.row_bytes,
8681            m.up_exps.row_bytes,
8682        )?;
8683        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8684        e.moe_down8_fma_into(
8685            crate::WPtr8(d),
8686            crate::F32x8(wv),
8687            &act,
8688            &mut dst,
8689            n_ff_exp,
8690            n_embd,
8691            n_used,
8692            m.down_exps.qtype,
8693            m.down_exps.row_bytes,
8694        )?;
8695        Ok(true)
8696    }
8697
8698    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
8699    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
8700    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
8701    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
8702    fn moe_cached_gemm_q8(
8703        e: &Engine,
8704        il: u16,
8705        proj: u8,
8706        ex: usize,
8707        m: &MoeWeights,
8708        max_block: usize,
8709        aq: &CudaSlice<i8>,
8710        ad: &CudaSlice<f32>,
8711    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8712        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8713        let exps = match proj {
8714            PROJ_GATE => &m.gate_exps,
8715            PROJ_UP => &m.up_exps,
8716            _ => &m.down_exps,
8717        };
8718        let layout = exps.expert_layout(ex);
8719        let id = BlockId::new(il, proj, ex as u16);
8720        let source = exps.expert_source(ex);
8721        e.with_moe_cache(max_block, |c, eng| {
8722            let slot = c.dispatch_source(id, source, eng)?;
8723            let DispatchSlot::Resident(sl) = slot;
8724            let buf = c.slot(sl);
8725            eng.qmatvec_expert_q8(
8726                buf,
8727                0..layout.len,
8728                aq,
8729                ad,
8730                1,
8731                exps.in_f,
8732                exps.out_f,
8733                layout.qtype,
8734                layout.row_bytes,
8735            )
8736        })
8737    }
8738
8739    fn moe_cached_gemm(
8740        e: &Engine,
8741        il: u16,
8742        proj: u8,
8743        ex: usize,
8744        m: &MoeWeights,
8745        max_block: usize,
8746        x: &cudarc::driver::CudaView<f32>,
8747    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8748        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8749        let exps = match proj {
8750            PROJ_GATE => &m.gate_exps,
8751            PROJ_UP => &m.up_exps,
8752            _ => &m.down_exps,
8753        };
8754        let layout = exps.expert_layout(ex);
8755        let id = BlockId::new(il, proj, ex as u16);
8756        let source = exps.expert_source(ex);
8757        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
8758        e.with_moe_cache(max_block, |c, eng| {
8759            let slot = c.dispatch_source(id, source, eng)?;
8760            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
8761            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
8762            let DispatchSlot::Resident(sl) = slot;
8763            let buf = c.slot(sl);
8764            eng.qmatvec_view(
8765                buf,
8766                0..layout.len,
8767                x,
8768                1,
8769                exps.in_f,
8770                exps.out_f,
8771                layout.qtype,
8772                layout.row_bytes,
8773            )
8774        })
8775    }
8776
8777    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
8778    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
8779    /// so the current forward's backend assignment and output remain unchanged.
8780    fn moe_profile_admit_expert(
8781        e: &Engine,
8782        il: u16,
8783        ex: usize,
8784        m: &MoeWeights,
8785        max_block: usize,
8786    ) -> Result<(), Box<dyn std::error::Error>> {
8787        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8788        e.with_moe_cache(max_block, |cache, eng| {
8789            for (proj, exps) in [
8790                (PROJ_GATE, &m.gate_exps),
8791                (PROJ_UP, &m.up_exps),
8792                (PROJ_DOWN, &m.down_exps),
8793            ] {
8794                let id = BlockId::new(il, proj, ex as u16);
8795                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
8796            }
8797            Ok(())
8798        })
8799    }
8800
8801    /// Read a projection from the immutable residency set when present; otherwise use one
8802    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
8803    #[allow(clippy::too_many_arguments)]
8804    fn moe_frozen_gemm(
8805        e: &Engine,
8806        il: u16,
8807        proj: u8,
8808        ex: usize,
8809        m: &MoeWeights,
8810        max_block: usize,
8811        x: &cudarc::driver::CudaView<f32>,
8812        scratch: &mut Option<CudaSlice<u8>>,
8813        scratch_len: usize,
8814    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8815        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
8816        let exps = match proj {
8817            PROJ_GATE => &m.gate_exps,
8818            PROJ_UP => &m.up_exps,
8819            _ => &m.down_exps,
8820        };
8821        let layout = exps.expert_layout(ex);
8822        let id = BlockId::new(il, proj, ex as u16);
8823        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
8824            let Some(slot) = cache.resident(id) else {
8825                return Ok(None);
8826            };
8827            let buf = cache.slot(slot);
8828            Ok(Some(eng.qmatvec_view(
8829                buf,
8830                0..layout.len,
8831                x,
8832                1,
8833                exps.in_f,
8834                exps.out_f,
8835                layout.qtype,
8836                layout.row_bytes,
8837            )?))
8838        })? {
8839            return Ok(output);
8840        }
8841        if scratch.is_none() {
8842            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
8843        }
8844        let scratch = scratch.as_mut().unwrap();
8845        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
8846        e.qmatvec_view(
8847            scratch,
8848            0..layout.len,
8849            x,
8850            1,
8851            exps.in_f,
8852            exps.out_f,
8853            layout.qtype,
8854            layout.row_bytes,
8855        )
8856    }
8857
8858    fn moe_prefetch_expert(
8859        e: &Engine,
8860        il: u16,
8861        ex: usize,
8862        m: &MoeWeights,
8863        max_block: usize,
8864        keep: &[crate::moe_cache::BlockId],
8865    ) -> Result<(), Box<dyn std::error::Error>> {
8866        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8867        e.with_moe_cache(max_block, |c, eng| {
8868            for (proj, exps) in [
8869                (PROJ_GATE, &m.gate_exps),
8870                (PROJ_UP, &m.up_exps),
8871                (PROJ_DOWN, &m.down_exps),
8872            ] {
8873                let id = BlockId::new(il, proj, ex as u16);
8874                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
8875            }
8876            Ok(())
8877        })
8878    }
8879
8880    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
8881    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
8882    fn moe_prefetch_disk_expert(
8883        e: &Engine,
8884        il: u16,
8885        ex: usize,
8886        m: &MoeWeights,
8887        max_block: usize,
8888        keep: &[crate::moe_cache::BlockId],
8889    ) -> Result<(), Box<dyn std::error::Error>> {
8890        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8891        e.with_moe_cache(max_block, |c, eng| {
8892            for (proj, exps) in [
8893                (PROJ_GATE, &m.gate_exps),
8894                (PROJ_UP, &m.up_exps),
8895                (PROJ_DOWN, &m.down_exps),
8896            ] {
8897                let source = exps.expert_source(ex);
8898                if let crate::model::ExpertSource::Disk { .. } = &source {
8899                    let id = BlockId::new(il, proj, ex as u16);
8900                    let _ = c.prefetch_source(id, source, keep, eng)?;
8901                }
8902            }
8903            Ok(())
8904        })
8905    }
8906
8907    #[inline]
8908    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
8909        let _ = m.gate_exps.prefetch_expert_pages(ex);
8910        let _ = m.up_exps.prefetch_expert_pages(ex);
8911        let _ = m.down_exps.prefetch_expert_pages(ex);
8912    }
8913}
8914
8915// ================================================================================================
8916// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
8917//
8918// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
8919// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
8920// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
8921//
8922// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
8923// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
8924// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
8925// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
8926// identical to the per-token loop regardless of expert processing order.
8927//
8928// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
8929// ================================================================================================
8930
8931impl HybridModel {
8932    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
8933    /// sequential fused q8 program over the token axis; clamped layers use the separate
8934    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
8935    #[allow(clippy::too_many_arguments)]
8936    fn moe_ffn_grouped_resident_q8(
8937        e: &Engine,
8938        m: &MoeWeights,
8939        z: &CudaSlice<f32>,
8940        t: usize,
8941        cfg: &ModelConfig,
8942        il: u16,
8943        sel_all: &[u32],
8944        w_all: &[f32],
8945        table: &CudaSlice<u64>,
8946        gu_il: bool,
8947    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8948        let moe = cfg.moe.as_ref().unwrap();
8949        let n_embd = cfg.n_embd as usize;
8950        let n_expert = moe.expert_count as usize;
8951        let n_used = moe.expert_used_count as usize;
8952        let n_ff_exp = moe.expert_ff_length as usize;
8953        let n_pairs = t * n_used;
8954        debug_assert_eq!(sel_all.len(), n_pairs);
8955        debug_assert_eq!(w_all.len(), n_pairs);
8956        debug_assert!(
8957            m.gate_exps.macros.is_none()
8958                && m.up_exps.macros.is_none()
8959                && m.down_exps.macros.is_none(),
8960            "resident grouped q8 does not fold per-expert macro scales",
8961        );
8962
8963        // The rows twins run the resident sequential program verbatim on grid.z = token:
8964        // fused gate/up/SiLU per slot, batched activation quantization, then the original
8965        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
8966        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
8967        // never enter the softmax router.
8968        if !cfg.swiglu_clamped_at(il as u32) {
8969            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8970            let sel_d = e.htod_i32(&sel)?;
8971            let w_d = e.htod(w_all)?;
8972            let (gate_row_bytes, up_row_bytes) = if gu_il {
8973                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8974                (combined, combined)
8975            } else {
8976                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8977            };
8978            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8979            let act = e.moe_gate_up_silu8_dev_q8_rows(
8980                table,
8981                &sel_d,
8982                &zq,
8983                &zd,
8984                t,
8985                n_embd,
8986                n_ff_exp,
8987                n_used,
8988                n_expert,
8989                m.gate_exps.qtype,
8990                m.up_exps.qtype,
8991                gate_row_bytes,
8992                up_row_bytes,
8993                &m.dev_macros,
8994            )?;
8995            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8996            let mut moe_out = e.uninit(t * n_embd)?;
8997            e.moe_down8_fma_dev_q8_rows_g(
8998                table,
8999                &sel_d,
9000                &w_d,
9001                &aq2,
9002                &ad2,
9003                &mut moe_out,
9004                t,
9005                n_ff_exp,
9006                n_embd,
9007                n_used,
9008                n_expert,
9009                m.down_exps.qtype,
9010                m.down_exps.row_bytes,
9011            )?;
9012
9013            if std::env::var("MEMRA_MOE_STATS").is_ok() {
9014                let mut counts = vec![0usize; n_expert];
9015                for &expert in sel_all {
9016                    counts[expert as usize] += 1;
9017                }
9018                let mut sizes: Vec<usize> =
9019                    counts.into_iter().filter(|&count| count != 0).collect();
9020                sizes.sort_unstable();
9021                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9022                println!(
9023                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
9024                     m_e: min={} median={} mean={mean:.1} max={}",
9025                    sizes.len(),
9026                    n_expert,
9027                    sizes.first().copied().unwrap_or(0),
9028                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9029                    sizes.last().copied().unwrap_or(0),
9030                );
9031            }
9032            return Ok(moe_out);
9033        }
9034
9035        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
9036        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
9037        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
9038        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
9039        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9040        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
9041        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
9042
9043        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
9044        for (pair, &expert) in pair_ex.iter().enumerate() {
9045            by_expert[expert as usize].push(pair as i32);
9046        }
9047
9048        let pair_tok_d = e.htod_i32(&pair_tok)?;
9049        let pair_ex_d = e.htod_i32(&pair_ex)?;
9050        let pair_w_d = e.htod(w_all)?;
9051        let tok_off_d = e.htod_i32(&tok_off)?;
9052        let tok_ids_d = e.htod_i32(&tok_ids)?;
9053
9054        let matvec = |proj: i32,
9055                      pair_rows: &CudaSlice<i32>,
9056                      aq: &CudaSlice<i8>,
9057                      ad: &CudaSlice<f32>,
9058                      in_f: usize,
9059                      out_f: usize,
9060                      qtype: i32,
9061                      row_bytes: usize|
9062         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9063            e.moe_pairs_matvec_q8(
9064                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9065                row_bytes,
9066            )
9067        };
9068
9069        let (gate_row_bytes, up_row_bytes) = if gu_il {
9070            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9071            (combined, combined)
9072        } else {
9073            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9074        };
9075        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9076        let gate = matvec(
9077            0,
9078            &pair_tok_d,
9079            &zq,
9080            &zd,
9081            n_embd,
9082            n_ff_exp,
9083            m.gate_exps.qtype,
9084            gate_row_bytes,
9085        )?;
9086        let up = matvec(
9087            1,
9088            &pair_tok_d,
9089            &zq,
9090            &zd,
9091            n_embd,
9092            n_ff_exp,
9093            m.up_exps.qtype,
9094            up_row_bytes,
9095        )?;
9096        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9097        Self::ffn_act_lim(
9098            e,
9099            cfg,
9100            &gate,
9101            &up,
9102            1.0,
9103            1.0,
9104            cfg.clamp_exp_at(il as u32),
9105            &mut act,
9106            n_pairs * n_ff_exp,
9107        )?;
9108        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9109        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9110        let pair_self_d = e.htod_i32(&pair_self)?;
9111        let down = matvec(
9112            2,
9113            &pair_self_d,
9114            &aq2,
9115            &ad2,
9116            n_ff_exp,
9117            n_embd,
9118            m.down_exps.qtype,
9119            m.down_exps.row_bytes,
9120        )?;
9121        let mut moe_out = e.uninit(t * n_embd)?;
9122        e.moe_pairs_scatter(
9123            &down,
9124            &pair_w_d,
9125            &tok_off_d,
9126            &tok_ids_d,
9127            &mut moe_out,
9128            t,
9129            n_embd,
9130        )?;
9131
9132        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9133            let mut sizes: Vec<usize> = by_expert
9134                .iter()
9135                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9136                .collect();
9137            sizes.sort_unstable();
9138            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9139            println!(
9140                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9141                 m_e: min={} median={} mean={mean:.1} max={}",
9142                sizes.len(),
9143                n_expert,
9144                sizes.first().copied().unwrap_or(0),
9145                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9146                sizes.last().copied().unwrap_or(0),
9147            );
9148        }
9149        Ok(moe_out)
9150    }
9151
9152    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9153    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9154    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9155    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9156    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9157    #[allow(clippy::too_many_arguments)]
9158    fn shexp_split_matvec(
9159        e: &Engine,
9160        rank1: &Engine,
9161        wg: &CudaSlice<u8>,
9162        wu: &CudaSlice<u8>,
9163        wd: &CudaSlice<u8>,
9164        z: &CudaSlice<f32>,
9165        lim: Option<f32>,
9166        cfg: &ModelConfig,
9167        il: u16,
9168        n_embd: usize,
9169        n_ff_sh: usize,
9170    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9171        use cudarc::driver::DevicePtr;
9172        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9173            return Ok(None);
9174        }
9175        let hf = n_ff_sh / 2;
9176        let nd = n_embd / 2;
9177        struct Rep {
9178            wg1: CudaSlice<u8>,
9179            wu1: CudaSlice<u8>,
9180            wd1: CudaSlice<u8>,
9181        }
9182        struct SplitWs {
9183            pin_dev: usize,
9184            // e side
9185            gate0: CudaSlice<f32>,
9186            up0: CudaSlice<f32>,
9187            act: CudaSlice<f32>,
9188            sh_buf: CudaSlice<f32>,
9189            ev_z: cudarc::driver::CudaEvent,
9190            ev_act0: cudarc::driver::CudaEvent,
9191            // rank1 side
9192            z1: CudaSlice<f32>,
9193            g1: CudaSlice<f32>,
9194            u1: CudaSlice<f32>,
9195            a1h: CudaSlice<f32>,
9196            act1: CudaSlice<f32>,
9197            y1: CudaSlice<f32>,
9198            ev_act1: cudarc::driver::CudaEvent,
9199            ev_y1: cudarc::driver::CudaEvent,
9200            raw_act_e: u64,
9201            raw_sh_e: u64,
9202            raw_z1: u64,
9203            raw_a1h: u64,
9204            raw_act1: u64,
9205            raw_y1: u64,
9206        }
9207        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9208        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9209            std::sync::Mutex::new(None);
9210        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9211        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9212        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9213        let pins = e.ctx().ordinal();
9214        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9215            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9216                let _m = e.gpu.enter_main()?;
9217                (
9218                    e.htod(&vec![0.0f32; hf])?,
9219                    e.htod(&vec![0.0f32; hf])?,
9220                    e.htod(&vec![0.0f32; n_ff_sh])?,
9221                    e.htod(&vec![0.0f32; n_embd])?,
9222                    e.ctx().new_event(None)?,
9223                    e.ctx().new_event(None)?,
9224                )
9225            };
9226            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9227                let _r = rank1.gpu.enter_main()?;
9228                (
9229                    rank1.htod(&vec![0.0f32; n_embd])?,
9230                    rank1.htod(&vec![0.0f32; hf])?,
9231                    rank1.htod(&vec![0.0f32; hf])?,
9232                    rank1.htod(&vec![0.0f32; hf])?,
9233                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9234                    rank1.htod(&vec![0.0f32; nd])?,
9235                    rank1.ctx().new_event(None)?,
9236                    rank1.ctx().new_event(None)?,
9237                )
9238            };
9239            let (raw_act_e, raw_sh_e) = {
9240                let _m = e.gpu.enter_main()?;
9241                let stream = e.stream();
9242                let (a, _g0) = act.device_ptr(&stream);
9243                let (b, _g1) = sh_buf.device_ptr(&stream);
9244                (a as u64, b as u64)
9245            };
9246            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9247                let _r = rank1.gpu.enter_main()?;
9248                let rs = rank1.stream();
9249                let (a, _g0) = z1.device_ptr(&rs);
9250                let (b, _g1) = a1h.device_ptr(&rs);
9251                let (c, _g2) = act1.device_ptr(&rs);
9252                let (d, _g3) = y1.device_ptr(&rs);
9253                (a as u64, b as u64, c as u64, d as u64)
9254            };
9255            *guard = Some(SplitWs {
9256                pin_dev: pins,
9257                gate0,
9258                up0,
9259                act,
9260                sh_buf,
9261                ev_z,
9262                ev_act0,
9263                z1,
9264                g1,
9265                u1,
9266                a1h,
9267                act1,
9268                y1,
9269                ev_act1,
9270                ev_y1,
9271                raw_act_e,
9272                raw_sh_e,
9273                raw_z1,
9274                raw_a1h,
9275                raw_act1,
9276                raw_y1,
9277            });
9278        }
9279        let ws = guard.as_mut().expect("armed above");
9280        let wg_pin = {
9281            let _m = e.gpu.enter_main()?;
9282            let stream = e.stream();
9283            let (p, _g) = wg.device_ptr(&stream);
9284            p as u64
9285        };
9286        if !reps.contains_key(&wg_pin) {
9287            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9288            let mut up = |src: &CudaSlice<u8>,
9289                          off_bytes: usize,
9290                          len: usize|
9291             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9292                use cudarc::driver::sys;
9293                let sptr = {
9294                    let _m = e.gpu.enter_main()?;
9295                    let stream = e.stream();
9296                    let (p, _g) = src.device_ptr(&stream);
9297                    p as u64 + off_bytes as u64
9298                };
9299                let dst = {
9300                    let _r = rank1.gpu.enter_main()?;
9301                    rank1.alloc_u8_uninit(len)?
9302                };
9303                let dptr = {
9304                    let _r = rank1.gpu.enter_main()?;
9305                    let rs = rank1.stream();
9306                    let (p, _g) = dst.device_ptr(&rs);
9307                    p as u64
9308                };
9309                let _r = rank1.gpu.enter_main()?;
9310                let r = unsafe {
9311                    sys::cuMemcpyAsync(
9312                        dptr as sys::CUdeviceptr,
9313                        sptr as sys::CUdeviceptr,
9314                        len,
9315                        rank1.stream().cu_stream() as sys::CUstream,
9316                    )
9317                };
9318                if r != sys::CUresult::CUDA_SUCCESS {
9319                    return Err(format!("shexp split replica upload: {r:?}").into());
9320                }
9321                rank1.stream().synchronize()?;
9322                Ok(dst)
9323            };
9324            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9325            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9326            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9327            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9328        }
9329        let _ = il;
9330        // Per token, evented split flow.
9331        let raw_z = {
9332            let _m = e.gpu.enter_main()?;
9333            let stream = e.stream();
9334            let (p, _g) = z.device_ptr(&stream);
9335            ws.ev_z.record(&stream)?;
9336            p as u64
9337        };
9338        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9339        {
9340            let rep = reps.get(&wg_pin).expect("uploaded above");
9341            let _r = rank1.gpu.enter_main()?;
9342            rank1.stream().wait(&ws.ev_z)?;
9343            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9344            let SplitWs {
9345                z1, g1, u1, a1h, ..
9346            } = &mut *ws;
9347            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9348            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9349            // local place into act1[hf..] + P2P push into e's act[hf..]
9350            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9351            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9352            ws.ev_act1.record(&rank1.stream())?;
9353        }
9354        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9355        {
9356            let _m = e.gpu.enter_main()?;
9357            let SplitWs {
9358                gate0, up0, act, ..
9359            } = &mut *ws;
9360            let wg_lo = wg.slice(0..hf * n_embd * 2);
9361            let wu_lo = wu.slice(0..hf * n_embd * 2);
9362            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9363            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9364            ws.ev_act0.record(&e.stream())?;
9365        }
9366        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9367        {
9368            let rep = reps.get(&wg_pin).expect("uploaded above");
9369            let _r = rank1.gpu.enter_main()?;
9370            rank1.stream().wait(&ws.ev_act0)?;
9371            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9372            let SplitWs { act1, y1, .. } = &mut *ws;
9373            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9374            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9375            ws.ev_y1.record(&rank1.stream())?;
9376        }
9377        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9378        {
9379            let _m = e.gpu.enter_main()?;
9380            e.stream().wait(&ws.ev_act1)?;
9381            let SplitWs { act, sh_buf, .. } = &mut *ws;
9382            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9383            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9384            e.stream().wait(&ws.ev_y1)?;
9385            let mut sh = e.uninit(n_embd)?;
9386            {
9387                let mut dst = sh.slice_mut(0..n_embd);
9388                e.stream()
9389                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9390            }
9391            Ok(Some(sh))
9392        }
9393    }
9394
9395    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9396    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9397    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9398    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9399    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9400    /// the join with the exact add_scaled_rows expression: values unchanged.
9401    fn shexp_overlap_issue(
9402        e: &Engine,
9403        m: &MoeWeights,
9404        z: &CudaSlice<f32>,
9405        cfg: &ModelConfig,
9406        il: u16,
9407        n_embd: usize,
9408    ) -> Result<bool, Box<dyn std::error::Error>> {
9409        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9410            return Ok(false);
9411        }
9412        let (
9413            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9414            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9415            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9416        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9417        else {
9418            return Ok(false);
9419        };
9420        let n_ff_sh = m
9421            .gate_shexp
9422            .as_ref()
9423            .expect("matched Some above")
9424            .out_features();
9425        let lim = cfg.clamp_shexp_at(il as u32);
9426        let mut guard = SHEXP_OV_WS
9427            .lock()
9428            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9429        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9430        if guard
9431            .as_ref()
9432            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9433        {
9434            *guard = Some((
9435                pins.0,
9436                pins.1,
9437                pins.2,
9438                e.uninit(n_ff_sh)?,
9439                e.uninit(n_embd)?,
9440            ));
9441        }
9442        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9443        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9444        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9445        drop(guard);
9446        Ok(true)
9447    }
9448
9449    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9450    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9451    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9452    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9453    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9454    #[allow(clippy::too_many_arguments)]
9455    fn shexp_dev1_issue(
9456        e: &Engine,
9457        rank1: &Engine,
9458        m: &MoeWeights,
9459        z: &CudaSlice<f32>,
9460        cfg: &ModelConfig,
9461        il: u16,
9462        n_embd: usize,
9463    ) -> Result<bool, Box<dyn std::error::Error>> {
9464        use cudarc::driver::DevicePtr;
9465        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9466            return Ok(false);
9467        }
9468        let (
9469            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9470            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9471            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9472        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9473        else {
9474            return Ok(false);
9475        };
9476        let n_ff_sh = m
9477            .gate_shexp
9478            .as_ref()
9479            .expect("matched Some above")
9480            .out_features();
9481        let lim = cfg.clamp_shexp_at(il as u32);
9482        // Shared scratch, geometry-keyed.
9483        let mut ws_guard = SHEXP_D1_WS
9484            .lock()
9485            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9486        if ws_guard
9487            .as_ref()
9488            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9489        {
9490            let (act1, z1, ev_done) = {
9491                let _r1 = rank1.gpu.enter_main()?;
9492                (
9493                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9494                    rank1.htod(&vec![0.0f32; n_embd])?,
9495                    rank1.ctx().new_event(None)?,
9496                )
9497            };
9498            let (sh_root, ev_z) = {
9499                let _main = e.gpu.enter_main()?;
9500                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9501            };
9502            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9503        }
9504        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9505        let mut reps_guard = SHEXP_D1_REPS
9506            .lock()
9507            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9508        let reps = reps_guard.get_or_insert_with(Default::default);
9509        if !reps.contains_key(&il) {
9510            let (wg1, wu1, wd1) = {
9511                let _r1 = rank1.gpu.enter_main()?;
9512                (
9513                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9514                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9515                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9516                )
9517            };
9518            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9519                let s_ptr = {
9520                    let _main = e.gpu.enter_main()?;
9521                    let stream = e.stream();
9522                    let (p, _g) = src.device_ptr(&stream);
9523                    p as u64
9524                };
9525                let d_ptr = {
9526                    let _r1 = rank1.gpu.enter_main()?;
9527                    let stream = rank1.stream();
9528                    let (p, _g) = dst.device_ptr(&stream);
9529                    p as u64
9530                };
9531                let _r1 = rank1.gpu.enter_main()?;
9532                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9533            }
9534            {
9535                let _r1 = rank1.gpu.enter_main()?;
9536                rank1.stream().synchronize()?;
9537            }
9538            reps.insert(il, (wg1, wu1, wd1));
9539        }
9540        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9541        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9542        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9543        // row root-side (single store pass), rings ev_done.
9544        let (raw_z, raw_sh) = {
9545            let _main = e.gpu.enter_main()?;
9546            let stream = e.stream();
9547            let (a, _g0) = z.device_ptr(&stream);
9548            let (b, _g1) = sh_root.device_ptr(&stream);
9549            ev_z.record(&stream)?;
9550            (a as u64, b as u64)
9551        };
9552        {
9553            let _r1 = rank1.gpu.enter_main()?;
9554            rank1.stream().wait(ev_z)?;
9555            let raw_z1 = {
9556                let stream = rank1.stream();
9557                let (p, _g) = z1.device_ptr(&stream);
9558                p as u64
9559            };
9560            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9561            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9562            // down writes the ROOT-resident row over P2P via the raw-output twin of
9563            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9564            // cross-device, so launch on the raw pointer.
9565            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9566            ev_done.record(&rank1.stream())?;
9567        }
9568        Ok(true)
9569    }
9570
9571    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9572    fn shexp_dev1_apply(
9573        e: &Engine,
9574        output: &mut CudaSlice<f32>,
9575        n_embd: usize,
9576    ) -> Result<(), Box<dyn std::error::Error>> {
9577        let guard = SHEXP_D1_WS
9578            .lock()
9579            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9580        let (pin, _, _, sh_root, _, ev_done) =
9581            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9582        if pin.0 != n_embd {
9583            return Err("shexp dev1 width drifted".into());
9584        }
9585        let _main = e.gpu.enter_main()?;
9586        e.stream().wait(ev_done)?;
9587        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9588            std::sync::Mutex::new(None);
9589        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9590        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9591            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9592        }
9593        let ones = &og.as_ref().expect("armed above").1;
9594        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9595        Ok(())
9596    }
9597
9598    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9599    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9600    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9601    fn shexp_overlap_tail_ptrs(
9602        e: &Engine,
9603        m: &MoeWeights,
9604        cfg: &ModelConfig,
9605        n_embd: usize,
9606    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9607        use cudarc::driver::DevicePtr;
9608        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9609            return Ok(None);
9610        }
9611        let (
9612            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9613            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9614            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9615        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9616        else {
9617            return Ok(None);
9618        };
9619        let n_ff_sh = m
9620            .gate_shexp
9621            .as_ref()
9622            .expect("matched Some above")
9623            .out_features();
9624        let mut guard = SHEXP_OV_WS
9625            .lock()
9626            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9627        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9628        if guard
9629            .as_ref()
9630            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9631        {
9632            *guard = Some((
9633                pins.0,
9634                pins.1,
9635                pins.2,
9636                e.uninit(n_ff_sh)?,
9637                e.uninit(n_embd)?,
9638            ));
9639        }
9640        let sh_raw = {
9641            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9642            let stream = e.stream();
9643            let (p, _g) = sh.device_ptr(&stream);
9644            p as u64
9645        };
9646        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9647            std::sync::Mutex::new(None);
9648        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9649        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9650            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9651        }
9652        let ones_raw = {
9653            let stream = e.stream();
9654            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
9655            p as u64
9656        };
9657        Ok(Some((sh_raw, ones_raw)))
9658    }
9659
9660    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
9661    /// add_scaled_rows program the split path used (persistent ones row, no htod).
9662    fn shexp_overlap_apply(
9663        e: &Engine,
9664        output: &mut CudaSlice<f32>,
9665        n_embd: usize,
9666    ) -> Result<(), Box<dyn std::error::Error>> {
9667        let guard = SHEXP_OV_WS
9668            .lock()
9669            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9670        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
9671        if *ne != n_embd {
9672            return Err("shexp overlap width drifted".into());
9673        }
9674        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9675            std::sync::Mutex::new(None);
9676        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
9677        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9678            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9679        }
9680        let ones = &og.as_ref().expect("armed above").1;
9681        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
9682        Ok(())
9683    }
9684
9685    fn moe_ffn_grouped_add_shared(
9686        e: &Engine,
9687        m: &MoeWeights,
9688        z: &CudaSlice<f32>,
9689        t: usize,
9690        cfg: &ModelConfig,
9691        il: u16,
9692        moe_out: &mut CudaSlice<f32>,
9693    ) -> Result<(), Box<dyn std::error::Error>> {
9694        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
9695        // queued matmuls here rather than at the next host readback).
9696        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9697        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9698        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9699        let shexp_started = shexp_timing.then(std::time::Instant::now);
9700        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
9701        if let Some(started) = shexp_started {
9702            use std::sync::atomic::Ordering;
9703            e.stream().synchronize()?;
9704            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9705                + started.elapsed().as_nanos() as u64;
9706            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9707            if calls % 430 == 0 {
9708                eprintln!(
9709                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9710                    ns as f64 / 1.0e6,
9711                    ns as f64 / calls as f64 / 1.0e3,
9712                );
9713            }
9714        }
9715        result
9716    }
9717
9718    #[allow(clippy::too_many_arguments)]
9719    fn moe_ffn_grouped_add_shared_inner(
9720        e: &Engine,
9721        m: &MoeWeights,
9722        z: &CudaSlice<f32>,
9723        t: usize,
9724        cfg: &ModelConfig,
9725        il: u16,
9726        moe_out: &mut CudaSlice<f32>,
9727    ) -> Result<(), Box<dyn std::error::Error>> {
9728        let n_embd = cfg.n_embd as usize;
9729        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
9730            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9731        {
9732            let n_ff_sh = gate_shexp.out_features();
9733            let lim = cfg.clamp_shexp_at(il as u32);
9734            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
9735            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
9736            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
9737            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
9738            // operand pre-quantized (kernel_check-proven identities). This path measured
9739            // 167us/layer as separate matmuls + 5 allocs at decode.
9740            let fused = t == 1
9741                && lim.is_none()
9742                && cfg.m3.is_none()
9743                && e.uses_q8_1_fast(gate_shexp)
9744                && e.uses_q8_1_fast(up_shexp);
9745            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
9746            // the two matvec_bf16 launches matmul would issue).
9747            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
9748                match (gate_shexp, up_shexp) {
9749                    (
9750                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
9751                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
9752                    ) => Some((wg, wu)),
9753                    _ => None,
9754                }
9755            } else {
9756                None
9757            };
9758            let sh = if let Some((wg, wu)) = bf16_dual {
9759                // Persistent shared-expert workspace: sizes are constant across every MoE
9760                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
9761                // the four per-layer allocations. Buffers are fully overwritten each call.
9762                static SHEXP_WS: std::sync::Mutex<
9763                    Option<(
9764                        usize,
9765                        usize,
9766                        usize,
9767                        CudaSlice<f32>,
9768                        CudaSlice<f32>,
9769                        CudaSlice<f32>,
9770                        CudaSlice<f32>,
9771                    )>,
9772                > = std::sync::Mutex::new(None);
9773                let down_bf16 = match down_shexp {
9774                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9775                    _ => None,
9776                };
9777                let mut guard = SHEXP_WS
9778                    .lock()
9779                    .map_err(|_| "shexp workspace lock is poisoned")?;
9780                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9781                if guard
9782                    .as_ref()
9783                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9784                {
9785                    *guard = Some((
9786                        pins.0,
9787                        pins.1,
9788                        pins.2,
9789                        e.uninit(n_ff_sh)?,
9790                        e.uninit(n_ff_sh)?,
9791                        e.uninit(n_ff_sh)?,
9792                        e.uninit(n_embd)?,
9793                    ));
9794                }
9795                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
9796                // through to the single-device arm when ineligible.
9797                {
9798                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9799                    let split_on = *ON
9800                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
9801                    if split_on {
9802                        if let (Some(wd), Some(rank1)) = (
9803                            match down_shexp {
9804                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9805                                _ => None,
9806                            },
9807                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
9808                        ) {
9809                            if let Some(sh) = Self::shexp_split_matvec(
9810                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
9811                            )? {
9812                                drop(guard);
9813                                let gate = match &m.gate_inp_shexp {
9814                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
9815                                        z,
9816                                        gate_inp_shexp.float_data(),
9817                                        n_embd,
9818                                        t,
9819                                    )?,
9820                                    None => e.htod(&vec![1.0f32; t])?,
9821                                };
9822                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9823                                return Ok(());
9824                            }
9825                        }
9826                    }
9827                }
9828                let (_, _, _, gate, up, act, sh_buf) =
9829                    guard.as_mut().expect("shexp workspace initialized above");
9830                if cfg.m3.is_none() {
9831                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
9832                    // per-row program + exact silu/clamped expression, bit-identical.
9833                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9834                    let _ = (&gate, &up);
9835                } else {
9836                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
9837                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
9838                }
9839                if let Some(down) = down_bf16 {
9840                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
9841                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
9842                    // exact f32acc per-row program + the exact add_scaled_rows expression
9843                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
9844                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
9845                    // accumulate consumes the same f32 the split path stored and reloaded.
9846                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9847                    let fuse_da = *FUSE_DA.get_or_init(|| {
9848                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
9849                    });
9850                    if fuse_da && m.gate_inp_shexp.is_none() {
9851                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9852                            std::sync::Mutex::new(None);
9853                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
9854                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9855                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9856                        }
9857                        let ones = &og.as_ref().expect("armed above").1;
9858                        e.matvec_bf16_down_addscale_into(
9859                            down, act, ones, moe_out, n_ff_sh, n_embd,
9860                        )?;
9861                        return Ok(());
9862                    }
9863                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
9864                    let sh = e.uninit(n_embd)?;
9865                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
9866                    let mut sh = sh;
9867                    {
9868                        let mut dst = sh.slice_mut(0..n_embd);
9869                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
9870                    }
9871                    sh
9872                } else {
9873                    e.matmul(down_shexp, act, 1)?
9874                }
9875            } else if fused {
9876                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
9877                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
9878                    Some((gate, up)) => Some((gate, up)),
9879                    None => {
9880                        match (
9881                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
9882                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
9883                        ) {
9884                            (Some(gate), Some(up)) => Some((gate, up)),
9885                            _ => None,
9886                        }
9887                    }
9888                };
9889                match pair {
9890                    Some(((gate, gs), (up, us))) => {
9891                        if e.uses_q8_1_fast(down_shexp) {
9892                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
9893                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
9894                        } else {
9895                            let mut act = e.uninit(n_ff_sh)?;
9896                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
9897                            e.matmul(down_shexp, &act, 1)?
9898                        }
9899                    }
9900                    None => {
9901                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
9902                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
9903                        let mut act = e.uninit(n_ff_sh)?;
9904                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
9905                        e.matmul(down_shexp, &act, 1)?
9906                    }
9907                }
9908            } else {
9909                let sg_gate = e.matmul(gate_shexp, z, t)?;
9910                let sg_up = e.matmul(up_shexp, z, t)?;
9911                let mut sa = e.uninit(t * n_ff_sh)?;
9912                Self::ffn_act_lim(
9913                    e,
9914                    cfg,
9915                    &sg_gate,
9916                    &sg_up,
9917                    1.0,
9918                    1.0,
9919                    lim,
9920                    &mut sa,
9921                    t * n_ff_sh,
9922                )?;
9923                e.matmul(down_shexp, &sa, t)?
9924            };
9925            let gate = match &m.gate_inp_shexp {
9926                Some(gate_inp_shexp) => {
9927                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
9928                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
9929                    } else {
9930                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
9931                        let mut gate = e.uninit(t)?;
9932                        e.sigmoid(&raw, &mut gate, t)?;
9933                        gate
9934                    }
9935                }
9936                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
9937                // synchronizes the stream — measured as the biggest per-layer host gap
9938                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
9939                // device serves every layer; larger t (prefill) keeps the plain htod.
9940                None if t == 1 => {
9941                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9942                        std::sync::Mutex::new(None);
9943                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
9944                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9945                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9946                    }
9947                    let ones = &guard.as_ref().expect("armed above").1;
9948                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
9949                    return Ok(());
9950                }
9951                None => e.htod(&vec![1.0f32; t])?,
9952            };
9953            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9954        }
9955        Ok(())
9956    }
9957
9958    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
9959    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
9960    pub(crate) fn moe_ffn_grouped(
9961        e: &Engine,
9962        m: &MoeWeights,
9963        z: &CudaSlice<f32>,
9964        t: usize,
9965        cfg: &ModelConfig,
9966        il: u16,
9967        max_block: usize,
9968    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9969        let moe = cfg.moe.as_ref().unwrap();
9970        let n_embd = cfg.n_embd as usize;
9971        let n_expert = moe.expert_count as usize;
9972        let n_used = moe.expert_used_count as usize;
9973        let n_ff_exp = moe.expert_ff_length as usize;
9974        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
9975        let lim_exp = cfg.clamp_exp_at(il as u32);
9976
9977        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
9978        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
9979        // enters the softmax-only pairs/dev router.
9980        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
9981        if let Some(sig) = cfg.sigmoid_router() {
9982            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
9983        }
9984        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
9985            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
9986        } else {
9987            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
9988        };
9989        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
9990        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
9991        Self::trace_moe_input(e, il, t, n_embd, z)?;
9992
9993        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
9994        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
9995        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
9996        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
9997        let no_exp_macros = m.gate_exps.macros.is_none()
9998            && m.up_exps.macros.is_none()
9999            && m.down_exps.macros.is_none();
10000        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
10001            m.has_uniform_expert_layout()
10002                && no_exp_macros
10003                && moe_q8_enabled()
10004                && q8_expert_supported(m.gate_exps.qtype)
10005                && q8_expert_supported(m.up_exps.qtype)
10006                && q8_expert_supported(m.down_exps.qtype)
10007                && moe_slab_enabled()
10008                && dev.dev == e.ctx().ordinal()
10009        });
10010        if let Some(dev) = resident_q8 {
10011            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
10012                e,
10013                m,
10014                z,
10015                t,
10016                cfg,
10017                il,
10018                &sel_all,
10019                &w_all,
10020                &dev.ptr_row,
10021                dev.gu_il,
10022            )?;
10023            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10024            return Ok(moe_out);
10025        }
10026
10027        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
10028        // For each expert e, we need: which tokens use it, their positions in z, their top-k
10029        // slot index (for bit-identical accumulation), and their weights.
10030        struct ExpertGroup {
10031            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
10032            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
10033            weights: Vec<f32>,      // renormalized weight for that token-expert pair
10034        }
10035        let mut groups: Vec<ExpertGroup> = (0..n_expert)
10036            .map(|_| ExpertGroup {
10037                tok_indices: Vec::new(),
10038                slot_indices: Vec::new(),
10039                weights: Vec::new(),
10040            })
10041            .collect();
10042
10043        for tok in 0..t {
10044            for j in 0..n_used {
10045                let ex = sel_all[tok * n_used + j] as usize;
10046                let w = w_all[tok * n_used + j];
10047                groups[ex].tok_indices.push(tok as i32);
10048                groups[ex].slot_indices.push(j as i32);
10049                groups[ex].weights.push(w);
10050            }
10051        }
10052
10053        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
10054        // Each token's 8 expert contributions land in their respective slots.
10055        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
10056        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
10057
10058        // Expert weight dimensions (used in both cache and staging paths).
10059        let g_len = m.gate_exps.max_expert_bytes();
10060        let u_len = m.up_exps.max_expert_bytes();
10061        let d_len = m.down_exps.max_expert_bytes();
10062        let moe_q8 = m.has_uniform_expert_layout()
10063            && moe_q8_enabled()
10064            && q8_expert_supported(m.gate_exps.qtype)
10065            && q8_expert_supported(m.up_exps.qtype)
10066            && q8_expert_supported(m.down_exps.qtype);
10067        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10068        // Interleaved GU slabs require the pointer-table fast path above.
10069        let slab_local = m
10070            .dev_exps
10071            .as_ref()
10072            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10073        let use_cache =
10074            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10075        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10076        // also does: a local resident slab or a live SLRU dispatch.
10077        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10078
10079        // GPU scratch for staging (only allocated without a local slab or cache).
10080        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10081            (
10082                Some(e.alloc_u8(g_len)?),
10083                Some(e.alloc_u8(u_len)?),
10084                Some(e.alloc_u8(d_len)?),
10085            )
10086        } else {
10087            (None, None, None)
10088        };
10089
10090        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10091        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10092        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10093        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10094        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10095        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10096        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10097        // at long prompts where every expert stages regardless. Order is FREE to change without
10098        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10099        // regardless of expert processing order (the whole point of the slots).
10100        let mut order: Vec<usize> = (0..n_expert)
10101            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10102            .collect();
10103        order.sort_by(|&a, &b| {
10104            groups[b]
10105                .tok_indices
10106                .len()
10107                .cmp(&groups[a].tok_indices.len())
10108                .then(a.cmp(&b))
10109        });
10110        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10111        let page_window = moe_page_prefetch_window();
10112        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10113        if worker_disk_prefetch {
10114            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10115                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10116            }
10117        }
10118        for (order_pos, &ex) in order.iter().enumerate() {
10119            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10120                Self::moe_prefetch_host_expert(order[next], m);
10121            }
10122            if worker_disk_prefetch {
10123                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10124                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10125                    let keep = [
10126                        BlockId::new(il, PROJ_GATE, ex as u16),
10127                        BlockId::new(il, PROJ_UP, ex as u16),
10128                        BlockId::new(il, PROJ_DOWN, ex as u16),
10129                    ];
10130                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10131                }
10132            }
10133            let grp = &groups[ex];
10134            let m_e = grp.tok_indices.len();
10135            m_dist.push(m_e);
10136            let gl = m.gate_exps.expert_layout(ex);
10137            let ul = m.up_exps.expert_layout(ex);
10138            let dl = m.down_exps.expert_layout(ex);
10139
10140            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10141            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10142            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10143            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10144            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10145            let dmac = m.down_exps.macro_scale(ex);
10146            let weight_d = if dmac == 1.0 {
10147                e.htod(&grp.weights)?
10148            } else {
10149                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10150                e.htod(&scaled)?
10151            };
10152
10153            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10154            let mut gathered = e.zeros(m_e * n_embd)?;
10155            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10156            let gv = gathered.slice(0..m_e * n_embd);
10157
10158            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10159            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10160            let y = if let Some(dev) = slab_local {
10161                let gate_start = ex * m.gate_exps.expert_stride;
10162                let up_start = ex * m.up_exps.expert_stride;
10163                let down_start = ex * m.down_exps.expert_stride;
10164                if grouped_q8 {
10165                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10166                    let gate = e.qmatvec_expert_q8(
10167                        &dev.gate,
10168                        gate_start..gate_start + gl.len,
10169                        &zq,
10170                        &zd,
10171                        m_e,
10172                        m.gate_exps.in_f,
10173                        m.gate_exps.out_f,
10174                        gl.qtype,
10175                        gl.row_bytes,
10176                    )?;
10177                    let up = e.qmatvec_expert_q8(
10178                        &dev.up,
10179                        up_start..up_start + ul.len,
10180                        &zq,
10181                        &zd,
10182                        m_e,
10183                        m.up_exps.in_f,
10184                        m.up_exps.out_f,
10185                        ul.qtype,
10186                        ul.row_bytes,
10187                    )?;
10188                    let mut act = e.uninit(m_e * n_ff_exp)?;
10189                    Self::ffn_act_lim(
10190                        e,
10191                        cfg,
10192                        &gate,
10193                        &up,
10194                        m.gate_exps.macro_scale(ex),
10195                        m.up_exps.macro_scale(ex),
10196                        lim_exp,
10197                        &mut act,
10198                        m_e * n_ff_exp,
10199                    )?;
10200                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10201                    e.qmatvec_expert_q8(
10202                        &dev.down,
10203                        down_start..down_start + dl.len,
10204                        &aq2,
10205                        &ad2,
10206                        m_e,
10207                        m.down_exps.in_f,
10208                        m.down_exps.out_f,
10209                        dl.qtype,
10210                        dl.row_bytes,
10211                    )?
10212                } else {
10213                    let gate = e.qmatvec_view(
10214                        &dev.gate,
10215                        gate_start..gate_start + gl.len,
10216                        &gv,
10217                        m_e,
10218                        m.gate_exps.in_f,
10219                        m.gate_exps.out_f,
10220                        gl.qtype,
10221                        gl.row_bytes,
10222                    )?;
10223                    let up = e.qmatvec_view(
10224                        &dev.up,
10225                        up_start..up_start + ul.len,
10226                        &gv,
10227                        m_e,
10228                        m.up_exps.in_f,
10229                        m.up_exps.out_f,
10230                        ul.qtype,
10231                        ul.row_bytes,
10232                    )?;
10233                    let mut act = e.uninit(m_e * n_ff_exp)?;
10234                    Self::ffn_act_lim(
10235                        e,
10236                        cfg,
10237                        &gate,
10238                        &up,
10239                        m.gate_exps.macro_scale(ex),
10240                        m.up_exps.macro_scale(ex),
10241                        lim_exp,
10242                        &mut act,
10243                        m_e * n_ff_exp,
10244                    )?;
10245                    let actv = act.slice(0..m_e * n_ff_exp);
10246                    e.qmatvec_view(
10247                        &dev.down,
10248                        down_start..down_start + dl.len,
10249                        &actv,
10250                        m_e,
10251                        m.down_exps.in_f,
10252                        m.down_exps.out_f,
10253                        dl.qtype,
10254                        dl.row_bytes,
10255                    )?
10256                }
10257            } else if use_cache {
10258                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10259                if grouped_q8 {
10260                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10261                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10262                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10263                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10264                        eng.qmatvec_expert_q8(
10265                            cache.buf(slot),
10266                            0..gl.len,
10267                            &zq,
10268                            &zd,
10269                            m_e,
10270                            m.gate_exps.in_f,
10271                            m.gate_exps.out_f,
10272                            gl.qtype,
10273                            gl.row_bytes,
10274                        )
10275                    })?;
10276                    let up = e.with_moe_cache(max_block, |cache, eng| {
10277                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10278                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10279                        eng.qmatvec_expert_q8(
10280                            cache.buf(slot),
10281                            0..ul.len,
10282                            &zq,
10283                            &zd,
10284                            m_e,
10285                            m.up_exps.in_f,
10286                            m.up_exps.out_f,
10287                            ul.qtype,
10288                            ul.row_bytes,
10289                        )
10290                    })?;
10291                    let mut act = e.uninit(m_e * n_ff_exp)?;
10292                    Self::ffn_act_lim(
10293                        e,
10294                        cfg,
10295                        &gate,
10296                        &up,
10297                        m.gate_exps.macro_scale(ex),
10298                        m.up_exps.macro_scale(ex),
10299                        lim_exp,
10300                        &mut act,
10301                        m_e * n_ff_exp,
10302                    )?;
10303                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10304                    e.with_moe_cache(max_block, |cache, eng| {
10305                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10306                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10307                        eng.qmatvec_expert_q8(
10308                            cache.buf(slot),
10309                            0..dl.len,
10310                            &aq2,
10311                            &ad2,
10312                            m_e,
10313                            m.down_exps.in_f,
10314                            m.down_exps.out_f,
10315                            dl.qtype,
10316                            dl.row_bytes,
10317                        )
10318                    })?
10319                } else {
10320                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10321                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10322                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10323                        eng.qmatvec_view(
10324                            cache.buf(slot),
10325                            0..gl.len,
10326                            &gv,
10327                            m_e,
10328                            m.gate_exps.in_f,
10329                            m.gate_exps.out_f,
10330                            gl.qtype,
10331                            gl.row_bytes,
10332                        )
10333                    })?;
10334                    let up = e.with_moe_cache(max_block, |cache, eng| {
10335                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10336                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10337                        eng.qmatvec_view(
10338                            cache.buf(slot),
10339                            0..ul.len,
10340                            &gv,
10341                            m_e,
10342                            m.up_exps.in_f,
10343                            m.up_exps.out_f,
10344                            ul.qtype,
10345                            ul.row_bytes,
10346                        )
10347                    })?;
10348                    let mut act = e.uninit(m_e * n_ff_exp)?;
10349                    Self::ffn_act_lim(
10350                        e,
10351                        cfg,
10352                        &gate,
10353                        &up,
10354                        m.gate_exps.macro_scale(ex),
10355                        m.up_exps.macro_scale(ex),
10356                        lim_exp,
10357                        &mut act,
10358                        m_e * n_ff_exp,
10359                    )?;
10360                    let actv = act.slice(0..m_e * n_ff_exp);
10361                    e.with_moe_cache(max_block, |cache, eng| {
10362                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10363                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10364                        eng.qmatvec_view(
10365                            cache.buf(slot),
10366                            0..dl.len,
10367                            &actv,
10368                            m_e,
10369                            m.down_exps.in_f,
10370                            m.down_exps.out_f,
10371                            dl.qtype,
10372                            dl.row_bytes,
10373                        )
10374                    })?
10375                }
10376            } else {
10377                let sg = scratch_g.as_mut().unwrap();
10378                let su = scratch_u.as_mut().unwrap();
10379                let sd = scratch_d.as_mut().unwrap();
10380                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10381                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10382                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10383                if grouped_q8 {
10384                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10385                    let gate = e.qmatvec_expert_q8(
10386                        sg,
10387                        0..gl.len,
10388                        &zq,
10389                        &zd,
10390                        m_e,
10391                        m.gate_exps.in_f,
10392                        m.gate_exps.out_f,
10393                        gl.qtype,
10394                        gl.row_bytes,
10395                    )?;
10396                    let up = e.qmatvec_expert_q8(
10397                        su,
10398                        0..ul.len,
10399                        &zq,
10400                        &zd,
10401                        m_e,
10402                        m.up_exps.in_f,
10403                        m.up_exps.out_f,
10404                        ul.qtype,
10405                        ul.row_bytes,
10406                    )?;
10407                    let mut act = e.uninit(m_e * n_ff_exp)?;
10408                    Self::ffn_act_lim(
10409                        e,
10410                        cfg,
10411                        &gate,
10412                        &up,
10413                        m.gate_exps.macro_scale(ex),
10414                        m.up_exps.macro_scale(ex),
10415                        lim_exp,
10416                        &mut act,
10417                        m_e * n_ff_exp,
10418                    )?;
10419                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10420                    e.qmatvec_expert_q8(
10421                        sd,
10422                        0..dl.len,
10423                        &aq2,
10424                        &ad2,
10425                        m_e,
10426                        m.down_exps.in_f,
10427                        m.down_exps.out_f,
10428                        dl.qtype,
10429                        dl.row_bytes,
10430                    )?
10431                } else {
10432                    let gate = e.qmatvec_view(
10433                        sg,
10434                        0..gl.len,
10435                        &gv,
10436                        m_e,
10437                        m.gate_exps.in_f,
10438                        m.gate_exps.out_f,
10439                        gl.qtype,
10440                        gl.row_bytes,
10441                    )?;
10442                    let up = e.qmatvec_view(
10443                        su,
10444                        0..ul.len,
10445                        &gv,
10446                        m_e,
10447                        m.up_exps.in_f,
10448                        m.up_exps.out_f,
10449                        ul.qtype,
10450                        ul.row_bytes,
10451                    )?;
10452                    let mut act = e.uninit(m_e * n_ff_exp)?;
10453                    Self::ffn_act_lim(
10454                        e,
10455                        cfg,
10456                        &gate,
10457                        &up,
10458                        m.gate_exps.macro_scale(ex),
10459                        m.up_exps.macro_scale(ex),
10460                        lim_exp,
10461                        &mut act,
10462                        m_e * n_ff_exp,
10463                    )?;
10464                    let actv = act.slice(0..m_e * n_ff_exp);
10465                    e.qmatvec_view(
10466                        sd,
10467                        0..dl.len,
10468                        &actv,
10469                        m_e,
10470                        m.down_exps.in_f,
10471                        m.down_exps.out_f,
10472                        dl.qtype,
10473                        dl.row_bytes,
10474                    )?
10475                }
10476            };
10477
10478            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10479            e.scatter_slot(
10480                &y,
10481                &tok_idx_d,
10482                &slot_idx_d,
10483                &weight_d,
10484                &mut slot_buf,
10485                &mut wbuf,
10486                n_embd,
10487                n_used,
10488                m_e,
10489            )?;
10490        }
10491
10492        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10493        let mut moe_out = e.zeros(t * n_embd)?;
10494        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10495
10496        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10497        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10498            m_dist.sort_unstable();
10499            let active = m_dist.len();
10500            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10501            let median = m_dist[active / 2];
10502            let max_m = *m_dist.last().unwrap();
10503            let min_m = m_dist[0];
10504            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10505            println!(
10506                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10507                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10508                      above_gemm_threshold(>=16)={above16}/{active}"
10509            );
10510        }
10511
10512        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10513        Ok(moe_out)
10514    }
10515
10516    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10517    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10518    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10519    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10520    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10521    /// expert-sum order identical to the sequential path.
10522    pub(crate) fn moe_ffn_lockstep(
10523        &self,
10524        e: &Engine,
10525        m: &MoeWeights,
10526        zbatch: &CudaSlice<f32>,
10527        mrows: usize,
10528        il: u16,
10529        max_block: usize,
10530    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10531        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10532        let cfg = &self.cfg;
10533        let moe = cfg.moe.as_ref().unwrap();
10534        let n_embd = cfg.n_embd as usize;
10535        let n_expert = moe.expert_count as usize;
10536        let n_used = moe.expert_used_count as usize;
10537        let n_ff_exp = moe.expert_ff_length as usize;
10538        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10539        let lim_exp = cfg.clamp_exp_at(il as u32);
10540        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10541
10542        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10543        if let Some(sig) = cfg.sigmoid_router() {
10544            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10545        }
10546        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10547            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10548        } else {
10549            Self::moe_route_cfg(
10550                e,
10551                &logits,
10552                mrows,
10553                n_expert,
10554                n_used,
10555                m.active_experts.as_deref(),
10556            )?
10557        };
10558        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10559
10560        // Residency split at whole-expert granularity against the (frozen) cache.
10561        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10562            Ok((0..n_expert)
10563                .map(|ex| {
10564                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10565                        .into_iter()
10566                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10567                })
10568                .collect())
10569        })?;
10570
10571        struct Group {
10572            rows: Vec<i32>,
10573            slots: Vec<i32>,
10574            weights: Vec<f32>,
10575        }
10576        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10577        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10578        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10579            Default::default();
10580        for row in 0..mrows {
10581            for j in 0..n_used {
10582                let ex = sel_all[row * n_used + j] as usize;
10583                let w = w_all[row * n_used + j];
10584                if resident_expert[ex] {
10585                    let group = groups.entry(ex).or_insert_with(|| Group {
10586                        rows: Vec::new(),
10587                        slots: Vec::new(),
10588                        weights: Vec::new(),
10589                    });
10590                    group.rows.push(row as i32);
10591                    group.slots.push(j as i32);
10592                    group.weights.push(w);
10593                } else {
10594                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10595                    cpu_rows[row].push((ex, w));
10596                    cpu_by_expert.entry(ex).or_default().push((row, w));
10597                }
10598            }
10599        }
10600
10601        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10602        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10603        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10604        // order per row differs from the sequential single-call chunk — part of the
10605        // documented lockstep numeric class.
10606        let host_rows = e.dtoh(zbatch)?;
10607        let rows_ok = crate::cpu_experts::rows_supported();
10608        enum CpuPart {
10609            Single { row: usize },
10610            Rows { rows: Vec<usize> },
10611        }
10612        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10613        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10614        if rows_ok {
10615            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10616                .into_iter()
10617                .filter(|(_, rows)| rows.len() >= 2)
10618                .collect();
10619            shared.sort_by_key(|(ex, _)| *ex);
10620            for (ex, mut row_weights) in shared {
10621                row_weights.sort_by_key(|(row, _)| *row);
10622                let inputs: Vec<(&[f32], f32)> = row_weights
10623                    .iter()
10624                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10625                    .collect();
10626                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10627                    .map_err(std::io::Error::other)?;
10628                for &(row, _) in &row_weights {
10629                    rows_served.insert((row, ex));
10630                }
10631                tickets.push((
10632                    CpuPart::Rows {
10633                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10634                    },
10635                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10636                ));
10637            }
10638        }
10639        for (row, selected) in cpu_rows.iter().enumerate() {
10640            let leftover: Vec<(usize, f32)> = selected
10641                .iter()
10642                .copied()
10643                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10644                .collect();
10645            if leftover.is_empty() {
10646                continue;
10647            }
10648            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10649            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10650                .map_err(std::io::Error::other)?;
10651            tickets.push((
10652                CpuPart::Single { row },
10653                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
10654            ));
10655        }
10656
10657        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
10658        let mut wbuf = e.zeros(mrows * n_used)?;
10659        let mut order: Vec<usize> = groups.keys().copied().collect();
10660        order.sort_by(|&a, &b| {
10661            groups[&b]
10662                .rows
10663                .len()
10664                .cmp(&groups[&a].rows.len())
10665                .then(a.cmp(&b))
10666        });
10667        for &ex in &order {
10668            let group = &groups[&ex];
10669            let m_e = group.rows.len();
10670            let gl = m.gate_exps.expert_layout(ex);
10671            let ul = m.up_exps.expert_layout(ex);
10672            let dl = m.down_exps.expert_layout(ex);
10673            let row_idx_d = e.htod_i32(&group.rows)?;
10674            let slot_idx_d = e.htod_i32(&group.slots)?;
10675            let dmac = m.down_exps.macro_scale(ex);
10676            let weight_d = if dmac == 1.0 {
10677                e.htod(&group.weights)?
10678            } else {
10679                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
10680                e.htod(&scaled)?
10681            };
10682            let mut gathered = e.zeros(m_e * n_embd)?;
10683            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
10684            let gv = gathered.slice(0..m_e * n_embd);
10685            let gate = e.with_moe_cache(max_block, |c, eng| {
10686                let slot = c
10687                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
10688                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10689                eng.qmatvec_view(
10690                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10691                    0..gl.len,
10692                    &gv,
10693                    m_e,
10694                    m.gate_exps.in_f,
10695                    m.gate_exps.out_f,
10696                    gl.qtype,
10697                    gl.row_bytes,
10698                )
10699            })?;
10700            let up = e.with_moe_cache(max_block, |c, eng| {
10701                let slot = c
10702                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
10703                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10704                eng.qmatvec_view(
10705                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10706                    0..ul.len,
10707                    &gv,
10708                    m_e,
10709                    m.up_exps.in_f,
10710                    m.up_exps.out_f,
10711                    ul.qtype,
10712                    ul.row_bytes,
10713                )
10714            })?;
10715            let mut act = e.zeros(m_e * n_ff_exp)?;
10716            Self::ffn_act_lim(
10717                e,
10718                cfg,
10719                &gate,
10720                &up,
10721                m.gate_exps.macro_scale(ex),
10722                m.up_exps.macro_scale(ex),
10723                lim_exp,
10724                &mut act,
10725                m_e * n_ff_exp,
10726            )?;
10727            let actv = act.slice(0..m_e * n_ff_exp);
10728            let y = e.with_moe_cache(max_block, |c, eng| {
10729                let slot = c
10730                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
10731                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10732                eng.qmatvec_view(
10733                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10734                    0..dl.len,
10735                    &actv,
10736                    m_e,
10737                    m.down_exps.in_f,
10738                    m.down_exps.out_f,
10739                    dl.qtype,
10740                    dl.row_bytes,
10741                )
10742            })?;
10743            e.scatter_slot(
10744                &y,
10745                &row_idx_d,
10746                &slot_idx_d,
10747                &weight_d,
10748                &mut slot_buf,
10749                &mut wbuf,
10750                n_embd,
10751                n_used,
10752                m_e,
10753            )?;
10754        }
10755        let mut moe_out = e.zeros(mrows * n_embd)?;
10756        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
10757
10758        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
10759        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
10760        for (part, ticket) in tickets {
10761            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
10762            let mut add_row = |row: usize, chunk: &[f32]| {
10763                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
10764                for (accumulator, value) in sum.iter_mut().zip(chunk) {
10765                    *accumulator += value;
10766                }
10767            };
10768            match part {
10769                CpuPart::Single { row } => add_row(row, &cpu_output),
10770                CpuPart::Rows { rows } => {
10771                    for (slot, row) in rows.into_iter().enumerate() {
10772                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
10773                    }
10774                }
10775            }
10776        }
10777        for (row, sum) in row_sums.into_iter().enumerate() {
10778            let Some(sum) = sum else { continue };
10779            let cpu_output = e.htod(&sum)?;
10780            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
10781            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
10782        }
10783
10784        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10785            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10786        {
10787            let n_ff_sh = gate_shexp.out_features();
10788            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
10789            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
10790            let mut sa = e.zeros(mrows * n_ff_sh)?;
10791            Self::ffn_act_lim(
10792                e,
10793                cfg,
10794                &sg_gate,
10795                &sg_up,
10796                1.0,
10797                1.0,
10798                lim_shexp,
10799                &mut sa,
10800                mrows * n_ff_sh,
10801            )?;
10802            let sh = e.matmul(down_shexp, &sa, mrows)?;
10803            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
10804            // decode matches the single-sequence decode chain bit-for-bit.
10805            let g = match &m.gate_inp_shexp {
10806                Some(gate_inp_shexp) => {
10807                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
10808                }
10809                None => e.htod(&vec![1.0f32; mrows])?,
10810            };
10811            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
10812        }
10813
10814        Ok(moe_out)
10815    }
10816}
10817
10818// ============================ gemma4 (R8 verified wiring) ==================================
10819// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
10820// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
10821// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
10822// gemma variants after the correctness gate).
10823impl HybridModel {
10824    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
10825    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
10826    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
10827    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
10828    ///
10829    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
10830    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
10831    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
10832    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
10833    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
10834    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
10835    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
10836    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
10837    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
10838        let g = self
10839            .cfg
10840            .gemma4
10841            .as_ref()
10842            .expect("gemma4_rope_dims on a non-gemma4 config");
10843        if g.swa_pattern[il] {
10844            g.rope_dims_swa as usize
10845        } else {
10846            g.rope_dims_global as usize
10847        }
10848    }
10849
10850    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
10851        let g = self.cfg.gemma4.as_ref().unwrap();
10852        let swa = g.swa_pattern[il];
10853        let hd = if swa {
10854            g.key_length_swa
10855        } else {
10856            g.key_length_global
10857        } as usize;
10858        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
10859        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
10860        // rows exact (softmax over one element) while every later position drifted).
10861        (
10862            hd,
10863            g.head_count_kv[il] as usize,
10864            self.cfg.n_head as usize,
10865            if swa {
10866                g.rope_base_swa
10867            } else {
10868                g.rope_base_global
10869            },
10870            1.0,
10871            swa,
10872        )
10873    }
10874
10875    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
10876    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
10877    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
10878    pub(crate) fn gemma4_suppress(
10879        &self,
10880        e: &Engine,
10881        ld: &mut CudaSlice<f32>,
10882        t: usize,
10883    ) -> Result<(), Box<dyn std::error::Error>> {
10884        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
10885            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
10886            // stage as primary, and this tail runs only after the last stage). The assert turns
10887            // that argued invariant into a checked one: any topology violating primary==head
10888            // trips here in debug instead of silently peer-reading a device-0 buffer.
10889            #[cfg(debug_assertions)]
10890            crate::debug_assert_tensor_stream_device(
10891                ids,
10892                &e.stream(),
10893                "gemma4_suppress.suppress_d",
10894            );
10895            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
10896        }
10897        Ok(())
10898    }
10899
10900    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
10901    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
10902    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
10903    /// only (v0): attends within `tokens` via the f32 sdpa.
10904    #[allow(clippy::too_many_arguments)]
10905    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
10906    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
10907    /// switching program at `t > sliding_window`. The door is the measured cause of the
10908    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
10909    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
10910    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
10911    /// published prefix KV stops depending on the total prompt length. Off by default because
10912    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
10913    fn gemma_fa_one_program() -> bool {
10914        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10915        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
10916    }
10917
10918    fn gemma4_attn_prime(
10919        &self,
10920        e: &Engine,
10921        fa: &crate::hybrid::FullAttnLayer,
10922        il: usize,
10923        h: &CudaSlice<f32>,
10924        pos_d: &CudaSlice<i32>,
10925        t: usize,
10926        cache: Option<&mut Cache>,
10927        island: Option<&CudaSlice<i32>>,
10928    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10929        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
10930        let eps = self.cfg.rms_eps;
10931        let aux = self.gemma4_aux.as_ref().unwrap();
10932        let ones = aux.ones(e);
10933        #[cfg(debug_assertions)]
10934        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
10935
10936        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
10937        // (h stays borrowed across the triple, so the cache key can't go stale).
10938        e.mmq_act_begin();
10939        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
10940        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10941            let v = e.dtoh(&q0)?;
10942            let nan = v.iter().filter(|x| x.is_nan()).count();
10943            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10944            eprintln!(
10945                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
10946                v.len()
10947            );
10948        }
10949        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
10950        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
10951        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
10952        let v0 = if swa {
10953            e.matmul(&fa.wv, h, t)?
10954        } else {
10955            e.clone_dtod(&k0)?
10956        };
10957        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10958            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
10959                let v = e.dtoh(buf)?;
10960                let nan = v.iter().filter(|x| x.is_nan()).count();
10961                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10962                eprintln!(
10963                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
10964                    v.len()
10965                );
10966            }
10967        }
10968
10969        let mut q = e.uninit(t * nh * hd)?;
10970        let mut k = e.uninit(t * nkv * hd)?;
10971        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
10972        let mut v = e.uninit(t * nkv * hd)?;
10973        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
10974        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
10975        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
10976        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10977        // Island primes take the mask-capable naive kernel below; keep the operands f32
10978        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
10979        let emit = island.is_none()
10980            && t >= 16
10981            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
10982            && *EMIT.get_or_init(|| {
10983                std::env::var("MEMRA_FA_EMIT")
10984                    .map(|s| s != "0")
10985                    .unwrap_or(true)
10986            });
10987        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
10988        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10989        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10990        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
10991        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
10992        let v_f16 = emit
10993            && crate::fa_f16pv_on()
10994            && match hd {
10995                512 => true,
10996                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
10997                _ => false,
10998            };
10999        if emit {
11000            e.rms_norm_qkv_w4b(
11001                &q0,
11002                &k0,
11003                &v0,
11004                fa.q_norm.float_data(),
11005                fa.k_norm.float_data(),
11006                ones,
11007                &mut q,
11008                &mut k,
11009                &mut v,
11010                &mut vb,
11011                hd,
11012                nh * t,
11013                nkv * t,
11014                eps,
11015                v_f16,
11016            )?;
11017        } else {
11018            e.rms_norm_qkv(
11019                &q0,
11020                &k0,
11021                &v0,
11022                fa.q_norm.float_data(),
11023                fa.k_norm.float_data(),
11024                ones,
11025                &mut q,
11026                &mut k,
11027                &mut v,
11028                hd,
11029                nh * t,
11030                nkv * t,
11031                eps,
11032            )?;
11033        }
11034
11035        let ff = if swa {
11036            None
11037        } else {
11038            Some(
11039                aux.rope_freqs(e)
11040                    .expect("gemma4 global rope needs rope_freqs.weight"),
11041            )
11042        };
11043        #[cfg(debug_assertions)]
11044        if let Some(ff) = ff {
11045            crate::debug_assert_tensor_stream_device(
11046                ff,
11047                &e.stream(),
11048                "gemma4_attn_prime.rope_freqs",
11049            );
11050        }
11051        if emit {
11052            e.rope_neox2_bf16e(
11053                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
11054            )?;
11055        } else {
11056            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
11057        }
11058
11059        if let Some(cache) = cache {
11060            let kvl = cache.kv[il].as_mut().unwrap();
11061            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11062            e.append_kv_quantized_rows(
11063                &k,
11064                &v,
11065                &mut kvl.k,
11066                &mut kvl.v,
11067                kvl.len,
11068                t,
11069                kvl.kv_dim_k,
11070                kvl.kv_dim_v,
11071                kvl.k_tok_bytes,
11072                kvl.v_tok_bytes,
11073                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11074            )?;
11075            kvl.len += t;
11076        }
11077        let mut attn = e.zeros(t * nh * hd)?;
11078        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11079        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11080        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11081        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11082        if let Some(span) = island {
11083            // Masked-prefill arm: every layer routes through the island-aware naive
11084            // kernel (correctness-first, same posture as the vision tower v1). The
11085            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11086            // window, the real window beyond it.
11087            let w = if swa && t > win { win } else { 0 };
11088            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11089        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11090            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11091                if emit {
11092                    e.fa_prefill_w_pre(
11093                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11094                    )?;
11095                } else {
11096                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11097                }
11098            } else {
11099                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11100            }
11101        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11102            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11103        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11104            if emit {
11105                e.fa_prefill_hd512_pre(
11106                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11107                )?;
11108            } else {
11109                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11110            }
11111        } else {
11112            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11113        }
11114        Ok(e.matmul(&fa.wo, &attn, t)?)
11115    }
11116
11117    /// Back-compat wrapper (pure prefill, no cache).
11118    fn gemma4_attn(
11119        &self,
11120        e: &Engine,
11121        fa: &crate::hybrid::FullAttnLayer,
11122        il: usize,
11123        h: &CudaSlice<f32>,
11124        pos_d: &CudaSlice<i32>,
11125        t: usize,
11126    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11127        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11128    }
11129
11130    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11131    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11132    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11133    /// the q8z epilogue is quantize_q8_1 verbatim).
11134    fn gemma4_moe_q8(
11135        &self,
11136        e: &Engine,
11137        m: &crate::hybrid::MoeWeights,
11138        bits: &crate::hybrid::Gemma4MoeBits,
11139        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11140        router_in: &CudaSlice<f32>,
11141        t: usize,
11142    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11143        let cfg = &self.cfg;
11144        let moe = cfg.moe.as_ref().unwrap();
11145        let n_embd = cfg.n_embd as usize;
11146        let n_expert = moe.expert_count as usize;
11147        let n_used = moe.expert_used_count as usize;
11148        let n_ff_exp = moe.expert_ff_length as usize;
11149        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11150        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11151        // the pair's 12us is kernel time, not launch gaps.
11152        let logits = if crate::router_kernel_on() {
11153            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11154        } else {
11155            e.matmul(&m.gate_inp, router_in, t)?
11156        };
11157        let dev = m.dev_exps.as_ref().unwrap();
11158        let (sel_d, w_d) =
11159            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11160        let (zq, zd) = mq;
11161        if t == 1 {
11162            let selv = sel_d.slice(0..n_used);
11163            let wv = w_d.slice(0..n_used);
11164            let act = e.moe_gate_up_gelu8_dev_q8(
11165                &dev.ptr_row,
11166                &selv,
11167                zq,
11168                zd,
11169                n_embd,
11170                n_ff_exp,
11171                n_used,
11172                n_expert,
11173                m.gate_exps.qtype,
11174                m.up_exps.qtype,
11175                m.gate_exps.row_bytes,
11176                m.up_exps.row_bytes,
11177            )?;
11178            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11179            let mut moe_out = e.uninit(n_embd)?;
11180            e.moe_down8_fma_dev_q8(
11181                &dev.ptr_row,
11182                &selv,
11183                &wv,
11184                &aq2,
11185                &ad2,
11186                &mut moe_out.slice_mut(0..n_embd),
11187                n_ff_exp,
11188                n_embd,
11189                n_used,
11190                n_expert,
11191                m.down_exps.qtype,
11192                m.down_exps.row_bytes,
11193            )?;
11194            return Ok(moe_out);
11195        }
11196        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11197        let act = if csr {
11198            e.moe_gate_up_gelu8_dev_q8_csr(
11199                &dev.ptr_row,
11200                &sel_d,
11201                zq,
11202                zd,
11203                t * n_used,
11204                n_embd,
11205                n_ff_exp,
11206                n_used,
11207                n_expert,
11208                m.gate_exps.qtype,
11209                m.up_exps.qtype,
11210                m.gate_exps.row_bytes,
11211                m.up_exps.row_bytes,
11212            )?
11213        } else {
11214            e.moe_gate_up_gelu8_dev_q8_rows(
11215                &dev.ptr_row,
11216                &sel_d,
11217                zq,
11218                zd,
11219                t,
11220                n_embd,
11221                n_ff_exp,
11222                n_used,
11223                n_expert,
11224                m.gate_exps.qtype,
11225                m.up_exps.qtype,
11226                m.gate_exps.row_bytes,
11227                m.up_exps.row_bytes,
11228            )?
11229        };
11230        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11231        let mut moe_out = e.uninit(t * n_embd)?;
11232        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11233        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11234        e.moe_down8_fma_dev_q8_rows_g(
11235            &dev.ptr_row,
11236            &sel_d,
11237            &w_d,
11238            &aq2,
11239            &ad2,
11240            &mut moe_out,
11241            t,
11242            n_ff_exp,
11243            n_embd,
11244            n_used,
11245            n_expert,
11246            m.down_exps.qtype,
11247            m.down_exps.row_bytes,
11248        )?;
11249        Ok(moe_out)
11250    }
11251
11252    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11253    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11254    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11255    fn gemma4_moe(
11256        &self,
11257        e: &Engine,
11258        m: &crate::hybrid::MoeWeights,
11259        bits: &crate::hybrid::Gemma4MoeBits,
11260        moe_in: &CudaSlice<f32>,
11261        router_in: &CudaSlice<f32>,
11262        t: usize,
11263    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11264        let cfg = &self.cfg;
11265        let moe = cfg.moe.as_ref().unwrap();
11266        let n_embd = cfg.n_embd as usize;
11267        let n_expert = moe.expert_count as usize;
11268        let n_used = moe.expert_used_count as usize;
11269        let n_ff_exp = moe.expert_ff_length as usize;
11270
11271        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11272        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11273        // batched matmul only at real prefill.
11274        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11275            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11276        } else {
11277            e.matmul(&m.gate_inp, router_in, t)?
11278        };
11279
11280        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11281        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11282        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11283        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11284        if t < PRIME_MIN_T
11285            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11286            && expert_dp4a_supported(m.gate_exps.qtype)
11287            && expert_dp4a_supported(m.up_exps.qtype)
11288            && expert_dp4a_supported(m.down_exps.qtype)
11289            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11290        {
11291            let dev = m.dev_exps.as_ref().unwrap();
11292            let (sel_d, w_d) =
11293                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11294            if t == 1 {
11295                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11296                let selv = sel_d.slice(0..n_used);
11297                let wv = w_d.slice(0..n_used);
11298                let act = e.moe_gate_up_gelu8_dev_q8(
11299                    &dev.ptr_row,
11300                    &selv,
11301                    &zq,
11302                    &zd,
11303                    n_embd,
11304                    n_ff_exp,
11305                    n_used,
11306                    n_expert,
11307                    m.gate_exps.qtype,
11308                    m.up_exps.qtype,
11309                    m.gate_exps.row_bytes,
11310                    m.up_exps.row_bytes,
11311                )?;
11312                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11313                let mut moe_out = e.uninit(n_embd)?;
11314                e.moe_down8_fma_dev_q8(
11315                    &dev.ptr_row,
11316                    &selv,
11317                    &wv,
11318                    &aq2,
11319                    &ad2,
11320                    &mut moe_out.slice_mut(0..n_embd),
11321                    n_ff_exp,
11322                    n_embd,
11323                    n_used,
11324                    n_expert,
11325                    m.down_exps.qtype,
11326                    m.down_exps.row_bytes,
11327                )?;
11328                return Ok(moe_out);
11329            }
11330            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11331            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11332            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11333            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11334            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11335            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11336            let act = if csr {
11337                e.moe_gate_up_gelu8_dev_q8_csr(
11338                    &dev.ptr_row,
11339                    &sel_d,
11340                    &zq,
11341                    &zd,
11342                    t * n_used,
11343                    n_embd,
11344                    n_ff_exp,
11345                    n_used,
11346                    n_expert,
11347                    m.gate_exps.qtype,
11348                    m.up_exps.qtype,
11349                    m.gate_exps.row_bytes,
11350                    m.up_exps.row_bytes,
11351                )?
11352            } else {
11353                e.moe_gate_up_gelu8_dev_q8_rows(
11354                    &dev.ptr_row,
11355                    &sel_d,
11356                    &zq,
11357                    &zd,
11358                    t,
11359                    n_embd,
11360                    n_ff_exp,
11361                    n_used,
11362                    n_expert,
11363                    m.gate_exps.qtype,
11364                    m.up_exps.qtype,
11365                    m.gate_exps.row_bytes,
11366                    m.up_exps.row_bytes,
11367                )?
11368            };
11369            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11370            let mut moe_out = e.uninit(t * n_embd)?;
11371            e.moe_down8_fma_dev_q8_rows_g(
11372                &dev.ptr_row,
11373                &sel_d,
11374                &w_d,
11375                &aq2,
11376                &ad2,
11377                &mut moe_out,
11378                t,
11379                n_ff_exp,
11380                n_embd,
11381                n_used,
11382                n_expert,
11383                m.down_exps.qtype,
11384                m.down_exps.row_bytes,
11385            )?;
11386            return Ok(moe_out);
11387        }
11388
11389        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11390        for (i, &sx) in sel_all.iter().enumerate() {
11391            w_all[i] *= bits.per_expert_scale[sx as usize];
11392        }
11393
11394        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11395        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11396        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11397        if t >= PRIME_MIN_T
11398            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11399            && expert_dp4a_supported(m.gate_exps.qtype)
11400            && expert_dp4a_supported(m.up_exps.qtype)
11401            && expert_dp4a_supported(m.down_exps.qtype)
11402            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11403        {
11404            let dev = m.dev_exps.as_ref().unwrap();
11405            let n_pairs = t * n_used;
11406            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11407            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11408            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11409            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11410            let pt = e.htod_i32(&pair_tok)?;
11411            let pw = e.htod(&w_all)?;
11412            let toff = e.htod_i32(&tok_off)?;
11413            let tids = e.htod_i32(&tok_ids)?;
11414            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11415            for p in 0..n_pairs {
11416                by_ex[pair_ex[p] as usize].push(p as i32);
11417            }
11418            let mut ex_ids: Vec<i32> = Vec::new();
11419            let mut ex_off: Vec<i32> = vec![0];
11420            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11421            for (ex, list) in by_ex.iter().enumerate() {
11422                if list.is_empty() {
11423                    continue;
11424                }
11425                ex_ids.push(ex as i32);
11426                ex_pairs.extend_from_slice(list);
11427                ex_off.push(ex_pairs.len() as i32);
11428            }
11429            let n_active = ex_ids.len();
11430            let exi = e.htod_i32(&ex_ids)?;
11431            let exo = e.htod_i32(&ex_off)?;
11432            let exp_d = e.htod_i32(&ex_pairs)?;
11433            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11434            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11435            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11436            // ragged down k (704) needs no padding here — cublas takes any k.
11437            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11438            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11439            // Hopper default — see moe_f16g_gemma_on.
11440            if crate::moe_f16g_gemma_on()
11441                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11442                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11443                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11444            {
11445                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11446                let csr_tok_d = e.htod_i32(&csr_tok)?;
11447                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11448                let g_csr = e.moe_f16_grouped(
11449                    &dev.ptr_row,
11450                    0,
11451                    n_expert,
11452                    &exi,
11453                    &ex_off,
11454                    &exo,
11455                    &z_f16,
11456                    &z_s,
11457                    n_embd,
11458                    n_ff_exp,
11459                    n_active,
11460                    n_pairs,
11461                    m.gate_exps.qtype,
11462                    m.gate_exps.row_bytes,
11463                )?;
11464                let u_csr = e.moe_f16_grouped(
11465                    &dev.ptr_row,
11466                    1,
11467                    n_expert,
11468                    &exi,
11469                    &ex_off,
11470                    &exo,
11471                    &z_f16,
11472                    &z_s,
11473                    n_embd,
11474                    n_ff_exp,
11475                    n_active,
11476                    n_pairs,
11477                    m.up_exps.qtype,
11478                    m.up_exps.row_bytes,
11479                )?;
11480                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11481                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11482                let d_csr = e.moe_f16_grouped(
11483                    &dev.ptr_row,
11484                    2,
11485                    n_expert,
11486                    &exi,
11487                    &ex_off,
11488                    &exo,
11489                    &a_f16,
11490                    &a_s,
11491                    n_ff_exp,
11492                    n_embd,
11493                    n_active,
11494                    n_pairs,
11495                    m.down_exps.qtype,
11496                    m.down_exps.row_bytes,
11497                )?;
11498                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11499                let mut moe_out = e.uninit(t * n_embd)?;
11500                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11501                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11502                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11503                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11504                    eprintln!(
11505                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11506                        scan(&yd),
11507                        scan(&mo)
11508                    );
11509                }
11510                return Ok(moe_out);
11511            }
11512            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11513            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11514            let mma =
11515                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11516            let (gate, up) = if mma {
11517                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11518                (
11519                    e.mmq_iq_experts(
11520                        &dev.ptr_row,
11521                        0,
11522                        n_expert,
11523                        &exi,
11524                        &exo,
11525                        &exp_d,
11526                        &pt,
11527                        &z_scr,
11528                        n_embd,
11529                        n_ff_exp,
11530                        n_active,
11531                        n_pairs,
11532                        t,
11533                        m.gate_exps.qtype,
11534                        m.gate_exps.row_bytes,
11535                    )?,
11536                    e.mmq_iq_experts(
11537                        &dev.ptr_row,
11538                        1,
11539                        n_expert,
11540                        &exi,
11541                        &exo,
11542                        &exp_d,
11543                        &pt,
11544                        &z_scr,
11545                        n_embd,
11546                        n_ff_exp,
11547                        n_active,
11548                        n_pairs,
11549                        t,
11550                        m.up_exps.qtype,
11551                        m.up_exps.row_bytes,
11552                    )?,
11553                )
11554            } else {
11555                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11556                (
11557                    e.moe_pairs_matvec_q8_dec(
11558                        &dev.ptr_row,
11559                        0,
11560                        &exi,
11561                        &exo,
11562                        &exp_d,
11563                        &pt,
11564                        &zq,
11565                        &zd,
11566                        n_embd,
11567                        n_ff_exp,
11568                        n_expert,
11569                        n_active,
11570                        n_pairs,
11571                        m.gate_exps.qtype,
11572                        m.gate_exps.row_bytes,
11573                    )?,
11574                    e.moe_pairs_matvec_q8_dec(
11575                        &dev.ptr_row,
11576                        1,
11577                        &exi,
11578                        &exo,
11579                        &exp_d,
11580                        &pt,
11581                        &zq,
11582                        &zd,
11583                        n_embd,
11584                        n_ff_exp,
11585                        n_expert,
11586                        n_active,
11587                        n_pairs,
11588                        m.up_exps.qtype,
11589                        m.up_exps.row_bytes,
11590                    )?,
11591                )
11592            };
11593            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11594            let pself = e.htod_i32(&pair_self)?;
11595            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11596            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11597            // to the 256-val superblock (768) while the act quantizer's zero padding
11598            // makes every padded-k product exactly zero (weight overread bytes multiply
11599            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11600            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11601            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11602            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11603            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11604            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11605            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11606            let y_down = if mma {
11607                let in_pad = n_ff_exp.div_ceil(256) * 256;
11608                let a_scr = if crate::moe_fuse_actq_on() {
11609                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11610                } else {
11611                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11612                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11613                };
11614                e.mmq_iq_experts(
11615                    &dev.ptr_row,
11616                    2,
11617                    n_expert,
11618                    &exi,
11619                    &exo,
11620                    &exp_d,
11621                    &pself,
11622                    &a_scr,
11623                    in_pad,
11624                    n_embd,
11625                    n_active,
11626                    n_pairs,
11627                    n_pairs,
11628                    m.down_exps.qtype,
11629                    m.down_exps.row_bytes,
11630                )?
11631            } else {
11632                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11633                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11634                e.moe_pairs_matvec_q8_dec(
11635                    &dev.ptr_row,
11636                    2,
11637                    &exi,
11638                    &exo,
11639                    &exp_d,
11640                    &pself,
11641                    &aq2,
11642                    &ad2,
11643                    n_ff_exp,
11644                    n_embd,
11645                    n_expert,
11646                    n_active,
11647                    n_pairs,
11648                    m.down_exps.qtype,
11649                    m.down_exps.row_bytes,
11650                )?
11651            };
11652            let mut moe_out = e.uninit(t * n_embd)?;
11653            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11654            return Ok(moe_out);
11655        }
11656
11657        let g_len = m.gate_exps.expert_stride;
11658        let u_len = m.up_exps.expert_stride;
11659        let d_len = m.down_exps.expert_stride;
11660        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
11661        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
11662        // the spill fallback.
11663        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
11664        let (mut sg, mut su, mut sd) = if dev.is_some() {
11665            (None, None, None)
11666        } else {
11667            (
11668                Some(e.alloc_u8_uninit(g_len)?),
11669                Some(e.alloc_u8_uninit(u_len)?),
11670                Some(e.alloc_u8_uninit(d_len)?),
11671            )
11672        };
11673        let mut moe_out = e.zeros(t * n_embd)?;
11674        for tok in 0..t {
11675            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
11676            let w = &w_all[tok * n_used..(tok + 1) * n_used];
11677            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
11678            for (j, &ex) in sel.iter().enumerate() {
11679                let ex = ex as usize;
11680                let gate = match dev {
11681                    Some(d) => e.qmatvec_view(
11682                        &d.gate,
11683                        ex * g_len..(ex + 1) * g_len,
11684                        &zt,
11685                        1,
11686                        m.gate_exps.in_f,
11687                        m.gate_exps.out_f,
11688                        m.gate_exps.qtype,
11689                        m.gate_exps.row_bytes,
11690                    )?,
11691                    None => {
11692                        let sg = sg.as_mut().unwrap();
11693                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
11694                        e.qmatvec_view(
11695                            sg,
11696                            0..g_len,
11697                            &zt,
11698                            1,
11699                            m.gate_exps.in_f,
11700                            m.gate_exps.out_f,
11701                            m.gate_exps.qtype,
11702                            m.gate_exps.row_bytes,
11703                        )?
11704                    }
11705                };
11706                let up = match dev {
11707                    Some(d) => e.qmatvec_view(
11708                        &d.up,
11709                        ex * u_len..(ex + 1) * u_len,
11710                        &zt,
11711                        1,
11712                        m.up_exps.in_f,
11713                        m.up_exps.out_f,
11714                        m.up_exps.qtype,
11715                        m.up_exps.row_bytes,
11716                    )?,
11717                    None => {
11718                        let su = su.as_mut().unwrap();
11719                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
11720                        e.qmatvec_view(
11721                            su,
11722                            0..u_len,
11723                            &zt,
11724                            1,
11725                            m.up_exps.in_f,
11726                            m.up_exps.out_f,
11727                            m.up_exps.qtype,
11728                            m.up_exps.row_bytes,
11729                        )?
11730                    }
11731                };
11732                let mut act = e.uninit(n_ff_exp)?;
11733                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
11734                let actv = act.slice(0..n_ff_exp);
11735                let y = match dev {
11736                    Some(d) => e.qmatvec_view(
11737                        &d.down,
11738                        ex * d_len..(ex + 1) * d_len,
11739                        &actv,
11740                        1,
11741                        m.down_exps.in_f,
11742                        m.down_exps.out_f,
11743                        m.down_exps.qtype,
11744                        m.down_exps.row_bytes,
11745                    )?,
11746                    None => {
11747                        let sd = sd.as_mut().unwrap();
11748                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
11749                        e.qmatvec_view(
11750                            sd,
11751                            0..d_len,
11752                            &actv,
11753                            1,
11754                            m.down_exps.in_f,
11755                            m.down_exps.out_f,
11756                            m.down_exps.qtype,
11757                            m.down_exps.row_bytes,
11758                        )?
11759                    }
11760                };
11761                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
11762                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
11763            }
11764        }
11765        Ok(moe_out)
11766    }
11767
11768    /// One gemma4 trunk layer (R8): x -> x_next.
11769    fn gemma4_layer(
11770        &self,
11771        e: &Engine,
11772        il: usize,
11773        layer: &crate::hybrid::HybridLayer,
11774        x: &CudaSlice<f32>,
11775        pos_d: &CudaSlice<i32>,
11776        t: usize,
11777    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11778        let n_embd = self.cfg.n_embd as usize;
11779        let eps = self.cfg.rms_eps;
11780
11781        let mut h = e.zeros(t * n_embd)?;
11782        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
11783        let Mixer::Full(fa) = &layer.mixer else {
11784            panic!("gemma4 layer {il} not full-attn")
11785        };
11786        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
11787        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
11788        let mut cur = e.zeros(t * n_embd)?;
11789        e.rms_norm(
11790            &o,
11791            layer.post_attn_norm.float_data(),
11792            &mut cur,
11793            n_embd,
11794            t,
11795            eps,
11796        )?;
11797        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
11798    }
11799
11800    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
11801    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
11802    /// layer scale — shared verbatim by the prefill, decode and verify paths.
11803    fn gemma4_layer_tail_add(
11804        &self,
11805        e: &Engine,
11806        layer: &crate::hybrid::HybridLayer,
11807        cur: &CudaSlice<f32>,
11808        x: &CudaSlice<f32>,
11809        t: usize,
11810    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11811        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
11812    }
11813
11814    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
11815    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
11816    fn gemma4_layer_tail_add_n(
11817        &self,
11818        e: &Engine,
11819        layer: &crate::hybrid::HybridLayer,
11820        cur: &CudaSlice<f32>,
11821        x: &CudaSlice<f32>,
11822        t: usize,
11823        next_norm: Option<&CudaSlice<f32>>,
11824    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
11825        let n_embd = self.cfg.n_embd as usize;
11826        let bits = layer.gemma4.as_ref().unwrap();
11827        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
11828        let mut xn = e.uninit(t * n_embd)?;
11829        match next_norm {
11830            Some(w) => {
11831                let mut hn = e.uninit(t * n_embd)?;
11832                e.add_scale_rms_norm(
11833                    &sn,
11834                    &attn_out,
11835                    bits.layer_scale,
11836                    w,
11837                    &mut xn,
11838                    &mut hn,
11839                    n_embd,
11840                    t,
11841                    self.cfg.rms_eps,
11842                )?;
11843                Ok((xn, Some(hn)))
11844            }
11845            None => {
11846                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
11847                Ok((xn, None))
11848            }
11849        }
11850    }
11851
11852    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
11853    /// norm — returns (sn, attn_out) for the closing add+scale variants.
11854    fn gemma4_layer_tail_core(
11855        &self,
11856        e: &Engine,
11857        layer: &crate::hybrid::HybridLayer,
11858        cur: &CudaSlice<f32>,
11859        x: &CudaSlice<f32>,
11860        t: usize,
11861    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11862        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
11863    }
11864
11865    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
11866    /// means `cur` is the RAW attention output and the dense entry runs
11867    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
11868    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
11869    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
11870    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
11871    fn gemma4_layer_tail_core_pn(
11872        &self,
11873        e: &Engine,
11874        layer: &crate::hybrid::HybridLayer,
11875        cur: &CudaSlice<f32>,
11876        x: &CudaSlice<f32>,
11877        t: usize,
11878        pre_norm: Option<&CudaSlice<f32>>,
11879        defer_post_norm: bool,
11880    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11881        let n_embd = self.cfg.n_embd as usize;
11882        let eps = self.cfg.rms_eps;
11883        let bits = layer.gemma4.as_ref().unwrap();
11884
11885        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
11886        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
11887        let Some(mbits) = bits.moe_bits.as_ref() else {
11888            let crate::hybrid::Ffn::Dense {
11889                ffn_gate,
11890                ffn_up,
11891                ffn_down,
11892            } = &layer.ffn
11893            else {
11894                panic!("gemma4 dense layer without Dense ffn")
11895            };
11896            let mut attn_out = e.uninit(t * n_embd)?;
11897            let mut zsh = e.uninit(t * n_embd)?;
11898            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
11899            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
11900            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
11901            match pre_norm {
11902                Some(wa) if t == 1 => {
11903                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
11904                        cur,
11905                        wa,
11906                        x,
11907                        bits.ffn_norm.float_data(),
11908                        &mut attn_out,
11909                        &mut zsh,
11910                        n_embd,
11911                        t,
11912                        eps,
11913                    )?);
11914                }
11915                Some(wa) => e.rms_pre_add_rms_norm(
11916                    cur,
11917                    wa,
11918                    x,
11919                    bits.ffn_norm.float_data(),
11920                    &mut attn_out,
11921                    &mut zsh,
11922                    n_embd,
11923                    t,
11924                    eps,
11925                )?,
11926                None => e.add_rms_norm(
11927                    cur,
11928                    x,
11929                    bits.ffn_norm.float_data(),
11930                    &mut attn_out,
11931                    &mut zsh,
11932                    n_embd,
11933                    t,
11934                    eps,
11935                )?,
11936            }
11937            let n_ff = ffn_gate.out_features();
11938            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
11939            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
11940            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
11941            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
11942            // rescue segment C — the megakernel front is closed for the dense tail.
11943            let (gate, up) = if t == 1 {
11944                let (zq, zd) = match zpair {
11945                    Some(p) => p,
11946                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
11947                };
11948                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
11949                    Some(p) => p,
11950                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
11951                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
11952                        Some(p) => p,
11953                        None => (
11954                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
11955                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
11956                        ),
11957                    },
11958                }
11959            } else {
11960                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
11961                // launch for the verify's gate+up — the up segment's blocks fill SMs as
11962                // the gate segment drains (the launch-tail mechanism behind the b-tier
11963                // plateau; first positive after six falsified in-kernel variants).
11964                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11965                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
11966                let fused = if f2b {
11967                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
11968                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
11969                } else {
11970                    None
11971                };
11972                match fused {
11973                    Some(p) => p,
11974                    None => {
11975                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
11976                        e.mmq_act_begin();
11977                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
11978                    }
11979                }
11980            };
11981            let mut act = e.uninit(t * n_ff)?;
11982            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
11983            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
11984            let f0 = if e.uses_q8_1_fast(ffn_down) {
11985                let upv = e.view(&up, t * n_ff);
11986                let up_all = upv.slice(0..t * n_ff);
11987                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
11988                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
11989            } else {
11990                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11991                e.matmul(ffn_down, &act, t)?
11992            };
11993            if defer_post_norm {
11994                return Ok((f0, attn_out));
11995            }
11996            let mut sn = e.uninit(t * n_embd)?;
11997            e.rms_norm(
11998                &f0,
11999                bits.post_ffw_norm.float_data(),
12000                &mut sn,
12001                n_embd,
12002                t,
12003                eps,
12004            )?;
12005            return Ok((sn, attn_out));
12006        };
12007
12008        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
12009        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
12010        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
12011        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
12012        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
12013        let mut attn_out = e.uninit(t * n_embd)?;
12014        let mut router_in = e.uninit(t * n_embd)?;
12015        let fast_moe = match &layer.ffn {
12016            crate::hybrid::Ffn::Moe(m) => {
12017                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
12018                    && expert_dp4a_supported(m.gate_exps.qtype)
12019                    && expert_dp4a_supported(m.up_exps.qtype)
12020                    && expert_dp4a_supported(m.down_exps.qtype)
12021                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
12022            }
12023            _ => false,
12024        };
12025        let q8z = t < PRIME_MIN_T && fast_moe;
12026        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
12027            let (z0, m2) = e.add_rms_norm3_q8z(
12028                cur,
12029                x,
12030                bits.ffn_norm.float_data(),
12031                &mbits.router_scale_pre,
12032                mbits.pre_ffw_norm_2.float_data(),
12033                &mut attn_out,
12034                &mut router_in,
12035                n_embd,
12036                t,
12037                eps,
12038            )?;
12039            (None, Some(z0), Some(m2))
12040        } else {
12041            let mut zsh = e.uninit(t * n_embd)?;
12042            let mut moe_in = e.uninit(t * n_embd)?;
12043            e.add_rms_norm3(
12044                cur,
12045                x,
12046                bits.ffn_norm.float_data(),
12047                &mbits.router_scale_pre,
12048                mbits.pre_ffw_norm_2.float_data(),
12049                &mut attn_out,
12050                &mut zsh,
12051                &mut router_in,
12052                &mut moe_in,
12053                n_embd,
12054                t,
12055                eps,
12056            )?;
12057            (Some((zsh, moe_in)), None, None)
12058        };
12059        let attn_out2 = attn_out;
12060        #[allow(unused_variables)]
12061        let attn_out = &attn_out2;
12062        let n_ff = mbits.shared_gate.out_features();
12063        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12064            if t == 1 {
12065                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12066                    Some(p) => p,
12067                    None => match e.matmul_nvfp4_fused2(
12068                        &mbits.shared_gate,
12069                        &mbits.shared_up,
12070                        zq,
12071                        zd,
12072                        1,
12073                    )? {
12074                        Some(p) => p,
12075                        None => {
12076                            let h0 = e.zeros(0)?;
12077                            (
12078                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12079                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12080                            )
12081                        }
12082                    },
12083                }
12084            } else {
12085                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12086                let h0 = e.zeros(0)?;
12087                (
12088                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12089                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12090                )
12091            }
12092        } else {
12093            let (zsh, _) = zsh_f32.as_ref().unwrap();
12094            (
12095                e.matmul(&mbits.shared_gate, zsh, t)?,
12096                e.matmul(&mbits.shared_up, zsh, t)?,
12097            )
12098        };
12099        let mut act = e.uninit(t * n_ff)?;
12100        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12101        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12102        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12103            panic!("gemma4 layer not MoE")
12104        };
12105        let moe0 = match (&moe_q8, &zsh_f32) {
12106            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12107            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12108            _ => unreachable!(),
12109        };
12110        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12111        let mut mlp = e.uninit(t * n_embd)?;
12112        let mut moe = e.uninit(t * n_embd)?;
12113        e.rms_norm2x(
12114            &mlp0,
12115            &moe0,
12116            mbits.post_ffw_norm_1.float_data(),
12117            mbits.post_ffw_norm_2.float_data(),
12118            &mut mlp,
12119            &mut moe,
12120            n_embd,
12121            t,
12122            eps,
12123        )?;
12124
12125        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12126        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12127        let mut sum = e.uninit(t * n_embd)?;
12128        let mut sn = e.uninit(t * n_embd)?;
12129        e.add_rms_norm(
12130            &mlp,
12131            &moe,
12132            bits.post_ffw_norm.float_data(),
12133            &mut sum,
12134            &mut sn,
12135            n_embd,
12136            t,
12137            eps,
12138        )?;
12139        Ok((sn, attn_out2))
12140    }
12141
12142    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12143    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12144    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12145    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12146    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12147    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12148    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12149    /// decode == verify == graph parity holds by construction at either seam value.
12150    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12151    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12152        &self,
12153        e: &Engine,
12154        layer: &crate::hybrid::HybridLayer,
12155        o: &CudaSlice<f32>,
12156        x: &CudaSlice<f32>,
12157        t: usize,
12158        next_norm: Option<&CudaSlice<f32>>,
12159    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12160    {
12161        let n_embd = self.cfg.n_embd as usize;
12162        let eps = self.cfg.rms_eps;
12163        let bits = layer.gemma4.as_ref().unwrap();
12164        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12165            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12166                e,
12167                layer,
12168                o,
12169                x,
12170                t,
12171                Some(layer.post_attn_norm.float_data()),
12172                true,
12173            )?;
12174            let mut xn = e.uninit(t * n_embd)?;
12175            return match next_norm {
12176                Some(w) => {
12177                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12178                        &f0,
12179                        bits.post_ffw_norm.float_data(),
12180                        &attn_out,
12181                        bits.layer_scale,
12182                        w,
12183                        &mut xn,
12184                        n_embd,
12185                        t,
12186                        eps,
12187                    )?;
12188                    Ok((xn, Some(pair)))
12189                }
12190                None => {
12191                    let mut sn = e.uninit(t * n_embd)?;
12192                    e.rms_norm(
12193                        &f0,
12194                        bits.post_ffw_norm.float_data(),
12195                        &mut sn,
12196                        n_embd,
12197                        t,
12198                        eps,
12199                    )?;
12200                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12201                    Ok((xn, None))
12202                }
12203            };
12204        }
12205        let mut cur = e.uninit(t * n_embd)?;
12206        e.rms_norm(
12207            o,
12208            layer.post_attn_norm.float_data(),
12209            &mut cur,
12210            n_embd,
12211            t,
12212            eps,
12213        )?;
12214        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12215    }
12216
12217    pub(crate) fn gemma4_layer_tail_add_nq(
12218        &self,
12219        e: &Engine,
12220        layer: &crate::hybrid::HybridLayer,
12221        cur: &CudaSlice<f32>,
12222        x: &CudaSlice<f32>,
12223        t: usize,
12224        next_norm: Option<&CudaSlice<f32>>,
12225    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12226    {
12227        let n_embd = self.cfg.n_embd as usize;
12228        let bits = layer.gemma4.as_ref().unwrap();
12229        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12230        let mut xn = e.uninit(t * n_embd)?;
12231        match next_norm {
12232            Some(w) => {
12233                let pair = e.add_scale_rms_norm_q8_1(
12234                    &sn,
12235                    &attn_out,
12236                    bits.layer_scale,
12237                    w,
12238                    &mut xn,
12239                    n_embd,
12240                    t,
12241                    self.cfg.rms_eps,
12242                )?;
12243                Ok((xn, Some(pair)))
12244            }
12245            None => {
12246                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12247                Ok((xn, None))
12248            }
12249        }
12250    }
12251
12252    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12253    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12254    fn gemma4_forward(
12255        &self,
12256        e: &Engine,
12257        tokens: &[u32],
12258        last_only: bool,
12259    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12260        // E4B routes to its own forward regardless of the caller's entry point (forward /
12261        // forward_last / prime paths all funnel here for gemma4).
12262        if self.is_gemma4_e4b() {
12263            return self.gemma4_e4b_forward(e, tokens, last_only);
12264        }
12265        let n_embd = self.cfg.n_embd as usize;
12266        let t = tokens.len();
12267        let pos: Vec<i32> = (0..t as i32).collect();
12268        let pos_d = e.htod_i32(&pos)?;
12269
12270        let mut x = self.embed(e, tokens)?;
12271        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12272        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12273        // the bring-up bisect vs llama-eval-callback node stats.
12274        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12275        let stat =
12276            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12277                let h = e.dtoh(x)?;
12278                let bad = h.iter().filter(|v| !v.is_finite()).count();
12279                let mx = h
12280                    .iter()
12281                    .filter(|v| v.is_finite())
12282                    .fold(0.0f32, |m, v| m.max(v.abs()));
12283                eprintln!(
12284                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12285                    &h[..3]
12286                );
12287                Ok(())
12288            };
12289        if probe {
12290            stat(e, &x, "embed")?;
12291        }
12292        for (il, layer) in self.layers.iter().enumerate() {
12293            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12294            if probe {
12295                stat(e, &x, &format!("L{il}"))?;
12296            }
12297        }
12298        let mut hn = e.zeros(t * n_embd)?;
12299        e.rms_norm(
12300            &x,
12301            self.output_norm.float_data(),
12302            &mut hn,
12303            n_embd,
12304            t,
12305            self.cfg.rms_eps,
12306        )?;
12307        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12308        let n_vocab = self.output.out_features();
12309        let logits = if last_only {
12310            let hv = e.view(&hn, t * n_embd);
12311            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12312            let mut hlast = e.zeros(n_embd)?;
12313            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12314            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12315            e.softcap(&mut ld, cap, n_vocab)?;
12316            self.gemma4_suppress(e, &mut ld, 1)?;
12317            e.dtoh(&ld)?
12318        } else {
12319            let mut ld = e.matmul(&self.output, &hn, t)?;
12320            e.softcap(&mut ld, cap, t * n_vocab)?;
12321            self.gemma4_suppress(e, &mut ld, t)?;
12322            e.dtoh(&ld)?
12323        };
12324        Ok(logits)
12325    }
12326
12327    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12328    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12329    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12330    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12331    pub(crate) fn gemma4_prime(
12332        &self,
12333        e: &Engine,
12334        tokens: &[u32],
12335        cache: &mut Cache,
12336        overlay: Option<&crate::vision::EmbedOverlay>,
12337    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12338        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12339        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12340        // whole worker process on this line. The worker now primes gemma4 monolithically and
12341        // routes continuation suffixes tokenwise; this is the per-request backstop.
12342        if cache.pos != 0 {
12343            return Err(
12344                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12345                        — prime the full prompt in one call or decode tokenwise"
12346                    .into(),
12347            );
12348        }
12349        let n_embd = self.cfg.n_embd as usize;
12350        let eps = self.cfg.rms_eps;
12351        let t = tokens.len();
12352        let pos: Vec<i32> = (0..t as i32).collect();
12353        let pos_d = e.htod_i32(&pos)?;
12354        let mut x = self.embed(e, tokens)?;
12355        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12356        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12357        // sqrt(n_embd) text scale — the reference scales token batches only
12358        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12359        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12360        // bidirectional within itself, causal+SWA everywhere else, matching the
12361        // reference's llama_set_causal_attn(false) image batch exactly.
12362        let island: Option<CudaSlice<i32>> = match overlay {
12363            Some(ov) => {
12364                let mut span_id = vec![-1i32; t];
12365                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12366                    if pos + n_rows > t {
12367                        return Err(format!(
12368                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12369                            pos + n_rows
12370                        )
12371                        .into());
12372                    }
12373                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12374                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12375                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12376                        *s = i as i32;
12377                    }
12378                }
12379                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12380                // keep the plain causal mask. Exists only so the decisive probe can show
12381                // the island mask itself changes the answer; never on in serving.
12382                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12383                    None
12384                } else {
12385                    Some(e.htod_i32(&span_id)?)
12386                }
12387            }
12388            None => None,
12389        };
12390        for (il, layer) in self.layers.iter().enumerate() {
12391            let mut h = e.zeros(t * n_embd)?;
12392            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12393            let Mixer::Full(fa) = &layer.mixer else {
12394                panic!("gemma4 layer not full-attn")
12395            };
12396            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12397            if trace {
12398                let v = e.dtoh(&h)?;
12399                let nan = v.iter().filter(|x| x.is_nan()).count();
12400                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12401            }
12402            let o =
12403                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12404            if trace {
12405                let v = e.dtoh(&o)?;
12406                let nan = v.iter().filter(|x| x.is_nan()).count();
12407                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12408            }
12409            let mut cur = e.zeros(t * n_embd)?;
12410            e.rms_norm(
12411                &o,
12412                layer.post_attn_norm.float_data(),
12413                &mut cur,
12414                n_embd,
12415                t,
12416                eps,
12417            )?;
12418            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12419            self.dflash_tap(e, cache, il, &x, t)?;
12420            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12421            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12422                let h = e.dtoh(&x)?;
12423                let nan = h.iter().filter(|v| v.is_nan()).count();
12424                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12425                eprintln!(
12426                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12427                    h.len()
12428                );
12429                if nan > 0 {
12430                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12431                }
12432            }
12433        }
12434        cache.pos += t;
12435        let hiddens = e.clone_dtod(&x)?;
12436        let xv = e.view(&x, t * n_embd);
12437        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12438        let mut h_seed = e.zeros(n_embd)?;
12439        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12440        let mut hn = e.uninit(n_embd)?;
12441        e.rms_norm(
12442            &h_seed,
12443            self.output_norm.float_data(),
12444            &mut hn,
12445            n_embd,
12446            1,
12447            eps,
12448        )?;
12449        let mut ld = e.matmul(&self.output, &hn, 1)?;
12450        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12451        e.softcap(&mut ld, cap, self.output.out_features())?;
12452        self.gemma4_suppress(e, &mut ld, 1)?;
12453        let logits = e.dtoh(&ld)?;
12454        Ok((logits, h_seed, hiddens))
12455    }
12456
12457    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12458    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12459    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12460    /// fused norm emits q8 directly — the f32 h never materializes).
12461    fn gemma4_decode_attn(
12462        &self,
12463        e: &Engine,
12464        fa: &crate::hybrid::FullAttnLayer,
12465        il: usize,
12466        hq: &CudaSlice<i8>,
12467        hdq: &CudaSlice<f32>,
12468        pos_d: &CudaSlice<i32>,
12469        cache: &mut Cache,
12470    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12471        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12472        let eps = self.cfg.rms_eps;
12473        let aux = self.gemma4_aux.as_ref().unwrap();
12474        let ones = aux.ones(e);
12475        #[cfg(debug_assertions)]
12476        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12477        let (hq, hdq) = (hq, hdq);
12478        let h0 = e.zeros(0)?;
12479        let h = &h0;
12480        let (q0, k0, v0) = if swa {
12481            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12482                Some(t3) => t3,
12483                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12484                // match — fuse the uniform (q,k) pair and take v as its own single.
12485                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12486                    Some((q0, k0)) => {
12487                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12488                        (q0, k0, v0)
12489                    }
12490                    None => (
12491                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12492                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12493                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12494                    ),
12495                },
12496            }
12497        } else {
12498            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12499                Some(p) => p,
12500                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12501                    Some(p) => p,
12502                    None => (
12503                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12504                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12505                    ),
12506                },
12507            };
12508            let v0 = e.clone_dtod(&k0)?;
12509            (q0, k0, v0)
12510        };
12511        let mut q = e.uninit(nh * hd)?;
12512        let mut k = e.uninit(nkv * hd)?;
12513        let mut v = e.uninit(nkv * hd)?;
12514        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12515        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12516        let ff = if swa {
12517            None
12518        } else {
12519            Some(
12520                aux.rope_freqs(e)
12521                    .expect("gemma4 global rope needs rope_freqs.weight"),
12522            )
12523        };
12524        #[cfg(debug_assertions)]
12525        if let Some(ff) = ff {
12526            crate::debug_assert_tensor_stream_device(
12527                ff,
12528                &e.stream(),
12529                "gemma4_decode_attn.rope_freqs",
12530            );
12531        }
12532        let kvl = cache.kv[il].as_mut().unwrap();
12533        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12534        if crate::Engine::qkv_append_on() {
12535            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12536            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12537            // twin of the dc fold — bit-identical bodies, one launch per layer.
12538            e.rms_norm_qkv_rope_append(
12539                &q0,
12540                &k0,
12541                &v0,
12542                fa.q_norm.float_data(),
12543                fa.k_norm.float_data(),
12544                ones,
12545                &mut q,
12546                &mut k,
12547                &mut v,
12548                hd,
12549                self.gemma4_rope_dims(il),
12550                nh,
12551                nkv,
12552                pos_d,
12553                nh,
12554                nkv,
12555                base,
12556                1.0,
12557                ff,
12558                eps,
12559                &mut kvl.k,
12560                &mut kvl.v,
12561                kvl.len,
12562                kvl.k_tok_bytes,
12563                kvl.v_tok_bytes,
12564                kv_fp8,
12565            )?;
12566        } else {
12567            e.rms_norm_qkv_rope(
12568                &q0,
12569                &k0,
12570                &v0,
12571                fa.q_norm.float_data(),
12572                fa.k_norm.float_data(),
12573                ones,
12574                &mut q,
12575                &mut k,
12576                &mut v,
12577                hd,
12578                self.gemma4_rope_dims(il),
12579                nh,
12580                nkv,
12581                pos_d,
12582                nh,
12583                nkv,
12584                base,
12585                1.0,
12586                ff,
12587                eps,
12588            )?;
12589            e.append_kv_quantized(
12590                &k,
12591                &v,
12592                &mut kvl.k,
12593                &mut kvl.v,
12594                kvl.len,
12595                kvl.kv_dim_k,
12596                kvl.kv_dim_v,
12597                kvl.k_tok_bytes,
12598                kvl.v_tok_bytes,
12599                kv_fp8,
12600            )?;
12601        }
12602        kvl.len += 1;
12603        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12604        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12605        // positional). Globals attend the full history.
12606        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12607        let mut attn = e.uninit(nh * hd)?;
12608        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12609        if !swa
12610            && hd == 512
12611            && kvl.len >= crate::fa512_min_tkv()
12612            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12613        {
12614            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12615            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12616            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12617            let base = kvl.len as i32;
12618            e.i32_set_k(&mut kvl.len_d, base)?;
12619            e.fa_decode_rows(
12620                &q,
12621                &kp,
12622                &vp,
12623                &mut attn,
12624                hd,
12625                nh,
12626                nkv,
12627                kvl.len - 1,
12628                1,
12629                scale,
12630                kvl.k_tok_bytes,
12631                kvl.v_tok_bytes,
12632                Some((&kvl.len_d, -1)),
12633                false,
12634                false,
12635                None,
12636            )?;
12637            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12638        }
12639        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12640        if swa
12641            && kvl.len > win
12642            && hd == 256
12643            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12644        {
12645            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12646            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12647            let base = kvl.len as i32;
12648            e.i32_set_k(&mut kvl.len_d, base)?;
12649            e.fa_decode_rows_w(
12650                &q,
12651                &kp,
12652                &vp,
12653                &mut attn,
12654                hd,
12655                nh,
12656                nkv,
12657                &kvl.len_d,
12658                -1,
12659                1,
12660                scale,
12661                win,
12662                kvl.k_tok_bytes,
12663                kvl.v_tok_bytes,
12664                None,
12665            )?;
12666            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12667        }
12668        let (off_tok, t_kv) = if swa && kvl.len > win {
12669            (kvl.len - win, win)
12670        } else {
12671            (0, kvl.len)
12672        };
12673        let k_view = e.view_u8_range(
12674            &kvl.k,
12675            off_tok * kvl.k_tok_bytes,
12676            (off_tok + t_kv) * kvl.k_tok_bytes,
12677        );
12678        let v_view = e.view_u8_range(
12679            &kvl.v,
12680            off_tok * kvl.v_tok_bytes,
12681            (off_tok + t_kv) * kvl.v_tok_bytes,
12682        );
12683        e.fa_decode_kvmod(
12684            &q,
12685            &k_view,
12686            &v_view,
12687            &mut attn,
12688            hd,
12689            nh,
12690            nkv,
12691            t_kv,
12692            scale,
12693            kvl.k_tok_bytes,
12694            kvl.v_tok_bytes,
12695            swa && crate::Engine::wkv_on(),
12696        )?;
12697        Ok(e.matmul(&fa.wo, &attn, 1)?)
12698    }
12699
12700    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
12701    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
12702    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
12703    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
12704    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
12705    /// in-graph; the driver gates).
12706    #[allow(clippy::too_many_arguments)]
12707    pub fn gemma4_decode_step_dc(
12708        &self,
12709        e: &Engine,
12710        token_d: &CudaSlice<u32>,
12711        pos_d: &mut CudaSlice<i32>,
12712        embd_gpu: &CudaSlice<u8>,
12713        embd_qt: i32,
12714        embd_rb: usize,
12715        cache: &mut Cache,
12716        n_vocab: usize,
12717        cap_bucket_max: Option<(usize, usize)>,
12718    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
12719        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
12720        self.gemma4_decode_step_dc_into(
12721            e,
12722            token_d,
12723            pos_d,
12724            embd_gpu,
12725            embd_qt,
12726            embd_rb,
12727            cache,
12728            n_vocab,
12729            cap_bucket_max,
12730            &mut tok_out,
12731        )?;
12732        Ok(tok_out)
12733    }
12734
12735    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
12736    /// every replay; pass `token_d` itself for the self-feeding graph loop).
12737    #[allow(clippy::too_many_arguments)]
12738    pub fn gemma4_decode_step_dc_into(
12739        &self,
12740        e: &Engine,
12741        token_d: &CudaSlice<u32>,
12742        pos_d: &mut CudaSlice<i32>,
12743        embd_gpu: &CudaSlice<u8>,
12744        embd_qt: i32,
12745        embd_rb: usize,
12746        cache: &mut Cache,
12747        n_vocab: usize,
12748        cap_bucket_max: Option<(usize, usize)>,
12749        tok_out: &mut CudaSlice<u32>,
12750    ) -> Result<(), Box<dyn std::error::Error>> {
12751        let n_embd = self.cfg.n_embd as usize;
12752        let eps = self.cfg.rms_eps;
12753        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
12754        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
12755        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12756        let n_layers = self.layers.len();
12757        for (il, layer) in self.layers.iter().enumerate() {
12758            let (hq, hdq) = match h_carry.take() {
12759                Some(p) => p,
12760                None => {
12761                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
12762                }
12763            };
12764            let Mixer::Full(fa) = &layer.mixer else {
12765                panic!("gemma4 layer {il} not full-attn")
12766            };
12767            let o =
12768                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
12769            let next_norm = if il + 1 < n_layers {
12770                Some(self.layers[il + 1].attn_norm.float_data())
12771            } else {
12772                None
12773            };
12774            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
12775            x = xn;
12776            h_carry = hn;
12777        }
12778        let mut hn = e.uninit(n_embd)?;
12779        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
12780        let mut logits = e.matmul(&self.output, &hn, 1)?;
12781        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
12782        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
12783        e.inc_seqlen(pos_d)?;
12784        if cap_bucket_max.is_none() {
12785            cache.pos += 1;
12786        }
12787        Ok(())
12788    }
12789
12790    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
12791    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
12792    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
12793    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
12794
12795    /// Build the slot set (call OUTSIDE any capture).
12796    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
12797        let n_embd = self.cfg.n_embd as usize;
12798        let n_vocab = self.output.out_features();
12799        let n_layers = self.layers.len();
12800        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
12801        for il in 0..n_layers {
12802            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
12803            qmax = qmax.max(nh * hd);
12804            kvmax = kvmax.max(nkv * hd);
12805            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
12806                ffmax = ffmax.max(ffn_gate.out_features());
12807            }
12808        }
12809        Ok(G4DcSlots {
12810            x: e.uninit(n_embd)?,
12811            xn: e.uninit(n_embd)?,
12812            cur: e.uninit(n_embd)?,
12813            hq: e.alloc_i8_uninit(n_embd)?,
12814            hd_: e.uninit(n_embd / 32)?,
12815            q0: e.uninit(qmax)?,
12816            k0: e.uninit(kvmax)?,
12817            v0: e.uninit(kvmax)?,
12818            q: e.uninit(qmax)?,
12819            k: e.uninit(kvmax)?,
12820            v: e.uninit(kvmax)?,
12821            attn: e.uninit(qmax)?,
12822            o: e.uninit(n_embd)?,
12823            attn_out: e.uninit(n_embd)?,
12824            zsh: e.uninit(n_embd)?,
12825            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
12826            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
12827            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
12828            zd: e.uninit(n_embd.max(qmax) / 32)?,
12829            gate: e.uninit(ffmax)?,
12830            up: e.uninit(ffmax)?,
12831            act: e.uninit(ffmax)?,
12832            actq: e.alloc_i8_uninit(ffmax)?,
12833            actd: e.uninit(ffmax / 32)?,
12834            f0: e.uninit(n_embd)?,
12835            sn: e.uninit(n_embd)?,
12836            hn: e.uninit(n_embd)?,
12837            logits: e.uninit(n_vocab)?,
12838        })
12839    }
12840
12841    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
12842    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
12843    fn g4_matvec_m1_into(
12844        &self,
12845        e: &Engine,
12846        w: &crate::model::GpuTensor,
12847        aq: &CudaSlice<i8>,
12848        ad: &CudaSlice<f32>,
12849        y: &mut CudaSlice<f32>,
12850    ) -> Result<(), Box<dyn std::error::Error>> {
12851        use crate::model::GpuTensor;
12852        let (bytes, qtype, row_bytes, scale, rp) = match w {
12853            GpuTensor::Quant {
12854                bytes,
12855                qtype,
12856                row_bytes,
12857                scale,
12858                rp,
12859                ..
12860            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12861            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
12862        };
12863        let (mbytes, mrp) = match w {
12864            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12865            _ => (bytes, rp),
12866        };
12867        e.qmatvec_mmvq_into(
12868            mbytes,
12869            aq,
12870            ad,
12871            1,
12872            w.in_features(),
12873            w.out_features(),
12874            qtype,
12875            row_bytes,
12876            scale,
12877            mrp,
12878            y,
12879        )
12880    }
12881
12882    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
12883    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
12884    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
12885    #[allow(clippy::too_many_arguments)]
12886    pub fn gemma4_decode_step_dc_slotted(
12887        &self,
12888        e: &Engine,
12889        token_d: &CudaSlice<u32>,
12890        pos_d: &mut CudaSlice<i32>,
12891        embd_gpu: &CudaSlice<u8>,
12892        embd_qt: i32,
12893        embd_rb: usize,
12894        cache: &mut Cache,
12895        n_vocab: usize,
12896        cap_bucket_max: Option<(usize, usize)>,
12897        sl: &mut G4DcSlots,
12898        tok_out: &mut CudaSlice<u32>,
12899        ring: Option<(&mut CudaSlice<u32>, usize)>,
12900    ) -> Result<(), Box<dyn std::error::Error>> {
12901        let n_embd = self.cfg.n_embd as usize;
12902        let eps = self.cfg.rms_eps;
12903        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
12904        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
12905        let n_layers = self.layers.len();
12906        let mut has_carry = false;
12907        for il in 0..n_layers {
12908            if !has_carry {
12909                e.rms_norm_q8_1_into(
12910                    &sl.x,
12911                    self.layers[il].attn_norm.float_data(),
12912                    n_embd,
12913                    1,
12914                    eps,
12915                    &mut sl.hq,
12916                    &mut sl.hd_,
12917                )?;
12918            }
12919            has_carry = true;
12920            let layer = &self.layers[il];
12921            let Mixer::Full(fa) = &layer.mixer else {
12922                panic!("gemma4 layer {il} not full-attn")
12923            };
12924            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
12925            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
12926            // the standalone norm only survives on the unfused seam arm.
12927            if !Engine::g4_pnfold_on() {
12928                e.rms_norm(
12929                    &sl.o,
12930                    layer.post_attn_norm.float_data(),
12931                    &mut sl.cur,
12932                    n_embd,
12933                    1,
12934                    eps,
12935                )?;
12936            }
12937            let next_norm = if il + 1 < n_layers {
12938                Some(self.layers[il + 1].attn_norm.float_data())
12939            } else {
12940                None
12941            };
12942            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
12943            std::mem::swap(&mut sl.x, &mut sl.xn);
12944        }
12945        e.rms_norm(
12946            &sl.x,
12947            self.output_norm.float_data(),
12948            &mut sl.hn,
12949            n_embd,
12950            1,
12951            eps,
12952        )?;
12953        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
12954        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
12955        {
12956            let (zq, zd) = (&sl.zq, &sl.zd);
12957            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
12958            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
12959            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
12960        }
12961        self.gemma4_suppress(e, &mut sl.logits, 1)?;
12962        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
12963        if let Some((ring, base)) = ring {
12964            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
12965            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
12966            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
12967            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
12968        }
12969        e.inc_seqlen(pos_d)?;
12970        if cap_bucket_max.is_none() {
12971            cache.pos += 1;
12972        }
12973        Ok(())
12974    }
12975
12976    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
12977    #[allow(clippy::too_many_arguments)]
12978    fn gemma4_decode_attn_dc_slotted(
12979        &self,
12980        e: &Engine,
12981        fa: &crate::hybrid::FullAttnLayer,
12982        il: usize,
12983        pos_d: &CudaSlice<i32>,
12984        cache: &mut Cache,
12985        cap_bucket_max: Option<(usize, usize)>,
12986        sl: &mut G4DcSlots,
12987    ) -> Result<(), Box<dyn std::error::Error>> {
12988        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12989        let eps = self.cfg.rms_eps;
12990        let aux = self.gemma4_aux.as_ref().unwrap();
12991        let ones = aux.ones(e);
12992        #[cfg(debug_assertions)]
12993        crate::debug_assert_tensor_stream_device(
12994            ones,
12995            &e.stream(),
12996            "gemma4_decode_attn_dc_slotted.ones",
12997        );
12998        {
12999            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
13000            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
13001            if swa {
13002                if !e.matmul_q4_fused3_into(
13003                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
13004                )? {
13005                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
13006                    // (q,k) pair, v through the generic m1 slot matvec — the same two
13007                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
13008                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13009                    {
13010                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
13011                    } else {
13012                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
13013                    }
13014                }
13015            } else {
13016                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13017                    && !e
13018                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13019                {
13020                    return Err("slotted step: fused2 unavailable".into());
13021                }
13022                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
13023                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
13024            }
13025        }
13026        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
13027        // kernel-for-kernel (graph stream-identity gate).
13028        let ff = if swa {
13029            None
13030        } else {
13031            Some(
13032                aux.rope_freqs(e)
13033                    .expect("gemma4 global rope needs rope_freqs.weight"),
13034            )
13035        };
13036        #[cfg(debug_assertions)]
13037        if let Some(ff) = ff {
13038            crate::debug_assert_tensor_stream_device(
13039                ff,
13040                &e.stream(),
13041                "gemma4_decode_attn_dc_slotted.rope_freqs",
13042            );
13043        }
13044        let kvl = cache.kv[il].as_mut().unwrap();
13045        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13046        if crate::Engine::qkv_append_on() {
13047            // append fold (2026-07-23): mirrors dc_into.
13048            e.rms_norm_qkv_rope_append_dc(
13049                &sl.q0,
13050                &sl.k0,
13051                &sl.v0,
13052                fa.q_norm.float_data(),
13053                fa.k_norm.float_data(),
13054                ones,
13055                &mut sl.q,
13056                &mut sl.k,
13057                &mut sl.v,
13058                hd,
13059                self.gemma4_rope_dims(il),
13060                nh,
13061                nkv,
13062                pos_d,
13063                nh,
13064                nkv,
13065                base,
13066                1.0,
13067                ff,
13068                eps,
13069                &mut kvl.k,
13070                &mut kvl.v,
13071                &kvl.len_d,
13072                kvl.k_tok_bytes,
13073                kvl.v_tok_bytes,
13074                kv_fp8,
13075            )?;
13076        } else {
13077            e.rms_norm_qkv_rope(
13078                &sl.q0,
13079                &sl.k0,
13080                &sl.v0,
13081                fa.q_norm.float_data(),
13082                fa.k_norm.float_data(),
13083                ones,
13084                &mut sl.q,
13085                &mut sl.k,
13086                &mut sl.v,
13087                hd,
13088                self.gemma4_rope_dims(il),
13089                nh,
13090                nkv,
13091                pos_d,
13092                nh,
13093                nkv,
13094                base,
13095                1.0,
13096                ff,
13097                eps,
13098            )?;
13099            e.append_kv_quantized_dc(
13100                &sl.k,
13101                &sl.v,
13102                &mut kvl.k,
13103                &mut kvl.v,
13104                &kvl.len_d,
13105                kvl.kv_dim_k,
13106                kvl.kv_dim_v,
13107                kvl.k_tok_bytes,
13108                kvl.v_tok_bytes,
13109                kv_fp8,
13110            )?;
13111        }
13112        e.inc_seqlen(&mut kvl.len_d)?;
13113        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13114        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13115        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13116        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13117        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13118        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13119        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13120        // the dc_into arm branch-for-branch (stream gate).
13121        let mut fa_q8 = false;
13122        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13123            e.fa_decode_rows(
13124                &sl.q,
13125                &k_view,
13126                &v_view,
13127                &mut sl.attn,
13128                hd,
13129                nh,
13130                nkv,
13131                b_glob - 1,
13132                1,
13133                scale,
13134                kvl.k_tok_bytes,
13135                kvl.v_tok_bytes,
13136                Some((&kvl.len_d, -1)),
13137                false,
13138                false,
13139                Some((&mut sl.zq, &mut sl.zd)),
13140            )?;
13141            fa_q8 = true;
13142        } else if swa && b_swa > win && hd == 256 && rows_on {
13143            e.fa_decode_rows_w(
13144                &sl.q,
13145                &k_view,
13146                &v_view,
13147                &mut sl.attn,
13148                hd,
13149                nh,
13150                nkv,
13151                &kvl.len_d,
13152                -1,
13153                1,
13154                scale,
13155                win,
13156                kvl.k_tok_bytes,
13157                kvl.v_tok_bytes,
13158                Some((&mut sl.zq, &mut sl.zd)),
13159            )?;
13160            fa_q8 = true;
13161        } else {
13162            let b = if swa { b_swa } else { b_glob };
13163            e.fa_decode_dc(
13164                &sl.q,
13165                &k_view,
13166                &v_view,
13167                &mut sl.attn,
13168                hd,
13169                nh,
13170                nkv,
13171                &kvl.len_d,
13172                b,
13173                scale,
13174                kvl.k_tok_bytes,
13175                kvl.v_tok_bytes,
13176                swa && crate::Engine::wkv_on(),
13177            )?;
13178        }
13179        if !fa_q8 {
13180            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13181            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13182        }
13183        {
13184            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13185            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13186            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13187        }
13188        Ok(())
13189    }
13190
13191    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13192    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13193    fn gemma4_layer_tail_slotted(
13194        &self,
13195        e: &Engine,
13196        layer: &crate::hybrid::HybridLayer,
13197        next_norm: Option<&CudaSlice<f32>>,
13198        sl: &mut G4DcSlots,
13199    ) -> Result<(), Box<dyn std::error::Error>> {
13200        let n_embd = self.cfg.n_embd as usize;
13201        let eps = self.cfg.rms_eps;
13202        let bits = layer.gemma4.as_ref().unwrap();
13203        let crate::hybrid::Ffn::Dense {
13204            ffn_gate,
13205            ffn_up,
13206            ffn_down,
13207        } = &layer.ffn
13208        else {
13209            return Err("slotted tail: dense ffn only".into());
13210        };
13211        let pnfold = Engine::g4_pnfold_on();
13212        if pnfold {
13213            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13214            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13215            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13216            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13217            e.rms_pre_add_rms_norm_q8z_into(
13218                or,
13219                layer.post_attn_norm.float_data(),
13220                xr,
13221                bits.ffn_norm.float_data(),
13222                &mut sl.attn_out,
13223                &mut sl.zsh,
13224                n_embd,
13225                1,
13226                eps,
13227                &mut sl.zq,
13228                &mut sl.zd,
13229            )?;
13230        } else {
13231            e.add_rms_norm(
13232                &sl.cur,
13233                &sl.x,
13234                bits.ffn_norm.float_data(),
13235                &mut sl.attn_out,
13236                &mut sl.zsh,
13237                n_embd,
13238                1,
13239                eps,
13240            )?;
13241        }
13242        let n_ff = ffn_gate.out_features();
13243        if !pnfold {
13244            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13245            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13246        }
13247        {
13248            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13249            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13250            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13251                && !e.matmul_nvfp4_fused2_into(
13252                    ffn_gate,
13253                    ffn_up,
13254                    zq,
13255                    zd,
13256                    &mut sl.gate,
13257                    &mut sl.up,
13258                )?
13259            {
13260                return Err("slotted tail: ffn fused2 unavailable".into());
13261            }
13262        }
13263        debug_assert!(e.uses_q8_1_fast(ffn_down));
13264        {
13265            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13266            let upv = e.view(upr, n_ff);
13267            let up_all = upv.slice(0..n_ff);
13268            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13269            e.gelu_tanh_mul_q8_1_into(
13270                gr,
13271                &up_all,
13272                &mut sl.act,
13273                n_ff,
13274                1,
13275                &mut sl.actq,
13276                &mut sl.actd,
13277            )?;
13278        }
13279        {
13280            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13281            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13282            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13283        }
13284        if pnfold {
13285            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13286            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13287            if let Some(w) = next_norm {
13288                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13289                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13290                e.rms_pre_add_scale_rms_norm_q8_1_into(
13291                    f0r,
13292                    bits.post_ffw_norm.float_data(),
13293                    aor,
13294                    bits.layer_scale,
13295                    w,
13296                    &mut sl.xn,
13297                    n_embd,
13298                    1,
13299                    eps,
13300                    &mut sl.hq,
13301                    &mut sl.hd_,
13302                )?;
13303                return Ok(());
13304            }
13305        }
13306        e.rms_norm(
13307            &sl.f0,
13308            bits.post_ffw_norm.float_data(),
13309            &mut sl.sn,
13310            n_embd,
13311            1,
13312            eps,
13313        )?;
13314        match next_norm {
13315            Some(w) => {
13316                e.add_scale_rms_norm_q8_1_into(
13317                    &sl.sn,
13318                    &sl.attn_out,
13319                    bits.layer_scale,
13320                    w,
13321                    &mut sl.xn,
13322                    n_embd,
13323                    1,
13324                    eps,
13325                    &mut sl.hq,
13326                    &mut sl.hd_,
13327                )?;
13328            }
13329            None => {
13330                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13331            }
13332        }
13333        Ok(())
13334    }
13335
13336    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13337    #[allow(clippy::too_many_arguments)]
13338    fn gemma4_decode_attn_dc(
13339        &self,
13340        e: &Engine,
13341        fa: &crate::hybrid::FullAttnLayer,
13342        il: usize,
13343        hq: &CudaSlice<i8>,
13344        hdq: &CudaSlice<f32>,
13345        pos_d: &CudaSlice<i32>,
13346        cache: &mut Cache,
13347        cap_bucket_max: Option<(usize, usize)>,
13348    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13349        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13350        let eps = self.cfg.rms_eps;
13351        let aux = self.gemma4_aux.as_ref().unwrap();
13352        let ones = aux.ones(e);
13353        #[cfg(debug_assertions)]
13354        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13355        let (q0, k0, v0) = if swa {
13356            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13357                Some(t3) => t3,
13358                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13359                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13360                    Some((q0, k0)) => {
13361                        let h0 = e.zeros(0)?;
13362                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13363                        (q0, k0, v0)
13364                    }
13365                    None => {
13366                        let h0 = e.zeros(0)?;
13367                        (
13368                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13369                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13370                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13371                        )
13372                    }
13373                },
13374            }
13375        } else {
13376            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13377                Some(p) => p,
13378                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13379                    Some(p) => p,
13380                    None => {
13381                        let h0 = e.zeros(0)?;
13382                        (
13383                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13384                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13385                        )
13386                    }
13387                },
13388            };
13389            let v0 = e.clone_dtod(&k0)?;
13390            (q0, k0, v0)
13391        };
13392        let mut q = e.uninit(nh * hd)?;
13393        let mut k = e.uninit(nkv * hd)?;
13394        let mut v = e.uninit(nkv * hd)?;
13395        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13396        let ff = if swa {
13397            None
13398        } else {
13399            Some(
13400                aux.rope_freqs(e)
13401                    .expect("gemma4 global rope needs rope_freqs.weight"),
13402            )
13403        };
13404        #[cfg(debug_assertions)]
13405        if let Some(ff) = ff {
13406            crate::debug_assert_tensor_stream_device(
13407                ff,
13408                &e.stream(),
13409                "gemma4_decode_attn_dc.rope_freqs",
13410            );
13411        }
13412        let kvl = cache.kv[il].as_mut().unwrap();
13413        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13414        if crate::Engine::qkv_append_on() {
13415            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13416            e.rms_norm_qkv_rope_append_dc(
13417                &q0,
13418                &k0,
13419                &v0,
13420                fa.q_norm.float_data(),
13421                fa.k_norm.float_data(),
13422                ones,
13423                &mut q,
13424                &mut k,
13425                &mut v,
13426                hd,
13427                self.gemma4_rope_dims(il),
13428                nh,
13429                nkv,
13430                pos_d,
13431                nh,
13432                nkv,
13433                base,
13434                1.0,
13435                ff,
13436                eps,
13437                &mut kvl.k,
13438                &mut kvl.v,
13439                &kvl.len_d,
13440                kvl.k_tok_bytes,
13441                kvl.v_tok_bytes,
13442                kv_fp8,
13443            )?;
13444        } else {
13445            e.rms_norm_qkv_rope(
13446                &q0,
13447                &k0,
13448                &v0,
13449                fa.q_norm.float_data(),
13450                fa.k_norm.float_data(),
13451                ones,
13452                &mut q,
13453                &mut k,
13454                &mut v,
13455                hd,
13456                self.gemma4_rope_dims(il),
13457                nh,
13458                nkv,
13459                pos_d,
13460                nh,
13461                nkv,
13462                base,
13463                1.0,
13464                ff,
13465                eps,
13466            )?;
13467            e.append_kv_quantized_dc(
13468                &k,
13469                &v,
13470                &mut kvl.k,
13471                &mut kvl.v,
13472                &kvl.len_d,
13473                kvl.kv_dim_k,
13474                kvl.kv_dim_v,
13475                kvl.k_tok_bytes,
13476                kvl.v_tok_bytes,
13477                kv_fp8,
13478            )?;
13479        }
13480        e.inc_seqlen(&mut kvl.len_d)?;
13481        let mut attn = e.uninit(nh * hd)?;
13482        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13483        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13484        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13485        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13486        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13487        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13488        // (gemma4_e4b_attn, +0.65% valid window).
13489        match cap_bucket_max {
13490            None => {
13491                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13492                // decode (SWA layers attend the last `sliding_window` keys); the device
13493                // counters carry only the append slot + the graph seam.
13494                kvl.len += 1;
13495                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13496                if !swa
13497                    && hd == 512
13498                    && kvl.len >= crate::fa512_min_tkv()
13499                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13500                {
13501                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13502                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13503                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13504                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13505                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13506                    e.fa_decode_rows(
13507                        &q,
13508                        &kp,
13509                        &vp,
13510                        &mut attn,
13511                        hd,
13512                        nh,
13513                        nkv,
13514                        kvl.len - 1,
13515                        1,
13516                        scale,
13517                        kvl.k_tok_bytes,
13518                        kvl.v_tok_bytes,
13519                        Some((&kvl.len_d, -1)),
13520                        false,
13521                        false,
13522                        Some((&mut aq8, &mut ad8)),
13523                    )?;
13524                    fa_q8 = Some((aq8, ad8));
13525                } else if swa
13526                    && kvl.len > win
13527                    && hd == 256
13528                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13529                {
13530                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13531                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13532                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13533                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13534                    e.fa_decode_rows_w(
13535                        &q,
13536                        &kp,
13537                        &vp,
13538                        &mut attn,
13539                        hd,
13540                        nh,
13541                        nkv,
13542                        &kvl.len_d,
13543                        -1,
13544                        1,
13545                        scale,
13546                        win,
13547                        kvl.k_tok_bytes,
13548                        kvl.v_tok_bytes,
13549                        Some((&mut aq8, &mut ad8)),
13550                    )?;
13551                    fa_q8 = Some((aq8, ad8));
13552                } else {
13553                    let (off_tok, t_kv) = if swa && kvl.len > win {
13554                        (kvl.len - win, win)
13555                    } else {
13556                        (0, kvl.len)
13557                    };
13558                    let k_view = e.view_u8_range(
13559                        &kvl.k,
13560                        off_tok * kvl.k_tok_bytes,
13561                        (off_tok + t_kv) * kvl.k_tok_bytes,
13562                    );
13563                    let v_view = e.view_u8_range(
13564                        &kvl.v,
13565                        off_tok * kvl.v_tok_bytes,
13566                        (off_tok + t_kv) * kvl.v_tok_bytes,
13567                    );
13568                    e.fa_decode_kvmod(
13569                        &q,
13570                        &k_view,
13571                        &v_view,
13572                        &mut attn,
13573                        hd,
13574                        nh,
13575                        nkv,
13576                        t_kv,
13577                        scale,
13578                        kvl.k_tok_bytes,
13579                        kvl.v_tok_bytes,
13580                        swa && crate::Engine::wkv_on(),
13581                    )?;
13582                }
13583            }
13584            Some((b_swa, b_glob)) => {
13585                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13586                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13587                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13588                // the RUNG max for the rows family (kernels derive per-replay splits from
13589                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13590                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13591                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13592                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13593                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13594                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13595                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13596                    e.fa_decode_rows(
13597                        &q,
13598                        &k_view,
13599                        &v_view,
13600                        &mut attn,
13601                        hd,
13602                        nh,
13603                        nkv,
13604                        b_glob - 1,
13605                        1,
13606                        scale,
13607                        kvl.k_tok_bytes,
13608                        kvl.v_tok_bytes,
13609                        Some((&kvl.len_d, -1)),
13610                        false,
13611                        false,
13612                        Some((&mut aq8, &mut ad8)),
13613                    )?;
13614                    fa_q8 = Some((aq8, ad8));
13615                } else if swa && b_swa > win && hd == 256 && rows_on {
13616                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13617                    e.fa_decode_rows_w(
13618                        &q,
13619                        &k_view,
13620                        &v_view,
13621                        &mut attn,
13622                        hd,
13623                        nh,
13624                        nkv,
13625                        &kvl.len_d,
13626                        -1,
13627                        1,
13628                        scale,
13629                        win,
13630                        kvl.k_tok_bytes,
13631                        kvl.v_tok_bytes,
13632                        Some((&mut aq8, &mut ad8)),
13633                    )?;
13634                    fa_q8 = Some((aq8, ad8));
13635                } else {
13636                    let b = if swa { b_swa } else { b_glob };
13637                    e.fa_decode_dc(
13638                        &q,
13639                        &k_view,
13640                        &v_view,
13641                        &mut attn,
13642                        hd,
13643                        nh,
13644                        nkv,
13645                        &kvl.len_d,
13646                        b,
13647                        scale,
13648                        kvl.k_tok_bytes,
13649                        kvl.v_tok_bytes,
13650                        swa && crate::Engine::wkv_on(),
13651                    )?;
13652                }
13653            }
13654        }
13655        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
13656        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
13657        if let Some((aq8, ad8)) = fa_q8 {
13658            let mut y = e.uninit(fa.wo.out_features())?;
13659            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
13660            return Ok(y);
13661        }
13662        Ok(e.matmul(&fa.wo, &attn, 1)?)
13663    }
13664
13665    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
13666    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
13667    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
13668    /// views in-graph); caller gates and falls back to the dc-eager loop.
13669    pub fn gemma4_generate_graph(
13670        &self,
13671        e: &Engine,
13672        prompt_pos: usize,
13673        first_token: u32,
13674        cache: &mut Cache,
13675        max_new: usize,
13676        eos: &[u32],
13677        mut on_token: impl FnMut(u32) -> bool,
13678    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
13679        if self.is_gemma4_e4b() {
13680            return Err(
13681                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
13682                    .into(),
13683            );
13684        }
13685        use crate::decode::StopReason;
13686        let n_vocab = self.output.out_features();
13687        let n_embd = self.cfg.n_embd as usize;
13688        let embd_gpu = self
13689            .embd_gpu
13690            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13691        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13692        for kvl in cache.kv.iter_mut().flatten() {
13693            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
13694        }
13695        let mut token_d = e.stream().clone_htod(&[first_token])?;
13696        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
13697        let g4 = self.cfg.gemma4.as_ref().unwrap();
13698        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
13699        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
13700        let nkv_s = g4
13701            .head_count_kv
13702            .iter()
13703            .zip(g4.swa_pattern.iter())
13704            .find(|p| *p.1)
13705            .map(|p| *p.0 as usize)
13706            .unwrap_or(8);
13707        let nkv_g = g4
13708            .head_count_kv
13709            .iter()
13710            .zip(g4.swa_pattern.iter())
13711            .find(|p| !*p.1)
13712            .map(|p| *p.0 as usize)
13713            .unwrap_or(2);
13714        let mut graphs: std::collections::HashMap<
13715            ((bool, usize), (bool, usize), bool, bool),
13716            (
13717                cudarc::driver::CudaGraph,
13718                Vec<Box<dyn std::any::Any + Send>>,
13719            ),
13720        > = Default::default();
13721        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
13722        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
13723        let mut slots = self.g4_dc_slots(e)?;
13724        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
13725        // baked at the door entry (the modulo keeps every capture valid indefinitely).
13726        const RING: usize = 64;
13727        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
13728        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
13729        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
13730        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
13731        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
13732        const DRAIN: usize = 1;
13733        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
13734        let ring_base = prompt_pos;
13735        let mut out = Vec::with_capacity(max_new);
13736        let mut reason = StopReason::MaxNew;
13737        let mut next = first_token;
13738        let mut captures = 0usize;
13739        for _ in 0..max_new {
13740            out.push(next);
13741            if eos.contains(&next) {
13742                reason = StopReason::Eos;
13743                break;
13744            }
13745            if !on_token(next) {
13746                reason = StopReason::Callback;
13747                break;
13748            }
13749            let t_kv = cache.pos + 1;
13750            // Bucket key per ARM (graph arc step 3):
13751            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
13752            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
13753            //    the component collapses to a single marker).
13754            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
13755            //    at/above it — the kernel derives splits from len_d per replay, so buckets
13756            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
13757            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13758            let f512 = crate::fa512_min_tkv();
13759            let key_s = if t_kv > win {
13760                (true, usize::MAX)
13761            } else {
13762                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
13763            };
13764            let (key_g, rung_end) = if t_kv >= f512 {
13765                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
13766                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
13767                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
13768                ((true, end), end)
13769            } else {
13770                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
13771            };
13772            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
13773            if !graphs.contains_key(&key) {
13774                let bucket_max = (t_kv, rung_end);
13775                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
13776                let snap = cache.snapshot(e)?;
13777                let pos_save = e.dtoh_i32_one(&pos_d)?;
13778                let len_save: Vec<Option<i32>> = cache
13779                    .kv
13780                    .iter()
13781                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
13782                    .collect();
13783                let tok_save = e.dtoh_u32_one(&token_d)?;
13784                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
13785                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
13786                // regression class, and this door's measured -8.8%. The keeper pins warmup
13787                // transients so the captured graph holds kernel nodes only.
13788                let graph = {
13789                    let tok_ref = &mut token_d;
13790                    let pos_ref = &mut pos_d;
13791                    let cache_ref = &mut *cache;
13792                    let slots_ref = &mut slots;
13793                    let ring_ref = &mut ring;
13794                    e.capture_graph_retained_flags(
13795                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
13796                        |e| {
13797                        // self-feeding: the argmax writes token_d itself.
13798                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
13799                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
13800                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
13801                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
13802                                                           cache_ref, n_vocab, Some(bucket_max),
13803                                                           sl, tok_ref, Some((rg, ring_base)))
13804                    })?
13805                };
13806                cache.rollback(e, &snap, 0)?;
13807                e.set_i32_one(&mut pos_d, pos_save)?;
13808                for (il, ls) in len_save.iter().enumerate() {
13809                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
13810                        e.set_i32_one(&mut kvl.len_d, *v)?;
13811                    }
13812                }
13813                e.set_u32_one(&mut token_d, tok_save)?;
13814                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
13815                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
13816                        eprintln!("[graph-census] {c:?}");
13817                    }
13818                }
13819                graphs.insert(key, graph);
13820                captures += 1;
13821            }
13822            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
13823            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
13824            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
13825            // the budget; capture warmups already emitted their tokens through the ring.
13826            let mut chunk = 1usize;
13827            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
13828                .ok()
13829                .and_then(|v| v.parse().ok())
13830                .unwrap_or(DRAIN);
13831            while chunk < drain_cap && out.len() + chunk < max_new {
13832                let t_next = cache.pos + 1 + chunk;
13833                let key_s2 = if t_next > win {
13834                    (true, usize::MAX)
13835                } else {
13836                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
13837                };
13838                let key_g2 = if t_next >= f512 {
13839                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
13840                } else {
13841                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
13842                };
13843                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
13844                    break;
13845                }
13846                chunk += 1;
13847            }
13848            let g = &graphs.get(&key).unwrap().0;
13849            for _ in 0..chunk {
13850                g.launch()?;
13851            }
13852            e.stream().synchronize()?;
13853            let ringh = e.dtoh_u32(&ring)?;
13854            for j in 0..chunk {
13855                let pos_j = cache.pos + j;
13856                let tok_j = ringh[(pos_j - ring_base) % RING];
13857                cache.pos += 0; // advanced below in one shot
13858                if j + 1 == chunk {
13859                    next = tok_j;
13860                } else {
13861                    out.push(tok_j);
13862                    if eos.contains(&tok_j) || !on_token(tok_j) {
13863                        reason = if eos.contains(&tok_j) {
13864                            StopReason::Eos
13865                        } else {
13866                            StopReason::Callback
13867                        };
13868                        // roll device/host state back to the stop point.
13869                        let keep = cache.pos + j + 1;
13870                        e.set_i32_one(&mut pos_d, keep as i32)?;
13871                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13872                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
13873                            kvl.len = keep;
13874                        }
13875                        cache.pos = keep;
13876                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13877                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13878                        }
13879                        return Ok((out, reason));
13880                    }
13881                }
13882            }
13883            cache.pos += chunk;
13884            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13885                kvl.len += chunk;
13886            }
13887        }
13888        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13889            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13890        }
13891        Ok((out, reason))
13892    }
13893
13894    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
13895    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
13896    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
13897    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
13898    /// logits (host) + advances cache.pos by t.
13899    pub(crate) fn gemma4_decode_step_t(
13900        &self,
13901        e: &Engine,
13902        tokens: &[u32],
13903        pos0: usize,
13904        cache: &mut Cache,
13905    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13906        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
13907    }
13908
13909    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
13910    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
13911    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
13912    pub(crate) fn gemma4_decode_step_t_am(
13913        &self,
13914        e: &Engine,
13915        tokens: &[u32],
13916        pos0: usize,
13917        cache: &mut Cache,
13918    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13919        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13920        let t = tokens.len();
13921        let n_vocab = self.output.out_features();
13922        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
13923        for i in 0..t {
13924            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
13925        }
13926        Ok((e.dtoh_u32(&toks)?, hn))
13927    }
13928
13929    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
13930    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
13931    pub(crate) fn gemma4_decode_step_t_am_dev(
13932        &self,
13933        e: &Engine,
13934        tok_d: &CudaSlice<u32>,
13935        t: usize,
13936        pos0: usize,
13937        cache: &mut Cache,
13938    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13939        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
13940        let n_vocab = self.output.out_features();
13941        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13942        for i in 0..t {
13943            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13944        }
13945        Ok((vam, hn))
13946    }
13947
13948    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
13949    /// llama's h_nextn convention).
13950    pub(crate) fn gemma4_decode_step_t_h(
13951        &self,
13952        e: &Engine,
13953        tokens: &[u32],
13954        pos0: usize,
13955        cache: &mut Cache,
13956    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13957        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13958        let t = tokens.len();
13959        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
13960        e.softcap(&mut ld, cap, t * self.output.out_features())?;
13961        Ok((e.dtoh(&ld)?, hn))
13962    }
13963
13964    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
13965    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
13966    pub(crate) fn verify_stream_scratch(
13967        &self,
13968        e: &Engine,
13969        cap: usize,
13970    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
13971        Ok(VerifyStreamScratch {
13972            pos_d: e.htod_i32(&vec![0i32; cap])?,
13973            row_ctrs: (0..cap)
13974                .map(|_| e.htod_i32(&[0]))
13975                .collect::<Result<_, _>>()?,
13976        })
13977    }
13978
13979    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
13980    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
13981    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
13982    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
13983    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
13984    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
13985    /// sync, exactly the turnaround the burst exists to remove.
13986    pub(crate) fn gemma4_verify_t_am_stream(
13987        &self,
13988        e: &Engine,
13989        tok_d: &CudaSlice<u32>,
13990        t: usize,
13991        ctr: &CudaSlice<i32>,
13992        hint: usize,
13993        cache: &mut Cache,
13994        scr: &mut VerifyStreamScratch,
13995    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13996        let n_embd = self.cfg.n_embd as usize;
13997        let eps = self.cfg.rms_eps;
13998        assert!(t <= scr.row_ctrs.len() && t <= 64);
13999        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
14000        for i in 0..t {
14001            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
14002        }
14003        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
14004        let embd_gpu = self
14005            .embd_gpu
14006            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14007        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14008        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
14009        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14010        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14011        let n_layers = self.layers.len();
14012        for (il, layer) in self.layers.iter().enumerate() {
14013            let (hq, hdq) = match h_carry.take() {
14014                Some(p) => p,
14015                None => {
14016                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14017                }
14018            };
14019            let Mixer::Full(fa) = &layer.mixer else {
14020                panic!("gemma4 layer {il} not full-attn")
14021            };
14022            let o = self
14023                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
14024            let next_norm = if il + 1 < n_layers {
14025                Some(self.layers[il + 1].attn_norm.float_data())
14026            } else {
14027                None
14028            };
14029            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14030            x = xn;
14031            h_carry = hn;
14032            self.dflash_tap(e, cache, il, &x, t)?;
14033        }
14034        let mut hn = e.uninit(t * n_embd)?;
14035        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14036        let ld = e.matmul(&self.output, &hn, t)?;
14037        let n_vocab = self.output.out_features();
14038        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14039        for i in 0..t {
14040            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14041        }
14042        Ok((vam, hn))
14043    }
14044
14045    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
14046    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
14047    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
14048    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
14049    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
14050    /// kernel later if it shows in the profile).
14051    pub(crate) fn dflash_tap(
14052        &self,
14053        e: &Engine,
14054        cache: &mut Cache,
14055        il: usize,
14056        x: &CudaSlice<f32>,
14057        t: usize,
14058    ) -> Result<(), Box<dyn std::error::Error>> {
14059        let Some(taps) = cache.dflash_taps.as_mut() else {
14060            return Ok(());
14061        };
14062        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14063            return Ok(());
14064        };
14065        let h = taps.hidden;
14066        let n_taps = taps.layer_ids.len();
14067        let base = taps.base;
14068        debug_assert!(
14069            base + t <= taps.t,
14070            "tap window {base}+{t} exceeds sink {}",
14071            taps.t
14072        );
14073        let xv = e.view(x, t * h);
14074        for r in 0..t {
14075            let row = xv.slice(r * h..(r + 1) * h);
14076            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14077        }
14078        Ok(())
14079    }
14080
14081    fn gemma4_verify_trunk(
14082        &self,
14083        e: &Engine,
14084        tokens: &[u32],
14085        pos0: usize,
14086        cache: &mut Cache,
14087        tok_dev: Option<&CudaSlice<u32>>,
14088    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14089        let n_embd = self.cfg.n_embd as usize;
14090        let eps = self.cfg.rms_eps;
14091        let t = tokens.len();
14092        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14093        let pos_d = e.htod_i32(&pos)?;
14094        let mut x = match tok_dev {
14095            Some(td) => {
14096                let embd_gpu = self
14097                    .embd_gpu
14098                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14099                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14100                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14101            }
14102            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14103        };
14104        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14105        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14106        let n_layers = self.layers.len();
14107        for (il, layer) in self.layers.iter().enumerate() {
14108            let (hq, hdq) = match h_carry.take() {
14109                Some(p) => p,
14110                None => {
14111                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14112                }
14113            };
14114            let Mixer::Full(fa) = &layer.mixer else {
14115                panic!("gemma4 layer {il} not full-attn")
14116            };
14117            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14118            let next_norm = if il + 1 < n_layers {
14119                Some(self.layers[il + 1].attn_norm.float_data())
14120            } else {
14121                None
14122            };
14123            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14124            x = xn;
14125            h_carry = hn;
14126            self.dflash_tap(e, cache, il, &x, t)?;
14127        }
14128        let mut hn = e.uninit(t * n_embd)?;
14129        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14130        let mut ld = e.matmul(&self.output, &hn, t)?;
14131        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14132        cache.pos += t;
14133        Ok((ld, hn))
14134    }
14135
14136    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14137    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14138    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14139    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14140    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14141    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14142    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14143    #[allow(clippy::too_many_arguments)]
14144    fn gemma4_verify_attn_stream(
14145        &self,
14146        e: &Engine,
14147        fa: &crate::hybrid::FullAttnLayer,
14148        il: usize,
14149        hq: &CudaSlice<i8>,
14150        hdq: &CudaSlice<f32>,
14151        pos_d: &CudaSlice<i32>,
14152        t: usize,
14153        cache: &mut Cache,
14154        hint: usize,
14155        row_ctrs: &[CudaSlice<i32>],
14156    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14157        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14158        let eps = self.cfg.rms_eps;
14159        let aux = self.gemma4_aux.as_ref().unwrap();
14160        let ones = aux.ones(e);
14161        #[cfg(debug_assertions)]
14162        crate::debug_assert_tensor_stream_device(
14163            ones,
14164            &e.stream(),
14165            "gemma4_verify_attn_stream.ones",
14166        );
14167        let h0 = e.zeros(0)?;
14168        let h = &h0;
14169        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14170        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14171        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14172        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14173        let fused_qkv = if f2b {
14174            if swa {
14175                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14176                    .map(|(a, b, c)| (a, b, Some(c)))
14177            } else {
14178                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14179                    .map(|(a, b)| (a, b, None))
14180            }
14181        } else {
14182            None
14183        };
14184        let (q0, k0, v0) = match fused_qkv {
14185            Some((a, b, cv)) => {
14186                let v = match cv {
14187                    Some(c) => c,
14188                    None => e.clone_dtod(&b)?,
14189                };
14190                (a, b, v)
14191            }
14192            None => {
14193                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14194                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14195                let v0 = if swa {
14196                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14197                } else {
14198                    e.clone_dtod(&k0)?
14199                };
14200                (q0, k0, v0)
14201            }
14202        };
14203        let mut q = e.uninit(t * nh * hd)?;
14204        let mut k = e.uninit(t * nkv * hd)?;
14205        let mut v = e.uninit(t * nkv * hd)?;
14206        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14207        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14208        let ff = if swa {
14209            None
14210        } else {
14211            Some(
14212                aux.rope_freqs(e)
14213                    .expect("gemma4 global rope needs rope_freqs.weight"),
14214            )
14215        };
14216        #[cfg(debug_assertions)]
14217        if let Some(ff) = ff {
14218            crate::debug_assert_tensor_stream_device(
14219                ff,
14220                &e.stream(),
14221                "gemma4_verify_attn_stream.rope_freqs",
14222            );
14223        }
14224        e.rms_norm_qkv_rope(
14225            &q0,
14226            &k0,
14227            &v0,
14228            fa.q_norm.float_data(),
14229            fa.k_norm.float_data(),
14230            ones,
14231            &mut q,
14232            &mut k,
14233            &mut v,
14234            hd,
14235            self.gemma4_rope_dims(il),
14236            nh * t,
14237            nkv * t,
14238            pos_d,
14239            nh,
14240            nkv,
14241            base,
14242            1.0,
14243            ff,
14244            eps,
14245        )?;
14246        let kvl = cache.kv[il].as_mut().unwrap();
14247        // append at the DEVICE slot; the counter advances by t on-device.
14248        e.append_kv_quantized_rows_dc(
14249            &k,
14250            &v,
14251            &mut kvl.k,
14252            &mut kvl.v,
14253            &kvl.len_d,
14254            t,
14255            kvl.kv_dim_k,
14256            kvl.kv_dim_v,
14257            kvl.k_tok_bytes,
14258            kvl.v_tok_bytes,
14259            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14260        )?;
14261        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14262        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14263        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14264        let mut attn = e.uninit(t * nh * hd)?;
14265        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14266        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14267        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14268        // and a stable window regime — the same rung/regime keys as the draft graph).
14269        if swa && hint + 1 >= win {
14270            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14271            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14272            e.fa_decode_rows_w(
14273                &q,
14274                &k_view,
14275                &v_view,
14276                &mut attn,
14277                hd,
14278                nh,
14279                nkv,
14280                &kvl.len_d,
14281                0,
14282                t,
14283                scale,
14284                win,
14285                kvl.k_tok_bytes,
14286                kvl.v_tok_bytes,
14287                None,
14288            )?;
14289        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14290            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14291            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14292            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14293            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14294            // for every row.
14295            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14296            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14297            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14298            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14299            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14300            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14301            // any bucket >= the live length is exact.
14302            let bucket = (hint + t + 2)
14303                .next_power_of_two()
14304                .min(crate::fa512_min_tkv().saturating_sub(1));
14305            let qv = e.view(&q, t * nh * hd);
14306            for i in 0..t {
14307                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14308                let mut q_one = e.uninit(nh * hd)?;
14309                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14310                let mut a_one = e.uninit(nh * hd)?;
14311                e.fa_decode_dc(
14312                    &q_one,
14313                    &k_view,
14314                    &v_view,
14315                    &mut a_one,
14316                    hd,
14317                    nh,
14318                    nkv,
14319                    &row_ctrs[i],
14320                    bucket,
14321                    scale,
14322                    kvl.k_tok_bytes,
14323                    kvl.v_tok_bytes,
14324                    false,
14325                )?;
14326                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14327            }
14328        } else if hd == 512 {
14329            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14330            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14331            e.fa_decode_rows(
14332                &q,
14333                &k_view,
14334                &v_view,
14335                &mut attn,
14336                hd,
14337                nh,
14338                nkv,
14339                hint,
14340                t,
14341                scale,
14342                kvl.k_tok_bytes,
14343                kvl.v_tok_bytes,
14344                Some((&kvl.len_d, 0)),
14345                false,
14346                false,
14347                None,
14348            )?;
14349        } else {
14350            // hd256 under-window: v4 device-len rows twin.
14351            e.fa_decode_rows_dc(
14352                &q,
14353                &k_view,
14354                &v_view,
14355                &mut attn,
14356                hd,
14357                nh,
14358                nkv,
14359                &kvl.len_d,
14360                hint + t,
14361                t,
14362                scale,
14363                kvl.k_tok_bytes,
14364                kvl.v_tok_bytes,
14365                0,
14366                swa && crate::Engine::wkv_on(),
14367            )?;
14368        }
14369        Ok(e.matmul(&fa.wo, &attn, t)?)
14370    }
14371
14372    fn gemma4_verify_attn(
14373        &self,
14374        e: &Engine,
14375        fa: &crate::hybrid::FullAttnLayer,
14376        il: usize,
14377        hq: &CudaSlice<i8>,
14378        hdq: &CudaSlice<f32>,
14379        pos_d: &CudaSlice<i32>,
14380        t: usize,
14381        cache: &mut Cache,
14382    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14383        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14384        let eps = self.cfg.rms_eps;
14385        let aux = self.gemma4_aux.as_ref().unwrap();
14386        let ones = aux.ones(e);
14387        #[cfg(debug_assertions)]
14388        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14389        let n_embd = self.cfg.n_embd as usize;
14390        let _ = n_embd;
14391
14392        let h0 = e.zeros(0)?;
14393        let h = &h0;
14394        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14395        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14396        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14397        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14398        let fused_qkv = if f2b {
14399            if swa {
14400                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14401                    .map(|(a, b, c)| (a, b, Some(c)))
14402            } else {
14403                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14404                    .map(|(a, b)| (a, b, None))
14405            }
14406        } else {
14407            None
14408        };
14409        let (q0, k0, v0) = match fused_qkv {
14410            Some((a, b, cv)) => {
14411                let v = match cv {
14412                    Some(c) => c,
14413                    None => e.clone_dtod(&b)?,
14414                };
14415                (a, b, v)
14416            }
14417            None => {
14418                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14419                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14420                let v0 = if swa {
14421                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14422                } else {
14423                    e.clone_dtod(&k0)?
14424                };
14425                (q0, k0, v0)
14426            }
14427        };
14428        let mut q = e.uninit(t * nh * hd)?;
14429        let mut k = e.uninit(t * nkv * hd)?;
14430        let mut v = e.uninit(t * nkv * hd)?;
14431        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14432        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14433        let ff = if swa {
14434            None
14435        } else {
14436            Some(
14437                aux.rope_freqs(e)
14438                    .expect("gemma4 global rope needs rope_freqs.weight"),
14439            )
14440        };
14441        #[cfg(debug_assertions)]
14442        if let Some(ff) = ff {
14443            crate::debug_assert_tensor_stream_device(
14444                ff,
14445                &e.stream(),
14446                "gemma4_verify_attn.rope_freqs",
14447            );
14448        }
14449        e.rms_norm_qkv_rope(
14450            &q0,
14451            &k0,
14452            &v0,
14453            fa.q_norm.float_data(),
14454            fa.k_norm.float_data(),
14455            ones,
14456            &mut q,
14457            &mut k,
14458            &mut v,
14459            hd,
14460            self.gemma4_rope_dims(il),
14461            nh * t,
14462            nkv * t,
14463            pos_d,
14464            nh,
14465            nkv,
14466            base,
14467            1.0,
14468            ff,
14469            eps,
14470        )?;
14471        let kvl = cache.kv[il].as_mut().unwrap();
14472        let base_len = kvl.len;
14473        e.append_kv_quantized_rows(
14474            &k,
14475            &v,
14476            &mut kvl.k,
14477            &mut kvl.v,
14478            base_len,
14479            t,
14480            kvl.kv_dim_k,
14481            kvl.kv_dim_v,
14482            kvl.k_tok_bytes,
14483            kvl.v_tok_bytes,
14484            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14485        )?;
14486        kvl.len += t;
14487        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14488        let mut attn = e.uninit(t * nh * hd)?;
14489        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14490        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14491        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14492            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14493            // decode rides the SAME symbol at t=1 (parity law).
14494            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14495        if rows_ok && (!swa || base_len + t <= win) {
14496            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14497            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14498            if hd == 512 {
14499                // device-len twin: sync the counter to the verify base (async arg-store).
14500                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14501                e.fa_decode_rows(
14502                    &q,
14503                    &k_view,
14504                    &v_view,
14505                    &mut attn,
14506                    hd,
14507                    nh,
14508                    nkv,
14509                    base_len,
14510                    t,
14511                    scale,
14512                    kvl.k_tok_bytes,
14513                    kvl.v_tok_bytes,
14514                    Some((&kvl.len_d, 0)),
14515                    false,
14516                    swa && crate::Engine::wkv_on(),
14517                    None,
14518                )?;
14519            } else {
14520                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14521                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14522                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14523                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14524                e.fa_decode_rows_dc(
14525                    &q,
14526                    &k_view,
14527                    &v_view,
14528                    &mut attn,
14529                    hd,
14530                    nh,
14531                    nkv,
14532                    &kvl.len_d,
14533                    base_len + t,
14534                    t,
14535                    scale,
14536                    kvl.k_tok_bytes,
14537                    kvl.v_tok_bytes,
14538                    0,
14539                    swa && crate::Engine::wkv_on(),
14540                )?;
14541            }
14542            return Ok(e.matmul(&fa.wo, &attn, t)?);
14543        }
14544        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14545        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14546        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14547        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14548        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14549        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14550        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14551        if hd == 256
14552            && swa
14553            && base_len + 1 >= win
14554            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14555        {
14556            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14557            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14558            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14559            e.fa_decode_rows_w(
14560                &q,
14561                &k_view,
14562                &v_view,
14563                &mut attn,
14564                hd,
14565                nh,
14566                nkv,
14567                &kvl.len_d,
14568                0,
14569                t,
14570                scale,
14571                win,
14572                kvl.k_tok_bytes,
14573                kvl.v_tok_bytes,
14574                None,
14575            )?;
14576            return Ok(e.matmul(&fa.wo, &attn, t)?);
14577        }
14578        for i in 0..t {
14579            let avail = base_len + i + 1;
14580            let (off_tok, t_kv) = if swa && avail > win {
14581                (avail - win, win)
14582            } else {
14583                (0, avail)
14584            };
14585            let k_view = e.view_u8_range(
14586                &kvl.k,
14587                off_tok * kvl.k_tok_bytes,
14588                (off_tok + t_kv) * kvl.k_tok_bytes,
14589            );
14590            let v_view = e.view_u8_range(
14591                &kvl.v,
14592                off_tok * kvl.v_tok_bytes,
14593                (off_tok + t_kv) * kvl.v_tok_bytes,
14594            );
14595            let qi = e.view(&q, t * nh * hd);
14596            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14597            let mut q_one = e.uninit(nh * hd)?;
14598            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14599            let mut a_one = e.uninit(nh * hd)?;
14600            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14601            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14602            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14603            if swa
14604                && avail > win
14605                && hd == 256
14606                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14607            {
14608                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14609                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14610                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14611                e.fa_decode_rows_w(
14612                    &q_one,
14613                    &kp,
14614                    &vp,
14615                    &mut a_one,
14616                    hd,
14617                    nh,
14618                    nkv,
14619                    &kvl.len_d,
14620                    0,
14621                    1,
14622                    scale,
14623                    win,
14624                    kvl.k_tok_bytes,
14625                    kvl.v_tok_bytes,
14626                    None,
14627                )?;
14628            } else if !swa
14629                && hd == 512
14630                && avail >= crate::fa512_min_tkv()
14631                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14632            {
14633                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14634                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14635                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14636                e.fa_decode_rows(
14637                    &q_one,
14638                    &kp,
14639                    &vp,
14640                    &mut a_one,
14641                    hd,
14642                    nh,
14643                    nkv,
14644                    avail - 1,
14645                    1,
14646                    scale,
14647                    kvl.k_tok_bytes,
14648                    kvl.v_tok_bytes,
14649                    Some((&kvl.len_d, 0)),
14650                    false,
14651                    false,
14652                    None,
14653                )?;
14654            } else {
14655                e.fa_decode_kvmod(
14656                    &q_one,
14657                    &k_view,
14658                    &v_view,
14659                    &mut a_one,
14660                    hd,
14661                    nh,
14662                    nkv,
14663                    t_kv,
14664                    scale,
14665                    kvl.k_tok_bytes,
14666                    kvl.v_tok_bytes,
14667                    swa && crate::Engine::wkv_on(),
14668                )?;
14669            }
14670            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14671        }
14672        Ok(e.matmul(&fa.wo, &attn, t)?)
14673    }
14674
14675    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
14676    /// h_seed = pre-output_norm hidden). Advances cache.pos.
14677    pub(crate) fn gemma4_decode_step_h(
14678        &self,
14679        e: &Engine,
14680        token: u32,
14681        cache: &mut Cache,
14682    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14683        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
14684        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
14685        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
14686        // unsplit rather than guessing a fence.
14687        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
14688            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
14689        }
14690        if crate::pp::pp_cuts(self.layers.len()).is_some() {
14691            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
14692        }
14693        let n_embd = self.cfg.n_embd as usize;
14694        let eps = self.cfg.rms_eps;
14695        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14696        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14697        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14698        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
14699        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
14700        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14701        let n_layers = self.layers.len();
14702        for (il, layer) in self.layers.iter().enumerate() {
14703            let (hq, hdq) = match h_carry.take() {
14704                Some(p) => p,
14705                None => {
14706                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
14707                }
14708            };
14709            let Mixer::Full(fa) = &layer.mixer else {
14710                panic!("gemma4 layer {il} not full-attn")
14711            };
14712            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
14713            let next_norm = if il + 1 < n_layers {
14714                Some(self.layers[il + 1].attn_norm.float_data())
14715            } else {
14716                None
14717            };
14718            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14719            x = xn;
14720            h_carry = hn;
14721        }
14722        let mut hn = e.uninit(n_embd)?;
14723        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14724        let h_seed = e.clone_dtod(&x)?;
14725        let mut ld = e.matmul(&self.output, &hn, 1)?;
14726        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14727        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
14728        self.gemma4_suppress(e, &mut ld, 1)?;
14729        let logits = e.dtoh(&ld)?;
14730        cache.pos += 1;
14731        Ok((logits, h_seed))
14732    }
14733
14734    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
14735    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
14736    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
14737    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
14738    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
14739    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
14740    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
14741    fn gemma4_decode_layers(
14742        &self,
14743        e: &Engine,
14744        mut x: CudaSlice<f32>,
14745        lo: usize,
14746        hi: usize,
14747        pos_d: &CudaSlice<i32>,
14748        cache: &mut Cache,
14749    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14750        let n_embd = self.cfg.n_embd as usize;
14751        let eps = self.cfg.rms_eps;
14752        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14753        for il in lo..hi {
14754            let layer = &self.layers[il];
14755            let (hq, hdq) = match h_carry.take() {
14756                Some(p) => p,
14757                // range head: il == lo — norm against THIS layer's attn_norm.
14758                None => {
14759                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
14760                }
14761            };
14762            let Mixer::Full(fa) = &layer.mixer else {
14763                panic!("gemma4 layer {il} not full-attn")
14764            };
14765            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
14766            let next_norm = if il + 1 < hi {
14767                Some(self.layers[il + 1].attn_norm.float_data())
14768            } else {
14769                None
14770            };
14771            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14772            x = xn;
14773            h_carry = hn;
14774        }
14775        Ok(x)
14776    }
14777
14778    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
14779    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
14780    /// boundary handoff — same choreography as the generic arm (decode.rs), same
14781    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
14782    /// stage 1 = layers [split, n) + output_norm + softcapped head.
14783    /// Each stage uploads its own copy of the step's position scalar on its own stream.
14784    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
14785    fn gemma4_decode_step_h_pp2(
14786        &self,
14787        e: &Engine,
14788        token: u32,
14789        cache: &mut Cache,
14790        split: usize,
14791    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14792        if crate::pp::pp2_streams_off() {
14793            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
14794        }
14795        let rt = crate::pp::Pp2Rt::get(e)?;
14796        let e0 = rt.engine(0, e);
14797        let e1 = rt.engine(1, e);
14798        let n_embd = self.cfg.n_embd as usize;
14799        let eps = self.cfg.rms_eps;
14800        let pos = cache.pos as i32;
14801
14802        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
14803        let slot = {
14804            let _st0 = rt.enter(0);
14805            let pos_d = e0.htod_i32(&[pos])?;
14806            #[cfg(debug_assertions)]
14807            crate::debug_assert_tensor_stream_device(
14808                &pos_d,
14809                &e0.stream(),
14810                "gemma4_decode_step_h_pp2.stage0.pos_d",
14811            );
14812            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
14813            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14814            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
14815            rt.tx(0, &x, n_embd)?
14816        };
14817
14818        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
14819        let _st1 = rt.enter(1);
14820        let pos_d = e1.htod_i32(&[pos])?;
14821        #[cfg(debug_assertions)]
14822        crate::debug_assert_tensor_stream_device(
14823            &pos_d,
14824            &e1.stream(),
14825            "gemma4_decode_step_h_pp2.stage1.pos_d",
14826        );
14827        let x = rt.rx(0, slot, n_embd)?;
14828        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
14829
14830        let mut hn = e1.uninit(n_embd)?;
14831        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14832        let h_seed = e1.clone_dtod(&x)?;
14833        let mut ld = e1.matmul(&self.output, &hn, 1)?;
14834        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14835        e1.softcap(&mut ld, cap, self.output.out_features())?;
14836        self.gemma4_suppress(e1, &mut ld, 1)?;
14837        let logits = e1.dtoh(&ld)?;
14838        cache.pos += 1;
14839        Ok((logits, h_seed))
14840    }
14841
14842    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
14843    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
14844    fn gemma4_decode_step_h_pp2_samestream(
14845        &self,
14846        e: &Engine,
14847        token: u32,
14848        cache: &mut Cache,
14849        split: usize,
14850    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14851        let n_embd = self.cfg.n_embd as usize;
14852        let eps = self.cfg.rms_eps;
14853        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14854
14855        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
14856        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14857        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14858        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
14859
14860        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
14861        let boundary_tx = e.clone_dtod(&x)?;
14862        let boundary_rx = e.clone_dtod(&boundary_tx)?;
14863
14864        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
14865        let x =
14866            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
14867
14868        let mut hn = e.uninit(n_embd)?;
14869        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14870        let h_seed = e.clone_dtod(&x)?;
14871        let mut ld = e.matmul(&self.output, &hn, 1)?;
14872        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14873        e.softcap(&mut ld, cap, self.output.out_features())?;
14874        self.gemma4_suppress(e, &mut ld, 1)?;
14875        let logits = e.dtoh(&ld)?;
14876        cache.pos += 1;
14877        Ok((logits, h_seed))
14878    }
14879}
14880
14881// ============================ step35 (Step-3.7-Flash) ==================================
14882// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
14883// FAMILY and not a few branches inside the generic `full_attn*` chain:
14884//
14885//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
14886//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
14887//      shapes and the FA head counts would be wrong on 33 of 45 layers.
14888//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
14889//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
14890//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
14891//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
14892//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
14893//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
14894//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
14895//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
14896//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
14897//
14898// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
14899impl HybridModel {
14900    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
14901    /// synthesize a drafter or trunk layer from a neighboring class.
14902    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
14903        let geometry = self
14904            .cfg
14905            .layer_geometry(il as u32)
14906            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
14907        debug_assert_eq!(
14908            geometry.attention_gate,
14909            memra_gguf::config::AttentionGateKind::SeparateHead
14910        );
14911        geometry
14912    }
14913
14914    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
14915    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
14916    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
14917    ///
14918    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
14919    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
14920    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
14921    /// `cache`:
14922    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
14923    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
14924    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
14925    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
14926    ///     contract, lane/chunkinv-flip).
14927    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
14928    ///     q/k/v, no cache side effect.
14929    ///
14930    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
14931    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
14932    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
14933    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
14934    /// still contains must be masked per query. memra's window convention
14935    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
14936    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
14937    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
14938    ///
14939    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
14940    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
14941    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
14942    ///
14943    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
14944    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
14945    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
14946    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
14947    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
14948    /// hidden rows, and the generated text — a function of the chunk size:
14949    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
14950    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
14951    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
14952    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
14953    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
14954    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
14955    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
14956    ///   one-token change in a documented machine-config knob changed the answer.
14957    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
14958    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
14959    /// the same rows moves the logits by ~1.8.
14960    ///
14961    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
14962    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
14963    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
14964    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
14965    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
14966    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
14967    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
14968    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
14969    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
14970    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
14971    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
14972    /// those with t_kv <= win = 512.
14973    #[allow(clippy::too_many_arguments)]
14974    fn step35_attn_pre_wo(
14975        &self,
14976        e: &Engine,
14977        fa: &FullAttnLayer,
14978        mut g3: Vec<CudaSlice<f32>>,
14979        hg: Option<&CudaSlice<f32>>,
14980        gt_pre: Option<&CudaSlice<f32>>,
14981        pos_d: &CudaSlice<i32>,
14982        t: usize,
14983        cache: Option<&mut Cache>,
14984        il: usize,
14985        seq_end: usize,
14986    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14987        let geometry = self.step35_geom(il);
14988        let hd = geometry.head_dim_k as usize;
14989        let nkv = geometry.n_head_kv as usize;
14990        let nh = geometry.n_head as usize;
14991        let rbase = geometry.rope_base;
14992        let scale = geometry.attention_scale();
14993        let swa = geometry.window.is_some();
14994        let eps = self.cfg.rms_eps;
14995        let win = geometry.window.unwrap_or(0) as usize;
14996        let n_rot = geometry.n_rot as usize;
14997
14998        let v = g3.pop().unwrap();
14999        let k0 = g3.pop().unwrap();
15000        let q0 = g3.pop().unwrap();
15001
15002        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
15003        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
15004        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
15005        let mut q = e.uninit(t * nh * hd)?;
15006        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
15007        let mut k = e.uninit(t * nkv * hd)?;
15008        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
15009        let ff = if geometry.rope_factors {
15010            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
15011        } else {
15012            None
15013        };
15014        #[cfg(debug_assertions)]
15015        if let Some(ff) = ff {
15016            crate::debug_assert_tensor_stream_device(
15017                ff,
15018                &e.stream(),
15019                "step35_attn_pre_wo.rope_freqs",
15020            );
15021        }
15022        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
15023
15024        let mut attn = e.uninit(t * nh * hd)?;
15025        match cache {
15026            Some(cache) => {
15027                let base_len = cache.kv[il].as_ref().unwrap().len;
15028                // Read per layer call, never in a measured default.
15029                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
15030                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
15031                let off = if swa {
15032                    let raw = base_len.saturating_sub(win - 1);
15033                    if legacy_tkv || legacy_calllocal {
15034                        raw
15035                    } else {
15036                        raw & !31usize
15037                    }
15038                } else {
15039                    0
15040                };
15041                {
15042                    let kvl = cache.kv[il].as_mut().unwrap();
15043                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
15044                    let write_row = e.prepare_kv_append(kvl, off, t)?;
15045                    e.append_kv_quantized_rows(
15046                        &k,
15047                        &v,
15048                        &mut kvl.k,
15049                        &mut kvl.v,
15050                        write_row,
15051                        t,
15052                        kvl.kv_dim_k,
15053                        kvl.kv_dim_v,
15054                        kvl.k_tok_bytes,
15055                        kvl.v_tok_bytes,
15056                        crate::Engine::kv_fp8_on(),
15057                    )?;
15058                    kvl.len += t;
15059                    let new_len = kvl.len as i32;
15060                    e.set_i32_one(&mut kvl.len_d, new_len)?;
15061                }
15062                let kvl = cache.kv[il].as_ref().unwrap();
15063                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15064                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15065                // unaligned view offset here. Both halves are load-bearing for the canaries:
15066                // on the FA default the predicate arms agree bitwise wherever they can differ
15067                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15068                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15069                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15070                // on the current FA path: its tile grid starts at the chunk/call boundary.
15071                // SWA: trim the view to the oldest key any query in this chunk can reach —
15072                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15073                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15074                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15075                // the VIEW START — so an unaligned off regroups the same absolute keys into
15076                // different tiles at different chunk sizes = different (m,l) rounding =
15077                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15078                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15079                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15080                // size; the <=31 extra leading keys are older than EVERY query's window
15081                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15082                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15083                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15084                // the floor arm's bits do not move either (gated: G2f, battery 2).
15085                let t_kv = base_len + t - off;
15086                let physical = kvl.physical_rows(off, off + t_kv)?;
15087                let k_view = e.view_u8_range(
15088                    &kvl.k,
15089                    physical.start * kvl.k_tok_bytes,
15090                    physical.end * kvl.k_tok_bytes,
15091                );
15092                let v_view = e.view_u8_range(
15093                    &kvl.v,
15094                    physical.start * kvl.v_tok_bytes,
15095                    physical.end * kvl.v_tok_bytes,
15096                );
15097                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15098                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15099                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15100                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15101                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15102                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15103                // construction, so the invariance assertion MUST break under it (the seam whose
15104                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15105                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15106                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15107                // cached (probes flip it in-process). Never on in a measured default run.
15108                let swa_naive = if legacy_tkv {
15109                    t_kv > win
15110                } else {
15111                    seq_end > win
15112                };
15113                if swa && swa_naive {
15114                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15115                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15116                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15117                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15118                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15119                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15120                    // identically to the unwindowed one modulo the mask, which is the point.
15121                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15122                    // selected on `seq_end` like every arm here, so the class is uniform for
15123                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15124                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15125                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15126                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15127                        e.sdpa_naive_w_quantized_view(
15128                            &q,
15129                            &k_view,
15130                            &v_view,
15131                            &mut attn,
15132                            hd,
15133                            nh,
15134                            nkv,
15135                            t,
15136                            t_kv,
15137                            scale,
15138                            true,
15139                            win,
15140                            kvl.k_tok_bytes,
15141                            kvl.v_tok_bytes,
15142                        )?;
15143                    } else {
15144                        e.fa_prefill_view_ws_w_hd128(
15145                            &q,
15146                            &k_view,
15147                            &v_view,
15148                            &mut attn,
15149                            hd,
15150                            nh,
15151                            nkv,
15152                            t,
15153                            t_kv,
15154                            scale,
15155                            true,
15156                            win,
15157                            kvl.k_tok_bytes,
15158                            kvl.v_tok_bytes,
15159                        )?;
15160                    }
15161                } else if std::env::var("MEMRA_NOFA").is_ok() {
15162                    e.sdpa_naive_quantized_view(
15163                        &q,
15164                        &k_view,
15165                        &v_view,
15166                        &mut attn,
15167                        hd,
15168                        nh,
15169                        nkv,
15170                        t,
15171                        t_kv,
15172                        scale,
15173                        true,
15174                        kvl.k_tok_bytes,
15175                        kvl.v_tok_bytes,
15176                    )?;
15177                } else {
15178                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15179                    // reach past the window, so the window mask is a no-op under causal and every
15180                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15181                    // request either way, which is what makes the chunk size arithmetic-free.
15182                    e.fa_prefill_view_ws(
15183                        &q,
15184                        &k_view,
15185                        &v_view,
15186                        &mut attn,
15187                        hd,
15188                        nh,
15189                        nkv,
15190                        t,
15191                        t_kv,
15192                        scale,
15193                        true,
15194                        kvl.k_tok_bytes,
15195                        kvl.v_tok_bytes,
15196                        crate::Engine::kv_fp8_on(),
15197                    )?;
15198                }
15199            }
15200            None => {
15201                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15202                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15203                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15204                // seq_end here too or it re-opens the same door.
15205                debug_assert_eq!(
15206                    seq_end, t,
15207                    "step35 cacheless prefill is monolithic (seq_end == t)"
15208                );
15209                if swa && seq_end > win {
15210                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15211                } else if std::env::var("MEMRA_NOFA").is_ok() {
15212                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15213                } else {
15214                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15215                }
15216            }
15217        }
15218
15219        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15220        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15221        let gw = fa
15222            .attn_gate
15223            .as_ref()
15224            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15225        let gt_owned = if gt_pre.is_none() {
15226            Some(e.matmul(
15227                gw,
15228                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15229                t,
15230            )?)
15231        } else {
15232            None
15233        };
15234        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15235        let mut ag = e.uninit(t * nh * hd)?;
15236        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15237        Ok(ag)
15238    }
15239
15240    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15241    /// `forward_last`, t2probe). Post-`wo`.
15242    pub(crate) fn step35_attn(
15243        &self,
15244        e: &Engine,
15245        fa: &FullAttnLayer,
15246        h: &CudaSlice<f32>,
15247        pos_d: &CudaSlice<i32>,
15248        t: usize,
15249        il: usize,
15250    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15251        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15252            Some(g3) => g3,
15253            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15254        };
15255        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15256        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15257        self.step35_o(e, fa, &ag, t)
15258    }
15259
15260    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15261    /// resident quantized cache, attend through the cache view). Post-`wo`.
15262    ///
15263    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15264    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15265    /// own extent.
15266    #[allow(clippy::too_many_arguments)]
15267    pub(crate) fn step35_attn_prime(
15268        &self,
15269        e: &Engine,
15270        fa: &FullAttnLayer,
15271        h: &CudaSlice<f32>,
15272        hx: Option<&CudaSlice<u8>>,
15273        pos_d: &CudaSlice<i32>,
15274        t: usize,
15275        cache: &mut Cache,
15276        il: usize,
15277        seq_end: usize,
15278    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15279        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15280            if hx.is_some() {
15281                return Err(
15282                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15283                     pre-quantized prime path"
15284                        .into(),
15285                );
15286            }
15287            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15288        }
15289        let g3 = if fa.step_tp_qkv.is_some() {
15290            if hx.is_some() {
15291                return Err(
15292                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15293                     pre-quantized prime path"
15294                        .into(),
15295                );
15296            }
15297            self.step35_tp_qkv(e, fa, h, t)?
15298                .expect("Step Q/K/V TP disappeared after the presence check")
15299        } else {
15300            match hx {
15301                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15302                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15303            }
15304        };
15305        let ag =
15306            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15307        self.step35_o(e, fa, &ag, t)
15308    }
15309
15310    fn ensure_step_tp_kv_cache(
15311        &self,
15312        e: &Engine,
15313        fa: &FullAttnLayer,
15314        il: usize,
15315        cache: &mut Cache,
15316    ) -> Result<bool, Box<dyn std::error::Error>> {
15317        let tp = fa
15318            .step_tp_qkv
15319            .as_ref()
15320            .ok_or("Step TP cache hydration lost its resident projections")?;
15321        let geometry = self.step35_geom(il);
15322        let window = geometry.window.map(|window| window as usize);
15323        let ranks = tp.runtime.devices().len();
15324        let head_dim = geometry.head_dim_k as usize;
15325        let kv_heads = geometry.n_head_kv as usize;
15326        let max_ctx = cache.max_ctx;
15327
15328        if cache.tp_kv[il].is_some() {
15329            return Ok(false);
15330        }
15331        let local = cache.kv[il]
15332            .as_ref()
15333            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15334        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15335            return Err(format!(
15336                "Step TP layer {il} local KV geometry k={} v={} != {}",
15337                local.kv_dim_k,
15338                local.kv_dim_v,
15339                kv_heads * head_dim
15340            )
15341            .into());
15342        }
15343        let resident_start = window
15344            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15345            .unwrap_or(0);
15346        let resident_rows = local.len - resident_start;
15347        let physical = local.physical_rows(resident_start, local.len)?;
15348        let k_rows = if resident_rows == 0 {
15349            Vec::new()
15350        } else {
15351            e.dtoh_u8_view(&e.view_u8_range(
15352                &local.k,
15353                physical.start * local.k_tok_bytes,
15354                physical.end * local.k_tok_bytes,
15355            ))?
15356        };
15357        let v_rows = if resident_rows == 0 {
15358            Vec::new()
15359        } else {
15360            e.dtoh_u8_view(&e.view_u8_range(
15361                &local.v,
15362                physical.start * local.v_tok_bytes,
15363                physical.end * local.v_tok_bytes,
15364            ))?
15365        };
15366        let mut distributed = match window {
15367            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15368                kv_heads * head_dim,
15369                kv_heads * head_dim,
15370                max_ctx,
15371                window,
15372            )?,
15373            None => tp.runtime.allocate_tp_kv_cache(
15374                kv_heads * head_dim,
15375                kv_heads * head_dim,
15376                max_ctx,
15377            )?,
15378        };
15379        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15380            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15381        {
15382            return Err(format!(
15383                "Step TP layer {il} distributed/local KV token bytes disagree: \
15384                 k={}x{ranks}/{} v={}x{ranks}/{}",
15385                distributed.k_tok_bytes(),
15386                local.k_tok_bytes,
15387                distributed.v_tok_bytes(),
15388                local.v_tok_bytes,
15389            )
15390            .into());
15391        }
15392        tp.runtime.hydrate_tp_kv_cache_from(
15393            &mut distributed,
15394            local.len,
15395            resident_start,
15396            &k_rows,
15397            &v_rows,
15398        )?;
15399        cache.tp_kv[il] = Some(distributed);
15400        Ok(true)
15401    }
15402
15403    #[allow(clippy::too_many_arguments)]
15404    fn step35_tp_prefill_attn_resident(
15405        &self,
15406        e: &Engine,
15407        fa: &FullAttnLayer,
15408        il: usize,
15409        h: &CudaSlice<f32>,
15410        pos_d: &CudaSlice<i32>,
15411        tokens: usize,
15412        cache: &mut Cache,
15413        seq_end: usize,
15414    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15415        let tp = fa
15416            .step_tp_qkv
15417            .as_ref()
15418            .ok_or("Step TP prefill lost its resident projections")?;
15419        let attention = tp
15420            .attention
15421            .as_ref()
15422            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15423        let ranks = tp.runtime.devices().len();
15424        if !step_tp_prefill_shape(
15425            true,
15426            tokens,
15427            ranks,
15428            tp.runtime.native_p2p(),
15429            true,
15430            crate::Engine::kv_fp8_on(),
15431        ) {
15432            return Err(format!(
15433                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP2/TP4 native P2P, \
15434                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15435                 native_p2p={} fp8_kv={}",
15436                tp.runtime.native_p2p(),
15437                crate::Engine::kv_fp8_on(),
15438            )
15439            .into());
15440        }
15441        for seam in [
15442            "MEMRA_STEP35_SWA_TKV",
15443            "MEMRA_PRIME_CALLLOCAL",
15444            "MEMRA_PRIME_F32CHUNK0",
15445        ] {
15446            if std::env::var(seam).as_deref() == Ok("1") {
15447                return Err(format!(
15448                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15449                )
15450                .into());
15451            }
15452        }
15453
15454        let geometry = self.step35_geom(il);
15455        let window = geometry.window.map(|window| window as usize);
15456        let head_dim = geometry.head_dim_k as usize;
15457        let heads = geometry.n_head as usize;
15458        let kv_heads = geometry.n_head_kv as usize;
15459        if heads % ranks != 0 || kv_heads % ranks != 0 {
15460            return Err(format!(
15461                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15462            )
15463            .into());
15464        }
15465        let local_heads = heads / ranks;
15466        let local_kv_heads = kv_heads / ranks;
15467        let local_kv_dim = local_kv_heads * head_dim;
15468        let hidden = self.cfg.n_embd as usize;
15469        let expected_input = tokens
15470            .checked_mul(hidden)
15471            .ok_or("Step TP prefill input size overflow")?;
15472        if h.len() < expected_input {
15473            return Err(format!(
15474                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15475                h.len()
15476            )
15477            .into());
15478        }
15479        let positions = e.dtoh_i32(pos_d)?;
15480        if positions.len() != tokens {
15481            return Err(format!(
15482                "rank-local Step prefill positions {} != tokens {tokens}",
15483                positions.len()
15484            )
15485            .into());
15486        }
15487
15488        let mut active_input = e.uninit(expected_input)?;
15489        e.copy_view_into(
15490            &mut active_input,
15491            0,
15492            &h.slice(0..expected_input),
15493            expected_input,
15494        )?;
15495        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15496        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15497        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15498        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15499        // layer-count-amplified arm of the boot flake.
15500        e.stream().synchronize()?;
15501        tp.runtime
15502            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15503        let q_raw = tp
15504            .runtime
15505            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15506        let k_raw = tp
15507            .runtime
15508            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15509        let v_raw = tp
15510            .runtime
15511            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15512        let mut q = Vec::with_capacity(ranks);
15513        let mut k = Vec::with_capacity(ranks);
15514        for rank in 0..ranks {
15515            let engine = tp
15516                .runtime
15517                .rank_engine(rank)
15518                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15519            let _main = engine.gpu.enter_main()?;
15520            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15521            engine.rms_norm(
15522                &q_raw[rank],
15523                &attention.q_norm[rank],
15524                &mut q_rank,
15525                head_dim,
15526                tokens * local_heads,
15527                self.cfg.rms_eps,
15528            )?;
15529            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15530            engine.rms_norm(
15531                &k_raw[rank],
15532                &attention.k_norm[rank],
15533                &mut k_rank,
15534                head_dim,
15535                tokens * local_kv_heads,
15536                self.cfg.rms_eps,
15537            )?;
15538            let position = engine.htod_i32(&positions)?;
15539            let rope_freqs = if geometry.rope_factors {
15540                self.step35_aux
15541                    .as_ref()
15542                    .and_then(|aux| aux.rope_freqs(engine))
15543            } else {
15544                None
15545            };
15546            engine.rope_neox2(
15547                &mut q_rank,
15548                &mut k_rank,
15549                &position,
15550                head_dim,
15551                geometry.n_rot as usize,
15552                local_heads,
15553                local_kv_heads,
15554                tokens,
15555                geometry.rope_base,
15556                1.0,
15557                rope_freqs,
15558            )?;
15559            q.push(q_rank);
15560            k.push(k_rank);
15561        }
15562
15563        let gate_weight = fa
15564            .attn_gate
15565            .as_ref()
15566            .ok_or("step35 layer is missing attn_gate.weight")?;
15567        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15568        if gate.len() != tokens * heads {
15569            return Err(format!(
15570                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15571                gate.len()
15572            )
15573            .into());
15574        }
15575
15576        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15577        let base_len = cache.kv[il]
15578            .as_ref()
15579            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15580            .len;
15581        let distributed = cache.tp_kv[il]
15582            .as_ref()
15583            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15584        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15585            return Err(format!(
15586                "Step TP layer {il} cache lengths diverged before prefill: \
15587                 local={base_len} distributed={}/{}",
15588                distributed.committed_len(),
15589                distributed.staged_len()
15590            )
15591            .into());
15592        }
15593        let target_len = base_len
15594            .checked_add(tokens)
15595            .ok_or("Step TP prefill cache length overflow")?;
15596        if target_len > cache.max_ctx {
15597            return Err(format!(
15598                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15599                cache.max_ctx
15600            )
15601            .into());
15602        }
15603        if seq_end < target_len {
15604            return Err(format!(
15605                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15606            )
15607            .into());
15608        }
15609
15610        let transaction = cache.tp_kv[il]
15611            .as_mut()
15612            .expect("distributed cache checked above")
15613            .begin_transaction()?;
15614        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15615            cache.tp_kv[il]
15616                .as_mut()
15617                .expect("distributed cache checked above"),
15618            transaction,
15619            &k,
15620            &v_raw,
15621            tokens,
15622        ) {
15623            let _ = tp.runtime.rollback_tp_kv_transaction(
15624                cache.tp_kv[il]
15625                    .as_mut()
15626                    .expect("distributed cache checked above"),
15627                transaction,
15628            );
15629            return Err(error);
15630        }
15631
15632        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15633            let distributed = cache.tp_kv[il]
15634                .as_ref()
15635                .expect("distributed cache checked above");
15636            let staged_len = distributed.staged_len();
15637            let view_start = window
15638                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15639                .unwrap_or(0);
15640            let physical = distributed.physical_range(view_start, staged_len)?;
15641            let t_kv = staged_len - view_start;
15642            let swa_naive = window.is_some_and(|window| seq_end > window);
15643            let mut gated = Vec::with_capacity(ranks);
15644            for rank in 0..ranks {
15645                let engine = tp
15646                    .runtime
15647                    .rank_engine(rank)
15648                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15649                let _main = engine.gpu.enter_main()?;
15650                let rank_cache = distributed
15651                    .rank(rank)
15652                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15653                let k_view = engine.view_u8_range(
15654                    rank_cache.k(),
15655                    physical.start * distributed.k_tok_bytes(),
15656                    physical.end * distributed.k_tok_bytes(),
15657                );
15658                let v_view = engine.view_u8_range(
15659                    rank_cache.v(),
15660                    physical.start * distributed.v_tok_bytes(),
15661                    physical.end * distributed.v_tok_bytes(),
15662                );
15663                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
15664                if swa_naive {
15665                    let window = window.expect("SWA predicate requires a window");
15666                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15667                        engine.sdpa_naive_w_quantized_view(
15668                            &q[rank],
15669                            &k_view,
15670                            &v_view,
15671                            &mut attention_out,
15672                            head_dim,
15673                            local_heads,
15674                            local_kv_heads,
15675                            tokens,
15676                            t_kv,
15677                            geometry.attention_scale(),
15678                            true,
15679                            window,
15680                            distributed.k_tok_bytes(),
15681                            distributed.v_tok_bytes(),
15682                        )?;
15683                    } else {
15684                        engine.fa_prefill_view_ws_w_hd128(
15685                            &q[rank],
15686                            &k_view,
15687                            &v_view,
15688                            &mut attention_out,
15689                            head_dim,
15690                            local_heads,
15691                            local_kv_heads,
15692                            tokens,
15693                            t_kv,
15694                            geometry.attention_scale(),
15695                            true,
15696                            window,
15697                            distributed.k_tok_bytes(),
15698                            distributed.v_tok_bytes(),
15699                        )?;
15700                    }
15701                } else if std::env::var("MEMRA_NOFA").is_ok() {
15702                    engine.sdpa_naive_quantized_view(
15703                        &q[rank],
15704                        &k_view,
15705                        &v_view,
15706                        &mut attention_out,
15707                        head_dim,
15708                        local_heads,
15709                        local_kv_heads,
15710                        tokens,
15711                        t_kv,
15712                        geometry.attention_scale(),
15713                        true,
15714                        distributed.k_tok_bytes(),
15715                        distributed.v_tok_bytes(),
15716                    )?;
15717                } else {
15718                    engine.fa_prefill_view_ws(
15719                        &q[rank],
15720                        &k_view,
15721                        &v_view,
15722                        &mut attention_out,
15723                        head_dim,
15724                        local_heads,
15725                        local_kv_heads,
15726                        tokens,
15727                        t_kv,
15728                        geometry.attention_scale(),
15729                        true,
15730                        distributed.k_tok_bytes(),
15731                        distributed.v_tok_bytes(),
15732                        false,
15733                    )?;
15734                }
15735
15736                let gate_start = rank * local_heads;
15737                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
15738                for token in 0..tokens {
15739                    let start = token * heads + gate_start;
15740                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
15741                }
15742                let gate_rank = engine.htod(&gate_rank)?;
15743                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
15744                engine.attn_head_gate(
15745                    &attention_out,
15746                    &gate_rank,
15747                    &mut gated_rank,
15748                    None,
15749                    head_dim,
15750                    local_heads,
15751                    tokens,
15752                )?;
15753                gated.push(gated_rank);
15754            }
15755            for rank in 1..ranks {
15756                let engine = tp
15757                    .runtime
15758                    .rank_engine(rank)
15759                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15760                let _main = engine.gpu.enter_main()?;
15761                engine.stream().synchronize()?;
15762            }
15763
15764            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
15765                let output = tp
15766                    .runtime
15767                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
15768                let k_shadow =
15769                    tp.runtime
15770                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
15771                let v_shadow =
15772                    tp.runtime
15773                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
15774                let root = tp
15775                    .runtime
15776                    .rank_engine(0)
15777                    .ok_or("Step TP prefill lost its root engine")?;
15778                let _main = root.gpu.enter_main()?;
15779                root.stream().synchronize()?;
15780                (output, k_shadow, v_shadow)
15781            } else {
15782                let attention = tp.runtime.gather_native_column_shards(
15783                    &gated,
15784                    tokens,
15785                    local_heads * head_dim,
15786                )?;
15787                let output = tp
15788                    .runtime
15789                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
15790                let k_shadow = tp
15791                    .runtime
15792                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
15793                let v_shadow =
15794                    tp.runtime
15795                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
15796                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
15797            };
15798            let local = cache.kv[il]
15799                .as_mut()
15800                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
15801            if local.len != base_len {
15802                return Err(format!(
15803                    "Step TP layer {il} local cache changed during prefill: \
15804                     len={} base={base_len}",
15805                    local.len
15806                )
15807                .into());
15808            }
15809            let retain_from = window
15810                .map(|window| {
15811                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
15812                    let rollback_retain =
15813                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
15814                    staged_retain.min(rollback_retain)
15815                })
15816                .unwrap_or(0);
15817            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
15818            e.append_kv_quantized_rows(
15819                &k_shadow,
15820                &v_shadow,
15821                &mut local.k,
15822                &mut local.v,
15823                write_row,
15824                tokens,
15825                local.kv_dim_k,
15826                local.kv_dim_v,
15827                local.k_tok_bytes,
15828                local.v_tok_bytes,
15829                false,
15830            )?;
15831            local.len = staged_len;
15832            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
15833            Ok(output)
15834        })();
15835
15836        let output = match staged {
15837            Ok(output) => output,
15838            Err(error) => {
15839                let _ = tp.runtime.rollback_tp_kv_transaction(
15840                    cache.tp_kv[il]
15841                        .as_mut()
15842                        .expect("distributed cache checked above"),
15843                    transaction,
15844                );
15845                if let Some(local) = cache.kv[il].as_mut() {
15846                    local.len = base_len;
15847                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
15848                }
15849                return Err(error);
15850            }
15851        };
15852        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
15853            cache.tp_kv[il]
15854                .as_mut()
15855                .expect("distributed cache checked above"),
15856            transaction,
15857            tokens,
15858        ) {
15859            let _ = tp.runtime.rollback_tp_kv_transaction(
15860                cache.tp_kv[il]
15861                    .as_mut()
15862                    .expect("distributed cache checked above"),
15863                transaction,
15864            );
15865            let local = cache.kv[il].as_mut().expect("local cache checked above");
15866            local.len = base_len;
15867            e.set_i32_one(&mut local.len_d, base_len as i32)?;
15868            return Err(error);
15869        }
15870
15871        let committed = cache.tp_kv[il]
15872            .as_ref()
15873            .expect("distributed cache checked above")
15874            .committed_len();
15875        let local_len = cache.kv[il]
15876            .as_ref()
15877            .expect("local cache checked above")
15878            .len;
15879        if committed != local_len {
15880            return Err(format!(
15881                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
15882            )
15883            .into());
15884        }
15885        eprintln!(
15886            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
15887             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
15888             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
15889             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
15890             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
15891             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
15892             output={} performance_claim=false",
15893            tp.layer,
15894            tp.devices,
15895            hydrated,
15896            if window.is_some() {
15897                "rank-local-swa-ring"
15898            } else {
15899                "rank-local-global"
15900            },
15901            tp.runtime.transport_label(),
15902            tp.runtime.bulk_p2p(),
15903            if tp.runtime.bulk_p2p() {
15904                "root-device"
15905            } else {
15906                "root-readback"
15907            },
15908        );
15909        Ok(output)
15910    }
15911
15912    fn step35_tp_decode_attn_resident(
15913        &self,
15914        e: &Engine,
15915        fa: &FullAttnLayer,
15916        il: usize,
15917        h: &CudaSlice<f32>,
15918        pos_d: &CudaSlice<i32>,
15919        cache: &mut Cache,
15920    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15921        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
15922        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
15923        // nvfp4-dev-routes counter.
15924        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15925        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15926        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15927        let started = timing.then(std::time::Instant::now);
15928        let result = if crate::tp::step_tp_decode_v2_enabled()? {
15929            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
15930        } else {
15931            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
15932        };
15933        if let Some(started) = started {
15934            use std::sync::atomic::Ordering;
15935            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
15936                + started.elapsed().as_nanos() as u64;
15937            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
15938            if calls % 430 == 0 {
15939                eprintln!(
15940                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
15941                    ns as f64 / 1.0e6,
15942                    ns as f64 / calls as f64 / 1.0e3,
15943                );
15944            }
15945        }
15946        result
15947    }
15948
15949    #[allow(clippy::too_many_arguments)]
15950    fn step35_tp_decode_attn_resident_inner(
15951        &self,
15952        e: &Engine,
15953        fa: &FullAttnLayer,
15954        il: usize,
15955        h: &CudaSlice<f32>,
15956        pos_d: &CudaSlice<i32>,
15957        cache: &mut Cache,
15958    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15959        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
15960        // drains every stream so queued async work is billed to the phase that queued it — the
15961        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
15962        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
15963        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15964        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15965        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15966        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15967        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15968        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15969        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15970        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15971        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15972        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15973        fn lap(
15974            runtime: &crate::tp::TpE4m3HostBounce,
15975            e: &Engine,
15976            timer: &std::sync::atomic::AtomicU64,
15977            started: &mut Option<std::time::Instant>,
15978        ) -> Result<(), Box<dyn std::error::Error>> {
15979            let Some(start) = started.as_mut() else {
15980                return Ok(());
15981            };
15982            for rank in 0..runtime.devices().len() {
15983                if let Some(engine) = runtime.rank_engine(rank) {
15984                    let _main = engine.gpu.enter_main()?;
15985                    engine.stream().synchronize()?;
15986                }
15987            }
15988            e.stream().synchronize()?;
15989            timer.fetch_add(
15990                start.elapsed().as_nanos() as u64,
15991                std::sync::atomic::Ordering::Relaxed,
15992            );
15993            *start = std::time::Instant::now();
15994            Ok(())
15995        }
15996        let tp = fa
15997            .step_tp_qkv
15998            .as_ref()
15999            .ok_or("Step TP decode lost its resident projections")?;
16000        let attention = tp
16001            .attention
16002            .as_ref()
16003            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
16004        if !tp.runtime.native_p2p() {
16005            return Err("rank-local Step attention requires native P2P".into());
16006        }
16007        if crate::Engine::kv_fp8_on() {
16008            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
16009        }
16010
16011        let geometry = self.step35_geom(il);
16012        let window = geometry.window.map(|window| window as usize);
16013        let ranks = tp.runtime.devices().len();
16014        let head_dim = geometry.head_dim_k as usize;
16015        let heads = geometry.n_head as usize;
16016        let kv_heads = geometry.n_head_kv as usize;
16017        if heads % ranks != 0 || kv_heads % ranks != 0 {
16018            return Err(format!(
16019                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
16020            )
16021            .into());
16022        }
16023        let local_heads = heads / ranks;
16024        let local_kv_heads = kv_heads / ranks;
16025        let local_kv_dim = local_kv_heads * head_dim;
16026        let max_ctx = cache.max_ctx;
16027
16028        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
16029
16030        let base_len = cache.kv[il]
16031            .as_ref()
16032            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
16033            .len;
16034        let distributed = cache.tp_kv[il]
16035            .as_ref()
16036            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16037        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16038            return Err(format!(
16039                "Step TP layer {il} cache lengths diverged before decode: \
16040                 local={base_len} distributed={}/{}",
16041                distributed.committed_len(),
16042                distributed.staged_len()
16043            )
16044            .into());
16045        }
16046
16047        let mut lap_start = timing.then(std::time::Instant::now);
16048        let positions = e.dtoh_i32(pos_d)?;
16049        if positions.len() != 1 {
16050            return Err(format!(
16051                "rank-local Step decode requires one position, got {}",
16052                positions.len()
16053            )
16054            .into());
16055        }
16056        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
16057        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
16058            attention.decode_input.as_ref()
16059        {
16060            let mut decode_input = decode_input
16061                .lock()
16062                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16063            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16064            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16065            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16066            e.stream().synchronize()?;
16067            tp.runtime
16068                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16069            let q_raw = tp
16070                .runtime
16071                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16072            let k_raw = tp
16073                .runtime
16074                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16075            let v_raw = tp
16076                .runtime
16077                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16078            (q_raw, k_raw, v_raw, "root-device-replicated")
16079        } else {
16080            let activation = e.dtoh(h)?;
16081            let q_raw =
16082                tp.runtime
16083                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16084            let k_raw =
16085                tp.runtime
16086                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16087            let v_raw =
16088                tp.runtime
16089                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16090            (q_raw, k_raw, v_raw, "host-replicated")
16091        };
16092        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16093        let mut q = Vec::with_capacity(ranks);
16094        let mut k = Vec::with_capacity(ranks);
16095        for rank in 0..ranks {
16096            let engine = tp
16097                .runtime
16098                .rank_engine(rank)
16099                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16100            let _main = engine.gpu.enter_main()?;
16101            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16102            engine.rms_norm(
16103                &q_raw[rank],
16104                &attention.q_norm[rank],
16105                &mut q_rank,
16106                head_dim,
16107                local_heads,
16108                self.cfg.rms_eps,
16109            )?;
16110            let mut k_rank = engine.uninit(local_kv_dim)?;
16111            engine.rms_norm(
16112                &k_raw[rank],
16113                &attention.k_norm[rank],
16114                &mut k_rank,
16115                head_dim,
16116                local_kv_heads,
16117                self.cfg.rms_eps,
16118            )?;
16119            let position = engine.htod_i32(&positions)?;
16120            let rope_freqs = if geometry.rope_factors {
16121                self.step35_aux
16122                    .as_ref()
16123                    .and_then(|aux| aux.rope_freqs(engine))
16124            } else {
16125                None
16126            };
16127            engine.rope_neox2(
16128                &mut q_rank,
16129                &mut k_rank,
16130                &position,
16131                head_dim,
16132                geometry.n_rot as usize,
16133                local_heads,
16134                local_kv_heads,
16135                1,
16136                geometry.rope_base,
16137                1.0,
16138                rope_freqs,
16139            )?;
16140            q.push(q_rank);
16141            k.push(k_rank);
16142        }
16143        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16144
16145        let gate_weight = fa
16146            .attn_gate
16147            .as_ref()
16148            .ok_or("step35 layer is missing attn_gate.weight")?;
16149        let gate = e.matmul(gate_weight, h, 1)?;
16150        let gate = e.dtoh(&gate)?;
16151        if gate.len() != heads {
16152            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16153        }
16154        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16155
16156        let transaction = cache.tp_kv[il]
16157            .as_mut()
16158            .expect("distributed cache checked above")
16159            .begin_transaction()?;
16160        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16161            cache.tp_kv[il]
16162                .as_mut()
16163                .expect("distributed cache checked above"),
16164            transaction,
16165            &k,
16166            &v_raw,
16167            1,
16168        ) {
16169            let _ = tp.runtime.rollback_tp_kv_transaction(
16170                cache.tp_kv[il]
16171                    .as_mut()
16172                    .expect("distributed cache checked above"),
16173                transaction,
16174            );
16175            return Err(error);
16176        }
16177        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16178
16179        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16180            let distributed = cache.tp_kv[il]
16181                .as_ref()
16182                .expect("distributed cache checked above");
16183            let staged_len = distributed.staged_len();
16184            let view_start = window
16185                .map(|window| staged_len.saturating_sub(window))
16186                .unwrap_or(0);
16187            let physical = distributed.physical_range(view_start, staged_len)?;
16188            let t_kv = staged_len - view_start;
16189            let mut gated = Vec::with_capacity(ranks);
16190            for rank in 0..ranks {
16191                let engine = tp
16192                    .runtime
16193                    .rank_engine(rank)
16194                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16195                let _main = engine.gpu.enter_main()?;
16196                let rank_cache = distributed
16197                    .rank(rank)
16198                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16199                let k_view = engine.view_u8_range(
16200                    rank_cache.k(),
16201                    physical.start * distributed.k_tok_bytes(),
16202                    physical.end * distributed.k_tok_bytes(),
16203                );
16204                let v_view = engine.view_u8_range(
16205                    rank_cache.v(),
16206                    physical.start * distributed.v_tok_bytes(),
16207                    physical.end * distributed.v_tok_bytes(),
16208                );
16209                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16210                engine.fa_decode_kvmod(
16211                    &q[rank],
16212                    &k_view,
16213                    &v_view,
16214                    &mut attention_out,
16215                    head_dim,
16216                    local_heads,
16217                    local_kv_heads,
16218                    t_kv,
16219                    geometry.attention_scale(),
16220                    distributed.k_tok_bytes(),
16221                    distributed.v_tok_bytes(),
16222                    false,
16223                )?;
16224                let gate_start = rank * local_heads;
16225                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16226                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16227                engine.attn_head_gate(
16228                    &attention_out,
16229                    &gate_rank,
16230                    &mut gated_rank,
16231                    None,
16232                    head_dim,
16233                    local_heads,
16234                    1,
16235                )?;
16236                gated.push(gated_rank);
16237            }
16238            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16239
16240            let gathered =
16241                tp.runtime
16242                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16243            let output = tp
16244                .runtime
16245                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16246            let output = e.htod(&output)?;
16247            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16248
16249            let k_shadow = tp
16250                .runtime
16251                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16252            let v_shadow = tp
16253                .runtime
16254                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16255            let k_shadow = e.htod(&k_shadow)?;
16256            let v_shadow = e.htod(&v_shadow)?;
16257            let local = cache.kv[il]
16258                .as_mut()
16259                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16260            if local.len != base_len || base_len + 1 > max_ctx {
16261                return Err(format!(
16262                    "Step TP layer {il} local cache changed during decode: \
16263                     len={} base={base_len} max={max_ctx}",
16264                    local.len
16265                )
16266                .into());
16267            }
16268            let retain_from = window
16269                .map(|window| {
16270                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16271                    let rollback_retain =
16272                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16273                    staged_retain.min(rollback_retain)
16274                })
16275                .unwrap_or(0);
16276            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16277            e.append_kv_quantized(
16278                &k_shadow,
16279                &v_shadow,
16280                &mut local.k,
16281                &mut local.v,
16282                write_row,
16283                local.kv_dim_k,
16284                local.kv_dim_v,
16285                local.k_tok_bytes,
16286                local.v_tok_bytes,
16287                false,
16288            )?;
16289            local.len = base_len + 1;
16290            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16291            Ok(output)
16292        })();
16293
16294        let output = match staged {
16295            Ok(output) => output,
16296            Err(error) => {
16297                let _ = tp.runtime.rollback_tp_kv_transaction(
16298                    cache.tp_kv[il]
16299                        .as_mut()
16300                        .expect("distributed cache checked above"),
16301                    transaction,
16302                );
16303                if let Some(local) = cache.kv[il].as_mut() {
16304                    local.len = base_len;
16305                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16306                }
16307                return Err(error);
16308            }
16309        };
16310        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16311            cache.tp_kv[il]
16312                .as_mut()
16313                .expect("distributed cache checked above"),
16314            transaction,
16315            1,
16316        ) {
16317            let _ = tp.runtime.rollback_tp_kv_transaction(
16318                cache.tp_kv[il]
16319                    .as_mut()
16320                    .expect("distributed cache checked above"),
16321                transaction,
16322            );
16323            let local = cache.kv[il].as_mut().expect("local cache checked above");
16324            local.len = base_len;
16325            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16326            return Err(error);
16327        }
16328
16329        let committed = cache.tp_kv[il]
16330            .as_ref()
16331            .expect("distributed cache checked above")
16332            .committed_len();
16333        let local_len = cache.kv[il]
16334            .as_ref()
16335            .expect("local cache checked above")
16336            .len;
16337        if committed != local_len {
16338            return Err(format!(
16339                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16340            )
16341            .into());
16342        }
16343        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16344        if timing {
16345            use std::sync::atomic::Ordering;
16346            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16347            if calls % 430 == 0 {
16348                let avg = |t: &std::sync::atomic::AtomicU64| {
16349                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16350                };
16351                eprintln!(
16352                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16353                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16354                    avg(&T_POS),
16355                    avg(&T_QKV),
16356                    avg(&T_NORMROPE),
16357                    avg(&T_GATE),
16358                    avg(&T_APPEND),
16359                    avg(&T_ATTN),
16360                    avg(&T_OPROJ),
16361                    avg(&T_SHADOW),
16362                );
16363            }
16364        }
16365        eprintln!(
16366            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16367             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16368             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16369             attention_scope={} input_path={} kv_physical_rows={} \
16370             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16371             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16372             bulk_p2p={} output=root-readback performance_claim=false",
16373            tp.layer,
16374            tp.devices,
16375            hydrated,
16376            if window.is_some() {
16377                "rank-local-swa-ring"
16378            } else {
16379                "rank-local-global"
16380            },
16381            input_path,
16382            cache.tp_kv[il]
16383                .as_ref()
16384                .expect("distributed cache checked above")
16385                .physical_capacity(),
16386            tp.runtime.transport_label(),
16387            tp.runtime.bulk_p2p(),
16388        );
16389        Ok(output)
16390    }
16391
16392    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16393    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16394    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16395    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16396    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16397    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16398    #[allow(clippy::too_many_arguments)]
16399    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16400    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16401    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16402    /// the resident fused TP2 class (caller falls back to the per-row walk).
16403    pub(crate) fn step35_verify_qkv_precompute(
16404        &self,
16405        e: &Engine,
16406        il: usize,
16407        h_t: &CudaSlice<f32>,
16408        t: usize,
16409    ) -> Result<bool, Box<dyn std::error::Error>> {
16410        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16411            return Ok(false);
16412        };
16413        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16414            return Ok(false);
16415        };
16416        let Some(attention) = tp.attention.as_ref() else {
16417            return Ok(false);
16418        };
16419        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16420            return Ok(false);
16421        }
16422        let geometry = self.step35_geom(il);
16423        let heads = geometry.n_head as usize;
16424        let ws_index = tp
16425            .runtime
16426            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16427        let gate_shards = attention
16428            .gate_shards_bf16
16429            .as_deref()
16430            .map(crate::tp::StepTpGateShards::Bf16);
16431        tp.runtime.decode_v2_input_qkv_tcol(
16432            ws_index,
16433            e,
16434            h_t,
16435            t,
16436            &tp.q,
16437            &tp.k,
16438            &tp.v,
16439            gate_shards,
16440        )?;
16441        Ok(true)
16442    }
16443
16444    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16445    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16446    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16447    /// flag confirmed the defer engaged for every column.
16448    pub(crate) fn step35_verify_oproj_tcol(
16449        &self,
16450        e: &Engine,
16451        il: usize,
16452        t: usize,
16453    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16454        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16455            return Err("tcol o_proj join expects full attention".into());
16456        };
16457        let tp = fa
16458            .step_tp_qkv
16459            .as_ref()
16460            .ok_or("tcol o_proj join lost its resident projections")?;
16461        let heads = self.step35_geom(il).n_head as usize;
16462        let ws_index = tp
16463            .runtime
16464            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16465        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16466    }
16467
16468    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16469    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16470    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16471    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16472    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16473    /// walk runs the ordinary per-column program.
16474    pub(crate) fn step35_spec_fa2_precheck(
16475        &self,
16476        cache: &Cache,
16477        il: usize,
16478        pos0: usize,
16479    ) -> Result<bool, Box<dyn std::error::Error>> {
16480        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16481        // a silently-vacuous door is indistinguishable from a slow one without this.
16482        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16483            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16484            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16485            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16486                let mut seen = SEEN.lock().unwrap();
16487                if !seen.iter().any(|c| *c == clause) {
16488                    // leak: bounded by the clause-id set
16489                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16490                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16491                }
16492            }
16493            false
16494        }
16495        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16496        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16497        if let Some(only) =
16498            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16499        {
16500            if *only != il {
16501                return Ok(false);
16502            }
16503        }
16504        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16505            return Ok(nope("mixer", il, pos0));
16506        };
16507        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16508            return Ok(nope("step_tp", il, pos0));
16509        };
16510        let Some(attention) = tp.attention.as_ref() else {
16511            return Ok(nope("attention", il, pos0));
16512        };
16513        if !tp.runtime.native_p2p()
16514            || crate::Engine::kv_fp8_on()
16515            || !crate::tp::step_tp_dcw_enabled()?
16516            || !crate::tp::step_tp_qkv_fused_enabled()?
16517            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16518        {
16519            return Ok(nope("runtime-doors", il, pos0));
16520        }
16521        let geometry = self.step35_geom(il);
16522        let head_dim = geometry.head_dim_k as usize;
16523        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16524            return Ok(nope("fa-class", il, pos0));
16525        }
16526        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16527            return Ok(nope("tp-kv", il, pos0));
16528        };
16529        if distributed.staged_len() != pos0 {
16530            return Ok(nope("staged-len", il, pos0));
16531        }
16532        // Both appends must land without a ring rebase (rebase columns take the
16533        // host-row path, which cannot stash).
16534        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16535        if would_rebase {
16536            return Ok(nope("rebase", il, pos0));
16537        }
16538        let window = geometry.window.map(|w| w as usize);
16539        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16540        // shift by one key, so one shared tile grid cannot reproduce both rows'
16541        // per-column FP grouping) — and drifted verify logits change accept decisions,
16542        // breaking the spec==target contract. Engage only when BOTH rows' views start
16543        // at 0 (global, or SWA still inside its window): bitwise per row under the
16544        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16545        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16546        if let Some(w) = window {
16547            if pos0 + 2 > w {
16548                return Ok(nope("swa-capped", il, pos0));
16549            }
16550        }
16551        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16552        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16553        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16554        let (t0, t1) = (pos0 + 1, pos0 + 2);
16555        if t0 < 96 {
16556            return Ok(nope("dcw-floor", il, pos0));
16557        }
16558        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16559            return Ok(nope("vec-floor", il, pos0));
16560        }
16561        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16562        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16563        // the two rows' own launches — the joined kernel derives one grid from T1 and
16564        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16565        // the combine's merge rounding. Boundary rounds fall back per column.
16566        let ranks = tp.runtime.devices().len();
16567        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16568        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16569        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16570        if sp0 != sp1 {
16571            return Ok(nope("partition-sp", il, pos0));
16572        }
16573        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16574        if ns0 != ns1 {
16575            return Ok(nope("partition-ns", il, pos0));
16576        }
16577        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16578            return Ok(nope("partition-per", il, pos0));
16579        }
16580        Ok(true)
16581    }
16582
16583    /// T-ROW fa precheck (the rows kernel supersedes the dcw2 pair-join): every dynamic
16584    /// input of the engine-side dcw decision must hold for EVERY row — stashing is
16585    /// unrecoverable — plus the rows-launcher guards (big-rig ladder, no env split
16586    /// overrides). No partition or capped-SWA clauses: each row derives its OWN geometry.
16587    pub(crate) fn step35_fa_rows_precheck(
16588        &self,
16589        cache: &Cache,
16590        il: usize,
16591        pos0: usize,
16592        t: usize,
16593    ) -> Result<bool, Box<dyn std::error::Error>> {
16594        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16595            return Ok(false);
16596        };
16597        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16598            return Ok(false);
16599        };
16600        let Some(attention) = tp.attention.as_ref() else {
16601            return Ok(false);
16602        };
16603        if !tp.runtime.native_p2p()
16604            || crate::Engine::kv_fp8_on()
16605            || !crate::tp::step_tp_dcw_enabled()?
16606            || !crate::tp::step_tp_qkv_fused_enabled()?
16607            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16608        {
16609            return Ok(false);
16610        }
16611        let geometry = self.step35_geom(il);
16612        let head_dim = geometry.head_dim_k as usize;
16613        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16614            return Ok(false);
16615        }
16616        if crate::fa_sm_count() < 128
16617            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16618            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16619            || std::env::var("MEMRA_FA_SP16").is_ok()
16620            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16621        {
16622            return Ok(false);
16623        }
16624        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16625            return Ok(false);
16626        };
16627        if distributed.staged_len() != pos0 {
16628            return Ok(false);
16629        }
16630        let (_, would_rebase) = distributed.peek_append_ring(t)?;
16631        if would_rebase {
16632            return Ok(false);
16633        }
16634        // Row 0 sees the smallest view: its post-append effective t_kv must clear both
16635        // the dcw floor and the vec-class floor (later rows only grow).
16636        let window = geometry.window.map(|w| w as usize);
16637        let t0 = window.map(|w| (pos0 + 1).min(w)).unwrap_or(pos0 + 1);
16638        if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16639            return Ok(false);
16640        }
16641        Ok(true)
16642    }
16643
16644    /// T-ROW fa join for the verify walk (same-session rows: shared ring/len with
16645    /// len_back = t-1-r). Tables stage once per (layer, rank, ring, t) and live on the
16646    /// owning rank.
16647    pub(crate) fn step35_verify_fa_rows_join(
16648        &self,
16649        e: &Engine,
16650        il: usize,
16651        cache: &Cache,
16652        pos0: usize,
16653        t: usize,
16654    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16655        use cudarc::driver::DevicePtr;
16656        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16657            return Err("fa rows join expects full attention".into());
16658        };
16659        let tp = fa
16660            .step_tp_qkv
16661            .as_ref()
16662            .ok_or("fa rows join lost its resident projections")?;
16663        let geometry = self.step35_geom(il);
16664        let heads = geometry.n_head as usize;
16665        let head_dim = geometry.head_dim_k as usize;
16666        let window = geometry.window.map(|w| w as usize);
16667        let distributed = cache.tp_kv[il]
16668            .as_ref()
16669            .ok_or("fa rows join lost its distributed KV cache")?;
16670        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16671        // Host mirror of the kernel's big-rig ladder (launcher-guarded identical).
16672        let ladder = |t_kv: usize| -> usize {
16673            if t_kv <= 2048 {
16674                16
16675            } else if t_kv <= 16384 {
16676                64
16677            } else {
16678                128
16679            }
16680        };
16681        let mut max_ns = 1usize;
16682        for r in 0..t {
16683            let t_kv = window
16684                .map(|w| (pos0 + r + 1).min(w))
16685                .unwrap_or(pos0 + r + 1);
16686            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16687        }
16688        // Rebuild the tiny raw-pointer table from the live distributed cache immediately
16689        // before launch. A process-lifetime map cannot prove allocation generation: CUDA may
16690        // recycle len/base independently of the large K/V rings, making a pointer-key cache
16691        // hit refer to another session (Hermes `11339f5cd3c132a3`).
16692        let ranks = tp.runtime.devices().len();
16693        let mut tables = Vec::with_capacity(ranks);
16694        for rank in 0..ranks {
16695            let engine = tp
16696                .runtime
16697                .rank_engine(rank)
16698                .ok_or("fa rows join lost a rank engine")?;
16699            let rank_cache = distributed
16700                .rank(rank)
16701                .ok_or("fa rows join lost a KV cache rank")?;
16702            let _main = engine.gpu.enter_main()?;
16703            let s = engine.stream();
16704            let (kp, _g0) = rank_cache.k().device_ptr(&s);
16705            let (vp, _g1) = rank_cache.v().device_ptr(&s);
16706            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16707            let bp = match rank_cache.base_d() {
16708                Some(b) => {
16709                    let (p, _g) = b.device_ptr(&s);
16710                    p as u64
16711                }
16712                None => 0u64,
16713            };
16714            let mut host = Vec::with_capacity(t * 6);
16715            for r in 0..t {
16716                host.extend_from_slice(&[
16717                    kp as u64,
16718                    vp as u64,
16719                    lp as u64,
16720                    bp,
16721                    0u64,
16722                    (t - 1 - r) as u64,
16723                ]);
16724            }
16725            tables.push(engine.stream().clone_htod(&host)?);
16726        }
16727        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
16728        let ws_index = tp
16729            .runtime
16730            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16731        tp.runtime.decode_v2_fa_rows_join(
16732            ws_index,
16733            e,
16734            &tp.o,
16735            &tabs,
16736            t,
16737            head_dim,
16738            window.unwrap_or(0),
16739            max_ns,
16740            geometry.attention_scale(),
16741            k_tok_bytes,
16742            v_tok_bytes,
16743        )
16744    }
16745
16746    /// Multi-session t-row fa precheck (the batched serving walk): the static doors of
16747    /// the rows kernel plus per-SESSION dynamic checks — every row's own cache must be
16748    /// hydrated, in sync, rebase-free and above both floors.
16749    pub(crate) fn step35_batch_fa_rows_precheck(
16750        &self,
16751        caches: &[&mut Cache],
16752        row_to_cache: impl Fn(usize) -> usize,
16753        positions: &[i32],
16754        il: usize,
16755    ) -> Result<bool, Box<dyn std::error::Error>> {
16756        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16757            return Ok(false);
16758        };
16759        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16760            return Ok(false);
16761        };
16762        let Some(attention) = tp.attention.as_ref() else {
16763            return Ok(false);
16764        };
16765        if !tp.runtime.native_p2p()
16766            || crate::Engine::kv_fp8_on()
16767            || !crate::tp::step_tp_dcw_enabled()?
16768            || !crate::tp::step_tp_qkv_fused_enabled()?
16769            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16770        {
16771            return Ok(false);
16772        }
16773        let geometry = self.step35_geom(il);
16774        let head_dim = geometry.head_dim_k as usize;
16775        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16776            return Ok(false);
16777        }
16778        if crate::fa_sm_count() < 128
16779            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16780            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16781            || std::env::var("MEMRA_FA_SP16").is_ok()
16782            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16783        {
16784            return Ok(false);
16785        }
16786        let window = geometry.window.map(|w| w as usize);
16787        for (r, &pos) in positions.iter().enumerate() {
16788            let cache = &caches[row_to_cache(r)];
16789            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16790                return Ok(false);
16791            };
16792            if distributed.staged_len() != pos as usize {
16793                return Ok(false);
16794            }
16795            if distributed.peek_append_ring(1)?.1 {
16796                return Ok(false);
16797            }
16798            let t0 = window
16799                .map(|w| (pos as usize + 1).min(w))
16800                .unwrap_or(pos as usize + 1);
16801            if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16802                return Ok(false);
16803            }
16804        }
16805        Ok(true)
16806    }
16807
16808    /// FULL t-row attention pass for the VERIFY walk (same-session rows): rope/append +
16809    /// fa + combine + o_proj join in 3 launches/rank/layer. Row r appends at slot
16810    /// len-base+r and one last block advances len by t; the fa rows read len_back =
16811    /// t-1-r. Returns None when the fused-rope class does not hold (the walk keeps the
16812    /// per-column stash flow). Caller has passed `step35_fa_rows_precheck`.
16813    pub(crate) fn step35_verify_rope_fa_pass(
16814        &self,
16815        e: &Engine,
16816        il: usize,
16817        cache: &Cache,
16818        pos0: usize,
16819        t: usize,
16820        stage_pos: bool,
16821    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16822        use cudarc::driver::DevicePtr;
16823        if !crate::tp::fuse_rope_append_on() {
16824            return Ok(None);
16825        }
16826        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16827            return Ok(None);
16828        };
16829        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16830            return Ok(None);
16831        };
16832        let Some(attention) = tp.attention.as_ref() else {
16833            return Ok(None);
16834        };
16835        let geometry = self.step35_geom(il);
16836        let head_dim = geometry.head_dim_k as usize;
16837        if head_dim != 128 {
16838            return Ok(None);
16839        }
16840        let heads = geometry.n_head as usize;
16841        let window = geometry.window.map(|w| w as usize);
16842        let ranks = tp.runtime.devices().len();
16843        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16844            return Ok(None);
16845        };
16846        if distributed.kv_dim_k() != distributed.kv_dim_v() {
16847            return Ok(None);
16848        }
16849        {
16850            let rank0 = distributed.rank(0).ok_or("verify rope pass lost rank 0")?;
16851            if rank0.base_d().is_none()
16852                && distributed.staged_len() + t > distributed.physical_capacity()
16853            {
16854                return Ok(None);
16855            }
16856        }
16857        let mut rope_freqs = Vec::with_capacity(ranks);
16858        for rank in 0..ranks {
16859            let engine = tp
16860                .runtime
16861                .rank_engine(rank)
16862                .ok_or("verify rope pass lost a rank engine")?;
16863            rope_freqs.push(if geometry.rope_factors {
16864                match self
16865                    .step35_aux
16866                    .as_ref()
16867                    .and_then(|aux| aux.rope_freqs(engine))
16868                {
16869                    Some(f) => Some(f),
16870                    None => return Ok(None),
16871                }
16872            } else {
16873                None
16874            });
16875        }
16876        let ladder = |t_kv: usize| -> usize {
16877            if t_kv <= 2048 {
16878                16
16879            } else if t_kv <= 16384 {
16880                64
16881            } else {
16882                128
16883            }
16884        };
16885        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16886        let mut max_ns = 1usize;
16887        let mut positions = Vec::with_capacity(t);
16888        for r in 0..t {
16889            positions.push((pos0 + r) as i32);
16890            let t_kv = window
16891                .map(|w| (pos0 + r + 1).min(w))
16892                .unwrap_or(pos0 + r + 1);
16893            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16894        }
16895        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
16896        let mut tab_keys = vec![0u64; ranks];
16897        for rank in 0..ranks {
16898            let engine = tp
16899                .runtime
16900                .rank_engine(rank)
16901                .ok_or("verify rope pass lost a rank engine")?;
16902            let rank_cache = distributed
16903                .rank(rank)
16904                .ok_or("verify rope pass lost a KV cache rank")?;
16905            let _main = engine.gpu.enter_main()?;
16906            let s = engine.stream();
16907            let (kp, _g0) = rank_cache.k().device_ptr(&s);
16908            let (vp, _g1) = rank_cache.v().device_ptr(&s);
16909            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16910            let bp = match rank_cache.base_d() {
16911                Some(b) => {
16912                    let (p, _g) = b.device_ptr(&s);
16913                    p as u64
16914                }
16915                None => 0u64,
16916            };
16917            tab_keys[rank] = (kp as u64)
16918                .rotate_left(17)
16919                .wrapping_add(bp)
16920                .wrapping_add((il as u64) << 32)
16921                .wrapping_add(t as u64)
16922                .wrapping_add(1 << 63);
16923            for _r in 0..t {
16924                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
16925            }
16926        }
16927        let ws_index = tp
16928            .runtime
16929            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16930        tp.runtime
16931            .decode_v2_rope_fa_rows(
16932                ws_index,
16933                e,
16934                &tp.o,
16935                &session_parts,
16936                &tab_keys,
16937                &positions,
16938                stage_pos,
16939                true,
16940                &attention.q_norm,
16941                &attention.k_norm,
16942                &rope_freqs,
16943                t,
16944                head_dim,
16945                geometry.n_rot as usize,
16946                window.unwrap_or(0),
16947                max_ns,
16948                geometry.attention_scale(),
16949                k_tok_bytes,
16950                v_tok_bytes,
16951                self.cfg.rms_eps,
16952                geometry.rope_base,
16953            )
16954            .map(Some)
16955    }
16956
16957    /// FULL t-row attention pass for the batched walk (rope/append + fa + combine +
16958    /// o_proj join, 3 launches/rank/layer): returns None when the fused-rope class does
16959    /// not hold — the caller falls back to the per-row stash flow. The caller has
16960    /// already passed `step35_batch_fa_rows_precheck`.
16961    #[allow(clippy::too_many_arguments)]
16962    pub(crate) fn step35_batch_rope_fa_pass(
16963        &self,
16964        e: &Engine,
16965        il: usize,
16966        caches: &[&mut Cache],
16967        row_to_cache: impl Fn(usize) -> usize,
16968        positions: &[i32],
16969        t: usize,
16970        stage_pos: bool,
16971    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16972        use cudarc::driver::DevicePtr;
16973        if !crate::tp::fuse_rope_append_on() {
16974            return Ok(None);
16975        }
16976        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16977            return Ok(None);
16978        };
16979        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16980            return Ok(None);
16981        };
16982        let Some(attention) = tp.attention.as_ref() else {
16983            return Ok(None);
16984        };
16985        let geometry = self.step35_geom(il);
16986        let head_dim = geometry.head_dim_k as usize;
16987        if head_dim != 128 {
16988            return Ok(None);
16989        }
16990        let heads = geometry.n_head as usize;
16991        let window = geometry.window.map(|w| w as usize);
16992        let ranks = tp.runtime.devices().len();
16993        // The rows kernels never arm base_d; refuse once a ring could have rebased
16994        // without an armed base (the table would read base=0 after a real rebase).
16995        for r in 0..t {
16996            let cache = &caches[row_to_cache(r)];
16997            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16998                return Ok(None);
16999            };
17000            if distributed.kv_dim_k() != distributed.kv_dim_v() {
17001                return Ok(None);
17002            }
17003            let rank0 = distributed.rank(0).ok_or("rope fa pass lost rank 0")?;
17004            if rank0.base_d().is_none()
17005                && distributed.staged_len() + t > distributed.physical_capacity()
17006            {
17007                return Ok(None);
17008            }
17009        }
17010        let mut rope_freqs = Vec::with_capacity(ranks);
17011        for rank in 0..ranks {
17012            let engine = tp
17013                .runtime
17014                .rank_engine(rank)
17015                .ok_or("rope fa pass lost a rank engine")?;
17016            rope_freqs.push(if geometry.rope_factors {
17017                match self
17018                    .step35_aux
17019                    .as_ref()
17020                    .and_then(|aux| aux.rope_freqs(engine))
17021                {
17022                    Some(f) => Some(f),
17023                    None => return Ok(None),
17024                }
17025            } else {
17026                None
17027            });
17028        }
17029        let ladder = |t_kv: usize| -> usize {
17030            if t_kv <= 2048 {
17031                16
17032            } else if t_kv <= 16384 {
17033                64
17034            } else {
17035                128
17036            }
17037        };
17038        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17039        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17040        let mut tab_keys = vec![0u64; ranks];
17041        for (r, &pos) in positions.iter().enumerate().take(t) {
17042            let cache = &caches[row_to_cache(r)];
17043            let distributed = cache.tp_kv[il]
17044                .as_ref()
17045                .ok_or("rope fa pass lost a distributed KV cache")?;
17046            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17047            let t_kv = window
17048                .map(|w| (pos as usize + 1).min(w))
17049                .unwrap_or(pos as usize + 1);
17050            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17051            for rank in 0..ranks {
17052                let engine = tp
17053                    .runtime
17054                    .rank_engine(rank)
17055                    .ok_or("rope fa pass lost a rank engine")?;
17056                let rank_cache = distributed
17057                    .rank(rank)
17058                    .ok_or("rope fa pass lost a KV cache rank")?;
17059                let _main = engine.gpu.enter_main()?;
17060                let s = engine.stream();
17061                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17062                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17063                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17064                let bp = match rank_cache.base_d() {
17065                    Some(b) => {
17066                        let (p, _g) = b.device_ptr(&s);
17067                        p as u64
17068                    }
17069                    None => 0u64,
17070                };
17071                tab_keys[rank] = tab_keys[rank]
17072                    .rotate_left(9)
17073                    .wrapping_add(kp as u64)
17074                    .wrapping_add(bp)
17075                    .wrapping_add(il as u64);
17076                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17077            }
17078        }
17079        let ws_index = tp
17080            .runtime
17081            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17082        tp.runtime
17083            .decode_v2_rope_fa_rows(
17084                ws_index,
17085                e,
17086                &tp.o,
17087                &session_parts,
17088                &tab_keys,
17089                positions,
17090                stage_pos,
17091                false,
17092                &attention.q_norm,
17093                &attention.k_norm,
17094                &rope_freqs,
17095                t,
17096                head_dim,
17097                geometry.n_rot as usize,
17098                window.unwrap_or(0),
17099                max_ns,
17100                geometry.attention_scale(),
17101                k_tok_bytes,
17102                v_tok_bytes,
17103                self.cfg.rms_eps,
17104                geometry.rope_base,
17105            )
17106            .map(Some)
17107    }
17108
17109    /// Multi-session t-row fa join (batched serving): per-row table entries point at
17110    /// each row's OWN session rings/counters (len_back = 0 — every session appended
17111    /// exactly its one row). Tables stage once per (layer, rank, session-set, t).
17112    #[allow(clippy::too_many_arguments)]
17113    pub(crate) fn step35_batch_fa_rows_join(
17114        &self,
17115        e: &Engine,
17116        il: usize,
17117        caches: &[&mut Cache],
17118        row_to_cache: impl Fn(usize) -> usize,
17119        positions: &[i32],
17120        t: usize,
17121    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17122        use cudarc::driver::DevicePtr;
17123        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17124            return Err("batch fa rows join expects full attention".into());
17125        };
17126        let tp = fa
17127            .step_tp_qkv
17128            .as_ref()
17129            .ok_or("batch fa rows join lost its resident projections")?;
17130        let geometry = self.step35_geom(il);
17131        let heads = geometry.n_head as usize;
17132        let head_dim = geometry.head_dim_k as usize;
17133        let window = geometry.window.map(|w| w as usize);
17134        let ladder = |t_kv: usize| -> usize {
17135            if t_kv <= 2048 {
17136                16
17137            } else if t_kv <= 16384 {
17138                64
17139            } else {
17140                128
17141            }
17142        };
17143        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17144        for (r, &pos) in positions.iter().enumerate() {
17145            let cache = &caches[row_to_cache(r)];
17146            let distributed = cache.tp_kv[il]
17147                .as_ref()
17148                .ok_or("batch fa rows join lost a distributed KV cache")?;
17149            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17150            let t_kv = window
17151                .map(|w| (pos as usize + 1).min(w))
17152                .unwrap_or(pos as usize + 1);
17153            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17154        }
17155        // Multi-session tables also rebuild from every live K/V/len/base tuple. Keeping a
17156        // process-lifetime raw-pointer cache here omitted V and len identity and had no
17157        // allocation generation, so allocator reuse could bind one request to another.
17158        let ranks = tp.runtime.devices().len();
17159        let mut tables = Vec::with_capacity(ranks);
17160        for rank in 0..ranks {
17161            let engine = tp
17162                .runtime
17163                .rank_engine(rank)
17164                .ok_or("batch fa rows join lost a rank engine")?;
17165            let _main = engine.gpu.enter_main()?;
17166            let s = engine.stream();
17167            let mut host = Vec::with_capacity(t * 6);
17168            for r in 0..t {
17169                let cache = &caches[row_to_cache(r)];
17170                let distributed = cache.tp_kv[il]
17171                    .as_ref()
17172                    .ok_or("batch fa rows join lost a distributed KV cache")?;
17173                let rank_cache = distributed
17174                    .rank(rank)
17175                    .ok_or("batch fa rows join lost a KV cache rank")?;
17176                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17177                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17178                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17179                let bp = match rank_cache.base_d() {
17180                    Some(b) => {
17181                        let (p, _g) = b.device_ptr(&s);
17182                        p as u64
17183                    }
17184                    None => 0u64,
17185                };
17186                host.extend_from_slice(&[kp as u64, vp as u64, lp as u64, bp, 0u64, 0u64]);
17187            }
17188            tables.push(engine.stream().clone_htod(&host)?);
17189        }
17190        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17191        let ws_index = tp
17192            .runtime
17193            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17194        tp.runtime.decode_v2_fa_rows_join(
17195            ws_index,
17196            e,
17197            &tp.o,
17198            &tabs,
17199            t,
17200            head_dim,
17201            window.unwrap_or(0),
17202            max_ns,
17203            geometry.attention_scale(),
17204            k_tok_bytes,
17205            v_tok_bytes,
17206        )
17207    }
17208
17209    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
17210    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
17211    /// slab on `e`.
17212    pub(crate) fn step35_verify_spec_fa2_join(
17213        &self,
17214        e: &Engine,
17215        il: usize,
17216        cache: &Cache,
17217        pos0: usize,
17218    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17219        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17220            return Err("spec fa2 join expects full attention".into());
17221        };
17222        let tp = fa
17223            .step_tp_qkv
17224            .as_ref()
17225            .ok_or("spec fa2 join lost its resident projections")?;
17226        let geometry = self.step35_geom(il);
17227        let heads = geometry.n_head as usize;
17228        let head_dim = geometry.head_dim_k as usize;
17229        let window = geometry.window.map(|w| w as usize);
17230        // POST-append view of the second row (kernel T1 = len - lstart with len =
17231        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
17232        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
17233        let distributed = cache.tp_kv[il]
17234            .as_ref()
17235            .ok_or("spec fa2 join lost its distributed KV cache")?;
17236        let ws_index = tp
17237            .runtime
17238            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17239        tp.runtime.decode_v2_spec_fa2_join(
17240            ws_index,
17241            e,
17242            &tp.o,
17243            distributed,
17244            head_dim,
17245            window.unwrap_or(0),
17246            bucket,
17247            geometry.attention_scale(),
17248        )
17249    }
17250
17251    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
17252    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
17253    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
17254    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
17255    /// (caller falls back to the per-column walk).
17256    pub(crate) fn step35_verify_moe_tn(
17257        &self,
17258        e: &Engine,
17259        il: usize,
17260        z_t: &CudaSlice<f32>,
17261        t: usize,
17262    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17263        let layer = &self.layers[il];
17264        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
17265            return Ok(None);
17266        };
17267        let Some(tp) = m.step_tp.as_ref() else {
17268            return Ok(None);
17269        };
17270        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
17271            return Ok(None);
17272        };
17273        if !crate::tp::step_nvfp4_dev_routes_enabled()?
17274            || !crate::tp::step_tp_dev_router_enabled()?
17275            || !crate::tp::nvfp4_bank_v2_on()
17276            || bank.ep2
17277        {
17278            return Ok(None);
17279        }
17280        let cfg = &self.cfg;
17281        let Some(moe) = cfg.moe.as_ref() else {
17282            return Ok(None);
17283        };
17284        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
17285            return Ok(None);
17286        };
17287        let n_embd = cfg.n_embd as usize;
17288        let n_expert = moe.expert_count as usize;
17289        let n_used = moe.expert_used_count as usize;
17290        if t < 2 || t > 32 || z_t.len() < t * n_embd {
17291            return Err("verify moe t-row geometry".into());
17292        }
17293        let trace = std::env::var("MEMRA_TN_TRACE").as_deref() == Ok("1");
17294        if trace {
17295            eprintln!("[tn-trace] il={il} t={t} logits");
17296        }
17297        let logits = Self::moe_router_logits(e, m, z_t, t, cfg)?;
17298        // Persistent selection rows (host-op diet, same shape law as the t=1 SELW),
17299        // sized for the widest walk (t <= 8).
17300        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
17301            std::sync::Mutex::new(None);
17302        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
17303        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
17304            *selw = Some((
17305                e.ctx().ordinal(),
17306                e.htod_i32(&vec![0i32; 32 * n_used])?,
17307                e.htod(&vec![0.0f32; 32 * n_used])?,
17308            ));
17309        }
17310        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
17311        if trace {
17312            eprintln!("[tn-trace] il={il} topk logits_len={}", logits.len());
17313        }
17314        e.moe_router_sigmoid_topk_into(
17315            &logits,
17316            t,
17317            n_expert,
17318            n_used,
17319            m.active_count(),
17320            &m.exp_probs_b_dev,
17321            &m.active_experts_dev,
17322            sf,
17323            route_norm,
17324            sel_d,
17325            w_d,
17326        )?;
17327        if trace {
17328            eprintln!("[tn-trace] il={il} driver");
17329        }
17330        let mut out_t = tp
17331            .runtime
17332            .run_tensor_parallel_routes_nvfp4_device_routed_tn(
17333                bank,
17334                e,
17335                z_t,
17336                sel_d,
17337                w_d,
17338                t,
17339                n_used,
17340                tp.activation_limit,
17341            )?;
17342        if trace {
17343            eprintln!("[tn-trace] il={il} shexp out_t={}", out_t.len());
17344        }
17345        // Shared expert: ONE t-row pass through the per-row-exact twins when the bf16
17346        // dual-silu shape holds (each row's program == the t=1 fused path); otherwise the
17347        // exact t=1 program per column.
17348        if !Self::step35_shexp_rows(e, m, z_t, t, cfg, il as u16, &mut out_t)? {
17349            let mut z_row = e.uninit(n_embd)?;
17350            let mut out_row = e.uninit(n_embd)?;
17351            for c in 0..t {
17352                e.dtod_copy_view(&z_t.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
17353                e.dtod_copy_view(&out_t.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
17354                Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
17355                e.dtod_copy_into(&out_row, &mut out_t, c * n_embd)?;
17356            }
17357        }
17358        Ok(Some(out_t))
17359    }
17360
17361    /// T-ROW shared expert (spec verify / batched serving): dual-silu + down + gate +
17362    /// scaled accumulate over all rows in four launches, each the per-row-exact twin of
17363    /// the t=1 fused path. Returns false (untouched `out_t`) when the shape is ineligible.
17364    fn step35_shexp_rows(
17365        e: &Engine,
17366        m: &MoeWeights,
17367        z_t: &CudaSlice<f32>,
17368        t: usize,
17369        cfg: &ModelConfig,
17370        il: u16,
17371        out_t: &mut CudaSlice<f32>,
17372    ) -> Result<bool, Box<dyn std::error::Error>> {
17373        let n_embd = cfg.n_embd as usize;
17374        let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
17375            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
17376        else {
17377            return Ok(false);
17378        };
17379        if !crate::Engine::bf16_mmv_on() || n_embd % 8 != 0 || cfg.m3.is_some() {
17380            return Ok(false);
17381        }
17382        let (
17383            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
17384            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
17385            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
17386        ) = (gate_shexp, up_shexp, down_shexp)
17387        else {
17388            return Ok(false);
17389        };
17390        let n_ff_sh = gate_shexp.out_features();
17391        let lim = cfg.clamp_shexp_at(il as u32);
17392        // Persistent t-row buffers (widest walk t <= 8).
17393        static WS: std::sync::Mutex<Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>> =
17394            std::sync::Mutex::new(None);
17395        let mut guard = WS.lock().map_err(|_| "shexp rows ws lock is poisoned")?;
17396        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
17397        if guard
17398            .as_ref()
17399            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
17400        {
17401            *guard = Some((
17402                pins.0,
17403                pins.1,
17404                pins.2,
17405                e.uninit(32 * n_ff_sh)?,
17406                e.uninit(32 * n_embd)?,
17407            ));
17408        }
17409        let (_, _, _, act_t, sh_t) = guard.as_mut().expect("armed above");
17410        e.matvec_bf16_dual_silu_rows_into(wg, wu, z_t, act_t, n_embd, n_ff_sh, lim, t)?;
17411        e.matvec_bf16_rows_into(wd, act_t, sh_t, n_ff_sh, n_embd, t)?;
17412        // Head gate: sigmoid_dot_rows is the exact t=1 expression per row; gate-less
17413        // shexp accumulates at weight 1 (the fuse_da identity).
17414        let gate = match &m.gate_inp_shexp {
17415            Some(gate_inp_shexp) => {
17416                e.sigmoid_dot_rows(z_t, gate_inp_shexp.float_data(), n_embd, t)?
17417            }
17418            None => e.htod(&vec![1.0f32; t])?,
17419        };
17420        e.add_scaled_rows(sh_t, &gate, out_t, n_embd, t)?;
17421        Ok(true)
17422    }
17423
17424    fn step35_tp_decode_attn_resident_v2(
17425        &self,
17426        e: &Engine,
17427        fa: &FullAttnLayer,
17428        il: usize,
17429        h: &CudaSlice<f32>,
17430        pos_d: &CudaSlice<i32>,
17431        cache: &mut Cache,
17432    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17433        let tp = fa
17434            .step_tp_qkv
17435            .as_ref()
17436            .ok_or("Step TP decode lost its resident projections")?;
17437        let attention = tp
17438            .attention
17439            .as_ref()
17440            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
17441        if !tp.runtime.native_p2p() {
17442            return Err("rank-local Step attention requires native P2P".into());
17443        }
17444        if crate::Engine::kv_fp8_on() {
17445            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
17446        }
17447
17448        let geometry = self.step35_geom(il);
17449        let window = geometry.window.map(|window| window as usize);
17450        let ranks = tp.runtime.devices().len();
17451        let head_dim = geometry.head_dim_k as usize;
17452        let heads = geometry.n_head as usize;
17453        let kv_heads = geometry.n_head_kv as usize;
17454        if heads % ranks != 0 || kv_heads % ranks != 0 {
17455            return Err(format!(
17456                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
17457            )
17458            .into());
17459        }
17460        let local_heads = heads / ranks;
17461        let local_kv_heads = kv_heads / ranks;
17462        let max_ctx = cache.max_ctx;
17463
17464        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
17465
17466        let base_len = cache.kv[il]
17467            .as_ref()
17468            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
17469            .len;
17470        {
17471            let distributed = cache.tp_kv[il]
17472                .as_ref()
17473                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
17474            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
17475                return Err(format!(
17476                    "Step TP layer {il} cache lengths diverged before decode: \
17477                     local={base_len} distributed={}/{}",
17478                    distributed.committed_len(),
17479                    distributed.staged_len()
17480                )
17481                .into());
17482            }
17483        }
17484        if pos_d.len() != 1 {
17485            return Err(format!(
17486                "rank-local Step decode requires one position, got {}",
17487                pos_d.len()
17488            )
17489            .into());
17490        }
17491
17492        let decode_input = attention
17493            .decode_input
17494            .as_ref()
17495            .ok_or("Step TP decode v2 requires the replicated decode input")?;
17496        let mut decode_input = decode_input
17497            .lock()
17498            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
17499
17500        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
17501        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
17502        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
17503        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
17504        let use_gate_shards = (attention.gate_shards.is_some()
17505            || attention.gate_shards_bf16.is_some())
17506            && crate::tp::step_tp_qkv_fused_enabled()?;
17507        let gate_raw = if use_gate_shards {
17508            None
17509        } else {
17510            let gate_weight = fa
17511                .attn_gate
17512                .as_ref()
17513                .ok_or("step35 layer is missing attn_gate.weight")?;
17514            let gate_raw = e.matmul(gate_weight, h, 1)?;
17515            if gate_raw.len() != heads {
17516                return Err(format!(
17517                    "Step TP layer {il} gate output {} != {heads}",
17518                    gate_raw.len()
17519                )
17520                .into());
17521            }
17522            Some(gate_raw)
17523        };
17524
17525        let ws_index = tp
17526            .runtime
17527            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17528        let mut ws_guard = tp
17529            .runtime
17530            .decode_v2_workspace()
17531            .lock()
17532            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
17533        let ws = ws_guard
17534            .get_mut(ws_index)
17535            .ok_or("Step TP decode v2 workspace missing after ensure")?;
17536
17537        let mut rope_freqs = Vec::with_capacity(ranks);
17538        for rank in 0..ranks {
17539            let engine = tp
17540                .runtime
17541                .rank_engine(rank)
17542                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17543            rope_freqs.push(if geometry.rope_factors {
17544                self.step35_aux
17545                    .as_ref()
17546                    .and_then(|aux| aux.rope_freqs(engine))
17547            } else {
17548                None
17549            });
17550        }
17551        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
17552        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
17553        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
17554        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
17555        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
17556        // the fused rope+append+inc launch on dcw tokens.)
17557        let staged_next = base_len + 1;
17558        let t_kv_eff = window
17559            .map(|window| staged_next.min(window))
17560            .unwrap_or(staged_next);
17561        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
17562            let (write_row, would_rebase) = cache.tp_kv[il]
17563                .as_ref()
17564                .expect("distributed cache checked above")
17565                .peek_append_ring(1)?;
17566            if !would_rebase {
17567                // Arm the base mirrors on first use: base = logical staged - physical row.
17568                let base = (base_len - write_row) as i32;
17569                let distributed = cache.tp_kv[il]
17570                    .as_mut()
17571                    .expect("distributed cache checked above");
17572                for rank in 0..ranks {
17573                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
17574                        format!("Step TP layer {il} has no engine for rank {rank}")
17575                    })?;
17576                    let _main = engine.gpu.enter_main()?;
17577                    let rank_cache = distributed
17578                        .rank_mut(rank)
17579                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17580                    if rank_cache.base_d().is_none() {
17581                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
17582                    }
17583                }
17584            }
17585            !would_rebase
17586        };
17587        let fuse_rope = dcw
17588            && crate::tp::fuse_rope_append_on()
17589            && head_dim == 128
17590            && cache.tp_kv[il]
17591                .as_ref()
17592                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
17593                .unwrap_or(false);
17594
17595        let tcol_col = crate::tp::take_verify_tcol();
17596        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
17597        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
17598        // state must advance per column) but skips the fa+gate launch; post-rope q and
17599        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
17600        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
17601        // normally and the walk consumes the real output — stash flag stays unset).
17602        let fa2_col = crate::tp::take_spec_fa2_defer();
17603        tp.runtime.decode_v2_input_qkv(
17604            ws,
17605            e,
17606            h,
17607            pos_d,
17608            gate_raw.as_ref(),
17609            if !use_gate_shards {
17610                None
17611            } else if let Some(shards) = attention.gate_shards.as_deref() {
17612                Some(crate::tp::StepTpGateShards::F32(shards))
17613            } else {
17614                attention
17615                    .gate_shards_bf16
17616                    .as_deref()
17617                    .map(crate::tp::StepTpGateShards::Bf16)
17618            },
17619            &mut decode_input,
17620            &tp.q,
17621            &tp.k,
17622            &tp.v,
17623            &attention.q_norm,
17624            &attention.k_norm,
17625            head_dim,
17626            geometry.n_rot as usize,
17627            geometry.rope_base,
17628            &rope_freqs,
17629            self.cfg.rms_eps,
17630            fuse_rope,
17631            tcol_col,
17632        )?;
17633
17634        let transaction = cache.tp_kv[il]
17635            .as_mut()
17636            .expect("distributed cache checked above")
17637            .begin_transaction()?;
17638        let append_result = tp.runtime.append_tp_kv_transaction_inner(
17639            cache.tp_kv[il]
17640                .as_mut()
17641                .expect("distributed cache checked above"),
17642            transaction,
17643            &ws.k,
17644            &ws.v_raw,
17645            1,
17646            dcw,
17647        );
17648        if let Err(error) = append_result {
17649            let _ = tp.runtime.rollback_tp_kv_transaction(
17650                cache.tp_kv[il]
17651                    .as_mut()
17652                    .expect("distributed cache checked above"),
17653                transaction,
17654            );
17655            return Err(error);
17656        }
17657
17658        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17659            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
17660            // reborrows the cache mutably per rank.
17661            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
17662                let distributed = cache.tp_kv[il]
17663                    .as_ref()
17664                    .expect("distributed cache checked above");
17665                let staged_len = distributed.staged_len();
17666                let view_start = window
17667                    .map(|window| staged_len.saturating_sub(window))
17668                    .unwrap_or(0);
17669                (
17670                    staged_len,
17671                    distributed.physical_range(view_start, staged_len)?,
17672                    distributed.k_tok_bytes(),
17673                    distributed.v_tok_bytes(),
17674                    distributed.physical_capacity(),
17675                )
17676            };
17677            let view_start = window
17678                .map(|window| staged_len.saturating_sub(window))
17679                .unwrap_or(0);
17680            let t_kv = staged_len - view_start;
17681            for rank in 0..ranks {
17682                let engine = tp
17683                    .runtime
17684                    .rank_engine(rank)
17685                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17686                let _main = engine.gpu.enter_main()?;
17687                if dcw {
17688                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
17689                    // stream visit. distributed is borrowed shared here; the planes need mut —
17690                    // reborrow through the cache Option (the closure holds cache mutably).
17691                    {
17692                        let distributed_mut = cache.tp_kv[il]
17693                            .as_mut()
17694                            .expect("distributed cache checked above");
17695                        let (kv_dim_k, kv_dim_v) =
17696                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
17697                        let (k_tok_bytes, v_tok_bytes) =
17698                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
17699                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17700                            format!("Step TP layer {il} has no KV cache rank {rank}")
17701                        })?;
17702                        let (k_plane, v_plane, len_d, base_d) =
17703                            rank_cache.planes_and_counters_mut();
17704                        if fuse_rope {
17705                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
17706                            // + last-block len inc in ONE launch. Bit-identical bodies.
17707                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
17708                            let crate::tp::StepTpDecodeV2Ws {
17709                                q_raw,
17710                                k_raw,
17711                                v_raw,
17712                                q,
17713                                k,
17714                                pos,
17715                                pos_stage,
17716                                fuse_ctr,
17717                                ..
17718                            } = &mut *ws;
17719                            // Same-device rank: the staged-copy elision leaves pos[rank]
17720                            // stale — read the e-context pos stage directly (mirrors the
17721                            // rope elision in input_qkv_rank).
17722                            let pos_ref: &CudaSlice<i32> = if same_dev {
17723                                pos_stage
17724                                    .as_ref()
17725                                    .ok_or("step TP decode v2 pos stage not armed")?
17726                            } else {
17727                                &pos[rank]
17728                            };
17729                            engine.qk_norm_rope_append_inc_dcw(
17730                                &q_raw[rank],
17731                                &k_raw[rank],
17732                                &v_raw[rank],
17733                                &attention.q_norm[rank],
17734                                &attention.k_norm[rank],
17735                                &mut q[rank],
17736                                &mut k[rank],
17737                                pos_ref,
17738                                k_plane,
17739                                v_plane,
17740                                len_d,
17741                                base_d,
17742                                &mut fuse_ctr[rank],
17743                                kv_dim_k,
17744                                kv_dim_v,
17745                                k_tok_bytes,
17746                                v_tok_bytes,
17747                                head_dim,
17748                                geometry.n_rot as usize,
17749                                local_heads,
17750                                local_kv_heads,
17751                                self.cfg.rms_eps,
17752                                geometry.rope_base,
17753                                1.0,
17754                                rope_freqs[rank],
17755                            )?;
17756                        } else {
17757                            engine.append_kv_quantized_dcw(
17758                                &ws.k[rank],
17759                                &ws.v_raw[rank],
17760                                k_plane,
17761                                v_plane,
17762                                len_d,
17763                                base_d,
17764                                kv_dim_k,
17765                                kv_dim_v,
17766                                k_tok_bytes,
17767                                v_tok_bytes,
17768                            )?;
17769                        }
17770                        if !fuse_rope {
17771                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17772                                format!("Step TP layer {il} has no KV cache rank {rank}")
17773                            })?;
17774                            engine.inc_i32(rank_cache.len_d_mut())?;
17775                        }
17776                    }
17777                    let distributed = cache.tp_kv[il]
17778                        .as_ref()
17779                        .expect("distributed cache checked above");
17780                    let rank_cache = distributed
17781                        .rank(rank)
17782                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17783                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
17784                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
17785                    if fa2_col.is_some() {
17786                        // SPEC_FA2 defer: append landed above; the fa for this column
17787                        // runs in the T=2 joined launch after the pair's second append.
17788                        continue;
17789                    }
17790                    {
17791                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
17792                        // the gated output directly (bit-identical; one launch saved).
17793                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
17794                        engine.fa_decode_dcw(
17795                            &q[rank],
17796                            &k_ring,
17797                            &v_ring,
17798                            &mut gated[rank],
17799                            head_dim,
17800                            local_heads,
17801                            local_kv_heads,
17802                            rank_cache.len_d(),
17803                            rank_cache.base_d(),
17804                            window.unwrap_or(0),
17805                            t_kv,
17806                            geometry.attention_scale(),
17807                            k_tok_bytes_c,
17808                            v_tok_bytes_c,
17809                            Some(&gate[rank]),
17810                        )?;
17811                    }
17812                    continue;
17813                }
17814                let distributed = cache.tp_kv[il]
17815                    .as_ref()
17816                    .expect("distributed cache checked above");
17817                let rank_cache = distributed
17818                    .rank(rank)
17819                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17820                let k_view = engine.view_u8_range(
17821                    rank_cache.k(),
17822                    physical.start * k_tok_bytes_c,
17823                    physical.end * k_tok_bytes_c,
17824                );
17825                let v_view = engine.view_u8_range(
17826                    rank_cache.v(),
17827                    physical.start * v_tok_bytes_c,
17828                    physical.end * v_tok_bytes_c,
17829                );
17830                engine.fa_decode_kvmod(
17831                    &ws.q[rank],
17832                    &k_view,
17833                    &v_view,
17834                    &mut ws.attn_out[rank],
17835                    head_dim,
17836                    local_heads,
17837                    local_kv_heads,
17838                    t_kv,
17839                    geometry.attention_scale(),
17840                    k_tok_bytes_c,
17841                    v_tok_bytes_c,
17842                    false,
17843                )?;
17844                engine.attn_head_gate(
17845                    &ws.attn_out[rank],
17846                    &ws.gate[rank],
17847                    &mut ws.gated[rank],
17848                    None,
17849                    head_dim,
17850                    local_heads,
17851                    1,
17852                )?;
17853            }
17854
17855            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
17856            // column's `gated` rows and skip the per-column finish choreography entirely
17857            // (the batched b4_tcol + join runs after every column). The returned buffer
17858            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
17859            // stashed flag, never this buffer. Ineligible configs fall back to the
17860            // normal finish and the driver consumes the real `mixed` per column.
17861            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
17862                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
17863                // finish all run in the joined pass. Returned buffer is UNWRITTEN
17864                // (oproj-defer precedent — the walk reads the stash flag, never this).
17865                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
17866                crate::tp::set_spec_fa2_stashed();
17867                e.uninit(ws.o_out)?
17868            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
17869                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
17870                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
17871                    crate::tp::set_tcol_oproj_stashed();
17872                    e.uninit(ws.o_out)?
17873                } else {
17874                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17875                }
17876            } else {
17877                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17878            };
17879
17880            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
17881            // decode_v2_finish ordered behind the root event. Same math and cache state
17882            // transitions as v1.
17883            let local = cache.kv[il]
17884                .as_mut()
17885                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
17886            if local.len != base_len || base_len + 1 > max_ctx {
17887                return Err(format!(
17888                    "Step TP layer {il} local cache changed during decode: \
17889                     len={} base={base_len} max={max_ctx}",
17890                    local.len
17891                )
17892                .into());
17893            }
17894            if crate::tp::no_local_shadow_on() {
17895                // Lengths advance, contents stay stale (graph-door precedent: decode reads
17896                // only the distributed TP caches; local contents feed spec/MTP scratch).
17897                local.len = base_len + 1;
17898                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
17899                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
17900                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
17901                if !crate::tp::len_mirror_lazy_on() {
17902                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
17903                }
17904            } else {
17905                let retain_from = window
17906                    .map(|window| {
17907                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
17908                        let rollback_retain =
17909                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
17910                        staged_retain.min(rollback_retain)
17911                    })
17912                    .unwrap_or(0);
17913                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
17914                e.append_kv_quantized(
17915                    &ws.k_shadow,
17916                    &ws.v_shadow,
17917                    &mut local.k,
17918                    &mut local.v,
17919                    write_row,
17920                    local.kv_dim_k,
17921                    local.kv_dim_v,
17922                    local.k_tok_bytes,
17923                    local.v_tok_bytes,
17924                    false,
17925                )?;
17926                local.len = base_len + 1;
17927                e.set_i32_one(&mut local.len_d, local.len as i32)?;
17928            }
17929            Ok(output)
17930        })();
17931
17932        let output = match staged {
17933            Ok(output) => output,
17934            Err(error) => {
17935                let _ = tp.runtime.rollback_tp_kv_transaction(
17936                    cache.tp_kv[il]
17937                        .as_mut()
17938                        .expect("distributed cache checked above"),
17939                    transaction,
17940                );
17941                if let Some(local) = cache.kv[il].as_mut() {
17942                    local.len = base_len;
17943                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
17944                }
17945                return Err(error);
17946            }
17947        };
17948        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
17949        // the rank counters (same value as the absolute re-set on full accept), so commit
17950        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
17951        // keeps the absolute set (its appends do NOT inc).
17952        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
17953        if lazy_commit {
17954            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
17955                cache.tp_kv[il]
17956                    .as_mut()
17957                    .expect("distributed cache checked above"),
17958                transaction,
17959                1,
17960            ) {
17961                let _ = tp.runtime.rollback_tp_kv_transaction(
17962                    cache.tp_kv[il]
17963                        .as_mut()
17964                        .expect("distributed cache checked above"),
17965                    transaction,
17966                );
17967                let local = cache.kv[il].as_mut().expect("local cache checked above");
17968                local.len = base_len;
17969                e.set_i32_one(&mut local.len_d, base_len as i32)?;
17970                return Err(error);
17971            }
17972        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
17973            cache.tp_kv[il]
17974                .as_mut()
17975                .expect("distributed cache checked above"),
17976            transaction,
17977            1,
17978        ) {
17979            let _ = tp.runtime.rollback_tp_kv_transaction(
17980                cache.tp_kv[il]
17981                    .as_mut()
17982                    .expect("distributed cache checked above"),
17983                transaction,
17984            );
17985            let local = cache.kv[il].as_mut().expect("local cache checked above");
17986            local.len = base_len;
17987            e.set_i32_one(&mut local.len_d, base_len as i32)?;
17988            return Err(error);
17989        }
17990
17991        let committed = cache.tp_kv[il]
17992            .as_ref()
17993            .expect("distributed cache checked above")
17994            .committed_len();
17995        let local_len = cache.kv[il]
17996            .as_ref()
17997            .expect("local cache checked above")
17998            .len;
17999        if committed != local_len {
18000            return Err(format!(
18001                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
18002            )
18003            .into());
18004        }
18005        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
18006        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
18007            eprintln!(
18008                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
18009                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
18010                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
18011                 attention_tensor_parallel=true attention_scope={} \
18012                 input_path=root-device-replicated gate_tensor_parallel=false \
18013                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
18014                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
18015                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
18016                 performance_claim=false (logged once; every decode layer runs this driver)",
18017                tp.layer,
18018                tp.devices,
18019                if window.is_some() {
18020                    "rank-local-swa-ring"
18021                } else {
18022                    "rank-local-global"
18023                },
18024                tp.runtime.transport_label(),
18025                tp.runtime.bulk_p2p(),
18026            );
18027        }
18028        Ok(output)
18029    }
18030
18031    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
18032    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
18033    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
18034    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
18035    /// requiring `attn_gate`).
18036    ///
18037    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
18038    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
18039    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
18040    #[allow(clippy::too_many_arguments)]
18041    pub(crate) fn step35_decode_attn(
18042        &self,
18043        e: &Engine,
18044        fa: &FullAttnLayer,
18045        il: usize,
18046        h: &CudaSlice<f32>,
18047        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
18048        pos_d: &CudaSlice<i32>,
18049        cache: &mut Cache,
18050    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18051        if fa
18052            .step_tp_qkv
18053            .as_ref()
18054            .is_some_and(|tp| tp.attention.is_some())
18055        {
18056            if pre_q.is_some() {
18057                return Err(
18058                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
18059                     pre-quantized decode path"
18060                        .into(),
18061                );
18062            }
18063            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
18064        }
18065
18066        let geometry = self.step35_geom(il);
18067        let hd = geometry.head_dim_k as usize;
18068        let nkv = geometry.n_head_kv as usize;
18069        let nh = geometry.n_head as usize;
18070        let rbase = geometry.rope_base;
18071        let scale = geometry.attention_scale();
18072        let swa = geometry.window.is_some();
18073        let eps = self.cfg.rms_eps;
18074        let win = geometry.window.unwrap_or(0) as usize;
18075        let n_rot = geometry.n_rot as usize;
18076        let n_embd = self.cfg.n_embd as usize;
18077        let gw = fa
18078            .attn_gate
18079            .as_ref()
18080            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
18081
18082        let tp_qkv = if fa.step_tp_qkv.is_some() {
18083            if pre_q.is_some() {
18084                return Err(
18085                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
18086                     pre-quantized decode path"
18087                        .into(),
18088                );
18089            }
18090            self.step35_tp_qkv(e, fa, h, 1)?
18091        } else {
18092            None
18093        };
18094
18095        let (q0, k0, v0, gt) = match tp_qkv {
18096            Some(mut g3) => {
18097                let v = g3.pop().unwrap();
18098                let k = g3.pop().unwrap();
18099                let q = g3.pop().unwrap();
18100                let gt = e.matmul(gw, h, 1)?;
18101                (q, k, v, gt)
18102            }
18103            None => match pre_q {
18104                Some((hq, hdq)) => {
18105                    debug_assert!(
18106                        e.uses_q8_1_fast(gw),
18107                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
18108                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
18109                    );
18110                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
18111                        Some(t3) => t3,
18112                        None => (
18113                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
18114                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
18115                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
18116                        ),
18117                    };
18118                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
18119                    (a, b, c, gt)
18120                }
18121                None => {
18122                    if e.uses_q8_1_fast(&fa.wq)
18123                        && e.uses_q8_1_fast(&fa.wk)
18124                        && e.uses_q8_1_fast(&fa.wv)
18125                        && e.uses_q8_1_fast(gw)
18126                    {
18127                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
18128                        let (a, b, c) =
18129                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
18130                                Some(t3) => t3,
18131                                None => (
18132                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
18133                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
18134                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
18135                                ),
18136                            };
18137                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
18138                        (a, b, c, gt)
18139                    } else {
18140                        (
18141                            e.matmul(&fa.wq, h, 1)?,
18142                            e.matmul(&fa.wk, h, 1)?,
18143                            e.matmul(&fa.wv, h, 1)?,
18144                            e.matmul(gw, h, 1)?,
18145                        )
18146                    }
18147                }
18148            },
18149        };
18150
18151        let mut q = e.uninit(nh * hd)?;
18152        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
18153        let mut k = e.uninit(nkv * hd)?;
18154        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
18155        let ff = if swa {
18156            None
18157        } else {
18158            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
18159        };
18160        #[cfg(debug_assertions)]
18161        if let Some(ff) = ff {
18162            crate::debug_assert_tensor_stream_device(
18163                ff,
18164                &e.stream(),
18165                "step35_decode_attn.rope_freqs",
18166            );
18167        }
18168        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
18169
18170        if std::env::var("MEMRA_NOFA").is_ok() {
18171            return Err(
18172                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
18173                        cache; unset MEMRA_NOFA to use fa_decode"
18174                    .into(),
18175            );
18176        }
18177        let kvl = cache.kv[il].as_mut().unwrap();
18178        let next_len = kvl.len + 1;
18179        let (off, t_kv) = if swa && next_len > win {
18180            (next_len - win, win)
18181        } else {
18182            (0, next_len)
18183        };
18184        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
18185        e.append_kv_quantized(
18186            &k,
18187            &v0,
18188            &mut kvl.k,
18189            &mut kvl.v,
18190            write_row,
18191            kvl.kv_dim_k,
18192            kvl.kv_dim_v,
18193            kvl.k_tok_bytes,
18194            kvl.v_tok_bytes,
18195            crate::Engine::kv_fp8_on(),
18196        )?;
18197        kvl.len = next_len;
18198        let physical = kvl.physical_rows(off, off + t_kv)?;
18199        let k_view = e.view_u8_range(
18200            &kvl.k,
18201            physical.start * kvl.k_tok_bytes,
18202            physical.end * kvl.k_tok_bytes,
18203        );
18204        let v_view = e.view_u8_range(
18205            &kvl.v,
18206            physical.start * kvl.v_tok_bytes,
18207            physical.end * kvl.v_tok_bytes,
18208        );
18209        let mut attn = e.uninit(nh * hd)?;
18210        e.fa_decode_kvmod(
18211            &q,
18212            &k_view,
18213            &v_view,
18214            &mut attn,
18215            hd,
18216            nh,
18217            nkv,
18218            t_kv,
18219            scale,
18220            kvl.k_tok_bytes,
18221            kvl.v_tok_bytes,
18222            crate::Engine::kv_fp8_on(),
18223        )?;
18224
18225        let mut ag = e.uninit(nh * hd)?;
18226        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
18227        self.step35_o(e, fa, &ag, 1)
18228    }
18229}
18230
18231// ===================================================================================== //
18232//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
18233//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
18234//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
18235//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
18236//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
18237//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
18238// ===================================================================================== //
18239impl HybridModel {
18240    pub fn is_gemma4_e4b(&self) -> bool {
18241        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
18242    }
18243
18244    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
18245    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
18246    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
18247    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
18248        let g = self.cfg.gemma4.as_ref().unwrap();
18249        let swa = g.swa_pattern[il];
18250        let hd = if swa {
18251            g.key_length_swa
18252        } else {
18253            g.key_length_global
18254        } as usize;
18255        let Mixer::Full(fa) = &self.layers[il].mixer else {
18256            panic!("e4b layer {il} not full-attn")
18257        };
18258        let nh = fa.wq.out_features() / hd;
18259        let nkv = fa.wk.out_features() / hd;
18260        (
18261            hd,
18262            nkv,
18263            nh,
18264            if swa {
18265                g.rope_base_swa
18266            } else {
18267                g.rope_base_global
18268            },
18269            1.0,
18270            swa,
18271        )
18272    }
18273
18274    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
18275    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
18276        self.layers[il]
18277            .gemma4
18278            .as_ref()
18279            .and_then(|b| b.e4b.as_ref())
18280            .and_then(|e4| e4.kv_share.map(|t| t as usize))
18281    }
18282
18283    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
18284    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
18285    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
18286    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
18287    fn gemma4_e4b_inp_pl(
18288        &self,
18289        e: &Engine,
18290        tokens: &[u32],
18291        x_scaled: &CudaSlice<f32>,
18292        t: usize,
18293    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18294        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
18295        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
18296    }
18297
18298    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
18299    fn gemma4_e4b_inp_pl_dev(
18300        &self,
18301        e: &Engine,
18302        tok_d: &CudaSlice<u32>,
18303        x_scaled: &CudaSlice<f32>,
18304        t: usize,
18305    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18306        let aux = self.gemma4_aux.as_ref().unwrap();
18307        let m = aux.e4b.as_ref().unwrap();
18308        let n_embd = self.cfg.n_embd as usize;
18309        let n_layer = self.layers.len();
18310        let width = m.n_epl * n_layer;
18311        let tbl = m.tok_tbl_gpu.get_or_init(|| {
18312            e.upload_u8(&m.tok_embd_bytes)
18313                .expect("e4b per-layer token table upload")
18314        });
18315        let mut a =
18316            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
18317        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
18318        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
18319        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
18320        let mut pn = e.uninit(t * width)?;
18321        e.rms_norm(
18322            &p,
18323            m.proj_norm.float_data(),
18324            &mut pn,
18325            m.n_epl,
18326            t * n_layer,
18327            self.cfg.rms_eps,
18328        )?;
18329        let mut out = e.uninit(t * width)?;
18330        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
18331        Ok(out)
18332    }
18333
18334    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
18335    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
18336    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
18337    /// already holds this forward's rows — the target runs earlier in the stack).
18338    #[allow(clippy::too_many_arguments)]
18339    fn gemma4_e4b_attn(
18340        &self,
18341        e: &Engine,
18342        il: usize,
18343        hq: &CudaSlice<i8>,
18344        hdq: &CudaSlice<f32>,
18345        pos_d: &CudaSlice<i32>,
18346        t: usize,
18347        cache: &mut Cache,
18348        dc_bucket: Option<usize>,
18349    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18350        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
18351        let eps = self.cfg.rms_eps;
18352        let aux = self.gemma4_aux.as_ref().unwrap();
18353        let ones = aux.ones(e);
18354        #[cfg(debug_assertions)]
18355        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
18356        let Mixer::Full(fa) = &self.layers[il].mixer else {
18357            unreachable!()
18358        };
18359        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
18360        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
18361        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
18362        let h0 = e.zeros(0)?;
18363        let h = &h0;
18364
18365        let ff = if swa {
18366            None
18367        } else {
18368            Some(
18369                aux.rope_freqs(e)
18370                    .expect("e4b global rope needs rope_freqs.weight"),
18371            )
18372        };
18373        #[cfg(debug_assertions)]
18374        if let Some(ff) = ff {
18375            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
18376        }
18377        let share = self.gemma4_e4b_kv_target(il);
18378        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
18379        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
18380        let mut q;
18381        if let Some(_tgt) = share {
18382            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
18383            q = e.uninit(t * nh * hd)?;
18384            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
18385            // empty; q0 stands in for the unused k/v pointers).
18386            let mut kdummy = e.uninit(1)?;
18387            let mut vdummy = e.uninit(1)?;
18388            e.rms_norm_qkv_rope(
18389                &q0,
18390                &q0,
18391                &q0,
18392                fa.q_norm.float_data(),
18393                fa.q_norm.float_data(),
18394                ones,
18395                &mut q,
18396                &mut kdummy,
18397                &mut vdummy,
18398                hd,
18399                self.gemma4_rope_dims(il),
18400                nh * t,
18401                0,
18402                pos_d,
18403                nh,
18404                1,
18405                base,
18406                1.0,
18407                ff,
18408                eps,
18409            )?;
18410        } else {
18411            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
18412            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
18413            // q|k|v rows — the cat norm+rope twin consumes it directly.
18414            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
18415            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
18416            q = e.uninit(t * nh * hd)?;
18417            let mut k = e.uninit(t * nkv * hd)?;
18418            let mut v = e.uninit(t * nkv * hd)?;
18419            if t == 1 && cat.is_some() {
18420                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
18421                e.rms_norm_qkv_rope_cat(
18422                    &qkv0,
18423                    fa.q_norm.float_data(),
18424                    fa.k_norm.float_data(),
18425                    ones,
18426                    &mut q,
18427                    &mut k,
18428                    &mut v,
18429                    hd,
18430                    self.gemma4_rope_dims(il),
18431                    nh,
18432                    nkv,
18433                    pos_d,
18434                    nh,
18435                    nkv,
18436                    base,
18437                    1.0,
18438                    ff,
18439                    eps,
18440                )?;
18441            } else {
18442                let (q0, k0, v0) = match if t == 1 {
18443                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
18444                } else {
18445                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
18446                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
18447                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18448                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
18449                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
18450                    } else {
18451                        None
18452                    }
18453                } {
18454                    Some(triple) => triple,
18455                    None => (
18456                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
18457                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
18458                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
18459                    ), // E4B: real v (K != V)
18460                };
18461                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
18462                // the normed rows; V ones-rms, never roped).
18463                e.rms_norm_qkv_rope(
18464                    &q0,
18465                    &k0,
18466                    &v0,
18467                    fa.q_norm.float_data(),
18468                    fa.k_norm.float_data(),
18469                    ones,
18470                    &mut q,
18471                    &mut k,
18472                    &mut v,
18473                    hd,
18474                    self.gemma4_rope_dims(il),
18475                    nh * t,
18476                    nkv * t,
18477                    pos_d,
18478                    nh,
18479                    nkv,
18480                    base,
18481                    1.0,
18482                    ff,
18483                    eps,
18484                )?;
18485            }
18486            let kvl = cache.kv[il].as_mut().unwrap();
18487            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
18488            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
18489            // degenerate tok-0 stream, 2026-07-12).
18490            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18491            if dc_bucket.is_some() {
18492                // DC arm (graph serving): append at the len_d slot, advance the counter
18493                // in-stream — replay-correct, no host len in the launch args. Host mirrors
18494                // are NOT touched here (the replay loop owns them; a bump at capture-record
18495                // time would double-count the capture iteration).
18496                debug_assert!(t == 1);
18497                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
18498                e.append_kv_quantized_row_dc_inc(
18499                    &k,
18500                    &v,
18501                    &mut kvl.k,
18502                    &mut kvl.v,
18503                    &mut kvl.len_d,
18504                    kvl.kv_dim_k,
18505                    kvl.kv_dim_v,
18506                    kvl.k_tok_bytes,
18507                    kvl.v_tok_bytes,
18508                    cls,
18509                )?;
18510            } else {
18511                e.append_kv_quantized_rows(
18512                    &k,
18513                    &v,
18514                    &mut kvl.k,
18515                    &mut kvl.v,
18516                    kvl.len,
18517                    t,
18518                    kvl.kv_dim_k,
18519                    kvl.kv_dim_v,
18520                    kvl.k_tok_bytes,
18521                    kvl.v_tok_bytes,
18522                    cls,
18523                )?;
18524                kvl.len += t;
18525            }
18526            kv_f32 = Some((k, v));
18527        }
18528        // attention: per-row causal fa over the (own or target) quantized cache. The cache
18529        // already contains this forward's rows in both arms; row i attends [.., base+i].
18530        let kvl_idx = share.unwrap_or(il);
18531        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
18532        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
18533        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
18534        let mut attn = e.uninit(t * nh * hd)?;
18535        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
18536        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
18537        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
18538        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
18539        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
18540        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
18541        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
18542        //     rows (the T=K verify kernel; the target appended this forward's rows already).
18543        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
18544        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
18545        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
18546        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
18547            if let Some((kf, vf)) = &kv_f32 {
18548                if hd == 256 && t <= win {
18549                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18550                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18551                }
18552                if hd == 256 && swa && t > win {
18553                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18554                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18555                }
18556                if hd == 512 && !swa {
18557                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18558                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18559                }
18560            } else if share.is_some() {
18561                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18562                let k_view = e.view_u8(&kvl.k, kvl.k.len());
18563                let v_view = e.view_u8(&kvl.v, kvl.v.len());
18564                if hd == 256 && (!swa || t <= win) {
18565                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
18566                    e.fa_prefill_view(
18567                        &q,
18568                        &k_view,
18569                        &v_view,
18570                        &mut attn,
18571                        hd,
18572                        nh,
18573                        nkv,
18574                        t,
18575                        t,
18576                        scale,
18577                        true,
18578                        kvl.k_tok_bytes,
18579                        kvl.v_tok_bytes,
18580                        g,
18581                    )?;
18582                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18583                }
18584                // remaining shared classes (swa above the window; hd512 globals): dequant
18585                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
18586                let kv_dim = nkv * hd;
18587                let mut kf = e.uninit(t * kv_dim)?;
18588                let mut vf = e.uninit(t * kv_dim)?;
18589                e.fa_dequant_kv_view_f32(
18590                    &k_view,
18591                    &v_view,
18592                    &mut kf,
18593                    &mut vf,
18594                    kv_dim,
18595                    kv_dim,
18596                    t,
18597                    kvl.k_tok_bytes,
18598                    kvl.v_tok_bytes,
18599                    g,
18600                )?;
18601                if hd == 512 {
18602                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18603                } else {
18604                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18605                }
18606                return Ok(e.matmul(&fa.wo, &attn, t)?);
18607            }
18608        }
18609        if let Some(bucket) = dc_bucket {
18610            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
18611            // fa_decode_dc over the live counter. len_d already advanced past this token
18612            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
18613            // counter (advanced when the target ran earlier in the stack).
18614            assert!(t == 1);
18615            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
18616            // and under the window every live t_kv sits below it — cap the capture bucket
18617            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
18618            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
18619            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
18620            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
18621                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
18622            } else {
18623                bucket
18624            };
18625            let k_view = e.view_u8(&kvl.k, kvl.k.len());
18626            let v_view = e.view_u8(&kvl.v, kvl.v.len());
18627            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18628            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
18629            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
18630            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
18631            // captured into the dc graph like any other launch. Extending the cascade to
18632            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
18633            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
18634            // MEMRA_WPF=0 rollback seam.
18635            if crate::Engine::wpf_level() >= 1 {
18636                e.prefetch_weight_l2(&fa.wo)?;
18637            }
18638            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
18639            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
18640            if e.uses_q8_1_fast(&fa.wo) {
18641                let mut oq = e.alloc_i8_uninit(nh * hd)?;
18642                let mut od = e.zeros(nh * hd / 32)?;
18643                e.fa_decode_dc_q8(
18644                    &q,
18645                    &k_view,
18646                    &v_view,
18647                    &mut attn,
18648                    hd,
18649                    nh,
18650                    nkv,
18651                    &kvl.len_d,
18652                    bucket,
18653                    scale,
18654                    kvl.k_tok_bytes,
18655                    kvl.v_tok_bytes,
18656                    g,
18657                    Some((&mut oq, &mut od)),
18658                )?;
18659                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
18660            }
18661            e.fa_decode_dc(
18662                &q,
18663                &k_view,
18664                &v_view,
18665                &mut attn,
18666                hd,
18667                nh,
18668                nkv,
18669                &kvl.len_d,
18670                bucket,
18671                scale,
18672                kvl.k_tok_bytes,
18673                kvl.v_tok_bytes,
18674                g,
18675            )?;
18676            return Ok(e.matmul(&fa.wo, &attn, t)?);
18677        }
18678        for i in 0..t {
18679            let avail = base_len + i + 1;
18680            let (off_tok, t_kv) = if swa && avail > win {
18681                (avail - win, win)
18682            } else {
18683                (0, avail)
18684            };
18685            let k_view = e.view_u8_range(
18686                &kvl.k,
18687                off_tok * kvl.k_tok_bytes,
18688                (off_tok + t_kv) * kvl.k_tok_bytes,
18689            );
18690            let v_view = e.view_u8_range(
18691                &kvl.v,
18692                off_tok * kvl.v_tok_bytes,
18693                (off_tok + t_kv) * kvl.v_tok_bytes,
18694            );
18695            let qv = e.view(&q, t * nh * hd);
18696            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
18697            let mut q_one = e.uninit(nh * hd)?;
18698            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
18699            let mut a_one = e.uninit(nh * hd)?;
18700            // read class MUST match the append class (globals are e4m3 under gkv): the
18701            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
18702            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
18703            e.fa_decode_kvmod(
18704                &q_one,
18705                &k_view,
18706                &v_view,
18707                &mut a_one,
18708                hd,
18709                nh,
18710                nkv,
18711                t_kv,
18712                scale,
18713                kvl.k_tok_bytes,
18714                kvl.v_tok_bytes,
18715                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
18716            )?;
18717            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
18718        }
18719        Ok(e.matmul(&fa.wo, &attn, t)?)
18720    }
18721
18722    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
18723    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
18724    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
18725    /// layer; does NOT advance cache.pos (caller owns pos).
18726    fn gemma4_e4b_trunk(
18727        &self,
18728        e: &Engine,
18729        tokens: &[u32],
18730        pos0: usize,
18731        cache: &mut Cache,
18732        head_last: bool,
18733    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18734        let n_embd = self.cfg.n_embd as usize;
18735        let t = tokens.len();
18736        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18737        let pos_d = e.htod_i32(&pos)?;
18738        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
18739        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18740        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
18741        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
18742    }
18743
18744    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
18745    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
18746    /// eager chain by construction: SAME functions, not twins).
18747    fn gemma4_e4b_trunk_core(
18748        &self,
18749        e: &Engine,
18750        x_in: CudaSlice<f32>,
18751        inp_pl: CudaSlice<f32>,
18752        pos_d: &CudaSlice<i32>,
18753        t: usize,
18754        cache: &mut Cache,
18755        dc_bucket: Option<usize>,
18756        cap_logits: bool,
18757        head_last: bool,
18758    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18759        let n_embd = self.cfg.n_embd as usize;
18760        let eps = self.cfg.rms_eps;
18761        let n_layer = self.layers.len();
18762        let mut x = x_in;
18763        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
18764        let n_epl = aux_e4b.n_epl;
18765
18766        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
18767        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
18768        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
18769        // head rides matmul_pre too. First layer's pair comes from a standalone fused
18770        // norm+quant.
18771        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
18772        for il in 0..n_layer {
18773            let layer = &self.layers[il];
18774            let (hq, hdq) = match h_carry.take() {
18775                Some(p) => p,
18776                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
18777            };
18778            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
18779            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
18780            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
18781            let bits = layer.gemma4.as_ref().unwrap();
18782            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
18783            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
18784            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
18785            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
18786            // the fused single-phase reduction is NOT FP-order-identical to the unfused
18787            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
18788            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
18789            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
18790            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
18791            // gate dropped, decode AND verify ride the same fused chain — parity by
18792            // construction, VERIFY-GATE 0.000e0.
18793            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
18794            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
18795                e,
18796                layer,
18797                &o,
18798                &x,
18799                t,
18800                Some(layer.post_attn_norm.float_data()),
18801                fuse_exit,
18802            )?;
18803            let mut resid = e.uninit(t * n_embd)?;
18804            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
18805            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
18806            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
18807            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
18808            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
18809            let g = if fuse_exit {
18810                // sn here = RAW f0 (post_ffw deferred).
18811                let (rq, rd) = e.rms_pre_add_q8_1(
18812                    &sn,
18813                    bits.post_ffw_norm.float_data(),
18814                    &attn_out,
18815                    &mut resid,
18816                    n_embd,
18817                    t,
18818                    self.cfg.rms_eps,
18819                )?;
18820                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
18821            } else {
18822                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
18823                e.matmul(&e4b.inp_gate, &resid, t)?
18824            };
18825            let mut act = e.uninit(t * n_epl)?;
18826            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
18827                let ipv = e.view(&inp_pl, n_epl * n_layer);
18828                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
18829                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
18830                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
18831            } else {
18832                let mut inp_this = e.uninit(t * n_epl)?;
18833                e.copy_rows_strided(
18834                    &inp_pl,
18835                    &mut inp_this,
18836                    n_epl,
18837                    t,
18838                    n_epl * n_layer,
18839                    il * n_epl,
18840                )?;
18841                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
18842                e.matmul(&e4b.proj, &act, t)?
18843            };
18844            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
18845            // ONE launch (glue-fusion lane; last layer emits through output_norm).
18846            let next_norm = if il + 1 < n_layer {
18847                self.layers[il + 1].attn_norm.float_data()
18848            } else {
18849                self.output_norm.float_data()
18850            };
18851            let mut xn = e.uninit(t * n_embd)?;
18852            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
18853                &y,
18854                e4b.post_norm.float_data(),
18855                &resid,
18856                bits.layer_scale,
18857                next_norm,
18858                &mut xn,
18859                n_embd,
18860                t,
18861                eps,
18862            )?;
18863            h_carry = Some(pair);
18864            x = xn;
18865        }
18866        // the head consumes the last layer's fused (output_norm) emit. head_last callers
18867        // (prime, last_only forward) need only the final row's logits — the all-T head is
18868        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
18869        let (oq, odq) = h_carry.take().unwrap();
18870        let h0 = e.zeros(0)?;
18871        let hm = if head_last { 1 } else { t };
18872        let (hq, hd) = if head_last && t > 1 {
18873            let mut q1 = e.uninit_i8(n_embd)?;
18874            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
18875            let nb = n_embd / 32;
18876            let mut d1 = e.uninit(nb)?;
18877            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
18878            (q1, d1)
18879        } else {
18880            (oq, odq)
18881        };
18882        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
18883        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
18884        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
18885        // Logit-returning callers (host logits / spec prime) keep the capped emit.
18886        if cap_logits {
18887            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
18888            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
18889        }
18890        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
18891        Ok((ld, x))
18892    }
18893
18894    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
18895    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
18896    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
18897    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
18898    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
18899    /// covers exactly the layers that appended).
18900    pub fn gemma4_e4b_decode_step_t_am_dev(
18901        &self,
18902        e: &Engine,
18903        tok_d: &CudaSlice<u32>,
18904        t: usize,
18905        pos0: usize,
18906        cache: &mut Cache,
18907    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18908        let n_embd = self.cfg.n_embd as usize;
18909        let eps = self.cfg.rms_eps;
18910        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18911        let pos_d = e.htod_i32(&pos)?;
18912        let embd_gpu = self
18913            .embd_gpu
18914            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
18915        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
18916        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
18917        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18918        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
18919        let (ld, xp) =
18920            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
18921        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
18922        // emit is already capped, matching the eager chain bit-for-bit).
18923        let n_vocab = self.output.out_features();
18924        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
18925        for i in 0..t {
18926            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
18927        }
18928        let mut hn = e.uninit(t * n_embd)?;
18929        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18930        cache.pos += t;
18931        Ok((vam, hn))
18932    }
18933
18934    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
18935    /// prime path — mirror of `gemma4_decode_step_t_h`).
18936    pub(crate) fn gemma4_e4b_decode_step_t_h(
18937        &self,
18938        e: &Engine,
18939        tokens: &[u32],
18940        pos0: usize,
18941        cache: &mut Cache,
18942    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18943        let n_embd = self.cfg.n_embd as usize;
18944        let eps = self.cfg.rms_eps;
18945        let t = tokens.len();
18946        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
18947        let mut hn = e.uninit(t * n_embd)?;
18948        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18949        cache.pos += t;
18950        Ok((e.dtoh(&ld)?, hn))
18951    }
18952
18953    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
18954    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
18955    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
18956    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
18957    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
18958    pub fn gemma4_e4b_decode_step_dcg(
18959        &self,
18960        e: &Engine,
18961        token_d: &mut CudaSlice<u32>,
18962        pos_d: &mut CudaSlice<i32>,
18963        embd_gpu: &CudaSlice<u8>,
18964        embd_qt: i32,
18965        embd_rb: usize,
18966        cache: &mut Cache,
18967        n_vocab: usize,
18968        bucket: usize,
18969    ) -> Result<(), Box<dyn std::error::Error>> {
18970        let n_embd = self.cfg.n_embd as usize;
18971        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18972        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18973        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18974        let (ld, _x) =
18975            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
18976        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
18977        e.inc_seqlen(pos_d)?;
18978        Ok(())
18979    }
18980
18981    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
18982    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
18983    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
18984    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
18985    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
18986    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
18987    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
18988    #[allow(clippy::too_many_arguments)]
18989    pub fn gemma4_e4b_decode_step_dc(
18990        &self,
18991        e: &Engine,
18992        token_d: &CudaSlice<u32>,
18993        pos_d: &mut CudaSlice<i32>,
18994        embd_gpu: &CudaSlice<u8>,
18995        embd_qt: i32,
18996        embd_rb: usize,
18997        cache: &mut Cache,
18998        n_vocab: usize,
18999    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
19000        let n_embd = self.cfg.n_embd as usize;
19001        let eps = self.cfg.rms_eps;
19002        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
19003        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
19004        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
19005        let (ld, _x) =
19006            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
19007        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
19008        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
19009        e.inc_seqlen(pos_d)?;
19010        cache.pos += 1;
19011        let _ = eps;
19012        Ok(tok_out)
19013    }
19014
19015    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
19016    /// pre-output_norm hidden). Advances cache.pos.
19017    pub(crate) fn gemma4_e4b_decode_step_h(
19018        &self,
19019        e: &Engine,
19020        token: u32,
19021        cache: &mut Cache,
19022    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19023        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
19024        let logits = e.dtoh(&ld)?;
19025        cache.pos += 1;
19026        Ok((logits, x))
19027    }
19028
19029    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
19030    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
19031    /// fast; the prefill fa arms come later.
19032    pub(crate) fn gemma4_e4b_prime(
19033        &self,
19034        e: &Engine,
19035        tokens: &[u32],
19036        cache: &mut Cache,
19037    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19038        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
19039        // process-kill as gemma4_prime — refuse per-request.
19040        if cache.pos != 0 {
19041            return Err(
19042                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
19043                        call or decode tokenwise"
19044                    .into(),
19045            );
19046        }
19047        let n_embd = self.cfg.n_embd as usize;
19048        let t = tokens.len();
19049        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
19050        cache.pos += t;
19051        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
19052        let xv = e.view(&x, t * n_embd);
19053        let row = xv.slice((t - 1) * n_embd..t * n_embd);
19054        let mut h_seed = e.uninit(n_embd)?;
19055        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
19056        Ok((last, h_seed, x))
19057    }
19058
19059    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
19060    pub(crate) fn gemma4_e4b_forward(
19061        &self,
19062        e: &Engine,
19063        tokens: &[u32],
19064        last_only: bool,
19065    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19066        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
19067        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
19068        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
19069    }
19070}
19071
19072#[cfg(test)]
19073mod prime_chunk_schedule_tests {
19074    use super::{
19075        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
19076        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
19077        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
19078        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
19079    };
19080
19081    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
19082        ranges.iter().map(|(start, end)| end - start).collect()
19083    }
19084
19085    fn auto_chunk(t: usize) -> usize {
19086        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
19087    }
19088
19089    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
19090    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
19091    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
19092    /// must land every boundary on it without changing coverage.
19093    #[test]
19094    fn auto_prime_ranges_align_to_the_gdn_grid() {
19095        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
19096        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
19097            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
19098            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
19099            for w in ranges.windows(2) {
19100                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
19101            }
19102            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
19103        };
19104
19105        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
19106        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
19107        let t = 9510usize;
19108        let fill = auto_chunk(t);
19109        let fixed = fixed_prime_chunk_ranges(t, fill);
19110        assert!(
19111            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
19112            "broken arm vanished: fixed auto boundaries all landed on-grid"
19113        );
19114        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
19115        assert!(
19116            dynamic[..dynamic.len() - 1]
19117                .iter()
19118                .any(|&(_, e)| e % c != 0),
19119            "broken arm vanished: dynamic auto boundaries all landed on-grid"
19120        );
19121
19122        for ranges in [&fixed, &dynamic] {
19123            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
19124            assert_covers(&aligned, t);
19125            for &(_, e) in &aligned[..aligned.len() - 1] {
19126                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
19127            }
19128            // boundaries only move DOWN, at most c-1 tokens.
19129            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
19130                assert!(a <= b && b - a < c);
19131            }
19132        }
19133
19134        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
19135        // empty range; the schedule survives degenerate short fills.
19136        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
19137        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
19138        assert_covers(&aligned, 200);
19139        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
19140
19141        // No-ops: single range, c=0 (grid off), already-aligned schedules.
19142        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
19143        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
19144        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
19145        assert_eq!(
19146            align_prime_ranges_to_gdn(&on_grid, 300, c),
19147            on_grid.as_slice()
19148        );
19149    }
19150
19151    #[test]
19152    fn active_matrix_prefix_scopes_reused_prime_slabs() {
19153        assert_eq!(
19154            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
19155            29 * 4096
19156        );
19157        assert_eq!(
19158            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
19159            29 * 4096
19160        );
19161        assert_eq!(
19162            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
19163            24 * 4096
19164        );
19165        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
19166        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
19167    }
19168
19169    #[test]
19170    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
19171        assert!(validate_step_prime_batch_modes(false, false).is_ok());
19172
19173        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
19174        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
19175
19176        for grouped in [false, true] {
19177            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
19178            assert!(err.contains("did not clear the live-server performance gate"));
19179            assert!(err.contains("per-session grouped prefill"));
19180        }
19181    }
19182
19183    #[test]
19184    fn step_grouped_path_is_eager_single_token_only() {
19185        assert!(step_grouped_decode_shape(false, 1));
19186        assert!(!step_grouped_decode_shape(true, 1));
19187        assert!(!step_grouped_decode_shape(false, 2));
19188        assert!(!step_grouped_decode_shape(true, 2));
19189    }
19190
19191    #[test]
19192    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
19193        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
19194        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
19195        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
19196        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
19197        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
19198        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
19199
19200        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
19201        assert!(step_grouped_prefill_shape(
19202            true,
19203            true,
19204            crate::cache::PRIME_CHUNK_MAX_TOKENS,
19205        ));
19206        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
19207        assert!(!step_grouped_prefill_shape(
19208            true,
19209            true,
19210            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
19211        ));
19212        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
19213        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
19214    }
19215
19216    #[test]
19217    fn step_tp_prefill_door_is_strict_and_default_off() {
19218        assert!(!parse_step_tp_prefill(None).unwrap());
19219        assert!(!parse_step_tp_prefill(Some("")).unwrap());
19220        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
19221        assert!(parse_step_tp_prefill(Some("1")).unwrap());
19222        assert!(parse_step_tp_prefill(Some("true")).is_err());
19223        assert!(parse_step_tp_prefill(Some("2")).is_err());
19224    }
19225
19226    #[test]
19227    fn step_tp_prefill_requires_a_qualified_even_rank_shape() {
19228        assert!(step_tp_prefill_shape(
19229            true,
19230            PRIME_MIN_T,
19231            4,
19232            true,
19233            true,
19234            false,
19235        ));
19236        assert!(!step_tp_prefill_shape(
19237            false,
19238            PRIME_MIN_T,
19239            4,
19240            true,
19241            true,
19242            false,
19243        ));
19244        assert!(!step_tp_prefill_shape(
19245            true,
19246            PRIME_MIN_T - 1,
19247            4,
19248            true,
19249            true,
19250            false,
19251        ));
19252        // TP2 admits (2026-08-25); odd/1-card placements still refuse.
19253        assert!(step_tp_prefill_shape(
19254            true,
19255            PRIME_MIN_T,
19256            2,
19257            true,
19258            true,
19259            false
19260        ));
19261        assert!(!step_tp_prefill_shape(
19262            true,
19263            PRIME_MIN_T,
19264            1,
19265            true,
19266            true,
19267            false
19268        ));
19269        assert!(!step_tp_prefill_shape(
19270            true,
19271            PRIME_MIN_T,
19272            3,
19273            true,
19274            true,
19275            false
19276        ));
19277        assert!(!step_tp_prefill_shape(
19278            true,
19279            PRIME_MIN_T,
19280            4,
19281            false,
19282            true,
19283            false,
19284        ));
19285        assert!(!step_tp_prefill_shape(
19286            true,
19287            PRIME_MIN_T,
19288            4,
19289            true,
19290            false,
19291            false,
19292        ));
19293        assert!(!step_tp_prefill_shape(
19294            true,
19295            PRIME_MIN_T,
19296            4,
19297            true,
19298            true,
19299            true,
19300        ));
19301    }
19302
19303    #[test]
19304    fn fixed_schedule_retains_measured_geometry() {
19305        assert_eq!(
19306            sizes(&fixed_prime_chunk_ranges(461, 128)),
19307            vec![128, 128, 128, 77]
19308        );
19309        assert_eq!(
19310            sizes(&fixed_prime_chunk_ranges(1833, 230)),
19311            vec![230, 230, 230, 230, 230, 230, 230, 223]
19312        );
19313        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
19314        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
19315        assert_eq!(capped, vec![4096, 4088, 16]);
19316        assert!(capped.iter().all(|&rows| rows <= 4096));
19317        assert_eq!(
19318            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
19319            vec![4100],
19320            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
19321        );
19322    }
19323
19324    #[test]
19325    fn dynamic_schedule_matches_registered_shapes() {
19326        let cases = [
19327            (461, vec![64, 141, 132, 124]),
19328            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
19329            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
19330        ];
19331        for (t, expected) in cases {
19332            let chunk = auto_chunk(t);
19333            let fixed = fixed_prime_chunk_ranges(t, chunk);
19334            assert_eq!(
19335                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
19336                expected
19337            );
19338        }
19339    }
19340
19341    #[test]
19342    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
19343        for t in 256..=8192 {
19344            let chunk = auto_chunk(t);
19345            let fixed = fixed_prime_chunk_ranges(t, chunk);
19346            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
19347            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
19348            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
19349            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
19350            for pair in dynamic.windows(2) {
19351                assert_eq!(pair[0].1, pair[1].0, "T={t}");
19352            }
19353            assert!(
19354                dynamic
19355                    .iter()
19356                    .all(|(start, end)| end - start >= PRIME_MIN_T),
19357                "T={t} sizes={:?}",
19358                sizes(&dynamic)
19359            );
19360            if dynamic.len() >= 3 {
19361                let chunk_sizes = sizes(&dynamic);
19362                assert!(
19363                    chunk_sizes[0] < chunk_sizes[1],
19364                    "T={t} sizes={chunk_sizes:?}"
19365                );
19366                assert!(
19367                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
19368                    "T={t} sizes={chunk_sizes:?}"
19369                );
19370            }
19371        }
19372    }
19373}
19374
19375#[cfg(test)]
19376mod page_prefetch_tests {
19377    use super::{
19378        grouped_worker_prefetch_position, page_prefetch_positions,
19379        page_prefetch_window_from_values, worker_prefetch_positions,
19380    };
19381
19382    #[test]
19383    fn page_prefetch_window_keeps_existing_opt_in_default() {
19384        assert_eq!(page_prefetch_window_from_values(false, None), 0);
19385        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
19386        assert_eq!(page_prefetch_window_from_values(true, None), 1);
19387        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
19388        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
19389        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
19390    }
19391
19392    #[test]
19393    fn rolling_page_prefetch_advises_each_future_expert_once() {
19394        let advised: Vec<_> = (0..7)
19395            .flat_map(|position| page_prefetch_positions(position, 7, 3))
19396            .collect();
19397        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
19398
19399        let one_ahead: Vec<_> = (0..4)
19400            .flat_map(|position| page_prefetch_positions(position, 4, 1))
19401            .collect();
19402        assert_eq!(one_ahead, vec![1, 2, 3]);
19403        assert!(page_prefetch_positions(0, 4, 0).is_empty());
19404    }
19405
19406    #[test]
19407    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
19408        assert_eq!(grouped_worker_prefetch_position(0, None), None);
19409        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
19410            .chain(
19411                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
19412            )
19413            .collect();
19414        assert_eq!(positions, vec![0, 1, 2, 3]);
19415        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
19416    }
19417
19418    #[test]
19419    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
19420        let queued: Vec<_> = (0..8)
19421            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
19422            .collect();
19423        assert_eq!(queued, (0..8).collect::<Vec<_>>());
19424
19425        let one_at_a_time: Vec<_> = (0..4)
19426            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
19427            .collect();
19428        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
19429        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
19430    }
19431}
19432
19433pub struct G4DcSlots {
19434    x: CudaSlice<f32>,
19435    xn: CudaSlice<f32>,
19436    cur: CudaSlice<f32>,
19437    hq: CudaSlice<i8>,
19438    hd_: CudaSlice<f32>,
19439    q0: CudaSlice<f32>,
19440    k0: CudaSlice<f32>,
19441    v0: CudaSlice<f32>,
19442    q: CudaSlice<f32>,
19443    k: CudaSlice<f32>,
19444    v: CudaSlice<f32>,
19445    attn: CudaSlice<f32>,
19446    o: CudaSlice<f32>,
19447    attn_out: CudaSlice<f32>,
19448    zsh: CudaSlice<f32>,
19449    zq: CudaSlice<i8>,
19450    zd: CudaSlice<f32>,
19451    gate: CudaSlice<f32>,
19452    up: CudaSlice<f32>,
19453    act: CudaSlice<f32>,
19454    actq: CudaSlice<i8>,
19455    actd: CudaSlice<f32>,
19456    f0: CudaSlice<f32>,
19457    sn: CudaSlice<f32>,
19458    hn: CudaSlice<f32>,
19459    logits: CudaSlice<f32>,
19460}
19461
19462/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
19463/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
19464/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
19465/// fixed logits stage the head writes.
19466pub struct Step35TokenGraphState {
19467    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
19468    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
19469    pub token_d: cudarc::driver::CudaSlice<u32>,
19470    pub pos_d: cudarc::driver::CudaSlice<i32>,
19471    pub logits_stage: cudarc::driver::CudaSlice<f32>,
19472    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
19473    /// launch, so an alloc made inside one captured child is not referable from another):
19474    /// the running residual, the post-attention pair, the shared-expert row, and the
19475    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
19476    pub x: cudarc::driver::CudaSlice<f32>,
19477    pub x1: cudarc::driver::CudaSlice<f32>,
19478    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
19479    pub sh_stage: cudarc::driver::CudaSlice<f32>,
19480    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
19481    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
19482    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
19483    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
19484    pub router_logits: cudarc::driver::CudaSlice<f32>,
19485    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
19486    pub shexp_up: cudarc::driver::CudaSlice<f32>,
19487    pub shexp_act: cudarc::driver::CudaSlice<f32>,
19488    pub gate_sig: cudarc::driver::CudaSlice<f32>,
19489    pub dense_z: cudarc::driver::CudaSlice<f32>,
19490    pub dense_gate: cudarc::driver::CudaSlice<f32>,
19491    pub dense_up: cudarc::driver::CudaSlice<f32>,
19492    pub dense_act: cudarc::driver::CudaSlice<f32>,
19493    pub hn: cudarc::driver::CudaSlice<f32>,
19494    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
19495    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
19496    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
19497    pub probe_x: cudarc::driver::CudaSlice<f32>,
19498    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
19499    /// the in-graph tail argmax chain; host reads the ring once per chunk.
19500    pub token_hist: cudarc::driver::CudaSlice<u32>,
19501    pub hist_idx: cudarc::driver::CudaSlice<i32>,
19502}
19503
19504impl HybridModel {
19505    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
19506    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
19507    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
19508    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
19509    /// needs a rebuild this token).
19510    ///
19511    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
19512    /// but not their contents under this door (the TP rank caches are fully maintained
19513    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
19514    /// must not run with the door on until the local-dcw twin lands.
19515    pub(crate) fn step35_token_graph_step(
19516        &self,
19517        e: &Engine,
19518        token: u32,
19519        cache: &mut Cache,
19520    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
19521        if !self.uses_sliding_gated_moe_program()
19522            || !crate::tp::step_tp_graph_enabled()?
19523            || !crate::tp::step_tp_dcw_enabled()?
19524            || !crate::tp::step_tp_qkv_fused_enabled()?
19525            || !crate::tp::step_tp_dev_router_enabled()?
19526            || !crate::tp::step_nvfp4_dev_routes_enabled()?
19527        {
19528            return Ok(None);
19529        }
19530        let n_embd = self.cfg.n_embd as usize;
19531        let n_vocab = self.cfg.n_vocab as usize;
19532        let eps = self.cfg.rms_eps;
19533        let n_layers = self.layers.len();
19534        let pos = cache.pos;
19535        let staged_next = pos + 1;
19536        if staged_next < 96 {
19537            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
19538        }
19539
19540        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
19541        // eager fallback for the whole token; the host path also updates base_d there).
19542        for il in 0..n_layers {
19543            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
19544                return Ok(None); // caches not hydrated yet — eager warms them
19545            };
19546            if tp_kv.peek_append_ring(1)?.1 {
19547                return Ok(None);
19548            }
19549        }
19550
19551        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
19552        // their window and share one bucket forever after ctx > window).
19553        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
19554        if !fa_vec {
19555            return Ok(None);
19556        }
19557        let sp = crate::fa_split_keys(staged_next, 8);
19558        let bucket_max = (n_splits * sp).max(staged_next);
19559
19560        let mut state_guard = self
19561            .step35_token_graph
19562            .lock()
19563            .map_err(|_| "step35 token graph lock is poisoned")?;
19564        if state_guard.is_none() {
19565            let _main = e.gpu.enter_main()?;
19566            let n_expert = self
19567                .cfg
19568                .moe
19569                .as_ref()
19570                .map(|m| m.expert_count as usize)
19571                .unwrap_or(0);
19572            let n_ff_sh = self
19573                .layers
19574                .iter()
19575                .find_map(|l| match &l.ffn {
19576                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
19577                    _ => None,
19578                })
19579                .unwrap_or(0);
19580            let n_ff_dense = self
19581                .layers
19582                .iter()
19583                .find_map(|l| match &l.ffn {
19584                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
19585                    _ => None,
19586                })
19587                .unwrap_or(0);
19588            *state_guard = Some(Step35TokenGraphState {
19589                graphs: Vec::new(),
19590                token_d: e.stream().clone_htod(&[0u32])?,
19591                pos_d: e.htod_i32(&[pos as i32])?,
19592                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
19593                x: e.htod(&vec![0.0f32; n_embd])?,
19594                x1: e.htod(&vec![0.0f32; n_embd])?,
19595                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
19596                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
19597                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19598                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19599                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
19600                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19601                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19602                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19603                gate_sig: e.htod(&vec![1.0f32; 1])?,
19604                dense_z: e.htod(&vec![0.0f32; n_embd])?,
19605                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19606                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19607                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19608                hn: e.htod(&vec![0.0f32; n_embd])?,
19609                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
19610                probe_x: e.htod(&vec![0.0f32; n_embd])?,
19611                token_hist: e.stream().clone_htod(&[0u32; 16])?,
19612                hist_idx: e.htod_i32(&[0])?,
19613            });
19614        }
19615        let state = state_guard.as_mut().expect("state armed above");
19616        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
19617        // first use, and an alloc inside a captured section is a mem node (child graphs
19618        // reject those — the tail argmax chain needs them already resident).
19619        {
19620            let _main = e.gpu.enter_main()?;
19621            let Step35TokenGraphState {
19622                logits_stage,
19623                token_d,
19624                ..
19625            } = &mut *state;
19626            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
19627        }
19628
19629        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
19630        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
19631        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
19632        // ceiling at build so the baked pointers never move.
19633        if state.graphs.is_empty() {
19634            // Build the parent at this bucket. Capture executes nothing; correctness is
19635            // pinned at replay by the token-identity gate.
19636            self.step35_token_graph_build(e, cache, state, bucket_max)?;
19637        }
19638        {
19639            let (b, g) = state.graphs.first_mut().expect("graph built above");
19640            if *b != bucket_max {
19641                g.retarget_bucket(bucket_max)?;
19642                *b = bucket_max;
19643            }
19644        }
19645        let graph = state
19646            .graphs
19647            .first()
19648            .map(|(_, g)| g)
19649            .expect("graph built above");
19650
19651        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
19652        let t_fence = tg_timing.then(std::time::Instant::now);
19653        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
19654        // queued on the rank streams, and graph children carry no ordering edge to those
19655        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
19656        // sync is a no-op between consecutive replays.
19657        {
19658            let fa0 = match &self.layers[0].mixer {
19659                Mixer::Full(fa) => fa,
19660                _ => return Err("step35 token graph expects full-attention layers".into()),
19661            };
19662            let tp0 = fa0
19663                .step_tp_qkv
19664                .as_ref()
19665                .ok_or("step35 token graph lost its TP state")?;
19666            for rank in 0..tp0.runtime.devices().len() {
19667                let engine = tp0
19668                    .runtime
19669                    .rank_engine(rank)
19670                    .ok_or("step35 token graph lost a rank engine")?;
19671                let _main = engine.gpu.enter_main()?;
19672                engine.stream().synchronize()?;
19673            }
19674        }
19675
19676        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
19677        {
19678            let _main = e.gpu.enter_main()?;
19679            e.set_u32_one(&mut state.token_d, token)?;
19680            e.set_i32_one(&mut state.pos_d, pos as i32)?;
19681        }
19682        let t_launch = tg_timing.then(std::time::Instant::now);
19683        graph.launch(e)?;
19684        let t_book = tg_timing.then(std::time::Instant::now);
19685        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
19686        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
19687        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
19688        // replay error the counters are already advanced — acceptable: the decode aborts.
19689        for il in 0..n_layers {
19690            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
19691            let transaction = tp_kv.begin_transaction()?;
19692            let fa = match &self.layers[il].mixer {
19693                Mixer::Full(fa) => fa,
19694                _ => return Err("step35 token graph expects full-attention layers".into()),
19695            };
19696            let tp = fa
19697                .step_tp_qkv
19698                .as_ref()
19699                .ok_or("step35 token graph lost its TP state")?;
19700            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
19701            // incs own the counters). Shards unused.
19702            let empty: [CudaSlice<f32>; 0] = [];
19703            tp.runtime.append_tp_kv_transaction_inner(
19704                tp_kv,
19705                transaction,
19706                &empty,
19707                &empty,
19708                1,
19709                true,
19710            )?;
19711            tp.runtime
19712                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
19713            // Local shadow: lengths advance (v1 keeps contents stale under the door).
19714            if let Some(local) = cache.kv[il].as_mut() {
19715                local.len = pos + 1;
19716                let _main = e.gpu.enter_main()?;
19717                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
19718            }
19719        }
19720        cache.pos = pos + 1;
19721        let t_sync = tg_timing.then(std::time::Instant::now);
19722        let (logits, h_seed) = {
19723            let _main = e.gpu.enter_main()?;
19724            e.stream().synchronize()?;
19725            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
19726        };
19727        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
19728            use std::sync::atomic::{AtomicU64, Ordering};
19729            static NS: [AtomicU64; 5] = [
19730                AtomicU64::new(0),
19731                AtomicU64::new(0),
19732                AtomicU64::new(0),
19733                AtomicU64::new(0),
19734                AtomicU64::new(0),
19735            ];
19736            static CALLS: AtomicU64 = AtomicU64::new(0);
19737            let now = std::time::Instant::now();
19738            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
19739            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
19740            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
19741            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
19742            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
19743            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
19744            if calls % 100 == 0 {
19745                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
19746                eprintln!(
19747                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
19748                     syncdtoh_us={:.0} total_us={:.0}",
19749                    avg(0),
19750                    avg(1),
19751                    avg(2),
19752                    avg(3),
19753                    avg(4)
19754                );
19755            }
19756        }
19757        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
19758        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
19759            use std::io::Write;
19760            let (pm, px) = {
19761                let _main = e.gpu.enter_main()?;
19762                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
19763            };
19764            for (path, data) in [
19765                ("/root/tg-probe-mixed.bin", &pm),
19766                ("/root/tg-probe-x.bin", &px),
19767            ] {
19768                let mut fo = std::fs::OpenOptions::new()
19769                    .create(true)
19770                    .append(true)
19771                    .open(path)?;
19772                for v in data {
19773                    fo.write_all(&v.to_le_bytes())?;
19774                }
19775            }
19776        }
19777        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
19778        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
19779        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
19780            let hh = {
19781                let _main = e.gpu.enter_main()?;
19782                e.dtoh(&state.hn)?
19783            };
19784            use std::io::Write;
19785            let mut fo = std::fs::OpenOptions::new()
19786                .create(true)
19787                .append(true)
19788                .open(path)?;
19789            for v in &hh {
19790                fo.write_all(&v.to_le_bytes())?;
19791            }
19792        }
19793        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
19794        // per rank per token; diagnostics only.
19795        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
19796            for il in [0usize, 1, 44] {
19797                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
19798                let host_len = tp_kv.staged_len();
19799                let fa = match &self.layers[il].mixer {
19800                    Mixer::Full(fa) => fa,
19801                    _ => continue,
19802                };
19803                let tp = fa
19804                    .step_tp_qkv
19805                    .as_ref()
19806                    .ok_or("step35 token graph lost its TP state")?;
19807                for rank in 0..tp.runtime.devices().len() {
19808                    let engine = tp
19809                        .runtime
19810                        .rank_engine(rank)
19811                        .ok_or("step35 token graph lost a rank engine")?;
19812                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
19813                    let _main = engine.gpu.enter_main()?;
19814                    engine.stream().synchronize()?;
19815                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
19816                    let base_d = match rank_cache.base_d() {
19817                        Some(b) => engine.dtoh_i32_one(b)?,
19818                        None => -1,
19819                    };
19820                    eprintln!(
19821                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
19822                         len_d={len_d} base_d={base_d}"
19823                    );
19824                }
19825            }
19826        }
19827        Ok(Some((logits, h_seed)))
19828    }
19829
19830    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
19831    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
19832    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
19833    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
19834    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
19835    pub(crate) fn head_split_matvec(
19836        &self,
19837        e: &Engine,
19838        hn: &CudaSlice<f32>,
19839    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
19840        if self.head_split_fill_device(e, hn)?.is_none() {
19841            return Ok(None);
19842        }
19843        let guard = HEAD_SPLIT_WS
19844            .lock()
19845            .map_err(|_| "head split lock is poisoned")?;
19846        let ws = guard.as_ref().expect("filled above");
19847        let _main = e.gpu.enter_main()?;
19848        Ok(Some(e.dtoh(&ws.logits_e)?))
19849    }
19850
19851    /// Compute body of the split head: arms the replica + staging on first use, then fills
19852    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
19853    /// push) and orders e's stream behind it. None = ineligible.
19854    fn head_split_fill_device(
19855        &self,
19856        e: &Engine,
19857        hn: &CudaSlice<f32>,
19858    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
19859        use cudarc::driver::DevicePtr;
19860        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
19861            return Ok(None);
19862        };
19863        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
19864            Mixer::Full(fa) => fa
19865                .step_tp_qkv
19866                .as_ref()
19867                .and_then(|tp| tp.runtime.rank_engine(1)),
19868            _ => None,
19869        }) else {
19870            return Ok(None);
19871        };
19872        let n_embd = self.cfg.n_embd as usize;
19873        let n_vocab = self.cfg.n_vocab as usize;
19874        let half = n_vocab / 2;
19875        let mut guard = HEAD_SPLIT_WS
19876            .lock()
19877            .map_err(|_| "head split lock is poisoned")?;
19878        let pin = {
19879            let _main = e.gpu.enter_main()?;
19880            let stream = e.stream();
19881            let (ptr, _g) = head.device_ptr(&stream);
19882            ptr as u64
19883        };
19884        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
19885            // One-time: upload rank1's row half + persistent staging.
19886            let hi_rows = n_vocab - half;
19887            let (w1, hn1, y1, ev_done) = {
19888                let _r1 = rank1.gpu.enter_main()?;
19889                (
19890                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
19891                    rank1.htod(&vec![0.0f32; n_embd])?,
19892                    rank1.htod(&vec![0.0f32; hi_rows])?,
19893                    rank1.ctx().new_event(None)?,
19894                )
19895            };
19896            {
19897                use cudarc::driver::sys;
19898                let src = pin + (half * n_embd * 2) as u64;
19899                let dst = {
19900                    let _r1 = rank1.gpu.enter_main()?;
19901                    let rstream = rank1.stream();
19902                    let (d, _g) = w1.device_ptr(&rstream);
19903                    d as u64
19904                };
19905                let _r1 = rank1.gpu.enter_main()?;
19906                let r = unsafe {
19907                    sys::cuMemcpyAsync(
19908                        dst as sys::CUdeviceptr,
19909                        src as sys::CUdeviceptr,
19910                        hi_rows * n_embd * 2,
19911                        rank1.stream().cu_stream() as sys::CUstream,
19912                    )
19913                };
19914                if r != sys::CUresult::CUDA_SUCCESS {
19915                    return Err(format!("head split replica upload: {r:?}").into());
19916                }
19917                rank1.stream().synchronize()?;
19918            }
19919            let (logits_e, ev_hn) = {
19920                let _main = e.gpu.enter_main()?;
19921                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
19922            };
19923            let (raw_hn1, raw_y1) = {
19924                let _r1 = rank1.gpu.enter_main()?;
19925                let rstream = rank1.stream();
19926                let (a, _g0) = hn1.device_ptr(&rstream);
19927                let (b, _g1) = y1.device_ptr(&rstream);
19928                (a as u64, b as u64)
19929            };
19930            let raw_logits_hi = {
19931                let _main = e.gpu.enter_main()?;
19932                let stream = e.stream();
19933                let (l, _g) = logits_e.device_ptr(&stream);
19934                l as u64 + (half * 4) as u64
19935            };
19936            *guard = Some(HeadSplit {
19937                pin,
19938                w1,
19939                hn1,
19940                y1,
19941                logits_e,
19942                ev_hn,
19943                ev_done,
19944                raw_hn1,
19945                raw_y1,
19946                raw_logits_hi,
19947                samp: None,
19948            });
19949        }
19950        let ws = guard.as_mut().expect("armed above");
19951        let hi_rows = n_vocab - half;
19952        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
19953        let raw_hn = {
19954            let _main = e.gpu.enter_main()?;
19955            let stream = e.stream();
19956            let (h, _g) = hn.device_ptr(&stream);
19957            ws.ev_hn.record(&stream)?;
19958            h as u64
19959        };
19960        {
19961            let _r1 = rank1.gpu.enter_main()?;
19962            rank1.stream().wait(&ws.ev_hn)?;
19963            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
19964            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
19965            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
19966            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
19967            ws.ev_done.record(&rank1.stream())?;
19968        }
19969        {
19970            let _main = e.gpu.enter_main()?;
19971            let head_lo = head.slice(0..half * n_embd * 2);
19972            let HeadSplit { logits_e, .. } = &mut *ws;
19973            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
19974            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
19975            e.stream().wait(&ws.ev_done)?;
19976            Ok(Some(()))
19977        }
19978    }
19979
19980    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
19981    /// row exactly like the host variant (identical halves, identical concat) and runs the
19982    /// device argmax into `token_d` — NO host readback. Returns false when the split is
19983    /// ineligible (caller falls back to the plain matmul head).
19984    pub(crate) fn head_split_argmax_device(
19985        &self,
19986        e: &Engine,
19987        hn: &CudaSlice<f32>,
19988        token_d: &mut CudaSlice<u32>,
19989    ) -> Result<bool, Box<dyn std::error::Error>> {
19990        if self.head_split_fill_device(e, hn)?.is_none() {
19991            return Ok(false);
19992        }
19993        let n_vocab = self.cfg.n_vocab as usize;
19994        let guard = HEAD_SPLIT_WS
19995            .lock()
19996            .map_err(|_| "head split lock is poisoned")?;
19997        let ws = guard.as_ref().expect("filled above");
19998        let _main = e.gpu.enter_main()?;
19999        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
20000        Ok(true)
20001    }
20002
20003    /// SAMPLED twin of `head_split_argmax_device`. The split head already materializes the
20004    /// full concatenated row in `ws.logits_e`, so sampling does NOT have to give up HEAD_SPLIT
20005    /// — it draws from that row on device (filter thresholds, Gumbel perturbation, argmax)
20006    /// exactly as the serve tick does. Worth ~0.2 ms/token: the post-W8 census had the
20007    /// unsplit q8 head at ~364 us against ~82 us per half.
20008    pub(crate) fn head_split_sample_device(
20009        &self,
20010        e: &Engine,
20011        hn: &CudaSlice<f32>,
20012        token_d: &mut CudaSlice<u32>,
20013        samp: &crate::decode_batch::DevSamp,
20014        ctr: u32,
20015    ) -> Result<bool, Box<dyn std::error::Error>> {
20016        if self.head_split_fill_device(e, hn)?.is_none() {
20017            return Ok(false);
20018        }
20019        let n_vocab = self.cfg.n_vocab as usize;
20020        let guard = HEAD_SPLIT_WS
20021            .lock()
20022            .map_err(|_| "head split lock is poisoned")?;
20023        let mut guard = guard;
20024        let ws = guard.as_mut().expect("filled above");
20025        let _main = e.gpu.enter_main()?;
20026        if ws.samp.is_none() {
20027            ws.samp = Some(SampScratch {
20028                pb: e.zeros(n_vocab)?,
20029                th: e.zeros(1)?,
20030                z: e.zeros(1)?,
20031                mx: e.zeros(1)?,
20032                rows: e.htod_i32(&[0i32])?,
20033            });
20034        }
20035        let filtered = samp.top_k > 0 || samp.top_p < 1.0 || samp.min_p > 0.0;
20036        let HeadSplit {
20037            logits_e,
20038            samp: scratch,
20039            ..
20040        } = &mut *ws;
20041        let sc = scratch.as_mut().expect("armed above");
20042        if filtered {
20043            e.filter_stats(
20044                logits_e, n_vocab, &sc.rows, &mut sc.th, &mut sc.z, &mut sc.mx, n_vocab, 1,
20045                samp.temp, samp.top_k, samp.top_p, samp.min_p,
20046            )?;
20047            let SampScratch { pb, th, mx, .. } = sc;
20048            e.gumbel_perturb_filtered_col(
20049                logits_e, 0, pb, n_vocab, samp.seed, ctr, samp.temp, mx, th, 0,
20050            )?;
20051        } else {
20052            e.gumbel_perturb_col(logits_e, 0, &mut sc.pb, n_vocab, samp.seed, ctr, samp.temp)?;
20053        }
20054        e.argmax_token_device_col(&sc.pb, 0, n_vocab, token_d, 0)?;
20055        Ok(true)
20056    }
20057
20058    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
20059    /// token's row).
20060    pub(crate) fn head_split_logits_dtoh(
20061        &self,
20062        e: &Engine,
20063    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
20064        let guard = HEAD_SPLIT_WS
20065            .lock()
20066            .map_err(|_| "head split lock is poisoned")?;
20067        let ws = guard.as_ref().ok_or("head split logits not armed")?;
20068        let _main = e.gpu.enter_main()?;
20069        Ok(e.dtoh(&ws.logits_e)?)
20070    }
20071
20072    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
20073    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
20074    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
20075    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
20076    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
20077    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
20078    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
20079    /// own loop re-derive hist[k-1] from the returned row.
20080    pub fn step35_token_graph_chunk(
20081        &self,
20082        e: &Engine,
20083        token: u32,
20084        k_target: usize,
20085        cache: &mut Cache,
20086    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
20087        if !self.uses_sliding_gated_moe_program()
20088            || !crate::tp::step_tp_graph_enabled()?
20089            || !crate::tp::step_tp_dcw_enabled()?
20090            || !crate::tp::step_tp_qkv_fused_enabled()?
20091            || !crate::tp::step_tp_dev_router_enabled()?
20092            || !crate::tp::step_nvfp4_dev_routes_enabled()?
20093        {
20094            return Ok(None);
20095        }
20096        let n_layers = self.layers.len();
20097        let pos = cache.pos;
20098        let staged_next = pos + 1;
20099        if staged_next < 96 {
20100            return Ok(None);
20101        }
20102        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
20103        // exec's n_splits ladder must match eager per depth).
20104        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20105        if !fa_vec {
20106            return Ok(None);
20107        }
20108        let sp = crate::fa_split_keys(staged_next, 8);
20109        let bucket_max = (n_splits * sp).max(staged_next);
20110        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
20111        let mut k = k_target.min(to_boundary).min(16);
20112        if k < 2 {
20113            return Ok(None);
20114        }
20115        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
20116        for il in 0..n_layers {
20117            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20118                return Ok(None);
20119            };
20120            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
20121                k -= 1;
20122            }
20123            if k < 2 {
20124                return Ok(None);
20125            }
20126        }
20127
20128        let mut state_guard = self
20129            .step35_token_graph
20130            .lock()
20131            .map_err(|_| "step35 token graph lock is poisoned")?;
20132        let Some(state) = state_guard.as_mut() else {
20133            return Ok(None); // per-token path arms the state + stages first
20134        };
20135        if state.graphs.is_empty() {
20136            return Ok(None);
20137        }
20138        {
20139            let (b, g) = state.graphs.first_mut().expect("checked above");
20140            if *b != bucket_max {
20141                g.retarget_bucket(bucket_max)?;
20142                *b = bucket_max;
20143            }
20144        }
20145        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
20146
20147        // Rank-stream fence (eager stragglers; see the per-token path).
20148        {
20149            let fa0 = match &self.layers[0].mixer {
20150                Mixer::Full(fa) => fa,
20151                _ => return Err("step35 token graph expects full-attention layers".into()),
20152            };
20153            let tp0 = fa0
20154                .step_tp_qkv
20155                .as_ref()
20156                .ok_or("step35 token graph lost its TP state")?;
20157            for rank in 0..tp0.runtime.devices().len() {
20158                let engine = tp0
20159                    .runtime
20160                    .rank_engine(rank)
20161                    .ok_or("step35 token graph lost a rank engine")?;
20162                let _main = engine.gpu.enter_main()?;
20163                engine.stream().synchronize()?;
20164            }
20165        }
20166
20167        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
20168        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
20169        {
20170            let _main = e.gpu.enter_main()?;
20171            e.set_u32_one(&mut state.token_d, token)?;
20172            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20173            e.set_i32_one(&mut state.hist_idx, 0)?;
20174        }
20175        for _ in 0..k {
20176            graph.launch(e)?;
20177        }
20178        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
20179        for il in 0..n_layers {
20180            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20181            let transaction = tp_kv.begin_transaction()?;
20182            let fa = match &self.layers[il].mixer {
20183                Mixer::Full(fa) => fa,
20184                _ => return Err("step35 token graph expects full-attention layers".into()),
20185            };
20186            let tp = fa
20187                .step_tp_qkv
20188                .as_ref()
20189                .ok_or("step35 token graph lost its TP state")?;
20190            let empty: [CudaSlice<f32>; 0] = [];
20191            tp.runtime.append_tp_kv_transaction_inner(
20192                tp_kv,
20193                transaction,
20194                &empty,
20195                &empty,
20196                k,
20197                true,
20198            )?;
20199            tp.runtime
20200                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
20201            if let Some(local) = cache.kv[il].as_mut() {
20202                local.len = pos + k;
20203                let _main = e.gpu.enter_main()?;
20204                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
20205            }
20206        }
20207        cache.pos = pos + k;
20208        let (hist, logits) = {
20209            let _main = e.gpu.enter_main()?;
20210            e.stream().synchronize()?;
20211            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
20212        };
20213        Ok(Some((hist[..k].to_vec(), logits)))
20214    }
20215}
20216
20217impl HybridModel {
20218    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
20219    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
20220    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
20221    /// of each phase fork in parallel and merge into the following root section.
20222    #[allow(clippy::too_many_arguments)]
20223    fn step35_token_graph_build(
20224        &self,
20225        e: &Engine,
20226        cache: &mut Cache,
20227        state: &mut Step35TokenGraphState,
20228        bucket_max: usize,
20229    ) -> Result<(), Box<dyn std::error::Error>> {
20230        use cudarc::driver::DevicePtr;
20231        let n_embd = self.cfg.n_embd as usize;
20232        let eps = self.cfg.rms_eps;
20233        let n_layers = self.layers.len();
20234        let started = std::time::Instant::now();
20235        if !crate::router_kernel_on() {
20236            return Err(
20237                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
20238            );
20239        }
20240        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
20241            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
20242        }
20243
20244        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
20245        let embd_gpu = self
20246            .embd_gpu_try(e)
20247            .ok_or("step35 token graph could not upload the device embed table")?;
20248        let embd_qtype = match self.embd.ggml_type {
20249            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
20250            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
20251            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
20252        };
20253        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
20254
20255        // Fixed-stage pointers the sections reference.
20256        let (p_mixed, p_kshadow, p_vshadow) = {
20257            let _main = e.gpu.enter_main()?;
20258            let stream = e.stream();
20259            let (a, _g) = state.mixed_stage.device_ptr(&stream);
20260            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
20261            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
20262            (a as u64, b as u64, c as u64)
20263        };
20264
20265        crate::tp::token_graph_build_begin()?;
20266        let mut group_id: u32 = 0;
20267        for il in 0..n_layers {
20268            let layer = &self.layers[il];
20269            let fa = match &layer.mixer {
20270                Mixer::Full(fa) => fa,
20271                _ => return Err("step35 token graph expects full-attention layers".into()),
20272            };
20273            let tp = fa
20274                .step_tp_qkv
20275                .as_ref()
20276                .ok_or("step35 token graph lost its TP state")?;
20277            let attention = tp
20278                .attention
20279                .as_ref()
20280                .ok_or("step35 token graph lost its attention aux")?;
20281            let geometry = self.step35_geom(il);
20282            let window = geometry.window.map(|w| w as usize);
20283            let head_dim = geometry.head_dim_k as usize;
20284            let heads = geometry.n_head as usize;
20285            let kv_heads = geometry.n_head_kv as usize;
20286            let ranks = tp.runtime.devices().len();
20287            let local_heads = heads / ranks;
20288            let local_kv_heads = kv_heads / ranks;
20289            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
20290            let use_gate_shards =
20291                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
20292            if !use_gate_shards {
20293                return Err("step35 token graph requires the fused gate shards".into());
20294            }
20295
20296            let ws_index = tp
20297                .runtime
20298                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
20299            let ws_mutex = tp.runtime.decode_v2_workspace();
20300            let mut ws_guard = ws_mutex
20301                .lock()
20302                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
20303            let ws = ws_guard
20304                .get_mut(ws_index)
20305                .ok_or("step TP decode v2 workspace missing after ensure")?;
20306            tp.runtime
20307                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
20308            let mut rope_freqs = Vec::with_capacity(ranks);
20309            for rank in 0..ranks {
20310                let engine = tp
20311                    .runtime
20312                    .rank_engine(rank)
20313                    .ok_or("step35 token graph lost a rank engine")?;
20314                rope_freqs.push(if geometry.rope_factors {
20315                    self.step35_aux
20316                        .as_ref()
20317                        .and_then(|aux| aux.rope_freqs(engine))
20318                } else {
20319                    None
20320                });
20321            }
20322            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
20323                Some(crate::tp::StepTpGateShards::F32(shards))
20324            } else {
20325                attention
20326                    .gate_shards_bf16
20327                    .as_deref()
20328                    .map(crate::tp::StepTpGateShards::Bf16)
20329            };
20330
20331            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
20332            let decode_input = attention
20333                .decode_input
20334                .as_ref()
20335                .ok_or("step35 token graph requires the replicated decode input")?;
20336            let mut decode_input = decode_input
20337                .lock()
20338                .map_err(|_| "replicated decode input lock is poisoned")?;
20339            // Stage arming happens through the eager stage flow once; require it here.
20340            if ws.h_stage.is_none() {
20341                return Err(
20342                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
20343                );
20344            }
20345            {
20346                let state_x = &mut state.x;
20347                let token_d = &state.token_d;
20348                let pos_d = &state.pos_d;
20349                crate::tp::graph_section(e, None, || {
20350                    let _main = e.gpu.enter_main()?;
20351                    if il == 0 {
20352                        e.embed_gather_device_into(
20353                            embd_gpu,
20354                            token_d,
20355                            state_x,
20356                            n_embd,
20357                            embd_qtype,
20358                            embd_row_bytes,
20359                        )?;
20360                    }
20361                    {
20362                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
20363                        e.rms_norm(
20364                            state_x,
20365                            layer.attn_norm.float_data(),
20366                            h_stage,
20367                            n_embd,
20368                            1,
20369                            eps,
20370                        )?;
20371                    }
20372                    {
20373                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
20374                        let mut dst = pos_stage.slice_mut(0..1);
20375                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
20376                    }
20377                    Ok(())
20378                })?;
20379            }
20380
20381            // ---- R0/R1 (parallel): projections + dcw attention interior ----
20382            group_id += 1;
20383            for rank in 0..ranks {
20384                let engine = tp
20385                    .runtime
20386                    .rank_engine(rank)
20387                    .ok_or("step35 token graph lost a rank engine")?;
20388                {
20389                    // fa partial pool must reach the RUN CEILING before capture — an
20390                    // in-capture grow is a mem node (child graphs reject those), and the
20391                    // retarget path (increment C) widens the baked memsets up to the ceiling
20392                    // without moving the pool pointers. Two ensures cover both sp rungs.
20393                    let ceiling = window
20394                        .map(|w| cache.max_ctx.min(w))
20395                        .unwrap_or(cache.max_ctx);
20396                    let _main = engine.gpu.enter_main()?;
20397                    engine.fa_dcw_pool_ensure(
20398                        head_dim,
20399                        local_heads,
20400                        local_kv_heads,
20401                        ceiling.min(2048),
20402                    )?;
20403                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
20404                    engine.fa_dcw_pool_ensure(
20405                        head_dim,
20406                        local_heads,
20407                        local_kv_heads,
20408                        layer_bucket,
20409                    )?;
20410                }
20411                let runtime = &tp.runtime;
20412                let q_norm = &attention.q_norm;
20413                let k_norm = &attention.k_norm;
20414                let gate_ref = gate_shards_arg.as_ref();
20415                crate::tp::graph_section(engine, Some(group_id), || {
20416                    runtime.decode_v2_input_qkv_rank(
20417                        ws,
20418                        &state.pos_d,
20419                        &mut decode_input,
20420                        &tp.q,
20421                        &tp.k,
20422                        &tp.v,
20423                        q_norm,
20424                        k_norm,
20425                        head_dim,
20426                        geometry.n_rot as usize,
20427                        geometry.rope_base,
20428                        &rope_freqs,
20429                        eps,
20430                        gate_ref,
20431                        true,
20432                        false,
20433                        rank,
20434                        None,
20435                    )?;
20436                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
20437                    // replayed values track the live counters).
20438                    let distributed = cache.tp_kv[il]
20439                        .as_mut()
20440                        .ok_or("step35 token graph lost a TP cache")?;
20441                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
20442                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
20443                    let capacity = distributed.physical_capacity();
20444                    {
20445                        let rank_cache = distributed
20446                            .rank_mut(rank)
20447                            .ok_or("step35 token graph lost a rank cache")?;
20448                        let (k_plane, v_plane, len_d, base_d) =
20449                            rank_cache.planes_and_counters_mut();
20450                        engine.append_kv_quantized_dcw(
20451                            &ws.k[rank],
20452                            &ws.v_raw[rank],
20453                            k_plane,
20454                            v_plane,
20455                            len_d,
20456                            base_d,
20457                            kv_dim_k,
20458                            kv_dim_v,
20459                            ktb,
20460                            vtb,
20461                        )?;
20462                    }
20463                    {
20464                        let rank_cache = distributed
20465                            .rank_mut(rank)
20466                            .ok_or("step35 token graph lost a rank cache")?;
20467                        engine.inc_i32(rank_cache.len_d_mut())?;
20468                    }
20469                    let rank_cache = distributed
20470                        .rank(rank)
20471                        .ok_or("step35 token graph lost a rank cache")?;
20472                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
20473                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
20474                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
20475                    // retarget addresses combine's nsp at arg slot 6, and the fused
20476                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
20477                    // only the eager arm takes FUSION #2d.
20478                    engine.fa_decode_dcw(
20479                        &ws.q[rank],
20480                        &k_ring,
20481                        &v_ring,
20482                        &mut ws.attn_out[rank],
20483                        head_dim,
20484                        local_heads,
20485                        local_kv_heads,
20486                        rank_cache.len_d(),
20487                        rank_cache.base_d(),
20488                        window.unwrap_or(0),
20489                        layer_bucket,
20490                        geometry.attention_scale(),
20491                        ktb,
20492                        vtb,
20493                        None,
20494                    )?;
20495                    engine.attn_head_gate(
20496                        &ws.attn_out[rank],
20497                        &ws.gate[rank],
20498                        &mut ws.gated[rank],
20499                        None,
20500                        head_dim,
20501                        local_heads,
20502                        1,
20503                    )?;
20504                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
20505                    Ok(())
20506                })?;
20507            }
20508
20509            // ---- ROOT: combine + shadows + e-mirrors ----
20510            {
20511                let root = tp
20512                    .runtime
20513                    .rank_engine(0)
20514                    .ok_or("step35 token graph lost the root engine")?;
20515                let runtime = &tp.runtime;
20516                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
20517            }
20518            drop(ws_guard);
20519            drop(decode_input);
20520
20521            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
20522                .ok()
20523                .and_then(|v| v.parse().ok());
20524            if probe_layer == Some(il) {
20525                let Step35TokenGraphState {
20526                    mixed_stage,
20527                    probe_mixed,
20528                    ..
20529                } = &mut *state;
20530                crate::tp::graph_section(e, None, || {
20531                    let _main = e.gpu.enter_main()?;
20532                    let mut dst = probe_mixed.slice_mut(0..n_embd);
20533                    e.stream()
20534                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
20535                    Ok(())
20536                })?;
20537            }
20538
20539            // ---- FFN half ----
20540            match &layer.ffn {
20541                crate::hybrid::Ffn::Dense {
20542                    ffn_gate,
20543                    ffn_up,
20544                    ffn_down,
20545                } => {
20546                    let n_ff = ffn_gate.out_features();
20547                    let lim = self.cfg.clamp_shexp_at(il as u32);
20548                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
20549                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
20550                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
20551                    if lim.is_some() {
20552                        return Err("step35 token graph dense FFN with clamp unsupported".into());
20553                    }
20554                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
20555                        (
20556                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
20557                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
20558                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
20559                        ) => (wg, wu, wd),
20560                        _ => {
20561                            return Err(
20562                                "step35 token graph dense FFN requires bf16-resident weights"
20563                                    .into(),
20564                            );
20565                        }
20566                    };
20567                    crate::tp::graph_section(e, None, || {
20568                        let _main = e.gpu.enter_main()?;
20569                        let Step35TokenGraphState {
20570                            x,
20571                            x1,
20572                            mixed_stage,
20573                            dense_z,
20574                            dense_gate,
20575                            dense_up,
20576                            dense_act,
20577                            sh_stage,
20578                            ..
20579                        } = &mut *state;
20580                        e.add_rms_norm(
20581                            x,
20582                            mixed_stage,
20583                            layer.post_attn_norm.float_data(),
20584                            x1,
20585                            dense_z,
20586                            n_embd,
20587                            1,
20588                            eps,
20589                        )?;
20590                        // TWO SINGLE matvecs, not the dual: eager dense rides two
20591                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
20592                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
20593                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
20594                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
20595                        Self::ffn_act_lim(
20596                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
20597                        )?;
20598                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
20599                        e.add(x1, sh_stage, x, n_embd)?;
20600                        Ok(())
20601                    })?;
20602                }
20603                crate::hybrid::Ffn::Moe(m) => {
20604                    let moe = self
20605                        .cfg
20606                        .moe
20607                        .as_ref()
20608                        .ok_or("step35 token graph needs moe cfg")?;
20609                    let n_expert = moe.expert_count as usize;
20610                    let n_used = moe.expert_used_count as usize;
20611                    let sigmoid = self
20612                        .cfg
20613                        .sigmoid_router()
20614                        .ok_or("step35 token graph needs the sigmoid router")?;
20615                    let step_tp = m
20616                        .step_tp
20617                        .as_ref()
20618                        .ok_or("step35 token graph needs TP experts")?;
20619                    let bank = match &step_tp.experts {
20620                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
20621                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
20622                    };
20623                    let routes_ws_mutex = bank.device_workspace_handle();
20624                    let mut routes_guard = routes_ws_mutex
20625                        .lock()
20626                        .map_err(|_| "routes workspace lock is poisoned")?;
20627                    let routes_ws = routes_guard
20628                        .as_mut()
20629                        .ok_or("step35 token graph requires the routes workspace warmed")?;
20630                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
20631                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
20632                    let p_z = {
20633                        let root = step_tp
20634                            .runtime
20635                            .rank_engine(0)
20636                            .ok_or("routes root engine missing")?;
20637                        let _main = root.gpu.enter_main()?;
20638                        let stream = root.stream();
20639                        let in_stage = routes_ws
20640                            .in_stage_handle()
20641                            .ok_or("routes in stage not armed")?;
20642                        let (a, _g) = in_stage.device_ptr(&stream);
20643                        a as u64
20644                    };
20645                    let local_out = bank.expert_width / ranks;
20646
20647                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
20648                    crate::tp::graph_section(e, None, || {
20649                        let _main = e.gpu.enter_main()?;
20650                        {
20651                            let in_stage = routes_ws
20652                                .in_stage_mut()
20653                                .ok_or("routes in stage not armed")?;
20654                            let Step35TokenGraphState {
20655                                x, x1, mixed_stage, ..
20656                            } = &mut *state;
20657                            e.add_rms_norm(
20658                                x,
20659                                mixed_stage,
20660                                layer.post_attn_norm.float_data(),
20661                                x1,
20662                                in_stage,
20663                                n_embd,
20664                                1,
20665                                eps,
20666                            )?;
20667                        }
20668                        {
20669                            let z_ref = routes_ws
20670                                .in_stage_handle()
20671                                .ok_or("routes in stage not armed")?;
20672                            e.router_gemv_into(
20673                                m.gate_inp.float_data(),
20674                                z_ref,
20675                                &mut state.router_logits,
20676                                n_embd,
20677                                n_expert,
20678                                1,
20679                            )?;
20680                        }
20681                        let (sel_e, w_e) = routes_ws
20682                            .dev_route_e_mut()
20683                            .ok_or("routes staging not armed")?;
20684                        e.moe_router_sigmoid_topk_into(
20685                            &state.router_logits,
20686                            1,
20687                            n_expert,
20688                            n_used,
20689                            m.active_count(),
20690                            &m.exp_probs_b_dev,
20691                            &m.active_experts_dev,
20692                            sigmoid.0,
20693                            sigmoid.1,
20694                            sel_e,
20695                            w_e,
20696                        )?;
20697                        Ok(())
20698                    })?;
20699
20700                    // ---- R0r/R1r (parallel): routes sweeps ----
20701                    group_id += 1;
20702                    for rank in 0..ranks {
20703                        let engine = step_tp
20704                            .runtime
20705                            .rank_engine(rank)
20706                            .ok_or("routes rank engine missing")?;
20707                        let runtime = &step_tp.runtime;
20708                        crate::tp::graph_section(engine, Some(group_id), || {
20709                            runtime.routes_rank_section(
20710                                bank,
20711                                routes_ws,
20712                                p_z,
20713                                local_out,
20714                                n_used,
20715                                step_tp.activation_limit,
20716                                rank,
20717                            )
20718                        })?;
20719                    }
20720
20721                    // ---- ROOTr: combine into the out stage ----
20722                    {
20723                        let root = step_tp
20724                            .runtime
20725                            .rank_engine(0)
20726                            .ok_or("routes root engine missing")?;
20727                        let runtime = &step_tp.runtime;
20728                        crate::tp::graph_section(root, None, || {
20729                            runtime.routes_root_section(bank, routes_ws)
20730                        })?;
20731                    }
20732
20733                    // ---- E3: shexp + add_shared onto the out stage + residual ----
20734                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
20735                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
20736                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
20737                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
20738                        (
20739                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
20740                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
20741                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
20742                        ) => (wg, wu, wd),
20743                        _ => {
20744                            return Err(
20745                                "step35 token graph shexp requires bf16-resident weights".into()
20746                            );
20747                        }
20748                    };
20749                    let n_ff_sh = m
20750                        .gate_shexp
20751                        .as_ref()
20752                        .expect("matched Some above")
20753                        .out_features();
20754                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
20755                    // init, reproducing eager's ones vector without a launch.
20756                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
20757                    crate::tp::graph_section(e, None, || {
20758                        let _main = e.gpu.enter_main()?;
20759                        let (z_ref, out_stage) = routes_ws
20760                            .in_and_out_stages_mut()
20761                            .ok_or("routes stages not armed")?;
20762                        let Step35TokenGraphState {
20763                            x,
20764                            x1,
20765                            sh_stage,
20766                            shexp_gate,
20767                            shexp_up,
20768                            shexp_act,
20769                            gate_sig,
20770                            ..
20771                        } = &mut *state;
20772                        e.matvec_bf16_dual_into(
20773                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
20774                        )?;
20775                        Self::ffn_act_lim(
20776                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
20777                            n_ff_sh,
20778                        )?;
20779                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
20780                        if let Some(gate_w) = gate_inp_shexp {
20781                            e.sigmoid_dot_rows_into(
20782                                z_ref,
20783                                gate_w.float_data(),
20784                                gate_sig,
20785                                n_embd,
20786                                1,
20787                            )?;
20788                        }
20789                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
20790                        e.add(x1, out_stage, x, n_embd)?;
20791                        Ok(())
20792                    })?;
20793                }
20794            }
20795            if probe_layer == Some(il) {
20796                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
20797                crate::tp::graph_section(e, None, || {
20798                    let _main = e.gpu.enter_main()?;
20799                    let mut dst = probe_x.slice_mut(0..n_embd);
20800                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
20801                    Ok(())
20802                })?;
20803            }
20804        }
20805
20806        // ---- Tail: output norm + head into the logits stage ----
20807        let head = match &self.output {
20808            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
20809            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
20810        };
20811        crate::tp::graph_section(e, None, || {
20812            let _main = e.gpu.enter_main()?;
20813            let Step35TokenGraphState {
20814                x,
20815                hn,
20816                logits_stage,
20817                token_d,
20818                pos_d,
20819                token_hist,
20820                hist_idx,
20821                ..
20822            } = &mut *state;
20823            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
20824            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
20825            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
20826            // argmax_gate-validated), the id lands in the history ring, and pos advances on
20827            // device — consecutive launches chain with NO host sync. Single-token mode
20828            // overwrites token_d/pos_d from the host before each launch, so these nodes are
20829            // harmless there.
20830            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
20831            e.u32_hist_append(token_d, token_hist, hist_idx)?;
20832            e.inc_i32(pos_d)?;
20833            Ok(())
20834        })?;
20835
20836        let graph = crate::tp::token_graph_build_finish()?;
20837        state.graphs.push((bucket_max, graph));
20838        eprintln!(
20839            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
20840             build_ms={:.0} performance_claim=false",
20841            started.elapsed().as_secs_f64() * 1e3
20842        );
20843        Ok(())
20844    }
20845}