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}
759/// HEAD-SPLIT workspace (host + device twins share it).
760static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
761
762/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
763/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
764/// input bits — rank1's local selection is bit-equal to the root's.
765#[allow(clippy::type_complexity)]
766static DEV1_ROUTER_REPS: std::sync::Mutex<
767    Option<(
768        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
769        Option<CudaSlice<f32>>,
770    )>,
771> = std::sync::Mutex::new(None);
772
773/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
774/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
775/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
776#[allow(clippy::type_complexity)]
777static SHEXP_D1_REPS: std::sync::Mutex<
778    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
779> = std::sync::Mutex::new(None);
780#[allow(clippy::type_complexity)]
781static SHEXP_D1_WS: std::sync::Mutex<
782    Option<(
783        (usize, usize),
784        CudaSlice<f32>,
785        CudaSlice<f32>,
786        CudaSlice<f32>,
787        cudarc::driver::CudaEvent,
788        cudarc::driver::CudaEvent,
789    )>,
790> = std::sync::Mutex::new(None);
791
792/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
793static SHEXP_OV_WS: std::sync::Mutex<
794    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
795> = std::sync::Mutex::new(None);
796
797impl HybridModel {
798    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
799    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
800    /// an off-grid prime-call boundary shifts the WY fold grid (see
801    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
802    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
803    pub fn gdn_prime_grid_on(&self) -> bool {
804        Engine::gdn_chunked_enabled()
805            && self
806                .layers
807                .iter()
808                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
809    }
810
811    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
812    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
813    /// (the device buffers must be addressable on both sides — the TP registry builds its
814    /// own Engine per rank, so this is a real seam, not a formality).
815    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
816        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
817    }
818
819    fn step35_tp_qkv(
820        &self,
821        e: &Engine,
822        fa: &FullAttnLayer,
823        h: &CudaSlice<f32>,
824        t: usize,
825    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
826        let Some(tp) = fa.step_tp_qkv.as_ref() else {
827            return Ok(None);
828        };
829        let values = active_matrix_values(
830            h.len(),
831            t,
832            self.cfg.n_embd as usize,
833            "Step TP QKV activation",
834        )?;
835        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
836        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
837        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
838        // round-trip on every execute that the peer transport exists to remove. The
839        // device twins are byte-identical by construction (the same bytes travel dtod
840        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
841        // The host arm below remains the transport for !native_p2p (host staging IS that
842        // transport) and for a root context this engine cannot address.
843        if Self::step35_tp_device_resident(e, tp) {
844            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
845            // on theirs (same context, different streams).
846            e.stream().synchronize()?;
847            let q = tp
848                .runtime
849                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
850            let k = tp
851                .runtime
852                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
853            let v = tp
854                .runtime
855                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
856            Self::step35_tp_log_once(tp, "qkv", "device-resident");
857            return Ok(Some(vec![q, k, v]));
858        }
859        let host = e.dtoh_view(&h.slice(0..values))?;
860        let q = if tp.runtime.native_p2p() {
861            tp.runtime
862                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
863        } else {
864            tp.runtime
865                .bf16_column_parallel_resident(&tp.q, &host, t)?
866                .gathered
867        };
868        let k = if tp.runtime.native_p2p() {
869            tp.runtime
870                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
871        } else {
872            tp.runtime
873                .bf16_column_parallel_resident(&tp.k, &host, t)?
874                .gathered
875        };
876        let v = if tp.runtime.native_p2p() {
877            tp.runtime
878                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
879        } else {
880            tp.runtime
881                .bf16_column_parallel_resident(&tp.v, &host, t)?
882                .gathered
883        };
884        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
885        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
886    }
887
888    /// One transport banner per (projection, transport) — the old per-call eprintln fired
889    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
890    /// unbouncing (the sibling grouped-EP path already learned this).
891    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
892        use std::sync::atomic::{AtomicBool, Ordering};
893        static LOGGED: [AtomicBool; 4] = [
894            AtomicBool::new(false),
895            AtomicBool::new(false),
896            AtomicBool::new(false),
897            AtomicBool::new(false),
898        ];
899        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
900        if LOGGED[idx].swap(true, Ordering::Relaxed) {
901            return;
902        }
903        eprintln!(
904            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
905             tensor_parallel=true attention_local=true kv_local=true transport={} \
906             native_p2p={} bulk_p2p={} activation={activation} \
907             output={} performance_claim=false (logged once per transport)",
908            tp.layer,
909            tp.devices,
910            tp.runtime.transport_label(),
911            tp.runtime.native_p2p(),
912            tp.runtime.bulk_p2p(),
913            if activation == "device-resident" {
914                "root-resident"
915            } else {
916                "root-readback"
917            },
918        );
919    }
920
921    fn step35_tp_o(
922        &self,
923        e: &Engine,
924        fa: &FullAttnLayer,
925        activation: &CudaSlice<f32>,
926        tokens: usize,
927    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
928        let Some(tp) = fa.step_tp_qkv.as_ref() else {
929            return Ok(None);
930        };
931        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
932        // of the attention output, no host O staging, root-resident reduction consumed in
933        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
934        if Self::step35_tp_device_resident(e, tp) {
935            e.stream().synchronize()?; // producer fence, as the QKV half
936            let output = tp
937                .runtime
938                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
939            Self::step35_tp_log_once(tp, "o", "device-resident");
940            return Ok(Some(output));
941        }
942        let host = e.dtoh(activation)?;
943        let output = if tp.runtime.native_p2p() {
944            tp.runtime
945                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
946        } else {
947            tp.runtime
948                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
949        };
950        Self::step35_tp_log_once(tp, "o", "host-canonical");
951        Ok(Some(e.htod(&output)?))
952    }
953
954    fn step35_o(
955        &self,
956        e: &Engine,
957        fa: &FullAttnLayer,
958        activation: &CudaSlice<f32>,
959        tokens: usize,
960    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
961        match self.step35_tp_o(e, fa, activation, tokens)? {
962            Some(output) => Ok(output),
963            None => e.matmul(&fa.wo, activation, tokens),
964        }
965    }
966
967    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
968    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
969    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
970    /// (it forces a dtoh + host hash per layer).
971    fn prime_trace_path() -> Option<&'static str> {
972        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
973        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
974            .as_deref()
975    }
976
977    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
978    /// each prime_layers stage and accumulates wall time per stage class, printed after
979    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
980    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
981    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
982    fn prime_anatomy_on() -> bool {
983        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
984        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
985    }
986
987    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
988        static S: [std::sync::atomic::AtomicU64; 5] = [
989            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
990            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
991            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
992            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
993            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
994        ];
995        &S
996    }
997
998    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
999    pub fn forward(
1000        &self,
1001        e: &Engine,
1002        tokens: &[u32],
1003    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1004        if self.is_gemma4_e4b() {
1005            return self.gemma4_e4b_forward(e, tokens, false);
1006        }
1007        if self.uses_gemma_program() {
1008            return self.gemma4_forward(e, tokens, false);
1009        }
1010        let cfg = &self.cfg;
1011        let n_embd = cfg.n_embd as usize;
1012        let t = tokens.len();
1013        let eps = cfg.rms_eps;
1014        let pos: Vec<i32> = (0..t as i32).collect();
1015        let pos_d = e.htod_i32(&pos)?;
1016
1017        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1018
1019        for (il, layer) in self.layers.iter().enumerate() {
1020            // attn_norm
1021            let mut h = e.uninit(t * n_embd)?;
1022            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1023
1024            let mixed = match &layer.mixer {
1025                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1026                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1027                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1028            };
1029
1030            // residual 1
1031            let mut x1 = e.uninit(t * n_embd)?;
1032            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1033
1034            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1035            let mut z = e.uninit(t * n_embd)?;
1036            e.rms_norm(
1037                &x1,
1038                layer.post_attn_norm.float_data(),
1039                &mut z,
1040                n_embd,
1041                t,
1042                eps,
1043            )?;
1044            let ffn_out = match &layer.ffn {
1045                crate::hybrid::Ffn::Dense {
1046                    ffn_gate,
1047                    ffn_up,
1048                    ffn_down,
1049                } => {
1050                    let n_ff = ffn_gate.out_features();
1051                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1052                    let up = g2.pop().unwrap();
1053                    let gate = g2.pop().unwrap();
1054                    let mut act = e.uninit(t * n_ff)?;
1055                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1056                    // both the dense MLP and the shared expert, and its limit is
1057                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1058                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1059                    Self::ffn_act_lim(
1060                        e,
1061                        &self.cfg,
1062                        &gate,
1063                        &up,
1064                        1.0,
1065                        1.0,
1066                        self.cfg.clamp_shexp_at(il as u32),
1067                        &mut act,
1068                        t * n_ff,
1069                    )?;
1070                    e.matmul(ffn_down, &act, t)?
1071                }
1072                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1073            };
1074            let mut x2 = e.uninit(t * n_embd)?;
1075            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1076            x = x2;
1077        }
1078
1079        let mut hn = e.uninit(t * n_embd)?;
1080        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1081        let logits = e.matmul(&self.output, &hn, t)?;
1082        Ok(e.dtoh(&logits)?)
1083    }
1084
1085    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1086    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1087    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1088    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1089    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1090    pub fn forward_last(
1091        &self,
1092        e: &Engine,
1093        tokens: &[u32],
1094    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1095        if self.uses_gemma_program() {
1096            return self.gemma4_forward(e, tokens, true);
1097        }
1098        let cfg = &self.cfg;
1099        let n_embd = cfg.n_embd as usize;
1100        let t = tokens.len();
1101        let eps = cfg.rms_eps;
1102        let pos: Vec<i32> = (0..t as i32).collect();
1103        let pos_d = e.htod_i32(&pos)?;
1104
1105        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1106        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1107        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1108        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1109        let anat = Self::prime_anatomy_on();
1110        let mut anat_last = if anat {
1111            e.stream().synchronize()?;
1112            Some(std::time::Instant::now())
1113        } else {
1114            None
1115        };
1116        macro_rules! anat_mark {
1117            ($slot:expr) => {
1118                if let Some(ts) = anat_last.as_mut() {
1119                    e.stream().synchronize()?;
1120                    Self::prime_anatomy_slots()[$slot].fetch_add(
1121                        ts.elapsed().as_nanos() as u64,
1122                        std::sync::atomic::Ordering::Relaxed,
1123                    );
1124                    *ts = std::time::Instant::now();
1125                }
1126            };
1127        }
1128        for (il, layer) in self.layers.iter().enumerate() {
1129            let mut h = e.uninit(t * n_embd)?;
1130            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1131            if probe {
1132                e.stream().synchronize()?;
1133                eprintln!("[probe] L{il} norm ok");
1134            }
1135            anat_mark!(4);
1136            let mixed = match &layer.mixer {
1137                Mixer::Full(fa) => {
1138                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1139                    anat_mark!(0);
1140                    y
1141                }
1142                Mixer::Linear(la) => {
1143                    let y = self.linear_attn(e, la, &h, t)?;
1144                    anat_mark!(1);
1145                    y
1146                }
1147                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1148            };
1149            if probe {
1150                e.stream().synchronize()?;
1151                eprintln!("[probe] L{il} mixer ok");
1152            }
1153            let mut x1 = e.uninit(t * n_embd)?;
1154            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1155            let mut z = e.uninit(t * n_embd)?;
1156            e.rms_norm(
1157                &x1,
1158                layer.post_attn_norm.float_data(),
1159                &mut z,
1160                n_embd,
1161                t,
1162                eps,
1163            )?;
1164            anat_mark!(4);
1165            let ffn_out = match &layer.ffn {
1166                crate::hybrid::Ffn::Dense {
1167                    ffn_gate,
1168                    ffn_up,
1169                    ffn_down,
1170                } => {
1171                    let n_ff = ffn_gate.out_features();
1172                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1173                    let up = g2.pop().unwrap();
1174                    let gate = g2.pop().unwrap();
1175                    let mut act = e.uninit(t * n_ff)?;
1176                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1177                    Self::ffn_act_lim(
1178                        e,
1179                        &self.cfg,
1180                        &gate,
1181                        &up,
1182                        1.0,
1183                        1.0,
1184                        self.cfg.clamp_shexp_at(il as u32),
1185                        &mut act,
1186                        t * n_ff,
1187                    )?;
1188                    let y = e.matmul(ffn_down, &act, t)?;
1189                    anat_mark!(3);
1190                    y
1191                }
1192                crate::hybrid::Ffn::Moe(m) => {
1193                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1194                    anat_mark!(2);
1195                    y
1196                }
1197            };
1198            if probe {
1199                e.stream().synchronize()?;
1200                eprintln!("[probe] L{il} ffn ok");
1201            }
1202            let mut x2 = e.uninit(t * n_embd)?;
1203            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1204            x = x2;
1205        }
1206        if anat {
1207            let s = Self::prime_anatomy_slots();
1208            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1209            eprintln!(
1210                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1211                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1212                ms(0),
1213                ms(1),
1214                ms(2),
1215                ms(3),
1216                ms(4)
1217            );
1218        }
1219        // norm over all T, then slice the LAST row and run lm_head on that single row.
1220        let mut hn = e.uninit(t * n_embd)?;
1221        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1222        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1223        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1224        let mut hlast = e.uninit(n_embd)?;
1225        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1226        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1227        Ok(e.dtoh(&logits)?)
1228    }
1229
1230    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1231    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1232    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1233    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1234    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1235    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1236    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1237    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1238    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1239    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1240    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1241    ///   (c) `cache.pos`/KV len/len_d advance by T.
1242    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1243    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1244    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1245    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1246    ///
1247    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1248    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1249    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1250    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1251    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1252    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1253    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1254    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1255    /// differently under load — research/tick-seg-20260807, receipt in
1256    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1257    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1258    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1259    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1260    /// caller that SPLITS one request across calls passes the remainder.
1261    pub fn prime_cache(
1262        &self,
1263        e: &Engine,
1264        tokens: &[u32],
1265        cache: &mut Cache,
1266        queued_after: usize,
1267    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1268        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1269    }
1270
1271    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1272    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1273    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1274    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1275    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1276    pub fn prime_cache_overlaid(
1277        &self,
1278        e: &Engine,
1279        tokens: &[u32],
1280        cache: &mut Cache,
1281        queued_after: usize,
1282        overlay: Option<&crate::vision::EmbedOverlay>,
1283    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1284        let n_embd = self.cfg.n_embd as usize;
1285        let t = tokens.len();
1286        // MEMRA_PRIME_TROWS=1: prefill through the same-session t-row walk (per-row t=1
1287        // program = the tokenwise-prime ORACLE class) — replaces the host-canonical
1288        // per-token step-TP prime. Text-only fresh primes; anything else falls through.
1289        if overlay.is_none() {
1290            if let Some(out) = self.step35_prime_trows(e, tokens, cache)? {
1291                return Ok(out);
1292            }
1293        }
1294        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1295        // session cache — every chunk (including the first) takes the continuation arm
1296        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1297        assert!(
1298            t >= PRIME_MIN_T,
1299            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1300        );
1301        assert!(
1302            cache.pos + t <= cache.max_ctx,
1303            "prime_cache: prompt exceeds cache max_ctx"
1304        );
1305
1306        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1307        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1308        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1309        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1310        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1311        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1312        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1313        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1314        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1315        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1316        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1317        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1318            if self.is_gemma4_e4b() {
1319                if overlay.is_some() {
1320                    return Err(
1321                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1322                    );
1323                }
1324                return self.gemma4_e4b_prime(e, tokens, cache);
1325            }
1326            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1327            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1328            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1329            // spans become bidirectional attention islands (lane/gemma-vision).
1330            return self.gemma4_prime(e, tokens, cache, overlay);
1331        }
1332        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1333        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1334        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1335        // the prefill's ARITHMETIC, so two rigs with different values produced different
1336        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1337        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1338        // (VERDICT.md) — and it is NOT what docs originally said:
1339        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1340        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1341        //     output head), so growing a chunk cannot move an existing row's value.
1342        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1343        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1344        //     not describe our leak.
1345        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1346        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1347        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1348        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1349        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1350        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1351        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1352        // the source — every row is in one numeric class, so the chunk size no longer steers
1353        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1354        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1355        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1356        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1357        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1358        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1359        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1360        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1361        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1362        // across calls, the request still ends at the same absolute position, whatever the tick
1363        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1364        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1365        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1366        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1367        // on in a measured default run.
1368        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1369        let seq_end = if legacy_calllocal {
1370            cache.pos + t
1371        } else {
1372            cache.pos + t + queued_after
1373        };
1374        if ranges.len() == 1 {
1375            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1376        }
1377        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1378        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1379        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1380        // this lane owns the balanced two-stage schedule only.
1381        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1382            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1383                if overlay.is_some() {
1384                    return Err(
1385                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1386                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1387                            .into(),
1388                    );
1389                }
1390                if crate::pp::pp_multi_stream_same_device() {
1391                    return Err(
1392                        "prime chunk pipeline refused with 2 stage streams on one device — \
1393                         that concurrent-stream placement remains quarantined by the deferred \
1394                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1395                         the serial split."
1396                            .into(),
1397                    );
1398                }
1399                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1400            }
1401        }
1402        let mut hiddens = e.uninit(t * n_embd)?;
1403        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1404        for &(start, end) in &ranges {
1405            // chunked prime writes tap rows at the chunk's absolute offset
1406            if let Some(taps) = cache.dflash_taps.as_mut() {
1407                taps.base = start;
1408            }
1409            let (l, hs, x) =
1410                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1411            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1412            last = Some((l, hs));
1413        }
1414        let (logits, h_seed) = last.unwrap();
1415        Ok((logits, h_seed, hiddens))
1416    }
1417
1418    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1419    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1420    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1421    /// norm, lm head, and caller hidden-stack copy as the serial split.
1422    fn prime_cache_pp2_pipelined(
1423        &self,
1424        e: &Engine,
1425        tokens: &[u32],
1426        cache: &mut Cache,
1427        seq_end: usize,
1428        ranges: &[(usize, usize)],
1429        fence: &[usize],
1430    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1431        debug_assert_eq!(fence.len(), 3);
1432        debug_assert!(ranges.len() >= 2);
1433        let rt = crate::pp::PpNRt::get(e)?;
1434        assert_eq!(
1435            rt.n_stages(),
1436            2,
1437            "prime pipeline requires exactly two PP stages"
1438        );
1439        let n_embd = self.cfg.n_embd as usize;
1440        let t = tokens.len();
1441        let initial_base = cache.pos;
1442        let caller_stream = e.stream();
1443
1444        // #87 reverse publication before any new stage allocation, then prewarm both
1445        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1446        // after stage 1(N) is queued would synchronize that stream and erase the first
1447        // overlap on a two-chunk prompt.
1448        rt.fence_stages_behind(&caller_stream)?;
1449        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1450        rt.prepare_overlap_slots(0, max_payload)?;
1451
1452        let mut hiddens = e.uninit(t * n_embd)?;
1453        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1454        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1455        let (cache0, cache1) = stage_caches.parts();
1456        let (first_start, first_end) = ranges[0];
1457        let mut slot = self.prime_pp2_stage0_enqueue(
1458            e,
1459            rt,
1460            &tokens[first_start..first_end],
1461            cache0,
1462            seq_end,
1463            fence,
1464            initial_base + first_start,
1465            true,
1466        )?;
1467        cache0.pos = initial_base + first_end;
1468
1469        for (i, &(start, end)) in ranges.iter().enumerate() {
1470            let base = initial_base + start;
1471            debug_assert_eq!(
1472                cache1.pos, base,
1473                "stage 1 must drain chunks in original position order"
1474            );
1475            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1476                let next_base = initial_base + next_start;
1477                debug_assert_eq!(
1478                    cache0.pos, next_base,
1479                    "stage 0 must issue chunks in original position order"
1480                );
1481                let cache0_stage = &mut *cache0;
1482                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1483                // on one host thread therefore serialize even if the calls are ordered as
1484                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1485                // stage 1 consumes slot N while stage 0 produces slot N+1.
1486                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1487                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1488                        let next = self
1489                            .prime_pp2_stage0_enqueue(
1490                                e,
1491                                rt,
1492                                &tokens[next_start..next_end],
1493                                cache0_stage,
1494                                seq_end,
1495                                fence,
1496                                next_base,
1497                                true,
1498                            )
1499                            .map_err(|err| err.to_string())?;
1500                        cache0_stage.pos = initial_base + next_end;
1501                        Ok(next)
1502                    });
1503                    let x = self.prime_pp2_stage1_enqueue(
1504                        e,
1505                        rt,
1506                        slot,
1507                        end - start,
1508                        cache1,
1509                        seq_end,
1510                        fence,
1511                        base,
1512                        true,
1513                    )?;
1514                    let out = {
1515                        rt.bind_stage(1)?;
1516                        let _st1 = rt.enter(1);
1517                        let e1 = rt.engine(1, e);
1518                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1519                    };
1520                    let next = stage0
1521                        .join()
1522                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1523                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1524                    Ok((out, Some(next)))
1525                })?
1526            } else {
1527                let x = self.prime_pp2_stage1_enqueue(
1528                    e,
1529                    rt,
1530                    slot,
1531                    end - start,
1532                    cache1,
1533                    seq_end,
1534                    fence,
1535                    base,
1536                    true,
1537                )?;
1538                let out = {
1539                    rt.bind_stage(1)?;
1540                    let _st1 = rt.enter(1);
1541                    let e1 = rt.engine(1, e);
1542                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1543                };
1544                (out, None)
1545            };
1546
1547            rt.publish_to(1, &caller_stream)?;
1548            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1549            last = Some((out.0, out.1));
1550            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1551
1552            if let Some(next) = next_slot {
1553                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1554                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1555                // Stage 0(N+1) is already queued before this wait is appended, so its
1556                // overlap with stage 1(N) is preserved.
1557                rt.fence_stages_behind(&caller_stream)?;
1558                slot = next;
1559            }
1560        }
1561
1562        debug_assert_eq!(cache0.pos, initial_base + t);
1563        debug_assert_eq!(cache1.pos, initial_base + t);
1564        let (logits, h_seed) = last.unwrap();
1565        Ok((logits, h_seed, hiddens))
1566    }
1567
1568    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1569    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1570    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1571    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1572    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1573    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1574    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1575        if Engine::gdn_db_on()
1576            && Engine::gdn_chunked_enabled()
1577            && t >= 16
1578            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1579            && num_k * 2 == num_v
1580        {
1581            num_k
1582        } else {
1583            num_v
1584        }
1585    }
1586
1587    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1588    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1589    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1590    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1591    fn f16out_on(e: &Engine, t: usize) -> bool {
1592        crate::f16_ffi::pp_f16_enabled()
1593            && t >= 16
1594            && !e.verify_exact_on()
1595            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1596    }
1597
1598    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1599    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1600    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1601    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1602    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1603    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1604    /// see one entry, byte-identical behavior.
1605    pub fn prime_slabs_get(
1606        &self,
1607        e: &Engine,
1608        t: usize,
1609        n_embd: usize,
1610        n_ff_max: usize,
1611    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1612        let mut slabs = self.prime_slabs.lock().unwrap();
1613        let dev = e.ctx().ordinal();
1614        let need_new = match slabs.get(&dev) {
1615            None => true,
1616            Some(sl) => sl.lock().unwrap().t_cap < t,
1617        };
1618        if need_new {
1619            slabs.insert(
1620                dev,
1621                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1622                    t_cap: t,
1623                    h: e.uninit(t * n_embd)?,
1624                    x1: e.uninit(t * n_embd)?,
1625                    z: e.uninit(t * n_embd)?,
1626                    act: e.uninit(t * n_ff_max)?,
1627                    xa: e.uninit(t * n_embd)?,
1628                    xb: e.uninit(t * n_embd)?,
1629                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1630                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1631                    gate: e.uninit(t * n_ff_max)?,
1632                    up: e.uninit(t * n_ff_max)?,
1633                    ffn_out: e.uninit(t * n_embd)?,
1634                    seg_glue: Vec::new(),
1635                    mixed: e.uninit(t * n_embd)?,
1636                    seg_mid: Vec::new(),
1637                    seg_t: 0,
1638                })),
1639            );
1640        }
1641        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1642    }
1643
1644    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1645    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1646    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1647    fn prime_chunk(
1648        &self,
1649        e: &Engine,
1650        tokens: &[u32],
1651        cache: &mut Cache,
1652        seq_end: usize,
1653        chunk_off: usize,
1654        overlay: Option<&crate::vision::EmbedOverlay>,
1655    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1656        if crate::pp::pp_host_bounce_active()
1657            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1658        {
1659            return Err(
1660                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1661                 has no active prime stage split and would peer-read remote weights; keep \
1662                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1663                    .into(),
1664            );
1665        }
1666        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1667        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1668        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1669        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1670        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1671        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1672        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1673        // loader is off and there is nothing remote to split for.
1674        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1675            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1676                if overlay.is_some() {
1677                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1678                         run single-device or MEMRA_PRIME_PP=0"
1679                        .into());
1680                }
1681                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1682            }
1683        }
1684        if crate::pp::pp_host_bounce_active() {
1685            return Err(
1686                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1687                 refusing an unsplit remote-weight walk"
1688                    .into(),
1689            );
1690        }
1691        let t = tokens.len();
1692        let base = cache.pos;
1693        debug_assert!(
1694            seq_end >= base + t,
1695            "prime_chunk: seq_end must cover this chunk"
1696        );
1697        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1698        let pos_d = e.htod_i32(&pos)?;
1699
1700        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1701        if let Some(ov) = overlay {
1702            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1703            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1704            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1705            let n_embd = self.cfg.n_embd as usize;
1706            for &(pos, row_off, n_rows) in &ov.spans {
1707                let lo = pos.max(chunk_off);
1708                let hi = (pos + n_rows).min(chunk_off + t);
1709                if lo < hi {
1710                    let src_row = row_off + (lo - pos);
1711                    let view = ov
1712                        .rows
1713                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1714                    e.copy_view_into(
1715                        &mut x_embed,
1716                        (lo - chunk_off) * n_embd,
1717                        &view,
1718                        (hi - lo) * n_embd,
1719                    )?;
1720                }
1721            }
1722        }
1723        let x = self.prime_layers(
1724            e,
1725            x_embed,
1726            0,
1727            self.layers.len(),
1728            &pos_d,
1729            t,
1730            base,
1731            cache,
1732            seq_end,
1733        )?;
1734        self.prime_chunk_epilogue(e, x, t, cache)
1735    }
1736
1737    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1738    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1739    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1740    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1741    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1742    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1743    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1744    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1745    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1746    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1747    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1748    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1749    ///     each stage walks through its own resident transients;
1750    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1751    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1752    #[allow(clippy::too_many_arguments)]
1753    fn prime_layers(
1754        &self,
1755        e: &Engine,
1756        x_in: CudaSlice<f32>,
1757        lo: usize,
1758        hi: usize,
1759        pos_d: &CudaSlice<i32>,
1760        t: usize,
1761        base: usize,
1762        cache: &mut Cache,
1763        seq_end: usize,
1764    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1765        let cfg = &self.cfg;
1766        let n_embd = cfg.n_embd as usize;
1767        let eps = cfg.rms_eps;
1768        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1769        // standalone convert launches). Only when the f16 lane serves and T reaches the
1770        // GEMM tier; bit-identical either way.
1771        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1772        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1773        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1774        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1775        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1776        // capacity tail must stay behind checked views. The hidden-stack return clones the
1777        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1778        let n_ff_max = self
1779            .layers
1780            .iter()
1781            .map(|l| match &l.ffn {
1782                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1783                _ => n_embd,
1784            })
1785            .max()
1786            .unwrap_or(n_embd)
1787            .max(n_embd);
1788        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1789        let slab = if use_slabs {
1790            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1791        } else {
1792            None
1793        };
1794        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1795        let mut x_own; // fallback storage when slabs are off
1796        type SlabRefs<'a> = (
1797            &'a mut CudaSlice<f32>,
1798            &'a mut CudaSlice<f32>,
1799            &'a mut CudaSlice<f32>,
1800            &'a mut CudaSlice<f32>,
1801            &'a mut CudaSlice<u8>,
1802            &'a mut CudaSlice<u8>,
1803            &'a mut CudaSlice<f32>,
1804            &'a mut CudaSlice<f32>,
1805            &'a mut CudaSlice<f32>,
1806        );
1807        let (mut x_cur, mut x_nxt, sl): (
1808            &mut CudaSlice<f32>,
1809            &mut CudaSlice<f32>,
1810            Option<SlabRefs>,
1811        );
1812        let mut seg: Option<(
1813            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1814            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1815            &mut CudaSlice<f32>,
1816            &mut usize,
1817        )> = None;
1818        let mut x_own2;
1819        match slab_guard.as_mut() {
1820            Some(g) => {
1821                let slabs = &mut **g;
1822                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1823                let PrimeSlabs {
1824                    xa,
1825                    xb,
1826                    h,
1827                    x1,
1828                    z,
1829                    act,
1830                    h16,
1831                    z16,
1832                    gate,
1833                    up,
1834                    ffn_out,
1835                    seg_glue,
1836                    mixed,
1837                    seg_mid,
1838                    seg_t,
1839                    ..
1840                } = slabs;
1841                x_cur = xa;
1842                x_nxt = xb;
1843                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1844                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1845            }
1846            None => {
1847                x_own = x_in;
1848                x_own2 = e.uninit(t * n_embd)?;
1849                x_cur = &mut x_own;
1850                x_nxt = &mut x_own2;
1851                sl = None;
1852            }
1853        }
1854        let mut alloc_h;
1855        let mut alloc_x1;
1856        let mut alloc_z;
1857        let mut alloc_act;
1858        let mut alloc_h16;
1859        let mut alloc_z16;
1860        let mut alloc_gate;
1861        let mut alloc_up;
1862        let mut alloc_fo;
1863        let (h, x1, z, act): (
1864            &mut CudaSlice<f32>,
1865            &mut CudaSlice<f32>,
1866            &mut CudaSlice<f32>,
1867            &mut CudaSlice<f32>,
1868        );
1869        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
1870        let (sl_gate, sl_up, sl_fo): (
1871            &mut CudaSlice<f32>,
1872            &mut CudaSlice<f32>,
1873            &mut CudaSlice<f32>,
1874        );
1875        match sl {
1876            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
1877                h = a;
1878                x1 = b;
1879                z = c;
1880                act = d;
1881                h16 = e16;
1882                z16 = f16b;
1883                sl_gate = g;
1884                sl_up = u;
1885                sl_fo = fo;
1886            }
1887            None => {
1888                alloc_h = e.uninit(t * n_embd)?;
1889                alloc_x1 = e.uninit(t * n_embd)?;
1890                alloc_z = e.uninit(t * n_embd)?;
1891                alloc_act = e.uninit(t * n_ff_max)?;
1892                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1893                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
1894                alloc_gate = e.uninit(t * n_ff_max)?;
1895                alloc_up = e.uninit(t * n_ff_max)?;
1896                alloc_fo = e.uninit(t * n_embd)?;
1897                h = &mut alloc_h;
1898                x1 = &mut alloc_x1;
1899                z = &mut alloc_z;
1900                act = &mut alloc_act;
1901                h16 = &mut alloc_h16;
1902                z16 = &mut alloc_z16;
1903                sl_gate = &mut alloc_gate;
1904                sl_up = &mut alloc_up;
1905                sl_fo = &mut alloc_fo;
1906            }
1907        }
1908        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
1909        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
1910        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
1911        // first prime at this t (capture does not execute -> launch right after).
1912        let n_layers = self.layers.len();
1913        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
1914        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
1915        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
1916        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
1917        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
1918        // machinery stays (byte-identical) as their foundation.
1919        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
1920        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
1921        // step35 rides its own mixer through the normal per-layer arm below.
1922        let use_seg = f16fuse
1923            && seg.is_some()
1924            && !self.uses_sliding_gated_moe_program()
1925            && lo == 0
1926            && hi == n_layers
1927            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
1928        if let Some((sg, sm, _, st)) = seg.as_mut() {
1929            if **st != t {
1930                sg.clear();
1931                sg.extend((0..n_layers).map(|_| None));
1932                sm.clear();
1933                sm.extend((0..n_layers).map(|_| None));
1934                **st = t;
1935            }
1936        }
1937        {
1938            let layer_lo = &self.layers[lo];
1939            if f16fuse {
1940                e.rms_norm_f16out(
1941                    x_cur,
1942                    layer_lo.attn_norm.float_data(),
1943                    h,
1944                    h16,
1945                    n_embd,
1946                    t,
1947                    eps,
1948                )?;
1949            } else {
1950                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
1951            }
1952        }
1953        let anat = Self::prime_anatomy_on();
1954        let mut anat_last = if anat {
1955            e.stream().synchronize()?;
1956            Some(std::time::Instant::now())
1957        } else {
1958            None
1959        };
1960        // Closes the region that just ENDED into `slot`, restarting the clock.
1961        macro_rules! anat_mark {
1962            ($slot:expr) => {
1963                if let Some(ts) = anat_last.as_mut() {
1964                    e.stream().synchronize()?;
1965                    Self::prime_anatomy_slots()[$slot].fetch_add(
1966                        ts.elapsed().as_nanos() as u64,
1967                        std::sync::atomic::Ordering::Relaxed,
1968                    );
1969                    *ts = std::time::Instant::now();
1970                }
1971            };
1972        }
1973        for il in lo..hi {
1974            let layer = &self.layers[il];
1975            let hx16 = if f16fuse { Some(&*h16) } else { None };
1976            if use_seg {
1977                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
1978                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
1979                let (pre, pre16, w_out) = match &layer.mixer {
1980                    Mixer::Full(fa) => {
1981                        let g3 = match hx16 {
1982                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
1983                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
1984                        };
1985                        let (pre, pre16) =
1986                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
1987                        (pre, pre16, &fa.wo)
1988                    }
1989                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1990                    Mixer::Linear(la) => {
1991                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
1992                        let g4 = match hx16 {
1993                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
1994                            None => e.matmul_group(&ws, h, t)?,
1995                        };
1996                        let (pre, pre16) =
1997                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
1998                        (pre, pre16, &la.ssm_out)
1999                    }
2000                };
2001                {
2002                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
2003                    let pre_n = pre.len() / t;
2004                    let xh_pre = match pre16 {
2005                        Some(x) => x,
2006                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
2007                    };
2008                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
2009                        let y = e.matmul(w_out, &pre, t)?;
2010                        e.copy_into(mslab, 0, &y, t * n_embd)?;
2011                    }
2012                    if sm[il].is_none() {
2013                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2014                        let w_post = layer.post_attn_norm.float_data();
2015                        e.stream().synchronize()?;
2016                        e.stream()
2017                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2018                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2019                            e.add(x_cur, mslab, x1, t * n_embd)?;
2020                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2021                            Ok(())
2022                        })();
2023                        let g = e.stream().end_capture(
2024                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2025                        r?;
2026                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2027                    }
2028                    sm[il].as_ref().unwrap().launch()?;
2029                }
2030            } else {
2031                let mixed = match &layer.mixer {
2032                    Mixer::Full(fa) => {
2033                        let y =
2034                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2035                        anat_mark!(0);
2036                        y
2037                    }
2038                    Mixer::Linear(la) => {
2039                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2040                        anat_mark!(1);
2041                        y
2042                    }
2043                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2044                };
2045                if f16fuse {
2046                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2047                    // bit-identical) — the standalone add pass disappears.
2048                    e.add_rms_norm_f16out(
2049                        x_cur,
2050                        &mixed,
2051                        layer.post_attn_norm.float_data(),
2052                        x1,
2053                        z,
2054                        z16,
2055                        n_embd,
2056                        t,
2057                        eps,
2058                    )?;
2059                } else {
2060                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2061                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2062                }
2063                anat_mark!(4);
2064            }
2065            let zx16 = if f16fuse { Some(&*z16) } else { None };
2066            match &layer.ffn {
2067                crate::hybrid::Ffn::Dense {
2068                    ffn_gate,
2069                    ffn_up,
2070                    ffn_down,
2071                } => {
2072                    let n_ff = ffn_gate.out_features();
2073                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2074                    // the allocating group + copy when a mirror is missing.
2075                    let mut into_ok = false;
2076                    if let Some(xh) = zx16 {
2077                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2078                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2079                    }
2080                    if !into_ok {
2081                        let mut g2 = match zx16 {
2082                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2083                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2084                        };
2085                        let up_y = g2.pop().unwrap();
2086                        let gate_y = g2.pop().unwrap();
2087                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2088                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2089                    }
2090                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2091                    // operand in-epilogue; non-silu activations keep the standalone convert.
2092                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2093                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2094                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2095                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2096                    {
2097                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2098                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2099                        Some(a16)
2100                    } else {
2101                        Self::ffn_act_lim(
2102                            e,
2103                            &self.cfg,
2104                            sl_gate,
2105                            sl_up,
2106                            1.0,
2107                            1.0,
2108                            d_lim,
2109                            act,
2110                            t * n_ff,
2111                        )?;
2112                        None
2113                    };
2114                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2115                    let xh_act = match act16 {
2116                        Some(x) => x,
2117                        None => e.f16_act(act, t * n_ff, n_ff)?,
2118                    };
2119                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2120                        let y = e.matmul(ffn_down, &*act, t)?;
2121                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2122                    }
2123                }
2124                crate::hybrid::Ffn::Moe(m) => {
2125                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2126                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2127                    anat_mark!(2);
2128                }
2129            }
2130            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2131                anat_mark!(3);
2132            }
2133            if use_seg && il + 1 < hi {
2134                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2135                let w_next = self.layers[il + 1].attn_norm.float_data();
2136                let (sg, _, _, _) = seg.as_mut().unwrap();
2137                if sg[il].is_none() {
2138                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2139                    e.stream().synchronize()?;
2140                    e.stream()
2141                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2142                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2143                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2144                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2145                        Ok(())
2146                    })();
2147                    let g = e.stream().end_capture(
2148                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2149                    );
2150                    r?;
2151                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2152                }
2153                sg[il].as_ref().unwrap().launch()?;
2154            } else {
2155                if il + 1 < hi {
2156                    let w_next = self.layers[il + 1].attn_norm.float_data();
2157                    if f16fuse {
2158                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2159                    } else {
2160                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2161                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2162                    }
2163                } else {
2164                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2165                }
2166            }
2167            anat_mark!(4);
2168            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2169            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2170            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2171            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2172            // unset (the default) costs one OnceLock read per layer.
2173            if let Some(path) = Self::prime_trace_path() {
2174                let row = (base + t - 1) as usize;
2175                let host = e.dtoh(x_nxt)?;
2176                let last = &host[(t - 1) * n_embd..t * n_embd];
2177                use std::io::Write as _;
2178                let mut f = std::fs::OpenOptions::new()
2179                    .create(true)
2180                    .append(true)
2181                    .open(path)?;
2182                let mut h64: u64 = 0xcbf29ce484222325;
2183                for v in last {
2184                    h64 ^= v.to_bits() as u64;
2185                    h64 = h64.wrapping_mul(0x100000001b3);
2186                }
2187                writeln!(
2188                    f,
2189                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2190                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2191                    last[0], last[1], last[2]
2192                )?;
2193            }
2194            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2195            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2196            self.dflash_tap(e, cache, il, x_nxt, t)?;
2197            std::mem::swap(&mut x_cur, &mut x_nxt);
2198        }
2199        if anat {
2200            let s = Self::prime_anatomy_slots();
2201            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2202            eprintln!(
2203                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2204                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2205                ms(0),
2206                ms(1),
2207                ms(2),
2208                ms(3),
2209                ms(4)
2210            );
2211        }
2212        // hidden-stack return: clone the final x out of the slab
2213        let mut x = e.uninit(t * n_embd)?;
2214        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2215        drop(slab_guard);
2216        Ok(x)
2217    }
2218
2219    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2220    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2221    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2222    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2223    fn prime_chunk_epilogue(
2224        &self,
2225        e: &Engine,
2226        x: CudaSlice<f32>,
2227        t: usize,
2228        cache: &mut Cache,
2229    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2230        let n_embd = self.cfg.n_embd as usize;
2231        let eps = self.cfg.rms_eps;
2232        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2233        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2234        // the post-norm copy happens after hn exists).
2235        let mut h_seed = e.uninit(n_embd)?;
2236        if !crate::spec::spec_hpost() {
2237            e.copy_view_into(
2238                &mut h_seed,
2239                0,
2240                &x.slice((t - 1) * n_embd..t * n_embd),
2241                n_embd,
2242            )?;
2243        }
2244        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2245        let mut hn = e.uninit(t * n_embd)?;
2246        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2247        if crate::spec::spec_hpost() {
2248            e.copy_view_into(
2249                &mut h_seed,
2250                0,
2251                &hn.slice((t - 1) * n_embd..t * n_embd),
2252                n_embd,
2253            )?;
2254        }
2255        let last = e.view(&hn, t * n_embd);
2256        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2257        let mut hlast = e.uninit(n_embd)?;
2258        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2259        let logits = e.matmul(&self.output, &hlast, 1)?;
2260        cache.pos += t;
2261        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2262        // post-norm stack hn (MEMRA_SPEC_HPOST).
2263        Ok((
2264            e.dtoh(&logits)?,
2265            h_seed,
2266            if crate::spec::spec_hpost() { hn } else { x },
2267        ))
2268    }
2269
2270    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2271    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2272    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2273    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2274    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2275    /// prefill kernels. Structure mirrors the verify split exactly:
2276    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2277    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2278    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2279    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2280    ///                  there via the sharded loader) → `publish_to`
2281    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2282    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2283    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2284    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2285    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2286    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2287    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2288    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2289    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2290    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2291    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2292    /// and its liveness counter is bumped here — the gate goes green with this function.
2293    fn prime_chunk_ppn(
2294        &self,
2295        e: &Engine,
2296        tokens: &[u32],
2297        cache: &mut Cache,
2298        seq_end: usize,
2299        fence: &[usize],
2300    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2301        let rt = crate::pp::PpNRt::get(e)?;
2302        let n_st = fence.len() - 1;
2303        assert_eq!(
2304            rt.n_stages(),
2305            n_st,
2306            "PpNRt stage count {} != fence stages {n_st}",
2307            rt.n_stages()
2308        );
2309        let n_embd = self.cfg.n_embd as usize;
2310        let t = tokens.len();
2311        let base = cache.pos;
2312        debug_assert!(
2313            seq_end >= base + t,
2314            "prime_chunk_ppn: seq_end must cover this chunk"
2315        );
2316        let payload = t * n_embd;
2317        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2318        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2319        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2320        let caller_stream = e.stream();
2321        rt.fence_stages_behind(&caller_stream)?;
2322
2323        if n_st == 2 {
2324            let slot =
2325                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2326            let x =
2327                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2328            let out = {
2329                rt.bind_stage(1)?;
2330                let _st1 = rt.enter(1);
2331                let e1 = rt.engine(1, e);
2332                self.prime_chunk_epilogue(e1, x, t, cache)?
2333            };
2334            rt.publish_to(1, &caller_stream)?;
2335            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2336            return Ok(out);
2337        }
2338
2339        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2340
2341        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2342        let mut slot = {
2343            let _st0 = rt.enter(0);
2344            let e0 = rt.engine(0, e);
2345            let pos_d = e0.htod_i32(&pos)?;
2346            let x = self.embed(e0, tokens)?;
2347            let x =
2348                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2349            rt.tx(0, &x, payload)?
2350            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2351        };
2352
2353        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2354        for s in 1..n_st - 1 {
2355            let _st = rt.enter(s);
2356            let es = rt.engine(s, e);
2357            let pos_d = es.htod_i32(&pos)?;
2358            let x = rt.rx(s - 1, slot, payload)?;
2359            let x = self.prime_layers(
2360                es,
2361                x,
2362                fence[s],
2363                fence[s + 1],
2364                &pos_d,
2365                t,
2366                base,
2367                cache,
2368                seq_end,
2369            )?;
2370            slot = rt.tx(s, &x, payload)?;
2371        }
2372
2373        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2374        let _stl = rt.enter(n_st - 1);
2375        let el = rt.engine(n_st - 1, e);
2376        let pos_d = el.htod_i32(&pos)?;
2377        let x = rt.rx(n_st - 2, slot, payload)?;
2378        let x = self.prime_layers(
2379            el,
2380            x,
2381            fence[n_st - 1],
2382            fence[n_st],
2383            &pos_d,
2384            t,
2385            base,
2386            cache,
2387            seq_end,
2388        )?;
2389        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2390        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2391        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2392        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2393        // stage stream host-side, but the law is stated in events, not in a dtoh side
2394        // effect a later deferred form would remove.
2395        rt.publish_to(n_st - 1, &caller_stream)?;
2396        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2397        Ok(out)
2398    }
2399
2400    fn prime_pp2_stage0_enqueue(
2401        &self,
2402        e: &Engine,
2403        rt: &crate::pp::PpNRt,
2404        tokens: &[u32],
2405        cache: &mut Cache,
2406        seq_end: usize,
2407        fence: &[usize],
2408        base: usize,
2409        pipelined: bool,
2410    ) -> Result<usize, Box<dyn std::error::Error>> {
2411        let t = tokens.len();
2412        let n_embd = self.cfg.n_embd as usize;
2413        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2414        rt.bind_stage(0)?;
2415        let _st0 = rt.enter(0);
2416        let e0 = rt.engine(0, e);
2417        let pos_d = e0.htod_i32(&pos)?;
2418        let x = self.embed(e0, tokens)?;
2419        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2420        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2421        if pipelined {
2422            rt.tx_pipelined(0, &x, t * n_embd)
2423        } else {
2424            rt.tx(0, &x, t * n_embd)
2425        }
2426    }
2427
2428    fn prime_pp2_stage1_enqueue(
2429        &self,
2430        e: &Engine,
2431        rt: &crate::pp::PpNRt,
2432        slot: usize,
2433        t: usize,
2434        cache: &mut Cache,
2435        seq_end: usize,
2436        fence: &[usize],
2437        base: usize,
2438        pipelined: bool,
2439    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2440        let n_embd = self.cfg.n_embd as usize;
2441        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2442        rt.bind_stage(1)?;
2443        let _st1 = rt.enter(1);
2444        let e1 = rt.engine(1, e);
2445        let pos_d = e1.htod_i32(&pos)?;
2446        let x = rt.rx(0, slot, t * n_embd)?;
2447        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2448        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2449    }
2450
2451    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2452    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2453    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2454    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2455    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2456    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2457    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2458    /// bookkeeping still runs on the host per call — the real replay path moves the write
2459    /// slot to the len_d device counter (increment 3).
2460    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2461    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2462    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2463    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2464    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2465    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2466    pub fn prime_chunk_captured(
2467        &self,
2468        e: &Engine,
2469        x_in: &CudaSlice<f32>,
2470        pos_d: &CudaSlice<i32>,
2471        t: usize,
2472        cache: &mut Cache,
2473        len_d: &CudaSlice<i32>,
2474        logits_out: &mut CudaSlice<f32>,
2475        h_seed_out: &mut CudaSlice<f32>,
2476    ) -> Result<(), Box<dyn std::error::Error>> {
2477        let cfg = &self.cfg;
2478        let n_embd = cfg.n_embd as usize;
2479        let eps = cfg.rms_eps;
2480        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2481        let mut x = e.uninit(t * n_embd)?;
2482        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2483        for (il, layer) in self.layers.iter().enumerate() {
2484            let mut h = e.uninit(t * n_embd)?;
2485            let mut hx16: Option<CudaSlice<u8>> = None;
2486            if f16fuse {
2487                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2488                e.rms_norm_f16out(
2489                    &x,
2490                    layer.attn_norm.float_data(),
2491                    &mut h,
2492                    &mut b16,
2493                    n_embd,
2494                    t,
2495                    eps,
2496                )?;
2497                hx16 = Some(b16);
2498            } else {
2499                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2500            }
2501            let mixed = match &layer.mixer {
2502                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2503                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2504                // come from the caller (see step35_attn_pre_wo's doc note).
2505                Mixer::Full(fa) => {
2506                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2507                }
2508                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2509                Mixer::Linear(la) => {
2510                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2511                    let g4 = match hx16.as_ref() {
2512                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2513                        None => e.matmul_group(&ws, &h, t)?,
2514                    };
2515                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2516                }
2517            };
2518            let mut x1 = e.uninit(t * n_embd)?;
2519            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2520            let mut z = e.uninit(t * n_embd)?;
2521            let mut zx16: Option<CudaSlice<u8>> = None;
2522            if f16fuse {
2523                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2524                e.rms_norm_f16out(
2525                    &x1,
2526                    layer.post_attn_norm.float_data(),
2527                    &mut z,
2528                    &mut b16,
2529                    n_embd,
2530                    t,
2531                    eps,
2532                )?;
2533                zx16 = Some(b16);
2534            } else {
2535                e.rms_norm(
2536                    &x1,
2537                    layer.post_attn_norm.float_data(),
2538                    &mut z,
2539                    n_embd,
2540                    t,
2541                    eps,
2542                )?;
2543            }
2544            let ffn_out = match &layer.ffn {
2545                crate::hybrid::Ffn::Dense {
2546                    ffn_gate,
2547                    ffn_up,
2548                    ffn_down,
2549                } => {
2550                    let n_ff = ffn_gate.out_features();
2551                    let mut g2 = match &zx16 {
2552                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2553                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2554                    };
2555                    let up = g2.pop().unwrap();
2556                    let gate = g2.pop().unwrap();
2557                    let mut act = e.uninit(t * n_ff)?;
2558                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2559                    Self::ffn_act_lim(
2560                        e,
2561                        &self.cfg,
2562                        &gate,
2563                        &up,
2564                        1.0,
2565                        1.0,
2566                        self.cfg.clamp_shexp_at(il as u32),
2567                        &mut act,
2568                        t * n_ff,
2569                    )?;
2570                    e.matmul(ffn_down, &act, t)?
2571                }
2572                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2573            };
2574            let mut x2 = e.uninit(t * n_embd)?;
2575            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2576            x = x2;
2577        }
2578        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2579        if !crate::spec::spec_hpost() {
2580            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2581        }
2582        let mut hn = e.uninit(t * n_embd)?;
2583        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2584        if crate::spec::spec_hpost() {
2585            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2586        }
2587        let mut hlast = e.uninit(n_embd)?;
2588        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2589        let logits = e.matmul(&self.output, &hlast, 1)?;
2590        let nv = logits.len();
2591        e.copy_into(logits_out, 0, &logits, nv)?;
2592        Ok(())
2593    }
2594
2595    fn step35_prime_batch_on() -> bool {
2596        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2597    }
2598
2599    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2600    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2601    #[allow(clippy::too_many_arguments)]
2602    fn step35_prime_batch_layers(
2603        &self,
2604        e: &Engine,
2605        mut x: CudaSlice<f32>,
2606        lo: usize,
2607        hi: usize,
2608        ts: &[usize],
2609        offs: &[usize],
2610        pos_ds: &[CudaSlice<i32>],
2611        caches: &mut [&mut Cache],
2612    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2613        let cfg = &self.cfg;
2614        let n_embd = cfg.n_embd as usize;
2615        let eps = cfg.rms_eps;
2616        let b = ts.len();
2617        let total: usize = ts.iter().sum();
2618        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2619
2620        let split = |e: &Engine,
2621                     y: &CudaSlice<f32>,
2622                     dim: usize|
2623         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2624            let mut out = Vec::with_capacity(b);
2625            for s in 0..b {
2626                let mut ys = e.uninit(ts[s] * dim)?;
2627                e.copy_view_into(
2628                    &mut ys,
2629                    0,
2630                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2631                    ts[s] * dim,
2632                )?;
2633                out.push(ys);
2634            }
2635            Ok(out)
2636        };
2637
2638        for il in lo..hi {
2639            let layer = &self.layers[il];
2640            let Mixer::Full(fa) = &layer.mixer else {
2641                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2642            };
2643
2644            let mut h = e.uninit(total * n_embd)?;
2645            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2646            if f16fuse {
2647                e.rms_norm_f16out(
2648                    &x,
2649                    layer.attn_norm.float_data(),
2650                    &mut h,
2651                    &mut hx16,
2652                    n_embd,
2653                    total,
2654                    eps,
2655                )?;
2656            } else {
2657                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2658            }
2659
2660            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2661            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2662            // application stay verbatim.
2663            let gate_w = fa
2664                .attn_gate
2665                .as_ref()
2666                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2667            let mut g4 = if f16fuse {
2668                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2669            } else {
2670                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2671            };
2672            let gate = g4.pop().unwrap();
2673            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2674                (0..b).map(|_| Vec::with_capacity(3)).collect();
2675            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2676                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2677                    parts[s].push(ys);
2678                }
2679            }
2680            let gates = split(e, &gate, gate_w.out_features())?;
2681            let geometry = self.step35_geom(il);
2682            let hd = geometry.head_dim_k as usize;
2683            let nh = geometry.n_head as usize;
2684            let mut ag_cat = e.uninit(total * nh * hd)?;
2685            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2686                let ag = self.step35_attn_pre_wo(
2687                    e,
2688                    fa,
2689                    g3s,
2690                    None,
2691                    Some(&gate),
2692                    &pos_ds[s],
2693                    ts[s],
2694                    Some(&mut *caches[s]),
2695                    il,
2696                    ts[s],
2697                )?;
2698                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2699            }
2700            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2701
2702            let mut x1 = e.uninit(total * n_embd)?;
2703            let mut z = e.uninit(total * n_embd)?;
2704            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2705            if f16fuse {
2706                e.add_rms_norm_f16out(
2707                    &x,
2708                    &mixed,
2709                    layer.post_attn_norm.float_data(),
2710                    &mut x1,
2711                    &mut z,
2712                    &mut zx16,
2713                    n_embd,
2714                    total,
2715                    eps,
2716                )?;
2717            } else {
2718                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2719                e.rms_norm(
2720                    &x1,
2721                    layer.post_attn_norm.float_data(),
2722                    &mut z,
2723                    n_embd,
2724                    total,
2725                    eps,
2726                )?;
2727            }
2728
2729            let ffn_out = match &layer.ffn {
2730                crate::hybrid::Ffn::Dense {
2731                    ffn_gate,
2732                    ffn_up,
2733                    ffn_down,
2734                } => {
2735                    let n_ff = ffn_gate.out_features();
2736                    let mut g2 = if f16fuse {
2737                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2738                    } else {
2739                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2740                    };
2741                    let up = g2.pop().unwrap();
2742                    let gate = g2.pop().unwrap();
2743                    let mut act = e.uninit(total * n_ff)?;
2744                    let d_lim = cfg.clamp_shexp_at(il as u32);
2745                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2746                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2747                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2748                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2749                            Some(y) => y,
2750                            None => e.matmul(ffn_down, &act, total)?,
2751                        }
2752                    } else {
2753                        Self::ffn_act_lim(
2754                            e,
2755                            cfg,
2756                            &gate,
2757                            &up,
2758                            1.0,
2759                            1.0,
2760                            d_lim,
2761                            &mut act,
2762                            total * n_ff,
2763                        )?;
2764                        e.matmul(ffn_down, &act, total)?
2765                    }
2766                }
2767                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2768            };
2769            let mut x2 = e.uninit(total * n_embd)?;
2770            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2771            x = x2;
2772        }
2773        Ok(x)
2774    }
2775
2776    fn step35_prime_batch_epilogue(
2777        &self,
2778        e: &Engine,
2779        x: CudaSlice<f32>,
2780        ts: &[usize],
2781        offs: &[usize],
2782        caches: &mut [&mut Cache],
2783    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2784        let n_embd = self.cfg.n_embd as usize;
2785        let total: usize = ts.iter().sum();
2786        let mut hn = e.uninit(total * n_embd)?;
2787        e.rms_norm(
2788            &x,
2789            self.output_norm.float_data(),
2790            &mut hn,
2791            n_embd,
2792            total,
2793            self.cfg.rms_eps,
2794        )?;
2795
2796        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2797        let mut out = Vec::with_capacity(ts.len());
2798        for s in 0..ts.len() {
2799            let mut hidden = e.uninit(ts[s] * n_embd)?;
2800            e.copy_view_into(
2801                &mut hidden,
2802                0,
2803                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
2804                ts[s] * n_embd,
2805            )?;
2806            let last0 = (offs[s] + ts[s] - 1) * n_embd;
2807            let mut h_seed = e.uninit(n_embd)?;
2808            e.copy_view_into(
2809                &mut h_seed,
2810                0,
2811                &hidden_src.slice(last0..last0 + n_embd),
2812                n_embd,
2813            )?;
2814            // Exactness-first: the serial reference runs the output head at m=1.
2815            let mut hlast = e.uninit(n_embd)?;
2816            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
2817            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
2818            caches[s].pos += ts[s];
2819            out.push((logits, h_seed, hidden));
2820        }
2821        Ok(out)
2822    }
2823
2824    fn step35_prime_cache_batch(
2825        &self,
2826        e: &Engine,
2827        prompts: &[&[u32]],
2828        caches: &mut [&mut Cache],
2829    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2830        validate_step_prime_batch_modes(
2831            step_tp_prefill_enabled()?,
2832            step_ep_grouped_prefill_enabled()?,
2833        )?;
2834        if crate::pp::pp_host_bounce_active()
2835            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
2836        {
2837            return Err(
2838                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
2839                 stage split; refusing an unsplit remote-weight walk"
2840                    .into(),
2841            );
2842        }
2843        if !Self::step35_prime_batch_on() {
2844            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
2845        }
2846        if caches.iter().any(|c| c.pos != 0) {
2847            return Err(
2848                "step35 batched prime currently supports complete fresh prompts only; \
2849                 continuation/tick chunks require per-request queued_after"
2850                    .into(),
2851            );
2852        }
2853
2854        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
2855        for &t in &ts {
2856            assert!(
2857                t >= PRIME_MIN_T,
2858                "step35 batched prime needs T >= {PRIME_MIN_T}"
2859            );
2860        }
2861        for (s, c) in caches.iter().enumerate() {
2862            assert!(
2863                ts[s] <= c.max_ctx,
2864                "step35 batched prime exceeds cache max_ctx"
2865            );
2866        }
2867        let offs: Vec<usize> = ts
2868            .iter()
2869            .scan(0usize, |a, &t| {
2870                let o = *a;
2871                *a += t;
2872                Some(o)
2873            })
2874            .collect();
2875        let total: usize = ts.iter().sum();
2876        let payload = total * self.cfg.n_embd as usize;
2877        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
2878        let positions: Vec<Vec<i32>> = ts.iter().map(|&t| (0..t as i32).collect()).collect();
2879        let upload_positions =
2880            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
2881                positions
2882                    .iter()
2883                    .map(|p| e.htod_i32(p))
2884                    .collect::<Result<_, _>>()
2885            };
2886
2887        static ONCE: std::sync::Once = std::sync::Once::new();
2888        ONCE.call_once(|| {
2889            eprintln!(
2890                "[step35-prime-batch] first concat prime: B={} tokens={total}",
2891                prompts.len()
2892            );
2893        });
2894
2895        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
2896            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
2897                let rt = crate::pp::PpNRt::get(e)?;
2898                let n_st = fence.len() - 1;
2899                assert_eq!(
2900                    rt.n_stages(),
2901                    n_st,
2902                    "step35 prime batch stage count mismatch"
2903                );
2904                let caller_stream = e.stream();
2905                rt.fence_stages_behind(&caller_stream)?;
2906
2907                let mut slot = {
2908                    let _st0 = rt.enter(0);
2909                    let e0 = rt.engine(0, e);
2910                    let pos_ds = upload_positions(e0)?;
2911                    let x = self.embed(e0, &cat_tokens)?;
2912                    let x = self.step35_prime_batch_layers(
2913                        e0, x, fence[0], fence[1], &ts, &offs, &pos_ds, caches,
2914                    )?;
2915                    rt.tx(0, &x, payload)?
2916                };
2917                for s in 1..n_st - 1 {
2918                    let _st = rt.enter(s);
2919                    let es = rt.engine(s, e);
2920                    let pos_ds = upload_positions(es)?;
2921                    let x = rt.rx(s - 1, slot, payload)?;
2922                    let x = self.step35_prime_batch_layers(
2923                        es,
2924                        x,
2925                        fence[s],
2926                        fence[s + 1],
2927                        &ts,
2928                        &offs,
2929                        &pos_ds,
2930                        caches,
2931                    )?;
2932                    slot = rt.tx(s, &x, payload)?;
2933                }
2934
2935                let _stl = rt.enter(n_st - 1);
2936                let el = rt.engine(n_st - 1, e);
2937                let pos_ds = upload_positions(el)?;
2938                let x = rt.rx(n_st - 2, slot, payload)?;
2939                let x = self.step35_prime_batch_layers(
2940                    el,
2941                    x,
2942                    fence[n_st - 1],
2943                    fence[n_st],
2944                    &ts,
2945                    &offs,
2946                    &pos_ds,
2947                    caches,
2948                )?;
2949                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
2950                rt.publish_to(n_st - 1, &caller_stream)?;
2951                crate::pp::STEP35_PRIME_BATCH_SPLITS
2952                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2953                out
2954            } else {
2955                let pos_ds = upload_positions(e)?;
2956                let x = self.embed(e, &cat_tokens)?;
2957                let x = self.step35_prime_batch_layers(
2958                    e,
2959                    x,
2960                    0,
2961                    self.layers.len(),
2962                    &ts,
2963                    &offs,
2964                    &pos_ds,
2965                    caches,
2966                )?;
2967                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2968            }
2969        } else {
2970            let pos_ds = upload_positions(e)?;
2971            let x = self.embed(e, &cat_tokens)?;
2972            let x = self.step35_prime_batch_layers(
2973                e,
2974                x,
2975                0,
2976                self.layers.len(),
2977                &ts,
2978                &offs,
2979                &pos_ds,
2980                caches,
2981            )?;
2982            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
2983        };
2984        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2985        Ok(out)
2986    }
2987
2988    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
2989    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
2990    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
2991    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
2992    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
2993    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
2994    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
2995    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
2996    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
2997    /// over the quantized past; Linear: the stateful pad_view twin — the same state
2998    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
2999    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
3000    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
3001    /// back to single-chunk serving).
3002    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
3003    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
3004    pub fn prime_cache_batch(
3005        &self,
3006        e: &Engine,
3007        prompts: &[&[u32]],
3008        caches: &mut [&mut Cache],
3009    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3010        if crate::pp::pp_cuts(self.layers.len()).is_some()
3011            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
3012        {
3013            return Err("pipeline rewrite is not qualified for batched prime".into());
3014        }
3015        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3016            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3017                return Err("neither batched-prime nor eager rewrite is qualified".into());
3018            }
3019            if prompts.len() != caches.len() {
3020                return Err("prime fallback prompt/cache shape mismatch".into());
3021            }
3022            static ONCE: std::sync::Once = std::sync::Once::new();
3023            ONCE.call_once(|| {
3024                eprintln!(
3025                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3026                );
3027            });
3028            return prompts
3029                .iter()
3030                .copied()
3031                .zip(caches.iter_mut())
3032                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3033                .collect();
3034        }
3035        let cfg = &self.cfg;
3036        let n_embd = cfg.n_embd as usize;
3037        let eps = cfg.rms_eps;
3038        let b = prompts.len();
3039        assert!(b >= 1 && b == caches.len());
3040        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3041        let carried = pos0s.iter().any(|&p| p > 0);
3042        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3043        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3044        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3045        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3046        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3047        if self.uses_gemma_program() {
3048            return Err(
3049                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3050                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3051                    .into(),
3052            );
3053        }
3054        // Step35 has a dedicated concat walk: the generic core below cannot express its
3055        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3056        if self.uses_sliding_gated_moe_program() {
3057            return self.step35_prime_cache_batch(e, prompts, caches);
3058        }
3059        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3060        for &t in &ts {
3061            assert!(
3062                t >= PRIME_MIN_T,
3063                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3064            );
3065        }
3066        for (s, c) in caches.iter().enumerate() {
3067            assert!(
3068                c.pos + ts[s] <= c.max_ctx,
3069                "prime_cache_batch: prompt exceeds cache max_ctx"
3070            );
3071        }
3072        let total: usize = ts.iter().sum();
3073        let offs: Vec<usize> = ts
3074            .iter()
3075            .scan(0usize, |a, &t| {
3076                let o = *a;
3077                *a += t;
3078                Some(o)
3079            })
3080            .collect();
3081        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3082        let pos_ds: Vec<CudaSlice<i32>> = ts
3083            .iter()
3084            .zip(&pos0s)
3085            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3086            .collect::<Result<_, _>>()?;
3087        // split a concat [total, dim] buffer into per-seq copies
3088        let split = |e: &Engine,
3089                     y: &CudaSlice<f32>,
3090                     dim: usize|
3091         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3092            let mut out = Vec::with_capacity(b);
3093            for s in 0..b {
3094                let mut ys = e.uninit(ts[s] * dim)?;
3095                e.copy_view_into(
3096                    &mut ys,
3097                    0,
3098                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3099                    ts[s] * dim,
3100                )?;
3101                out.push(ys);
3102            }
3103            Ok(out)
3104        };
3105
3106        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3107        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3108        for (il, layer) in self.layers.iter().enumerate() {
3109            let mut h = e.uninit(total * n_embd)?;
3110            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3111            e.rms_norm_f16out(
3112                &x,
3113                layer.attn_norm.float_data(),
3114                &mut h,
3115                &mut hx16,
3116                n_embd,
3117                total,
3118                eps,
3119            )?;
3120            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3121            let mut mixed = e.uninit(total * n_embd)?;
3122            match &layer.mixer {
3123                Mixer::Full(fa) => {
3124                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3125                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3126                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3127                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3128                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3129                    // back to the per-seq dispatch.
3130                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3131                    let (n_head, n_head_kv, head_dim) = (
3132                        geometry.n_head as usize,
3133                        geometry.n_head_kv as usize,
3134                        geometry.head_dim_k as usize,
3135                    );
3136                    let fa_scale = geometry.attention_scale();
3137                    let use_favl = !carried
3138                        && (2..=8).contains(&b)
3139                        && (head_dim == 256 || head_dim == 128)
3140                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3141                        && std::env::var("MEMRA_NOFA").is_err()
3142                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3143                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3144                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3145                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3146                    if use_favl {
3147                        let (qf_w, kf_w, vf_w) = (
3148                            fa.wq.out_features(),
3149                            fa.wk.out_features(),
3150                            fa.wv.out_features(),
3151                        );
3152                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3153                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3154                        // cannot check its own extents; `qf_w` is the wq out-features that set
3155                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3156                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3157                        struct APre {
3158                            q: CudaSlice<f32>,
3159                            gate: Option<CudaSlice<f32>>,
3160                            qn: CudaSlice<f32>,
3161                            kn: CudaSlice<f32>,
3162                        }
3163                        let mut aps = Vec::with_capacity(b);
3164                        for &t in ts.iter().take(b) {
3165                            aps.push(APre {
3166                                q: e.uninit(t * n_head * head_dim)?,
3167                                gate: Some(e.uninit(t * n_head * head_dim)?),
3168                                qn: e.uninit(t * n_head * head_dim)?,
3169                                kn: e.uninit(t * n_head_kv * head_dim)?,
3170                            });
3171                        }
3172                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3173                            let kvl = caches[0].kv[il].as_ref().unwrap();
3174                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3175                        };
3176                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3177                            .map(|s| {
3178                                let (o, t) = (offs[s], ts[s]);
3179                                let kvl = caches[s].kv[il].as_ref().unwrap();
3180                                assert!(
3181                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3182                                    "prime_cache_batch attn vl: fresh + capacity"
3183                                );
3184                                crate::AttnPreVl {
3185                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3186                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3187                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3188                                    q: e.addr_f32(&aps[s].q),
3189                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3190                                    qn: e.addr_f32(&aps[s].qn),
3191                                    kn: e.addr_f32(&aps[s].kn),
3192                                    kc: e.addr_u8(&kvl.k),
3193                                    vc: e.addr_u8(&kvl.v),
3194                                    t: t as i32,
3195                                    pad: 0,
3196                                }
3197                            })
3198                            .collect();
3199                        e.attn_pre_vl8(
3200                            &pargs,
3201                            fa.q_norm.float_data(),
3202                            fa.k_norm.float_data(),
3203                            head_dim,
3204                            geometry.n_rot as usize,
3205                            n_head,
3206                            n_head_kv,
3207                            self.cfg.rms_eps,
3208                            geometry.rope_base,
3209                            1.0,
3210                            kv_dim_k,
3211                            kv_dim_v,
3212                            ktb,
3213                            vtb,
3214                        )?;
3215                        for s in 0..b {
3216                            let kvl = caches[s].kv[il].as_mut().unwrap();
3217                            kvl.len += ts[s];
3218                            let new_len = kvl.len as i32;
3219                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3220                        }
3221                        let mut attns = Vec::with_capacity(b);
3222                        let mut mirrors = Vec::with_capacity(b);
3223                        for &t in ts.iter().take(b) {
3224                            attns.push(e.uninit(t * n_head * head_dim)?);
3225                            let n = t * n_head_kv * head_dim;
3226                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3227                        }
3228                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3229                        // promoted single-seq config is on; else the mma favl.
3230                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3231                            Ok("0") => false,
3232                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3233                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3234                            // portable build.
3235                            Ok("1") => {
3236                                crate::refuse_portable_force(
3237                                    "MEMRA_FA3=1",
3238                                    "the sm_90a fa3/bf16 kernels",
3239                                );
3240                                true
3241                            }
3242                            _ => cfg!(memra_hopper_mma),
3243                        };
3244                        if fa3_on {
3245                            let mut q16s = Vec::with_capacity(b);
3246                            let mut v16s = Vec::with_capacity(b);
3247                            for s in 0..b {
3248                                let t = ts[s];
3249                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3250                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3251                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3252                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3253                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3254                                e.f32_to_bf16_v(
3255                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3256                                    &mut v16,
3257                                    t * n_head_kv * head_dim,
3258                                )?;
3259                                q16s.push(q16);
3260                                v16s.push((k16, v16));
3261                            }
3262                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3263                            let mut kp = qp;
3264                            let mut vp = qp;
3265                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3266                            let mut tsv = [0i32; 8];
3267                            for s in 0..b {
3268                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3269                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3270                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3271                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3272                                tsv[s] = ts[s] as i32;
3273                            }
3274                            let rc = unsafe {
3275                                crate::fa3_vl_raw(
3276                                    qp.as_ptr(),
3277                                    kp.as_ptr(),
3278                                    vp.as_ptr(),
3279                                    op.as_ptr(),
3280                                    tsv.as_ptr(),
3281                                    b as i32,
3282                                    n_head as i32,
3283                                    n_head_kv as i32,
3284                                    head_dim as i32,
3285                                    fa_scale,
3286                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3287                                )
3288                            };
3289                            if rc != 0 {
3290                                return Err(format!("memra_fa3_vl rc={rc}").into());
3291                            }
3292                        } else {
3293                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3294                                .map(|s| crate::FaSeqVl {
3295                                    q: e.addr_f32(&aps[s].qn),
3296                                    k16: e.addr_u8(&mirrors[s].0),
3297                                    v16: e.addr_u8(&mirrors[s].1),
3298                                    o: e.addr_f32(&attns[s]),
3299                                    kf: e.addr_f32(&aps[s].kn),
3300                                    vf: e.addr_f32v(
3301                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3302                                    ),
3303                                    t: ts[s] as i32,
3304                                    pad: 0,
3305                                })
3306                                .collect();
3307                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3308                        }
3309                        for (s, attn) in attns.into_iter().enumerate() {
3310                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3311                                e,
3312                                attn,
3313                                &aps[s].gate,
3314                                ts[s],
3315                                n_head,
3316                                head_dim,
3317                            )?;
3318                            let mut done = false;
3319                            if let Some(xh) = &ag16 {
3320                                done = e.try_f16_gemm_pre_into_off(
3321                                    &fa.wo,
3322                                    xh,
3323                                    ts[s],
3324                                    &mut mixed,
3325                                    offs[s] * n_embd,
3326                                )?;
3327                            }
3328                            if !done {
3329                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3330                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3331                            }
3332                        }
3333                    } else {
3334                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3335                            (0..b).map(|_| Vec::new()).collect();
3336                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3337                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3338                                parts[s].push(ys);
3339                            }
3340                        }
3341                        for (s, g3s) in parts.into_iter().enumerate() {
3342                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3343                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3344                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3345                            )?;
3346                            let mut done = false;
3347                            if let Some(xh) = &ag16 {
3348                                done = e.try_f16_gemm_pre_into_off(
3349                                    &fa.wo,
3350                                    xh,
3351                                    ts[s],
3352                                    &mut mixed,
3353                                    offs[s] * n_embd,
3354                                )?;
3355                            }
3356                            if !done {
3357                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3358                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3359                            }
3360                        }
3361                    }
3362                }
3363                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3364                Mixer::Linear(la) => {
3365                    // task #16: NO split copies (cores read row-offset views of the concat
3366                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3367                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3368                    // varlen K5 launch for all sequences.
3369                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3370                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3371                    let outs =
3372                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3373                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3374                        let (o, t) = (offs[s], ts[s]);
3375                        let mut done = false;
3376                        if let Some(xh) = &gn16 {
3377                            done = e.try_f16_gemm_pre_into_off(
3378                                &la.ssm_out,
3379                                xh,
3380                                t,
3381                                &mut mixed,
3382                                o * n_embd,
3383                            )?;
3384                        }
3385                        if !done {
3386                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3387                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3388                        }
3389                    }
3390                }
3391            }
3392            let mut x1 = e.uninit(total * n_embd)?;
3393            let mut z = e.uninit(total * n_embd)?;
3394            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3395            e.add_rms_norm_f16out(
3396                &x,
3397                &mixed,
3398                layer.post_attn_norm.float_data(),
3399                &mut x1,
3400                &mut z,
3401                &mut zx16,
3402                n_embd,
3403                total,
3404                eps,
3405            )?;
3406            let ffn_out = match &layer.ffn {
3407                crate::hybrid::Ffn::Dense {
3408                    ffn_gate,
3409                    ffn_up,
3410                    ffn_down,
3411                } => {
3412                    let n_ff = ffn_gate.out_features();
3413                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3414                    let up = g2.pop().unwrap();
3415                    let gate = g2.pop().unwrap();
3416                    let mut act = e.uninit(total * n_ff)?;
3417                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3418                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3419                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3420                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3421                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3422                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3423                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3424                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3425                            Some(y) => y,
3426                            None => e.matmul(ffn_down, &act, total)?,
3427                        }
3428                    } else {
3429                        Self::ffn_act_lim(
3430                            e,
3431                            &self.cfg,
3432                            &gate,
3433                            &up,
3434                            1.0,
3435                            1.0,
3436                            d_lim,
3437                            &mut act,
3438                            total * n_ff,
3439                        )?;
3440                        e.matmul(ffn_down, &act, total)?
3441                    }
3442                }
3443                crate::hybrid::Ffn::Moe(m) => {
3444                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3445                }
3446            };
3447            let mut x2 = e.uninit(total * n_embd)?;
3448            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3449            x = x2;
3450        }
3451        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3452        let mut hn = e.uninit(total * n_embd)?;
3453        e.rms_norm(
3454            &x,
3455            self.output_norm.float_data(),
3456            &mut hn,
3457            n_embd,
3458            total,
3459            eps,
3460        )?;
3461        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3462        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3463        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3464        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3465        // argmax battery arbitrates, same as every other prefill GEMM change.
3466        let mut hcat = e.uninit(b * n_embd)?;
3467        for s in 0..b {
3468            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3469            e.copy_view_into(
3470                &mut hcat,
3471                s * n_embd,
3472                &hn.slice(last0..last0 + n_embd),
3473                n_embd,
3474            )?;
3475        }
3476        let logits_cat = if b >= 2 {
3477            e.try_f16_gemm(&self.output, &hcat, b)?
3478        } else {
3479            None
3480        };
3481        let logits_host: Option<Vec<f32>> = match &logits_cat {
3482            Some(lc) => Some(e.dtoh(lc)?),
3483            None => None,
3484        };
3485        let n_vocab = self.output.out_features();
3486        let mut hidden_all = if crate::spec::spec_hpost() {
3487            split(e, &hn, n_embd)?
3488        } else {
3489            split(e, &x, n_embd)?
3490        };
3491        let mut out = Vec::with_capacity(b);
3492        for s in 0..b {
3493            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3494            let mut h_seed = e.uninit(n_embd)?;
3495            if !crate::spec::spec_hpost() {
3496                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3497            } else {
3498                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3499            }
3500            let logits = match &logits_host {
3501                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3502                None => {
3503                    let mut hlast = e.uninit(n_embd)?;
3504                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3505                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3506                }
3507            };
3508            caches[s].pos += ts[s];
3509            out.push((logits, h_seed, hidden_all.remove(0)));
3510        }
3511        Ok(out)
3512    }
3513
3514    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3515    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3516    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3517    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3518    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3519    ///
3520    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3521    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3522    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3523    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3524    #[allow(clippy::too_many_arguments)]
3525    fn full_attn_prime(
3526        &self,
3527        e: &Engine,
3528        fa: &FullAttnLayer,
3529        h: &CudaSlice<f32>,
3530        hx: Option<&CudaSlice<u8>>,
3531        pos_d: &CudaSlice<i32>,
3532        t: usize,
3533        cache: &mut Cache,
3534        il: usize,
3535        seq_end: usize,
3536    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3537        if self.uses_sliding_gated_moe_program() {
3538            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3539        }
3540        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3541        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3542        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3543        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3544        let g3 = match hx {
3545            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3546            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3547        };
3548        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3549    }
3550
3551    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3552    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3553    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3554    fn full_attn_prime_core(
3555        &self,
3556        e: &Engine,
3557        fa: &FullAttnLayer,
3558        g3: Vec<CudaSlice<f32>>,
3559        pos_d: &CudaSlice<i32>,
3560        t: usize,
3561        cache: &mut Cache,
3562        il: usize,
3563    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3564        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3565        if let Some(xh) = &ag16 {
3566            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3567                return Ok(y);
3568            }
3569        }
3570        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3571    }
3572
3573    fn full_attn_prime_core_inner(
3574        &self,
3575        e: &Engine,
3576        fa: &FullAttnLayer,
3577        g3: Vec<CudaSlice<f32>>,
3578        pos_d: &CudaSlice<i32>,
3579        t: usize,
3580        cache: &mut Cache,
3581        il: usize,
3582    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3583        let cfg = &self.cfg;
3584        let geometry = cfg.full_attention_geometry_at(il as u32);
3585        let n_head = geometry.n_head as usize;
3586        let n_head_kv = geometry.n_head_kv as usize;
3587        let head_dim = geometry.head_dim_k as usize;
3588        let scale = geometry.attention_scale();
3589        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3590        let AttnPre { q, k, v, gate } = pre;
3591        let mut attn = e.uninit(t * n_head * head_dim)?;
3592        self.full_attn_prime_fa_dispatch(
3593            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3594        )?;
3595        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3596    }
3597
3598    /// task #18 (attn side): projections tail through KV append — everything before the
3599    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3600    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3601    #[allow(clippy::type_complexity)]
3602    fn full_attn_prime_pre_fa(
3603        &self,
3604        e: &Engine,
3605        fa: &FullAttnLayer,
3606        mut g3: Vec<CudaSlice<f32>>,
3607        pos_d: &CudaSlice<i32>,
3608        t: usize,
3609        cache: &mut Cache,
3610        il: usize,
3611    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3612        let cfg = &self.cfg;
3613        let geometry = cfg.full_attention_geometry_at(il as u32);
3614        let n_head = geometry.n_head as usize;
3615        let n_head_kv = geometry.n_head_kv as usize;
3616        let head_dim = geometry.head_dim_k as usize;
3617        let eps = cfg.rms_eps;
3618
3619        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3620        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3621        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3622        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3623        let v = g3.pop().unwrap();
3624        let mut k = g3.pop().unwrap();
3625        let qf = g3.pop().unwrap();
3626        let (mut q, gate) = if gated {
3627            let mut q = e.uninit(t * n_head * head_dim)?;
3628            let mut gate = e.uninit(t * n_head * head_dim)?;
3629            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3630            (q, Some(gate))
3631        } else {
3632            (qf, None)
3633        };
3634
3635        let mut qn = e.uninit(t * n_head * head_dim)?;
3636        e.rms_norm(
3637            &q,
3638            fa.q_norm.float_data(),
3639            &mut qn,
3640            head_dim,
3641            n_head * t,
3642            eps,
3643        )?;
3644        q = qn;
3645        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3646        e.rms_norm(
3647            &k,
3648            fa.k_norm.float_data(),
3649            &mut kn,
3650            head_dim,
3651            n_head_kv * t,
3652            eps,
3653        )?;
3654        k = kn;
3655        let rope_dims = geometry.n_rot as usize;
3656        e.rope_neox(
3657            &mut q,
3658            pos_d,
3659            head_dim,
3660            rope_dims,
3661            n_head,
3662            t,
3663            geometry.rope_base,
3664            1.0,
3665        )?;
3666        e.rope_neox(
3667            &mut k,
3668            pos_d,
3669            head_dim,
3670            rope_dims,
3671            n_head_kv,
3672            t,
3673            geometry.rope_base,
3674            1.0,
3675        )?;
3676
3677        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3678        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3679        {
3680            let kvl = cache.kv[il].as_mut().unwrap();
3681            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3682            e.append_kv_quantized_rows(
3683                &k,
3684                &v,
3685                &mut kvl.k,
3686                &mut kvl.v,
3687                kvl.len,
3688                t,
3689                kvl.kv_dim_k,
3690                kvl.kv_dim_v,
3691                kvl.k_tok_bytes,
3692                kvl.v_tok_bytes,
3693                crate::Engine::kv_fp8_on(),
3694            )?;
3695            kvl.len += t;
3696            let new_len = kvl.len as i32;
3697            e.set_i32_one(&mut kvl.len_d, new_len)?;
3698        }
3699
3700        let base_len = {
3701            let kvl = cache.kv[il].as_ref().unwrap();
3702            kvl.len - t // KV rows present BEFORE this chunk's append above
3703        };
3704        Ok((AttnPre { q, k, v, gate }, base_len))
3705    }
3706
3707    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3708    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3709    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3710    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3711    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3712    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3713    #[allow(clippy::too_many_arguments)]
3714    fn full_attn_prime_fa_dispatch(
3715        &self,
3716        e: &Engine,
3717        q: &CudaSlice<f32>,
3718        k: &CudaSlice<f32>,
3719        v: &CudaSlice<f32>,
3720        attn: &mut CudaSlice<f32>,
3721        base_len: usize,
3722        t: usize,
3723        cache: &mut Cache,
3724        il: usize,
3725        head_dim: usize,
3726        n_head: usize,
3727        n_head_kv: usize,
3728        scale: f32,
3729    ) -> Result<(), Box<dyn std::error::Error>> {
3730        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3731        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3732        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3733        // attend through the quantized cache exactly like every later chunk (quantize-then-
3734        // attend). One numeric class for every row => the chunk size cannot decide where a
3735        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3736        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
3737        // pin-the-boundary approach).
3738        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
3739        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
3740        // with the fix unconditional, only re-introducing the class edge can prove the gate
3741        // still detects the mechanism. Never on in a measured default run.
3742        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
3743            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3744                e.sdpa_naive(
3745                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3746                )?;
3747            } else {
3748                e.fa_prefill(
3749                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
3750                )?;
3751            }
3752            return Ok(());
3753        }
3754        let kvl = cache.kv[il].as_ref().unwrap();
3755        let t_kv = base_len + t;
3756        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
3757        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
3758        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
3759        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
3760        // same numeric class, so the uniform contract holds on the fallback too.
3761        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
3762            e.sdpa_naive_quantized_view(
3763                q,
3764                &k_view,
3765                &v_view,
3766                attn,
3767                head_dim,
3768                n_head,
3769                n_head_kv,
3770                t,
3771                t_kv,
3772                scale,
3773                true,
3774                kvl.k_tok_bytes,
3775                kvl.v_tok_bytes,
3776            )?;
3777            return Ok(());
3778        }
3779        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
3780        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
3781        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
3782        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
3783        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
3784        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
3785        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
3786        let deqw = std::env::var("MEMRA_PRIME_DEQW")
3787            .map(|v| v != "0")
3788            .unwrap_or(true);
3789        if deqw {
3790            e.fa_prefill_view_ws(
3791                q,
3792                &k_view,
3793                &v_view,
3794                attn,
3795                head_dim,
3796                n_head,
3797                n_head_kv,
3798                t,
3799                t_kv,
3800                scale,
3801                true,
3802                kvl.k_tok_bytes,
3803                kvl.v_tok_bytes,
3804                crate::Engine::kv_fp8_on(),
3805            )?;
3806        } else {
3807            e.fa_prefill_view(
3808                q,
3809                &k_view,
3810                &v_view,
3811                attn,
3812                head_dim,
3813                n_head,
3814                n_head_kv,
3815                t,
3816                t_kv,
3817                scale,
3818                true,
3819                kvl.k_tok_bytes,
3820                kvl.v_tok_bytes,
3821                crate::Engine::kv_fp8_on(),
3822            )?;
3823        }
3824        Ok(())
3825    }
3826
3827    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
3828    /// (bit-identical composition) and hands wo its fp16 operand directly.
3829    fn full_attn_prime_post_fa(
3830        &self,
3831        e: &Engine,
3832        attn: CudaSlice<f32>,
3833        gate: &Option<CudaSlice<f32>>,
3834        t: usize,
3835        n_head: usize,
3836        head_dim: usize,
3837    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3838        let (attn_g, ag16) = match gate {
3839            Some(gate) => {
3840                let n = t * n_head * head_dim;
3841                let mut ag = e.uninit(n)?;
3842                if Self::f16out_on(e, t) {
3843                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
3844                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
3845                    (ag, Some(a16))
3846                } else {
3847                    let mut gsig = e.uninit(n)?;
3848                    e.sigmoid(gate, &mut gsig, n)?;
3849                    e.mul(&attn, &gsig, &mut ag, n)?;
3850                    (ag, None)
3851                }
3852            }
3853            None => (attn, None),
3854        };
3855        Ok((attn_g, ag16))
3856    }
3857
3858    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
3859    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
3860    /// carried THROUGH the cache like the spec verify does: carried-ring conv
3861    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
3862    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
3863    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
3864    fn linear_attn_prime(
3865        &self,
3866        e: &Engine,
3867        la: &LinearAttnLayer,
3868        h: &CudaSlice<f32>,
3869        hx: Option<&CudaSlice<u8>>,
3870        t: usize,
3871        cache: &mut Cache,
3872        il: usize,
3873    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3874        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
3875        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3876        let g4 = match hx {
3877            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
3878            None => e.matmul_group(&ws, h, t)?,
3879        };
3880        self.linear_attn_prime_core(e, la, g4, t, cache, il)
3881    }
3882
3883    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
3884    fn linear_attn_prime_core(
3885        &self,
3886        e: &Engine,
3887        la: &LinearAttnLayer,
3888        mut g4: Vec<CudaSlice<f32>>,
3889        t: usize,
3890        cache: &mut Cache,
3891        il: usize,
3892    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3893        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
3894    }
3895
3896    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
3897    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
3898    /// conv ring writes back from the true tail. None = classic path, byte-identical.
3899    #[allow(clippy::too_many_arguments)]
3900    fn linear_attn_prime_core_pad_inner(
3901        &self,
3902        e: &Engine,
3903        la: &LinearAttnLayer,
3904        mut g4: Vec<CudaSlice<f32>>,
3905        t: usize,
3906        cache: &mut Cache,
3907        il: usize,
3908        pad_len: Option<&CudaSlice<i32>>,
3909    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3910        // shim over the view twin (task #16): full-range views of the owned buffers.
3911        let geometry = la.geometry;
3912        let d_state = geometry.key_head_dim as usize;
3913        let num_k = geometry.key_heads as usize;
3914        let num_v = geometry.value_heads as usize;
3915        let key_dim = d_state * num_k;
3916        let value_dim = geometry.value_head_dim as usize * num_v;
3917        let conv_dim = key_dim * 2 + value_dim;
3918        let alpha = g4.pop().unwrap(); // [T, num_v]
3919        let beta_raw = g4.pop().unwrap(); // [T, num_v]
3920        let z = g4.pop().unwrap(); // [T, value_dim]
3921        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
3922        self.linear_attn_prime_core_pad_view(
3923            e,
3924            la,
3925            &qkv_mixed.slice(0..t * conv_dim),
3926            &z.slice(0..t * value_dim),
3927            &beta_raw.slice(0..t * num_v),
3928            &alpha.slice(0..t * num_v),
3929            t,
3930            cache,
3931            il,
3932            pad_len,
3933        )
3934    }
3935
3936    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
3937    /// shared verbatim by the per-seq scan path and the varlen batched path.
3938    #[allow(clippy::too_many_arguments)]
3939    fn linear_attn_gdn_prep(
3940        &self,
3941        e: &Engine,
3942        la: &LinearAttnLayer,
3943        qkv_mixed: &cudarc::driver::CudaView<f32>,
3944        beta_raw: &cudarc::driver::CudaView<f32>,
3945        alpha: &cudarc::driver::CudaView<f32>,
3946        t: usize,
3947        cache: &mut Cache,
3948        il: usize,
3949        pad_len: Option<&CudaSlice<i32>>,
3950    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
3951        let cfg = &self.cfg;
3952        let geometry = la.geometry;
3953        let d_state = geometry.key_head_dim as usize;
3954        let num_k = geometry.key_heads as usize;
3955        let num_v = geometry.value_heads as usize;
3956        let d_conv = geometry.conv_kernel as usize;
3957        let key_dim = d_state * num_k; // 2048
3958        let value_dim = geometry.value_head_dim as usize * num_v;
3959        let conv_dim = key_dim * 2 + value_dim; // 8192
3960        let eps = cfg.rms_eps;
3961        debug_assert!(
3962            t >= d_conv - 1,
3963            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
3964        );
3965
3966        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
3967        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
3968        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
3969        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
3970        let rl = cache.recur[il].as_mut().unwrap();
3971        let hk = Self::gdn_hk(e, t, num_v, num_k);
3972        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
3973        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
3974        let mut q_g = e.uninit(d_state * hk * t)?;
3975        let mut k_g = e.uninit(d_state * hk * t)?;
3976        let mut v_g = e.uninit(d_state * num_v * t)?;
3977        if conv_fuse {
3978            e.ssm_conv1d_gdn_state_pad(
3979                qkv_mixed,
3980                &mut rl.conv_state,
3981                la.ssm_conv1d.float_data(),
3982                &mut q_g,
3983                &mut k_g,
3984                &mut v_g,
3985                conv_dim,
3986                t,
3987                d_conv,
3988                d_state,
3989                num_v,
3990                num_k,
3991                key_dim,
3992                hk,
3993                pad_len,
3994            )?;
3995        } else {
3996            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
3997            e.ssm_conv1d_tm_state_pad_v(
3998                qkv_mixed,
3999                &mut rl.conv_state,
4000                la.ssm_conv1d.float_data(),
4001                &mut conv_out,
4002                conv_dim,
4003                t,
4004                d_conv,
4005                pad_len,
4006            )?;
4007            e.qkv_to_gdn_repack(
4008                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
4009            )?;
4010        }
4011        let mut q_l2 = e.uninit(d_state * hk * t)?;
4012        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
4013        // Emitted only where a consumer exists (the wgmma config) — on other arches the
4014        // alloc + epilogue stores would be pure waste.
4015        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4016            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4017            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4018            Some(qb)
4019        } else {
4020            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4021            None
4022        };
4023        let mut k_l2 = e.uninit(d_state * hk * t)?;
4024        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4025        let kb16 = if Engine::l2_v2_on(d_state) {
4026            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4027            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4028            Some(kb)
4029        } else {
4030            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4031            None
4032        };
4033        let mut beta = e.uninit(t * num_v)?;
4034        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4035        let mut g_log = e.uninit(t * num_v)?;
4036        e.gdn_glog_v(
4037            alpha,
4038            la.ssm_dt.float_data(),
4039            la.ssm_a.float_data(),
4040            &mut g_log,
4041            num_v,
4042            t,
4043        )?;
4044        if let Some(len_d) = pad_len {
4045            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4046        }
4047        Ok(GdnPrep {
4048            hk,
4049            q_l2,
4050            k_l2,
4051            v_g,
4052            beta,
4053            g_log,
4054            kb16,
4055            qb16,
4056        })
4057    }
4058
4059    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4060    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4061    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4062    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4063    #[allow(clippy::too_many_arguments)]
4064    fn linear_attn_prime_core_batch(
4065        &self,
4066        e: &Engine,
4067        la: &LinearAttnLayer,
4068        g4: &[CudaSlice<f32>],
4069        offs: &[usize],
4070        ts: &[usize],
4071        caches: &mut [&mut Cache],
4072        il: usize,
4073    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4074        let geometry = la.geometry;
4075        let d_state = geometry.key_head_dim as usize;
4076        let num_k = geometry.key_heads as usize;
4077        let num_v = geometry.value_heads as usize;
4078        let d_conv = geometry.conv_kernel as usize;
4079        let key_dim = d_state * num_k;
4080        let value_dim = geometry.value_head_dim as usize * num_v;
4081        let conv_dim = key_dim * 2 + value_dim;
4082        let eps = self.cfg.rms_eps;
4083        let scale = 1.0 / (d_state as f32).sqrt();
4084        let b = ts.len();
4085        let c = Engine::gdn_chunk_size();
4086        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4087        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4088        let carried = caches.iter().any(|c| c.pos > 0);
4089        let use_vl = !carried
4090            && (2..=8).contains(&b)
4091            && Engine::gdn_chunked_enabled()
4092            && ts.iter().all(|&t| t >= 16)
4093            && e.gdn_mma_enabled(c)
4094            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4095        if !use_vl {
4096            return (0..b)
4097                .map(|s| {
4098                    let (o, t) = (offs[s], ts[s]);
4099                    self.linear_attn_prime_core_pad_view(
4100                        e,
4101                        la,
4102                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4103                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4104                        &g4[2].slice(o * num_v..(o + t) * num_v),
4105                        &g4[3].slice(o * num_v..(o + t) * num_v),
4106                        t,
4107                        caches[s],
4108                        il,
4109                        None,
4110                    )
4111                })
4112                .collect();
4113        }
4114        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4115        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4116        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4117        struct SeqBufs {
4118            conv_out: CudaSlice<f32>,
4119            q_g: CudaSlice<f32>,
4120            k_g: CudaSlice<f32>,
4121            v_g: CudaSlice<f32>,
4122            q_l2: CudaSlice<f32>,
4123            k_l2: CudaSlice<f32>,
4124            beta: CudaSlice<f32>,
4125            g_log: CudaSlice<f32>,
4126            gn: CudaSlice<f32>,
4127            gn16: CudaSlice<u8>,
4128        }
4129        let f16o = Self::f16out_on(e, 16);
4130        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4131        let mut sb = Vec::with_capacity(b);
4132        let mut pres = Vec::with_capacity(b);
4133        for &t in ts.iter().take(b) {
4134            sb.push(SeqBufs {
4135                conv_out: e.uninit(conv_dim * t)?,
4136                q_g: e.uninit(d_state * hk * t)?,
4137                k_g: e.uninit(d_state * hk * t)?,
4138                v_g: e.uninit(d_state * num_v * t)?,
4139                q_l2: e.uninit(d_state * hk * t)?,
4140                k_l2: e.uninit(d_state * hk * t)?,
4141                beta: e.uninit(t * num_v)?,
4142                g_log: e.uninit(t * num_v)?,
4143                gn: e.uninit(d_state * num_v * t)?,
4144                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4145            });
4146            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4147        }
4148        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4149            .map(|s| {
4150                let (o, t) = (offs[s], ts[s]);
4151                let rl = caches[s].recur[il].as_ref().unwrap();
4152                crate::GdnPrepVl {
4153                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4154                    conv_state: e.addr_f32(&rl.conv_state),
4155                    conv_out: e.addr_f32(&sb[s].conv_out),
4156                    q_g: e.addr_f32(&sb[s].q_g),
4157                    k_g: e.addr_f32(&sb[s].k_g),
4158                    v_g: e.addr_f32(&sb[s].v_g),
4159                    q_l2: e.addr_f32(&sb[s].q_l2),
4160                    k_l2: e.addr_f32(&sb[s].k_l2),
4161                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4162                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4163                    beta: e.addr_f32(&sb[s].beta),
4164                    g_log: e.addr_f32(&sb[s].g_log),
4165                    o: e.addr_f32(&pres[s].o),
4166                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4167                    gn: e.addr_f32(&sb[s].gn),
4168                    gn16: e.addr_u8(&sb[s].gn16),
4169                    kb16: if Engine::l2_v2_on(d_state) {
4170                        e.addr_u8(&pres[s].kb16)
4171                    } else {
4172                        0
4173                    },
4174                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4175                        e.addr_u8(&pres[s].qb16)
4176                    } else {
4177                        0
4178                    },
4179                    t: t as i32,
4180                    pad: 0,
4181                }
4182            })
4183            .collect();
4184        let args: Vec<crate::GdnSeqVl> = (0..b)
4185            .map(|s| {
4186                let rl = caches[s].recur[il].as_ref().unwrap();
4187                crate::GdnSeqVl {
4188                    kb16: e.addr_u8(&pres[s].kb16),
4189                    gcum: e.addr_f32(&pres[s].gcum),
4190                    beta: e.addr_f32(&sb[s].beta),
4191                    u: e.addr_f32(&pres[s].u),
4192                    wb16: e.addr_u8(&pres[s].wb16),
4193                    y: e.addr_u8(&pres[s].y16),
4194                    ssnap: e.addr_u8(&pres[s].ssnap16),
4195                    state_in: e.addr_f32(&rl.ssm_state),
4196                    state_out: e.addr_f32(&rl.ssm_state_alt),
4197                    q: e.addr_f32(&sb[s].q_l2),
4198                    p: e.addr_f32(&pres[s].p),
4199                    o: e.addr_f32(&pres[s].o),
4200                    k: e.addr_f32(&sb[s].k_l2),
4201                    v: e.addr_f32(&sb[s].v_g),
4202                    g: e.addr_f32(&sb[s].g_log),
4203                    a: e.addr_f32(&pres[s].a),
4204                    w: e.addr_f32(&pres[s].w),
4205                    t: ts[s] as i32,
4206                    nc: pres[s].nc as i32,
4207                }
4208            })
4209            .collect();
4210        e.gdn_prep_vl8(
4211            &prep_args,
4212            la.ssm_conv1d.float_data(),
4213            la.ssm_dt.float_data(),
4214            la.ssm_a.float_data(),
4215            conv_dim,
4216            d_conv,
4217            d_state,
4218            num_v,
4219            num_k,
4220            key_dim,
4221            hk,
4222            eps,
4223        )?;
4224        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4225        // both standalone mirror launches vanish on the default config.
4226        if !Engine::l2_v2_on(d_state) {
4227            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4228        }
4229        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4230        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4231            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4232            if !Engine::l2_v2_on(d_state) {
4233                for s in 0..b {
4234                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4235                }
4236            }
4237            let mut wa = [crate::GdnWVl::default(); 8];
4238            for s in 0..b {
4239                wa[s] = crate::GdnWVl {
4240                    qb16: e.addr_u8(&pres[s].qb16),
4241                    pb16: e.addr_u8(&pres[s].pb16),
4242                };
4243            }
4244            Some(crate::GdnWVl8(wa))
4245        } else {
4246            None
4247        };
4248        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4249        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4250        if f16o {
4251            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4252        }
4253        // per-seq state swap (+ non-f16out tail fallback)
4254        let mut out = Vec::with_capacity(b);
4255        for (s, bufs) in sb.into_iter().enumerate() {
4256            let rl = caches[s].recur[il].as_mut().unwrap();
4257            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4258            let (o, t) = (offs[s], ts[s]);
4259            let SeqBufs { mut gn, gn16, .. } = bufs;
4260            if f16o {
4261                out.push((gn, Some(gn16)));
4262            } else {
4263                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4264                e.gated_rmsnorm_zv(
4265                    &pres[s].o,
4266                    la.ssm_norm.float_data(),
4267                    &z_v,
4268                    &mut gn,
4269                    d_state,
4270                    num_v * t,
4271                    eps,
4272                )?;
4273                out.push((gn, None));
4274            }
4275        }
4276        Ok(out)
4277    }
4278
4279    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4280    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4281    /// Same kernels, same values, byte-identical to the Vec shim above.
4282    #[allow(clippy::too_many_arguments)]
4283    fn linear_attn_prime_core_pad_view(
4284        &self,
4285        e: &Engine,
4286        la: &LinearAttnLayer,
4287        qkv_mixed: &cudarc::driver::CudaView<f32>,
4288        z: &cudarc::driver::CudaView<f32>,
4289        beta_raw: &cudarc::driver::CudaView<f32>,
4290        alpha: &cudarc::driver::CudaView<f32>,
4291        t: usize,
4292        cache: &mut Cache,
4293        il: usize,
4294        pad_len: Option<&CudaSlice<i32>>,
4295    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4296        let cfg = &self.cfg;
4297        let geometry = la.geometry;
4298        let d_state = geometry.key_head_dim as usize;
4299        let num_v = geometry.value_heads as usize;
4300        let eps = cfg.rms_eps;
4301        let scale = 1.0 / (d_state as f32).sqrt();
4302
4303        let prep =
4304            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4305
4306        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4307        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4308        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4309        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4310        // verify keep the sequential kernel).
4311        let mut o = e.uninit(d_state * num_v * t)?;
4312        let rl = cache.recur[il].as_mut().unwrap();
4313        {
4314            let crate::cache::RecurLayer {
4315                ssm_state,
4316                ssm_state_alt,
4317                ..
4318            } = rl;
4319            e.gdn_scan_prefill(
4320                &prep.q_l2,
4321                &prep.k_l2,
4322                &prep.v_g,
4323                &prep.g_log,
4324                &prep.beta,
4325                prep.kb16.as_ref(),
4326                prep.qb16.as_ref(),
4327                ssm_state,
4328                ssm_state_alt,
4329                &mut o,
4330                num_v,
4331                t,
4332                scale,
4333                prep.hk,
4334            )?;
4335        }
4336        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4337
4338        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4339        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4340        let mut gn = e.uninit(d_state * num_v * t)?;
4341        let gn16 = if Self::f16out_on(e, t) {
4342            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4343            e.gated_rmsnorm_f16out_zv(
4344                &o,
4345                la.ssm_norm.float_data(),
4346                z,
4347                &mut gn,
4348                &mut g16,
4349                d_state,
4350                num_v * t,
4351                eps,
4352            )?;
4353            Some(g16)
4354        } else {
4355            e.gated_rmsnorm_zv(
4356                &o,
4357                la.ssm_norm.float_data(),
4358                z,
4359                &mut gn,
4360                d_state,
4361                num_v * t,
4362                eps,
4363            )?;
4364            None
4365        };
4366        Ok((gn, gn16))
4367    }
4368
4369    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4370    #[allow(clippy::too_many_arguments)]
4371    fn linear_attn_prime_core_pad(
4372        &self,
4373        e: &Engine,
4374        la: &LinearAttnLayer,
4375        g4: Vec<CudaSlice<f32>>,
4376        t: usize,
4377        cache: &mut Cache,
4378        il: usize,
4379        pad_len: Option<&CudaSlice<i32>>,
4380    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4381        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4382        if let Some(xh) = &gn16 {
4383            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4384                return Ok(y);
4385            }
4386        }
4387        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4388    }
4389
4390    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4391    ///
4392    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4393    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4394    pub fn full_attn(
4395        &self,
4396        e: &Engine,
4397        fa: &FullAttnLayer,
4398        h: &CudaSlice<f32>,
4399        pos_d: &CudaSlice<i32>,
4400        t: usize,
4401        il: usize,
4402    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4403        if self.uses_sliding_gated_moe_program() {
4404            return self.step35_attn(e, fa, h, pos_d, t, il);
4405        }
4406        let cfg = &self.cfg;
4407        let _n_embd = cfg.n_embd as usize;
4408        let geometry = cfg.full_attention_geometry_at(il as u32);
4409        let n_head = geometry.n_head as usize;
4410        let n_head_kv = geometry.n_head_kv as usize;
4411        let head_dim = geometry.head_dim_k as usize;
4412        let eps = cfg.rms_eps;
4413        let scale = geometry.attention_scale();
4414
4415        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4416        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4417        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4418        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4419        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4420        let v = g3.pop().unwrap();
4421        let mut k = g3.pop().unwrap();
4422        let qf = g3.pop().unwrap();
4423        let (mut q, gate) = if gated {
4424            let mut q = e.uninit(t * n_head * head_dim)?;
4425            let mut gate = e.uninit(t * n_head * head_dim)?;
4426            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4427            (q, Some(gate))
4428        } else {
4429            (qf, None)
4430        };
4431
4432        // QK-norm (per head_dim row), then partial RoPE.
4433        let mut qn = e.uninit(t * n_head * head_dim)?;
4434        e.rms_norm(
4435            &q,
4436            fa.q_norm.float_data(),
4437            &mut qn,
4438            head_dim,
4439            n_head * t,
4440            eps,
4441        )?;
4442        q = qn;
4443        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4444        e.rms_norm(
4445            &k,
4446            fa.k_norm.float_data(),
4447            &mut kn,
4448            head_dim,
4449            n_head_kv * t,
4450            eps,
4451        )?;
4452        k = kn;
4453        let rope_dims = geometry.n_rot as usize;
4454        e.rope_neox(
4455            &mut q,
4456            pos_d,
4457            head_dim,
4458            rope_dims,
4459            n_head,
4460            t,
4461            geometry.rope_base,
4462            1.0,
4463        )?;
4464        e.rope_neox(
4465            &mut k,
4466            pos_d,
4467            head_dim,
4468            rope_dims,
4469            n_head_kv,
4470            t,
4471            geometry.rope_base,
4472            1.0,
4473        )?;
4474
4475        // SDPA
4476        let mut attn = e.uninit(t * n_head * head_dim)?;
4477        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4478        // falls back to naive sdpa.
4479        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4480            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4481            e.sdpa_naive(
4482                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4483            )?;
4484        } else {
4485            e.fa_prefill(
4486                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4487            )?;
4488        }
4489
4490        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4491        let attn_g = match &gate {
4492            Some(gate) => {
4493                let mut gsig = e.uninit(t * n_head * head_dim)?;
4494                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4495                let mut ag = e.uninit(t * n_head * head_dim)?;
4496                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4497                ag
4498            }
4499            None => attn,
4500        };
4501
4502        // o projection
4503        let o = e.matmul(&fa.wo, &attn_g, t)?;
4504        Ok(o)
4505    }
4506
4507    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4508    pub fn linear_attn(
4509        &self,
4510        e: &Engine,
4511        la: &LinearAttnLayer,
4512        h: &CudaSlice<f32>,
4513        t: usize,
4514    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4515        let cfg = &self.cfg;
4516        let _n_embd = cfg.n_embd as usize;
4517        let geometry = la.geometry;
4518        let d_state = geometry.key_head_dim as usize;
4519        let num_k = geometry.key_heads as usize;
4520        let num_v = geometry.value_heads as usize;
4521        let d_conv = geometry.conv_kernel as usize;
4522        let head_k = d_state;
4523        let head_v = geometry.value_head_dim as usize;
4524        let key_dim = head_k * num_k; // 2048
4525        let value_dim = head_v * num_v; // 4096
4526        let conv_dim = key_dim * 2 + value_dim; // 8192
4527        let eps = cfg.rms_eps;
4528        let scale = 1.0 / (d_state as f32).sqrt();
4529
4530        // projections
4531        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4532        let mut g4 = e.matmul_group(
4533            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4534            h,
4535            t,
4536        )?;
4537        let alpha = g4.pop().unwrap(); // [T, num_v]
4538        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4539        let z = g4.pop().unwrap(); // [T, value_dim]
4540        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4541
4542        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4543        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4544        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4545        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4546        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4547        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4548        let _ = (head_k, head_v);
4549        let mut q_g = e.uninit(d_state * num_v * t)?;
4550        let mut k_g = e.uninit(d_state * num_v * t)?;
4551        let mut v_g = e.uninit(d_state * num_v * t)?;
4552        e.ssm_conv1d_gdn(
4553            &qkv_mixed,
4554            la.ssm_conv1d.float_data(),
4555            &mut q_g,
4556            &mut k_g,
4557            &mut v_g,
4558            conv_dim,
4559            t,
4560            d_conv,
4561            d_state,
4562            num_v,
4563            num_k,
4564            key_dim,
4565        )?;
4566        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4567        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4568        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4569        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4570        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4571        let v_gd = v_g;
4572
4573        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4574        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4575        let mut beta = e.uninit(t * num_v)?;
4576        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4577        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4578        let mut g_log = e.uninit(t * num_v)?;
4579        e.gdn_glog(
4580            &alpha,
4581            la.ssm_dt.float_data(),
4582            la.ssm_a.float_data(),
4583            &mut g_log,
4584            num_v,
4585            t,
4586        )?;
4587
4588        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4589        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4590        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4591        let mut o = e.uninit(d_state * num_v * t)?;
4592        e.gdn_scan_prefill(
4593            &q_l2,
4594            &k_l2,
4595            &v_gd,
4596            &g_log,
4597            &beta,
4598            None,
4599            None,
4600            &state_in,
4601            &mut state_out,
4602            &mut o,
4603            num_v,
4604            t,
4605            scale,
4606            num_v,
4607        )?;
4608
4609        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4610        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4611        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4612        // o rows are (t*num_v+vh) too. Good.
4613        let mut gn = e.uninit(d_state * num_v * t)?;
4614        e.gated_rmsnorm(
4615            &o,
4616            la.ssm_norm.float_data(),
4617            &z,
4618            &mut gn,
4619            d_state,
4620            num_v * t,
4621            eps,
4622        )?;
4623
4624        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4625        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4626        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4627        let out = e.matmul(&la.ssm_out, &gn, t)?;
4628        Ok(out)
4629    }
4630}
4631
4632impl HybridModel {
4633    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4634    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4635    ///
4636    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4637    /// different 860160-byte block than the same expert of layer 7).
4638    ///
4639    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4640    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4641    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4642    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4643    pub fn moe_ffn_il(
4644        &self,
4645        e: &Engine,
4646        m: &MoeWeights,
4647        z: &CudaSlice<f32>,
4648        t: usize,
4649        il: u16,
4650    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4651        Self::moe_ffn_inner(
4652            e,
4653            m,
4654            z,
4655            None,
4656            t,
4657            &self.cfg,
4658            il,
4659            self.max_moe_block(),
4660            false,
4661            None,
4662            self.uses_sliding_gated_moe_program(),
4663        )
4664    }
4665
4666    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4667    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4668    pub fn moe_ffn_il_prefill(
4669        &self,
4670        e: &Engine,
4671        m: &MoeWeights,
4672        z: &CudaSlice<f32>,
4673        t: usize,
4674        il: u16,
4675    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4676        Self::moe_ffn_inner(
4677            e,
4678            m,
4679            z,
4680            None,
4681            t,
4682            &self.cfg,
4683            il,
4684            self.max_moe_block(),
4685            true,
4686            Some(&self.step_grouped_prefill),
4687            self.uses_sliding_gated_moe_program(),
4688        )
4689    }
4690
4691    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4692    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4693    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4694    pub fn moe_ffn_il_zq8(
4695        &self,
4696        e: &Engine,
4697        m: &MoeWeights,
4698        z: &CudaSlice<f32>,
4699        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4700        t: usize,
4701        il: u16,
4702    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4703        Self::moe_ffn_inner(
4704            e,
4705            m,
4706            z,
4707            zq8,
4708            t,
4709            &self.cfg,
4710            il,
4711            self.max_moe_block(),
4712            false,
4713            None,
4714            self.uses_sliding_gated_moe_program(),
4715        )
4716    }
4717
4718    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4719    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4720    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4721    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4722    ///
4723    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4724    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4725    pub(crate) fn moe_ffn(
4726        e: &Engine,
4727        m: &MoeWeights,
4728        z: &CudaSlice<f32>,
4729        t: usize,
4730        cfg: &ModelConfig,
4731        il: u16,
4732        max_block: usize,
4733    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4734        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4735    }
4736
4737    #[allow(clippy::too_many_arguments)]
4738    pub(crate) fn moe_ffn_inner(
4739        e: &Engine,
4740        m: &MoeWeights,
4741        z: &CudaSlice<f32>,
4742        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4743        t: usize,
4744        cfg: &ModelConfig,
4745        il: u16,
4746        max_block: usize,
4747        prefill: bool,
4748        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
4749        sliding_gated_moe: bool,
4750    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4751        let worker_io = crate::spill_pread::worker_enabled();
4752        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
4753        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
4754            e.with_moe_cache(max_block, |cache, _| {
4755                cache.begin_forward_epoch(il, t);
4756                if worker_io {
4757                    cache.begin_worker_scope();
4758                }
4759                Ok(())
4760            })?;
4761        }
4762        if m.step_ep.is_some() || m.step_tp.is_some() {
4763            let moe = cfg
4764                .moe
4765                .as_ref()
4766                .ok_or("Step distributed execution requires MoE model metadata")?;
4767            let n_embd = cfg.n_embd as usize;
4768            let n_expert = moe.expert_count as usize;
4769            let n_used = moe.expert_used_count as usize;
4770            let sigmoid = cfg
4771                .sigmoid_router()
4772                .ok_or("Step distributed execution requires the Step sigmoid router")?;
4773            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
4774            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
4775            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
4776            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
4777                return Err(
4778                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
4779                );
4780            }
4781            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
4782                return Err(format!(
4783                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
4784                    PRIME_MIN_T,
4785                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
4786                )
4787                .into());
4788            }
4789            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
4790            let grouped_prefill_shape =
4791                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
4792            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
4793                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
4794            }) {
4795                let (selected, route_weights) =
4796                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
4797                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
4798                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
4799                Self::trace_moe_input(e, il, t, n_embd, z)?;
4800                let selected = selected
4801                    .iter()
4802                    .map(|&expert| expert as usize)
4803                    .collect::<Vec<_>>();
4804
4805                // The narrow route readback above orders the owning-stage producer. The grouped
4806                // runtime then copies the resident root activation into its persistent rank inputs.
4807                e.stream().synchronize()?;
4808                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
4809                    state.projection.set_activation_limit(ep.activation_limit)?;
4810                    ep.runtime
4811                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
4812                            ep.experts.e4m3()?,
4813                            &mut state.projection,
4814                            z,
4815                            t,
4816                            &selected,
4817                        )?;
4818                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
4819                        &state.projection,
4820                        &mut state.combine,
4821                        &route_weights,
4822                    )?;
4823                    ep.runtime.execute_step_grouped_expert_parallel_gate(
4824                        ep.experts.e4m3()?,
4825                        &mut state.projection,
4826                    )?;
4827                    ep.runtime.execute_step_grouped_expert_parallel_combine(
4828                        &state.projection,
4829                        &mut state.combine,
4830                    )?;
4831                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
4832                        &state.projection,
4833                        &state.combine,
4834                        e,
4835                    )?;
4836                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
4837                    if prefill {
4838                        // A shared plan may be reused by the next layer on a different runtime
4839                        // stream. Complete the owning-stage copy before its source is overwritten.
4840                        e.stream().synchronize()?;
4841                    }
4842                    eprintln!(
4843                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
4844                         attention_layout=tensor-parallel expert_layout=expert-parallel \
4845                         expert_transport={} native_p2p=true route_control=host-narrow \
4846                         input=root-device projection_workspaces=persistent \
4847                         combine=root-device output=owning-stage-device \
4848                         prefill={prefill} batched_decode=false capacity={} \
4849                         performance_claim=false",
4850                        ep.devices,
4851                        ep.runtime.transport_label(),
4852                        state.projection.max_tokens(),
4853                    );
4854                    Ok::<_, Box<dyn std::error::Error>>(output)
4855                };
4856
4857                if grouped_prefill_shape {
4858                    let grouped_prefill = grouped_prefill
4859                        .ok_or("Step grouped prefill has no model-scoped executor")?;
4860                    let mut shared = grouped_prefill
4861                        .lock()
4862                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
4863                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
4864                        state.devices != ep.devices
4865                            || state.grouped.projection.max_tokens() < t
4866                            || state.grouped.projection.input_width() != n_embd
4867                            || state.grouped.projection.expert_width()
4868                                != moe.expert_ff_length as usize
4869                    });
4870                    if needs_prepare {
4871                        let seed_input = vec![0.0f32; n_embd];
4872                        let seed_selected = &selected[..n_used];
4873                        let seed_weights = &route_weights[..n_used];
4874                        let projection = ep
4875                            .runtime
4876                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
4877                                ep.experts.e4m3()?,
4878                                &seed_input,
4879                                1,
4880                                seed_selected,
4881                                ep.activation_limit,
4882                                t,
4883                            )?;
4884                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
4885                            &projection,
4886                            seed_weights,
4887                        )?;
4888                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
4889                            devices: ep.devices.clone(),
4890                            grouped: crate::hybrid::StepEpGroupedDecode {
4891                                projection,
4892                                combine,
4893                            },
4894                        });
4895                        eprintln!(
4896                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
4897                             shared_across_layers=true performance_claim=false",
4898                            ep.devices,
4899                        );
4900                    }
4901                    return execute(
4902                        &mut shared
4903                            .state
4904                            .as_mut()
4905                            .expect("Step grouped prefill state prepared above")
4906                            .grouped,
4907                    );
4908                }
4909
4910                let mut grouped = ep
4911                    .grouped_decode
4912                    .as_ref()
4913                    .expect("grouped decode presence checked above")
4914                    .lock()
4915                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
4916                return execute(&mut grouped);
4917            }
4918            if grouped_prefill_shape {
4919                return Err(
4920                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
4921                        .into(),
4922                );
4923            }
4924            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
4925            // expert program — the per-layer host logits readback (the last per-layer host
4926            // sync) disappears. Selection tie-breaking may differ from the host router:
4927            // numeric-class door, run-gen argmax gate + boot battery.
4928            if t == 1
4929                && crate::tp::step_nvfp4_dev_routes_enabled()?
4930                && crate::tp::step_tp_dev_router_enabled()?
4931            {
4932                if let Some(tp) = &m.step_tp {
4933                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
4934                        let (sf, route_norm) = sigmoid;
4935                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
4936                        // before the router — the rank streams overlap the gemv+topk.
4937                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
4938                        // from its own z copy (replicated deterministic router — identical
4939                        // bits in, identical sel/w out) and starts its sweep without
4940                        // waiting the root's sel broadcast.
4941                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4942                        let d1_router = *D1_ROUTER.get_or_init(|| {
4943                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
4944                        });
4945                        if d1_router {
4946                            let (sf_h, rn_h) = sigmoid;
4947                            let n_ex = m.gate_exps.n_expert;
4948                            let act_ct = m.active_count();
4949                            let _ = tp.runtime.nvfp4_routes_prestage_with(
4950                                bank,
4951                                e,
4952                                z,
4953                                |rank1, in1, sel1, w1| {
4954                                    let mut guard = DEV1_ROUTER_REPS
4955                                        .lock()
4956                                        .map_err(|_| "dev1 router replica lock")?;
4957                                    let (reps, scratch) =
4958                                        guard.get_or_insert_with(|| (Default::default(), None));
4959                                    if !reps.contains_key(&il) {
4960                                        use cudarc::driver::DevicePtr;
4961                                        let (g1, p1, a1) = (
4962                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
4963                                            rank1.htod(&vec![0.0f32; n_ex])?,
4964                                            rank1.alloc_u8_uninit(n_ex)?,
4965                                        );
4966                                        for (src, dst_len, dst) in [
4967                                            (
4968                                                {
4969                                                    let s = e.stream();
4970                                                    let (p, _g) =
4971                                                        m.gate_inp.float_data().device_ptr(&s);
4972                                                    p as u64
4973                                                },
4974                                                n_ex * n_embd * 4,
4975                                                {
4976                                                    let s = rank1.stream();
4977                                                    let (p, _g) = g1.device_ptr(&s);
4978                                                    p as u64
4979                                                },
4980                                            ),
4981                                            (
4982                                                {
4983                                                    let s = e.stream();
4984                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
4985                                                    p as u64
4986                                                },
4987                                                n_ex * 4,
4988                                                {
4989                                                    let s = rank1.stream();
4990                                                    let (p, _g) = p1.device_ptr(&s);
4991                                                    p as u64
4992                                                },
4993                                            ),
4994                                            (
4995                                                {
4996                                                    let s = e.stream();
4997                                                    let (p, _g) =
4998                                                        m.active_experts_dev.device_ptr(&s);
4999                                                    p as u64
5000                                                },
5001                                                n_ex,
5002                                                {
5003                                                    let s = rank1.stream();
5004                                                    let (p, _g) = a1.device_ptr(&s);
5005                                                    p as u64
5006                                                },
5007                                            ),
5008                                        ] {
5009                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
5010                                        }
5011                                        rank1.stream().synchronize()?;
5012                                        reps.insert(il, (g1, p1, a1));
5013                                    }
5014                                    if scratch.is_none() {
5015                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5016                                    }
5017                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5018                                    let logits1 = scratch.as_mut().expect("armed above");
5019                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5020                                    rank1.moe_router_sigmoid_topk_into(
5021                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5022                                        w1,
5023                                    )?;
5024                                    Ok(true)
5025                                },
5026                            )?;
5027                        } else {
5028                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5029                        }
5030                        // Persistent selection buffers: the allocating topk built two fresh
5031                        // slices per layer; sel/w land in process-static rows instead
5032                        // (host-op diet — same kernel, same bytes).
5033                        static SELW: std::sync::Mutex<
5034                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5035                        > = std::sync::Mutex::new(None);
5036                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5037                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5038                            *selw = Some((
5039                                e.ctx().ordinal(),
5040                                e.htod_i32(&vec![0i32; n_used])?,
5041                                e.htod(&vec![0.0f32; n_used])?,
5042                            ));
5043                        }
5044                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5045                        e.moe_router_sigmoid_topk_into(
5046                            &logits,
5047                            t,
5048                            n_expert,
5049                            n_used,
5050                            m.active_count(),
5051                            &m.exp_probs_b_dev,
5052                            &m.active_experts_dev,
5053                            sf,
5054                            route_norm,
5055                            sel_d,
5056                            w_d,
5057                        )?;
5058                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5059                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5060                        // PREJOIN hook so it executes while the peer rank drains its sweep
5061                        // (fills dev0's join wait); apply adds the identical values after.
5062                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5063                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5064                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5065                        });
5066                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5067                        // expert runs on rank1 — the idle device — same kernels, same
5068                        // split program, down row root-resident: bit-identical.
5069                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5070                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5071                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5072                        }) && tp.runtime.rank_engine(1).is_some();
5073                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5074                        // overlap ws + ones row and hand their RAW pointers to the routed
5075                        // run — the join add folds the shexp apply into one launch.
5076                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5077                        let tail3 = *TAIL3
5078                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5079                        let mut ov_issued = false;
5080                        let mut d1_issued = false;
5081                        let mut tail_folded = false;
5082                        let mut output = if shexp_d1 {
5083                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5084                            tp.runtime
5085                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5086                                    bank,
5087                                    e,
5088                                    z,
5089                                    &sel_d,
5090                                    &w_d,
5091                                    n_used,
5092                                    tp.activation_limit,
5093                                    || {
5094                                        d1_issued = Self::shexp_dev1_issue(
5095                                            e, rank1, m, z, cfg, il, n_embd,
5096                                        )?;
5097                                        Ok(())
5098                                    },
5099                                )?
5100                        } else if shexp_ov {
5101                            // Raw sh/ones pointers for the fused tail (persistent statics;
5102                            // pointers stable, no lock held across the routed call). The
5103                            // sh CONTENT is written by the prejoin-issued kernels earlier
5104                            // on e's stream — stream order covers the fused add.
5105                            let post_add = if tail3 {
5106                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5107                            } else {
5108                                None
5109                            };
5110                            let used_post = post_add.is_some();
5111                            let out = tp
5112                                .runtime
5113                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5114                                    bank,
5115                                    e,
5116                                    z,
5117                                    &sel_d,
5118                                    &w_d,
5119                                    n_used,
5120                                    tp.activation_limit,
5121                                    || {
5122                                        ov_issued =
5123                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5124                                        Ok(())
5125                                    },
5126                                    post_add,
5127                                )?;
5128                            // ov_issued false with post_add armed = an early-return arm
5129                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5130                            // fall through to the normal shexp add (battery v22 receipt:
5131                            // the strict error here failed every graph-door boot).
5132                            if used_post && ov_issued {
5133                                tail_folded = true; // apply folded into the join add
5134                            }
5135                            out
5136                        } else {
5137                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5138                                bank,
5139                                e,
5140                                z,
5141                                &sel_d,
5142                                &w_d,
5143                                n_used,
5144                                tp.activation_limit,
5145                            )?
5146                        };
5147                        if output.len() != t * n_embd {
5148                            return Err(format!(
5149                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5150                                output.len()
5151                            )
5152                            .into());
5153                        }
5154                        if tail_folded {
5155                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5156                        } else if d1_issued {
5157                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5158                        } else if ov_issued {
5159                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5160                        } else {
5161                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5162                        }
5163                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5164                            std::sync::atomic::AtomicU64::new(0);
5165                        let layer_bit = 1u64 << (il as u64 % 64);
5166                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5167                            & layer_bit
5168                            == 0
5169                        {
5170                            eprintln!(
5171                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5172                                 expert_transport={} native_p2p={} router=device \
5173                                 activation=host-canonical accumulation=host-canonical \
5174                                 output=e-device io=device performance_claim=false \
5175                                 (logged once per layer)",
5176                                tp.devices,
5177                                tp.runtime.transport_label(),
5178                                tp.runtime.native_p2p(),
5179                            );
5180                        }
5181                        return Ok(output);
5182                    }
5183                }
5184            }
5185            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5186            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5187            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5188            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5189            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5190            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5191            let route_started = route_timing.then(std::time::Instant::now);
5192            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5193                e,
5194                &logits,
5195                z,
5196                t,
5197                n_embd,
5198                n_expert,
5199                n_used,
5200                m.exp_probs_b.as_deref(),
5201                sigmoid,
5202                m.active_experts.as_deref(),
5203            )?;
5204            if let Some(started) = route_started {
5205                use std::sync::atomic::Ordering;
5206                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5207                    + started.elapsed().as_nanos() as u64;
5208                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5209                if calls % 430 == 0 {
5210                    eprintln!(
5211                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5212                        ns as f64 / 1.0e6,
5213                        ns as f64 / calls as f64 / 1.0e3,
5214                    );
5215                }
5216            }
5217            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5218            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5219            Self::trace_moe_input(e, il, t, n_embd, z)?;
5220            let selected = selected
5221                .iter()
5222                .map(|&expert| expert as usize)
5223                .collect::<Vec<_>>();
5224            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5225            // combined output comes back as an e-context row — no host round-trip, no host
5226            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5227            // both preserve f32 bits), gated by greedy token identity.
5228            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5229                if let Some(tp) = &m.step_tp {
5230                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5231                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5232                            bank,
5233                            e,
5234                            z,
5235                            &selected,
5236                            &route_weights,
5237                            n_used,
5238                            tp.activation_limit,
5239                        )?;
5240                        if output.len() != t * n_embd {
5241                            return Err(format!(
5242                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5243                                output.len()
5244                            )
5245                            .into());
5246                        }
5247                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5248                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5249                            std::sync::atomic::AtomicU64::new(0);
5250                        let layer_bit = 1u64 << (il as u64 % 64);
5251                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5252                            & layer_bit
5253                            == 0
5254                        {
5255                            eprintln!(
5256                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5257                                 expert_transport={} native_p2p={} activation=host-canonical \
5258                                 accumulation=host-canonical output=e-device io=device \
5259                                 performance_claim=false (logged once per layer)",
5260                                tp.devices,
5261                                tp.runtime.transport_label(),
5262                                tp.runtime.native_p2p(),
5263                            );
5264                        }
5265                        return Ok(output);
5266                    }
5267                }
5268            }
5269            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5270                (
5271                    match &tp.experts {
5272                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5273                            tp.runtime.run_tensor_parallel_routes(
5274                                bank,
5275                                &input,
5276                                t,
5277                                &selected,
5278                                &route_weights,
5279                                n_used,
5280                            )?
5281                        }
5282                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5283                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5284                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5285                                    bank,
5286                                    &input,
5287                                    &selected,
5288                                    &route_weights,
5289                                    n_used,
5290                                    tp.activation_limit,
5291                                )?
5292                            } else {
5293                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5294                                    bank,
5295                                    &input,
5296                                    t,
5297                                    &selected,
5298                                    &route_weights,
5299                                    n_used,
5300                                    tp.activation_limit,
5301                                )?
5302                            }
5303                        }
5304                    },
5305                    "tp",
5306                    &tp.devices,
5307                    tp.runtime.transport_label(),
5308                    tp.runtime.native_p2p(),
5309                )
5310            } else {
5311                let ep = m
5312                    .step_ep
5313                    .as_ref()
5314                    .ok_or("Step distributed runtime has no EP or TP state")?;
5315                (
5316                    match &ep.experts {
5317                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5318                            ep.runtime.run_routed_experts(
5319                                bank,
5320                                &input,
5321                                t,
5322                                &selected,
5323                                &route_weights,
5324                                n_used,
5325                                ep.activation_limit,
5326                            )?
5327                        }
5328                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5329                            ep.runtime.run_routed_experts_nvfp4(
5330                                bank,
5331                                &input,
5332                                t,
5333                                &selected,
5334                                &route_weights,
5335                                n_used,
5336                                ep.activation_limit,
5337                            )?
5338                        }
5339                    },
5340                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5341                    &ep.devices,
5342                    ep.runtime.transport_label(),
5343                    ep.runtime.native_p2p(),
5344                )
5345            };
5346            if routed.len() != t * n_embd {
5347                return Err(format!(
5348                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5349                    routed.len()
5350                )
5351                .into());
5352            }
5353            let mut output = e.htod(&routed)?;
5354            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5355            // Once per layer per process: the topology contract line is a boot receipt, not a
5356            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5357            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5358            let layer_bit = 1u64 << (il as u64 % 64);
5359            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5360                == 0
5361            {
5362                eprintln!(
5363                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5364                     expert_transport={transport} native_p2p={native_p2p} \
5365                     activation={} accumulation={} output={} \
5366                     performance_claim=false (logged once per layer)",
5367                    if let Some(ep) = &m.step_ep {
5368                        ep.runtime.expert_activation_label()
5369                    } else {
5370                        "host-canonical"
5371                    },
5372                    if let Some(ep) = &m.step_ep {
5373                        ep.runtime.expert_accumulation_label()
5374                    } else {
5375                        "host-canonical"
5376                    },
5377                    if let Some(ep) = &m.step_ep {
5378                        ep.runtime.expert_output_label()
5379                    } else {
5380                        "host-accumulated"
5381                    },
5382                );
5383                if let Some(ep) = &m.step_ep {
5384                    if let Some(limit) = ep.activation_limit {
5385                        eprintln!(
5386                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5387                             formula=min-silu-times-clamped-up performance_claim=false"
5388                        );
5389                    }
5390                }
5391            }
5392            return Ok(output);
5393        }
5394        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5395            let moe = cfg.moe.as_ref().unwrap();
5396            let n_expert = moe.expert_count as usize;
5397            let n_used = moe.expert_used_count as usize;
5398            let sigmoid = cfg.sigmoid_router().unwrap();
5399            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5400            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5401            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5402        }
5403        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5404        // current caller into this research arm; the naked default stays on the established path.
5405        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5406            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5407            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5408            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5409            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5410            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5411            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5412                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5413                let g_host = e.dtoh(&grouped_out)?;
5414                let s_host = e.dtoh(&seq_out)?;
5415                let g_bytes: &[u8] = unsafe {
5416                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5417                };
5418                let s_bytes: &[u8] = unsafe {
5419                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5420                };
5421                if g_bytes == s_bytes {
5422                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5423                } else {
5424                    let diffs = g_host
5425                        .iter()
5426                        .zip(s_host.iter())
5427                        .enumerate()
5428                        .filter(|(_, (a, b))| a != b)
5429                        .count();
5430                    let maxdiff = g_host
5431                        .iter()
5432                        .zip(s_host.iter())
5433                        .map(|(a, b)| (a - b).abs())
5434                        .fold(0.0f32, f32::max);
5435                    panic!(
5436                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5437                        g_host.len()
5438                    );
5439                }
5440            }
5441            return Ok(grouped_out);
5442        }
5443        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5444    }
5445
5446    fn sigmoid_resident_dev_eligible(
5447        e: &Engine,
5448        m: &MoeWeights,
5449        cfg: &ModelConfig,
5450        sliding_gated_moe: bool,
5451    ) -> bool {
5452        let Some(moe) = cfg.moe.as_ref() else {
5453            return false;
5454        };
5455        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5456        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5457        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5458        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5459            std::env::var("MEMRA_MOE_STATS").is_ok()
5460                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5461                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5462                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5463                || std::env::var("MEMRA_MOE_GATE").is_ok()
5464        });
5465        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5466            if dev.dev != e.ctx().ordinal() {
5467                return false;
5468            }
5469            let q8 = moe_q8_enabled()
5470                && q8_expert_supported(m.gate_exps.qtype)
5471                && q8_expert_supported(m.up_exps.qtype)
5472                && q8_expert_supported(m.down_exps.qtype);
5473            let fp8 = dev.fp8_blk.is_some()
5474                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5475                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5476                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5477            q8 || fp8
5478        });
5479        sliding_gated_moe
5480            && sigmoid_router_enabled()
5481            && moe_dev_enabled()
5482            && moe_slab_enabled()
5483            && !observation_mode
5484            && moe.expert_used_count <= 8
5485            && m.has_uniform_expert_layout()
5486            && m.gate_exps.macros.is_none()
5487            && m.up_exps.macros.is_none()
5488            && m.down_exps.macros.is_none()
5489            && !m.has_macros
5490            && resident_layout_supported
5491    }
5492
5493    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5494    pub(crate) fn moe_ffn_sequential(
5495        e: &Engine,
5496        m: &MoeWeights,
5497        z: &CudaSlice<f32>,
5498        t: usize,
5499        cfg: &ModelConfig,
5500        il: u16,
5501        max_block: usize,
5502    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5503        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5504    }
5505
5506    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5507    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5508    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5509    fn moe_router_logits(
5510        e: &Engine,
5511        m: &MoeWeights,
5512        z: &CudaSlice<f32>,
5513        t: usize,
5514        cfg: &ModelConfig,
5515    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5516        if t < PRIME_MIN_T {
5517            // Decode and speculative verify use one fixed per-row reduction program.
5518            if crate::router_kernel_on() {
5519                e.router_gemv(
5520                    m.gate_inp.float_data(),
5521                    z,
5522                    cfg.n_embd as usize,
5523                    m.gate_exps.n_expert,
5524                    t,
5525                )
5526            } else {
5527                e.matmul_decode_exact(&m.gate_inp, z, t)
5528            }
5529        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5530            e.router_gemv(
5531                m.gate_inp.float_data(),
5532                z,
5533                cfg.n_embd as usize,
5534                m.gate_exps.n_expert,
5535                t,
5536            )
5537        } else {
5538            e.matmul(&m.gate_inp, z, t)
5539        }
5540    }
5541
5542    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5543    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5544    /// trace is independent of the dispatch optimization selected for the forward.
5545    fn trace_moe_routes(
5546        il: u16,
5547        t: usize,
5548        sel_all: &[u32],
5549        weights: &[f32],
5550    ) -> Result<(), Box<dyn std::error::Error>> {
5551        use std::io::Write as _;
5552        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5553            let mut f = std::fs::OpenOptions::new()
5554                .create(true)
5555                .append(true)
5556                .open(path)?;
5557            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5558            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5559        }
5560        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5561            let mut f = std::fs::OpenOptions::new()
5562                .create(true)
5563                .append(true)
5564                .open(path)?;
5565            let pairs: Vec<String> = sel_all
5566                .iter()
5567                .zip(weights)
5568                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5569                .collect();
5570            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5571        }
5572        Ok(())
5573    }
5574
5575    #[allow(clippy::too_many_arguments)]
5576    fn trace_sigmoid_router_logits(
5577        e: &Engine,
5578        il: u16,
5579        t: usize,
5580        n_expert: usize,
5581        n_used: usize,
5582        logits: &CudaSlice<f32>,
5583        m: &MoeWeights,
5584        (scaling_factor, route_norm): (f32, bool),
5585    ) -> Result<(), Box<dyn std::error::Error>> {
5586        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5587            return Ok(());
5588        }
5589        let logits = e.dtoh(logits)?;
5590        let active: Vec<u8> = m
5591            .active_experts
5592            .as_ref()
5593            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5594            .unwrap_or_else(|| vec![1; n_expert]);
5595        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5596        crate::sigrouter_contract::capture_served_logits(
5597            il as u32,
5598            t,
5599            n_expert,
5600            n_used,
5601            scaling_factor,
5602            route_norm,
5603            &active,
5604            &bias,
5605            &logits,
5606        )?;
5607        Ok(())
5608    }
5609
5610    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
5611    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
5612    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
5613    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
5614    fn trace_moe_input(
5615        e: &Engine,
5616        il: u16,
5617        t: usize,
5618        n_embd: usize,
5619        z: &CudaSlice<f32>,
5620    ) -> Result<(), Box<dyn std::error::Error>> {
5621        use std::io::Write as _;
5622        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
5623            return Ok(());
5624        };
5625        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
5626        let host = e.dtoh_view(&z.slice(0..values))?;
5627        let bytes = unsafe {
5628            std::slice::from_raw_parts(
5629                host.as_ptr().cast::<u8>(),
5630                host.len() * std::mem::size_of::<f32>(),
5631            )
5632        };
5633        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
5634        let mut state = state
5635            .lock()
5636            .map_err(|_| "MoE input trace writer lock is poisoned")?;
5637        if state.is_none() {
5638            let dir = std::path::PathBuf::from(&dir);
5639            std::fs::create_dir_all(&dir)?;
5640            let index = std::fs::OpenOptions::new()
5641                .create(true)
5642                .append(true)
5643                .open(dir.join("index.jsonl"))?;
5644            *state = Some(MoeInputTraceWriter {
5645                dir,
5646                index,
5647                payloads: std::collections::HashMap::new(),
5648            });
5649        }
5650        let writer = state.as_mut().unwrap();
5651        if writer.dir != std::path::Path::new(&dir) {
5652            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
5653        }
5654        let file_name = format!("layer-{il:03}.f32");
5655        if !writer.payloads.contains_key(&il) {
5656            let payload = std::fs::OpenOptions::new()
5657                .create(true)
5658                .append(true)
5659                .open(writer.dir.join(&file_name))?;
5660            let offset = payload.metadata()?.len();
5661            writer.payloads.insert(il, (payload, offset));
5662        }
5663        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
5664        let row_offset = *offset;
5665        payload.write_all(bytes)?;
5666        *offset += bytes.len() as u64;
5667        writeln!(
5668            writer.index,
5669            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
5670             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
5671             \"payload_bytes\":{}}}",
5672            bytes.len()
5673        )?;
5674        Ok(())
5675    }
5676
5677    #[allow(clippy::too_many_arguments)]
5678    pub(crate) fn moe_ffn_sequential_zq8(
5679        e: &Engine,
5680        m: &MoeWeights,
5681        z: &CudaSlice<f32>,
5682        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5683        t: usize,
5684        cfg: &ModelConfig,
5685        il: u16,
5686        max_block: usize,
5687    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5688        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
5689        let moe = cfg.moe.as_ref().unwrap();
5690        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
5691        let n_expert = moe.expert_count as usize; // 256
5692        let n_used = moe.expert_used_count as usize; // 8
5693        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
5694
5695        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
5696        debug_assert_eq!(m.gate_exps.in_f, n_embd);
5697        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
5698        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
5699        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
5700        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
5701
5702        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
5703        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
5704        let lim_exp = cfg.clamp_exp_at(il as u32);
5705        let lim_shexp = cfg.clamp_shexp_at(il as u32);
5706        let use_cache = Engine::moe_cache_enabled();
5707        let uniform_experts = m.has_uniform_expert_layout();
5708        let moe_q8 = uniform_experts
5709            && moe_q8_enabled()
5710            && q8_expert_supported(m.gate_exps.qtype)
5711            && q8_expert_supported(m.up_exps.qtype)
5712            && q8_expert_supported(m.down_exps.qtype);
5713        // Experimental secondary backend: complete experts already resident in the SLRU stay on
5714        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
5715        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
5716        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
5717        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
5718        // commands and CI have no llama.cpp or OpenMP dependency.
5719        let cpu_expert_requested = crate::cpu_experts::configured();
5720        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
5721            return Err(std::io::Error::other(
5722                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
5723            )
5724            .into());
5725        }
5726        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
5727        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
5728        // Those backends are each deterministic but are different numeric configurations, so a
5729        // later prefill eviction can change greedy output. Freeze after the first real prefill;
5730        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
5731        // staging below and cannot change backend assignment.
5732        let freeze_cpu_residency = cpu_expert_requested
5733            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
5734        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
5735            .ok()
5736            .and_then(|value| value.parse::<usize>().ok())
5737            .is_some_and(|tokens| tokens > 0);
5738        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
5739            e.freeze_moe_cache();
5740        }
5741        let cache_frozen = use_cache && e.moe_cache_frozen();
5742        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
5743
5744        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
5745        // cannot change logits, selected expert ids, or routing weights.
5746        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5747        if let Some(sig) = cfg.sigmoid_router() {
5748            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
5749        }
5750
5751        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
5752        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
5753        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
5754        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
5755        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
5756        // per-token host stall that dominated the 35B decode wall after stages 1+2.
5757        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
5758        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
5759        // only difference is where sel/w/pointers are READ from (device instead of params).
5760        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
5761        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
5762        // Any non-resident layer falls through to host routing + the gdec/sequential path.
5763        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
5764        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
5765        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
5766        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
5767        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
5768        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
5769        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
5770        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
5771        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
5772        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
5773        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
5774        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
5775        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
5776        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
5777        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
5778        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
5779        // now rides the dev loop below (same kernels per token as decode); pairs serves real
5780        // prefill (t >= 16, where spec never verifies).
5781        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
5782        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
5783        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
5784        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
5785        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
5786        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
5787        // ride the macro-aware sequential/staged paths below or every expert output is off by
5788        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
5789        let no_exp_macros = m.gate_exps.macros.is_none()
5790            && m.up_exps.macros.is_none()
5791            && m.down_exps.macros.is_none();
5792        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
5793        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
5794        // so it cannot even see the per-layer limit.
5795        if cfg.sigmoid_router().is_none()
5796            && cfg.m3.is_none()
5797            && cfg.hy3.is_none()
5798            && !cfg.swiglu_clamped_at(il as u32)
5799            && no_exp_macros
5800            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
5801            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
5802            // pairs serves real prefill from 17 up.
5803            && t > MOE_DEV_MAX_T
5804            && m.dev_exps.is_some()
5805            && moe_q8_enabled()
5806            && q8_expert_supported(m.gate_exps.qtype)
5807            && q8_expert_supported(m.up_exps.qtype)
5808            && q8_expert_supported(m.down_exps.qtype)
5809            && std::env::var("MEMRA_MOE_PAIRS")
5810                .map(|v| v != "0")
5811                .unwrap_or(true)
5812            && std::env::var("MEMRA_MOE_STATS").is_err()
5813        {
5814            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
5815        }
5816
5817        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
5818        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
5819        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
5820        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
5821        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
5822        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
5823        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
5824        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
5825        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
5826        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
5827        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
5828        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
5829        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
5830        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
5831        // Keyed off sigmoid_router() so arch #4 is denied by construction.
5832        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
5833        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
5834        let dev_ok = uniform_experts
5835            && cfg.sigmoid_router().is_none()
5836            && cfg.m3.is_none()
5837            && cfg.hy3.is_none()
5838            && !cfg.swiglu_clamped_at(il as u32);
5839        // Observation modes must route through the host-visible selection below. Otherwise a fully
5840        // resident layer returns through device dispatch before its trace/stats row is recorded,
5841        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
5842        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
5843            || std::env::var("MEMRA_MOE_TRACE").is_ok()
5844            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5845            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
5846        if dev_ok
5847            && t <= MOE_DEV_MAX_T
5848            && m.dev_exps.is_some()
5849            && n_used <= 8
5850            && moe_dev_enabled()
5851            && !observe_routes
5852        {
5853            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5854        }
5855        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
5856            let row_ok = e.with_moe_cache(max_block, |c, eng| {
5857                if moe_prewarm_enabled() {
5858                    c.prewarm_layer(il, m, eng)?;
5859                }
5860                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
5861            })?;
5862            if row_ok {
5863                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
5864            }
5865        }
5866
5867        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
5868        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
5869            if cpu_hybrid {
5870                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
5871                    e,
5872                    &logits,
5873                    z,
5874                    t,
5875                    n_embd,
5876                    n_expert,
5877                    n_used,
5878                    m.exp_probs_b.as_deref(),
5879                    sig,
5880                    m.active_experts.as_deref(),
5881                )?;
5882                (sel, w, Some(input))
5883            } else {
5884                let (sel, w) =
5885                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
5886                (sel, w, None)
5887            }
5888        } else {
5889            let (sel, w) =
5890                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
5891            (sel, w, None)
5892        };
5893        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
5894
5895        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
5896        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
5897        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
5898        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
5899        Self::trace_moe_input(e, il, t, n_embd, z)?;
5900
5901        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
5902        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
5903        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
5904        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
5905        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
5906        // wait for each pending block, so later copies can overlap the earlier expert kernels while
5907        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
5908        // T=1; batched forwards can have token-local consumers still in flight between selections.
5909        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
5910        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
5911        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
5912        let worker_disk_prefetch =
5913            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
5914        let promote_worker_h2d =
5915            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
5916        if promote_worker_h2d {
5917            let mut selected_blocks = Vec::with_capacity(n_used * 3);
5918            for &ex in sel_all.iter().take(n_used) {
5919                let ex = ex as u16;
5920                selected_blocks.extend([
5921                    BlockId::new(il, PROJ_GATE, ex),
5922                    BlockId::new(il, PROJ_UP, ex),
5923                    BlockId::new(il, PROJ_DOWN, ex),
5924                ]);
5925            }
5926            for &ex in sel_all.iter().take(n_used) {
5927                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
5928            }
5929            e.with_moe_cache(max_block, |cache, eng| {
5930                cache.promote_worker_reads_at_safe_boundary(
5931                    &selected_blocks,
5932                    &selected_blocks,
5933                    eng,
5934                )?;
5935                Ok(())
5936            })?;
5937        }
5938
5939        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
5940        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
5941        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
5942            let mut cnt = vec![0u32; n_expert];
5943            for &s in sel_all.iter() {
5944                cnt[s as usize] += 1;
5945            }
5946            let total = sel_all.len() as f64;
5947            let mut h = 0.0f64;
5948            let mut active = 0usize;
5949            for &c in &cnt {
5950                if c > 0 {
5951                    active += 1;
5952                    let p = c as f64 / total;
5953                    h -= p * p.log2();
5954                }
5955            }
5956            let maxc = cnt.iter().copied().max().unwrap_or(0);
5957            println!(
5958                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
5959                il,
5960                t,
5961                sel_all.len(),
5962                active,
5963                n_expert,
5964                h,
5965                (n_expert as f64).log2(),
5966                total / active.max(1) as f64,
5967                maxc
5968            );
5969        }
5970
5971        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
5972        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
5973        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
5974        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
5975        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
5976        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
5977        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
5978        // zeroed-then-accumulated exactly as before (fallback).
5979        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
5980        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
5981        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
5982        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
5983        let gdec_may_fire = uniform_experts
5984            && use_cache
5985            && n_used <= 8
5986            && gdec_enabled()
5987            && !cfg.swiglu_clamped_at(il as u32);
5988        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
5989        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
5990        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
5991        // archs the slabs were uploaded but never read, and every expert went through the
5992        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
5993        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
5994        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
5995        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
5996        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
5997        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
5998        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
5999        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
6000        // strictly worse than staging); under PP-2 without the prime walker this admits
6001        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
6002        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
6003        let slab_local = m
6004            .dev_exps
6005            .as_ref()
6006            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
6007        let slab_bases = slab_local.map(|d| {
6008            use cudarc::driver::DevicePtr;
6009            let s = e.stream();
6010            let (pg, _g0) = d.gate.device_ptr(&s);
6011            let (pu, _g1) = d.up.device_ptr(&s);
6012            let (pd, _g2) = d.down.device_ptr(&s);
6013            (pg as u64, pu as u64, pd as u64)
6014        });
6015        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6016        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6017        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6018        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6019        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6020        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6021        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6022        // all-resident tokens, staged loop for misses), which is a dispatch-class
6023        // comparison, not a provenance one.
6024        let slab_fused_may_fire = slab_bases.is_some()
6025            && n_used <= 8
6026            && gdec_enabled()
6027            && !cfg.swiglu_clamped_at(il as u32)
6028            && cfg.m3.is_none()
6029            && no_exp_macros
6030            && moe_q8;
6031        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6032        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6033        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6034            e.uninit(t * n_embd)?
6035        } else {
6036            e.zeros(t * n_embd)?
6037        };
6038        // The router readback above already established a host boundary. Copy each small-t hidden
6039        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6040        let cpu_input = if cpu_hybrid {
6041            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6042        } else {
6043            None
6044        };
6045
6046        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6047        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6048        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6049        // measured ~123 memsets/token of the decode wall).
6050        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6051        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6052        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6053        let mut scratch_g: Option<CudaSlice<u8>> = None;
6054        let mut scratch_u: Option<CudaSlice<u8>> = None;
6055        let mut scratch_d: Option<CudaSlice<u8>> = None;
6056        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6057        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6058
6059        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6060        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6061        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6062        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6063        let page_window = moe_page_prefetch_window();
6064
6065        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6066        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6067        for tok in 0..t {
6068            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6069            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6070            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6071            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6072
6073            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6074            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6075            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6076            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6077            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6078            // miss falls through to the sequential loop below, which admits as before. In steady
6079            // state on a fully-resident rig every token-layer takes the grouped path.
6080            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6081            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6082            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6083            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6084            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6085            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6086            let no_macros = m.gate_exps.macros.is_none()
6087                && m.up_exps.macros.is_none()
6088                && m.down_exps.macros.is_none();
6089            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6090            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6091            // with pointers computed from the resident slab base + ex*stride instead of
6092            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6093            // slab holds every expert by construction, so this arm never falls through
6094            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6095            // staging both die). Bit-identity class: pointer provenance only, the same
6096            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6097            // slab exists it is strictly better (no lock, no miss).
6098            if slab_fused_may_fire {
6099                let (pg, pu, pd) = slab_bases.unwrap();
6100                let mut gp = [0u64; 8];
6101                let mut up = [0u64; 8];
6102                let mut dp = [0u64; 8];
6103                for (j, &ex) in sel.iter().enumerate() {
6104                    let ex = ex as usize;
6105                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6106                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6107                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6108                }
6109                let mut wv = [0f32; 8];
6110                wv[..n_used].copy_from_slice(w);
6111                if tok_q8.is_none() {
6112                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6113                }
6114                let (zq, zd) = tok_q8.as_ref().unwrap();
6115                let act = e.moe_gate_up_silu8_q8(
6116                    crate::WPtr8(gp),
6117                    crate::WPtr8(up),
6118                    zq,
6119                    zd,
6120                    n_embd,
6121                    n_ff_exp,
6122                    n_used,
6123                    m.gate_exps.qtype,
6124                    m.up_exps.qtype,
6125                    m.gate_exps.row_bytes,
6126                    m.up_exps.row_bytes,
6127                )?;
6128                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6129                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6130                e.moe_down8_fma_q8(
6131                    crate::WPtr8(dp),
6132                    crate::F32x8(wv),
6133                    &aq2,
6134                    &ad2,
6135                    &mut dst,
6136                    n_ff_exp,
6137                    n_embd,
6138                    n_used,
6139                    m.down_exps.qtype,
6140                    m.down_exps.row_bytes,
6141                )?;
6142                continue;
6143            }
6144            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6145                if tok_q8.is_none() {
6146                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6147                }
6148                let (zq, zd) = tok_q8.as_ref().unwrap();
6149                if Self::moe_gdec_token_q8(
6150                    e,
6151                    m,
6152                    il,
6153                    max_block,
6154                    zq,
6155                    zd,
6156                    sel,
6157                    w,
6158                    &mut moe_out,
6159                    tok,
6160                    n_embd,
6161                    n_ff_exp,
6162                    n_used,
6163                )? {
6164                    continue;
6165                }
6166            } else if gdec_may_fire
6167                && cfg.m3.is_none()
6168                && no_macros
6169                && Self::moe_gdec_token(
6170                    e,
6171                    m,
6172                    il,
6173                    max_block,
6174                    &zt,
6175                    sel,
6176                    w,
6177                    &mut moe_out,
6178                    tok,
6179                    n_embd,
6180                    n_ff_exp,
6181                    n_used,
6182                )?
6183            {
6184                continue;
6185            }
6186
6187            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6188            // slab pair could fire. This token fell through to a sequential axpy loop, which
6189            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6190            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6191            // has no fallible predicate), included for the allocation invariant's symmetry.
6192            if gdec_may_fire || slab_fused_may_fire {
6193                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6194                e.memset_zeros_view(&mut row)?;
6195            }
6196
6197            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6198            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6199            // stall this path exists to remove, while mixing projections would require another
6200            // activation round-trip. Weight addresses remain valid until this worker is joined at
6201            // the bottom of the token scope.
6202            let mut cpu_mask = vec![false; sel.len()];
6203            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6204                let gpu_resident = if use_cache {
6205                    e.with_moe_cache(max_block, |cache, _| {
6206                        Ok(sel
6207                            .iter()
6208                            .map(|&expert| {
6209                                let expert = expert as u16;
6210                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6211                                    .into_iter()
6212                                    .filter(|&projection| {
6213                                        cache
6214                                            .resident(BlockId::new(il, projection, expert))
6215                                            .is_some()
6216                                    })
6217                                    .count()
6218                            })
6219                            .collect::<Vec<_>>())
6220                    })?
6221                } else {
6222                    vec![0; sel.len()]
6223                };
6224                let mut cpu_selected = Vec::new();
6225                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6226                    if gpu_resident[index] != 3 {
6227                        cpu_mask[index] = true;
6228                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6229                        let expert = expert as usize;
6230                        cpu_selected.push((expert, route_weight));
6231                    }
6232                }
6233                if crate::cpu_experts::predictor_enabled() {
6234                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6235                    // from this layer's MoE input and prefetches predicted-and-missing
6236                    // experts into the companion RAM cache. Never blocks this thread.
6237                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6238                    crate::cpu_experts::predictor_submit(il, row);
6239                }
6240                if cpu_selected.is_empty() {
6241                    None
6242                } else {
6243                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6244                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6245                        .map_err(std::io::Error::other)?;
6246                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6247                }
6248            } else {
6249                None
6250            };
6251
6252            let worker_window = worker_disk_prefetch
6253                .then(worker_prefetch_window)
6254                .unwrap_or(0);
6255            for (j, &ex) in sel.iter().enumerate() {
6256                if cpu_mask[j] {
6257                    continue;
6258                }
6259                let ex = ex as usize;
6260                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6261                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6262                // fused form) and macro-carrying artifacts — still have their bytes in the
6263                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6264                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6265                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6266                if let Some(d) = slab_local {
6267                    let gl = m.gate_exps.expert_layout(ex);
6268                    let ul = m.up_exps.expert_layout(ex);
6269                    let dl = m.down_exps.expert_layout(ex);
6270                    let (g0, u0, d0) = (
6271                        ex * m.gate_exps.expert_stride,
6272                        ex * m.up_exps.expert_stride,
6273                        ex * m.down_exps.expert_stride,
6274                    );
6275                    let (gate, up) = if moe_q8 {
6276                        if tok_q8.is_none() {
6277                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6278                        }
6279                        let (zq, zd) = tok_q8.as_ref().unwrap();
6280                        (
6281                            e.qmatvec_expert_q8(
6282                                &d.gate,
6283                                g0..g0 + gl.len,
6284                                zq,
6285                                zd,
6286                                1,
6287                                m.gate_exps.in_f,
6288                                m.gate_exps.out_f,
6289                                gl.qtype,
6290                                gl.row_bytes,
6291                            )?,
6292                            e.qmatvec_expert_q8(
6293                                &d.up,
6294                                u0..u0 + ul.len,
6295                                zq,
6296                                zd,
6297                                1,
6298                                m.up_exps.in_f,
6299                                m.up_exps.out_f,
6300                                ul.qtype,
6301                                ul.row_bytes,
6302                            )?,
6303                        )
6304                    } else {
6305                        (
6306                            e.qmatvec_view(
6307                                &d.gate,
6308                                g0..g0 + gl.len,
6309                                &zt,
6310                                1,
6311                                m.gate_exps.in_f,
6312                                m.gate_exps.out_f,
6313                                gl.qtype,
6314                                gl.row_bytes,
6315                            )?,
6316                            e.qmatvec_view(
6317                                &d.up,
6318                                u0..u0 + ul.len,
6319                                &zt,
6320                                1,
6321                                m.up_exps.in_f,
6322                                m.up_exps.out_f,
6323                                ul.qtype,
6324                                ul.row_bytes,
6325                            )?,
6326                        )
6327                    };
6328                    let mut act = e.uninit(n_ff_exp)?;
6329                    Self::ffn_act_lim(
6330                        e,
6331                        cfg,
6332                        &gate,
6333                        &up,
6334                        m.gate_exps.macro_scale(ex),
6335                        m.up_exps.macro_scale(ex),
6336                        lim_exp,
6337                        &mut act,
6338                        n_ff_exp,
6339                    )?;
6340                    let y = if moe_q8 {
6341                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6342                        e.qmatvec_expert_q8(
6343                            &d.down,
6344                            d0..d0 + dl.len,
6345                            &aq2,
6346                            &ad2,
6347                            1,
6348                            m.down_exps.in_f,
6349                            m.down_exps.out_f,
6350                            dl.qtype,
6351                            dl.row_bytes,
6352                        )?
6353                    } else {
6354                        let actv = act.slice(0..n_ff_exp);
6355                        e.qmatvec_view(
6356                            &d.down,
6357                            d0..d0 + dl.len,
6358                            &actv,
6359                            1,
6360                            m.down_exps.in_f,
6361                            m.down_exps.out_f,
6362                            dl.qtype,
6363                            dl.row_bytes,
6364                        )?
6365                    };
6366                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6367                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6368                    continue;
6369                }
6370                for next in page_prefetch_positions(j, sel.len(), page_window) {
6371                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6372                }
6373                let keep = [
6374                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6375                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6376                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6377                ];
6378                if worker_disk_prefetch && worker_window > 0 {
6379                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6380                        Self::moe_prefetch_disk_expert(
6381                            e,
6382                            il,
6383                            sel[next] as usize,
6384                            m,
6385                            max_block,
6386                            &keep,
6387                        )?;
6388                    }
6389                } else if cache_dispatch
6390                    && !cpu_hybrid
6391                    && moe_prefetch_enabled()
6392                    && j + 1 < sel.len()
6393                {
6394                    let next = sel[j + 1] as usize;
6395                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6396                }
6397                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6398                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6399                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6400                    // layouts stay on the metadata-aware f32 path.
6401                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6402                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6403                    }
6404                    let gate = if gate_q8 {
6405                        let (zq, zd) = tok_q8.as_ref().unwrap();
6406                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6407                    } else {
6408                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6409                    };
6410                    let up = if up_q8 {
6411                        let (zq, zd) = tok_q8.as_ref().unwrap();
6412                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6413                    } else {
6414                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6415                    };
6416                    let mut act = e.uninit(n_ff_exp)?;
6417                    Self::ffn_act_lim(
6418                        e,
6419                        cfg,
6420                        &gate,
6421                        &up,
6422                        m.gate_exps.macro_scale(ex),
6423                        m.up_exps.macro_scale(ex),
6424                        lim_exp,
6425                        &mut act,
6426                        n_ff_exp,
6427                    )?;
6428                    let y = if down_q8 {
6429                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6430                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6431                    } else {
6432                        let actv = act.slice(0..n_ff_exp);
6433                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6434                    };
6435                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6436                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6437                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6438                } else if cache_dispatch {
6439                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6440                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6441                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6442                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6443                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6444                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6445                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6446                    Self::ffn_act_lim(
6447                        e,
6448                        cfg,
6449                        &gate,
6450                        &up,
6451                        m.gate_exps.macro_scale(ex),
6452                        m.up_exps.macro_scale(ex),
6453                        lim_exp,
6454                        &mut act,
6455                        n_ff_exp,
6456                    )?;
6457                    let actv = act.slice(0..n_ff_exp);
6458                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6459                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6460                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6461                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6462                } else if cache_frozen {
6463                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6464                    // first prime. Reuse every fixed resident projection directly and stage only a
6465                    // true miss through the ordinary scratch slot. This preserves the established
6466                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6467                    let gate = Self::moe_frozen_gemm(
6468                        e,
6469                        il,
6470                        PROJ_GATE,
6471                        ex,
6472                        m,
6473                        max_block,
6474                        &zt,
6475                        &mut scratch_g,
6476                        g_len,
6477                    )?;
6478                    let up = Self::moe_frozen_gemm(
6479                        e,
6480                        il,
6481                        PROJ_UP,
6482                        ex,
6483                        m,
6484                        max_block,
6485                        &zt,
6486                        &mut scratch_u,
6487                        u_len,
6488                    )?;
6489                    let mut act = e.uninit(n_ff_exp)?;
6490                    Self::ffn_act_lim(
6491                        e,
6492                        cfg,
6493                        &gate,
6494                        &up,
6495                        m.gate_exps.macro_scale(ex),
6496                        m.up_exps.macro_scale(ex),
6497                        lim_exp,
6498                        &mut act,
6499                        n_ff_exp,
6500                    )?;
6501                    let actv = act.slice(0..n_ff_exp);
6502                    let y = Self::moe_frozen_gemm(
6503                        e,
6504                        il,
6505                        PROJ_DOWN,
6506                        ex,
6507                        m,
6508                        max_block,
6509                        &actv,
6510                        &mut scratch_d,
6511                        d_len,
6512                    )?;
6513                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6514                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6515                } else {
6516                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6517                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6518                    // fully overwrites the byte range the GEMM reads).
6519                    if scratch_g.is_none() {
6520                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6521                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6522                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6523                    }
6524                    let (sg, su, sd) = (
6525                        scratch_g.as_mut().unwrap(),
6526                        scratch_u.as_mut().unwrap(),
6527                        scratch_d.as_mut().unwrap(),
6528                    );
6529                    let gl = m.gate_exps.expert_layout(ex);
6530                    let ul = m.up_exps.expert_layout(ex);
6531                    let dl = m.down_exps.expert_layout(ex);
6532                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6533                    let gate = e.qmatvec_view(
6534                        sg,
6535                        0..gl.len,
6536                        &zt,
6537                        1,
6538                        m.gate_exps.in_f,
6539                        m.gate_exps.out_f,
6540                        gl.qtype,
6541                        gl.row_bytes,
6542                    )?;
6543
6544                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6545                    let up = e.qmatvec_view(
6546                        su,
6547                        0..ul.len,
6548                        &zt,
6549                        1,
6550                        m.up_exps.in_f,
6551                        m.up_exps.out_f,
6552                        ul.qtype,
6553                        ul.row_bytes,
6554                    )?;
6555
6556                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6557                    Self::ffn_act_lim(
6558                        e,
6559                        cfg,
6560                        &gate,
6561                        &up,
6562                        m.gate_exps.macro_scale(ex),
6563                        m.up_exps.macro_scale(ex),
6564                        lim_exp,
6565                        &mut act,
6566                        n_ff_exp,
6567                    )?;
6568
6569                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6570                    let actv = act.slice(0..n_ff_exp);
6571                    let y = e.qmatvec_view(
6572                        sd,
6573                        0..dl.len,
6574                        &actv,
6575                        1,
6576                        m.down_exps.in_f,
6577                        m.down_exps.out_f,
6578                        dl.qtype,
6579                        dl.row_bytes,
6580                    )?;
6581
6582                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6583                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6584                }
6585            }
6586            if let Some(worker) = cpu_worker {
6587                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6588                let cpu_output = e.htod(&cpu_output)?;
6589                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6590                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6591            }
6592            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6593                for (j, &ex) in sel.iter().enumerate() {
6594                    if cpu_mask[j] {
6595                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6596                    }
6597                }
6598            }
6599        }
6600
6601        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6602        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6603        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6604        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6605        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6606            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
6607        {
6608            let n_ff_sh = gate_shexp.out_features(); // 512
6609            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
6610            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
6611            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
6612            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
6613            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
6614            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
6615            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
6616            let verify_t = t > 1 && t < PRIME_MIN_T;
6617            let (sg_gate, sg_up) = if t == 1 {
6618                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
6619            } else if verify_t {
6620                (
6621                    e.matmul_decode_exact(gate_shexp, z, t)?,
6622                    e.matmul_decode_exact(up_shexp, z, t)?,
6623                )
6624            } else {
6625                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
6626            };
6627            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
6628            Self::ffn_act_lim(
6629                e,
6630                cfg,
6631                &sg_gate,
6632                &sg_up,
6633                1.0,
6634                1.0,
6635                lim_shexp,
6636                &mut sa,
6637                t * n_ff_sh,
6638            )?;
6639            let sh = if verify_t {
6640                e.matmul_decode_exact(down_shexp, &sa, t)?
6641            } else {
6642                e.matmul(down_shexp, &sa, t)?
6643            }; // [T, n_embd]
6644
6645            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
6646            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
6647            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
6648            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
6649            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
6650            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
6651            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
6652            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
6653            // expert's contribution into every token's residual, so under cross-request
6654            // concat prefill a session's hidden state depended on its co-arrivals' token
6655            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
6656            let g = match &m.gate_inp_shexp {
6657                Some(gate_inp_shexp) => {
6658                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
6659                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
6660                    } else {
6661                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
6662                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
6663                        e.sigmoid(&gs, &mut g, t)?;
6664                        g
6665                    }
6666                }
6667                None => e.htod(&vec![1.0f32; t])?,
6668            };
6669            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
6670            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
6671        }
6672
6673        Ok(moe_out)
6674    }
6675
6676    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
6677    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
6678    pub fn stage1_h2d_per_token(&self) -> u64 {
6679        use crate::hybrid::Ffn;
6680        let n_used = self
6681            .cfg
6682            .moe
6683            .as_ref()
6684            .map(|m| m.expert_used_count as u64)
6685            .unwrap_or(0);
6686        let mut bytes = 0u64;
6687        for l in self.layers.iter() {
6688            if let Ffn::Moe(m) = &l.ffn {
6689                bytes += n_used
6690                    * (m.gate_exps.max_expert_bytes()
6691                        + m.up_exps.max_expert_bytes()
6692                        + m.down_exps.max_expert_bytes()) as u64;
6693            }
6694        }
6695        bytes
6696    }
6697
6698    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
6699    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
6700    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
6701    pub(crate) fn max_moe_block(&self) -> usize {
6702        use crate::hybrid::Ffn;
6703        let mut mx = 0usize;
6704        let mut scan = |ffn: &Ffn| {
6705            if let Ffn::Moe(m) = ffn {
6706                mx = mx
6707                    .max(m.gate_exps.max_expert_bytes())
6708                    .max(m.up_exps.max_expert_bytes())
6709                    .max(m.down_exps.max_expert_bytes());
6710            }
6711        };
6712        for l in self.layers.iter() {
6713            scan(&l.ffn);
6714        }
6715        if let Some(mtp) = self.mtp.as_ref() {
6716            scan(&mtp.ffn);
6717        }
6718        mx
6719    }
6720
6721    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
6722    /// but have no bytes and therefore consume no residency slot.
6723    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
6724        use crate::hybrid::Ffn;
6725        let mut sizes = Vec::new();
6726        let mut scan = |ffn: &Ffn| {
6727            let Ffn::Moe(m) = ffn else { return };
6728            for ex in 0..m.gate_exps.n_expert {
6729                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
6730                    continue;
6731                }
6732                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
6733                    let len = exps.expert_layout(ex).len;
6734                    if len > 0 {
6735                        sizes.push(len);
6736                    }
6737                }
6738            }
6739        };
6740        for layer in &self.layers {
6741            scan(&layer.ffn);
6742        }
6743        if let Some(mtp) = &self.mtp {
6744            scan(&mtp.ffn);
6745        }
6746        sizes
6747    }
6748
6749    /// Persist the frozen residency set so a later process can restage it directly and skip
6750    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
6751    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
6752    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
6753    /// post-freeze argmax gate still validates the serving assignment.
6754    pub fn save_cpu_expert_residency_profile(
6755        &self,
6756        e: &Engine,
6757        path: &std::path::Path,
6758    ) -> Result<(), Box<dyn std::error::Error>> {
6759        let Some(ids) = e.export_moe_residency() else {
6760            return Err("no MoE residency cache to persist".into());
6761        };
6762        let mut body = format!(
6763            "memra-freeze-profile v1 max_block={} blocks={}\n",
6764            self.max_moe_block(),
6765            ids.len()
6766        );
6767        for (layer, proj, ex) in &ids {
6768            body.push_str(&format!("{layer} {proj} {ex}\n"));
6769        }
6770        let tmp = path.with_extension("tmp");
6771        std::fs::write(&tmp, body)?;
6772        std::fs::rename(&tmp, path)?;
6773        println!(
6774            "[moe-cache] freeze profile saved: {} blocks -> {}",
6775            ids.len(),
6776            path.display()
6777        );
6778        Ok(())
6779    }
6780
6781    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
6782    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
6783    /// missing or its header does not match this model's slot geometry.
6784    pub fn restore_cpu_expert_residency_profile(
6785        &self,
6786        e: &Engine,
6787        path: &std::path::Path,
6788    ) -> Result<bool, Box<dyn std::error::Error>> {
6789        use crate::hybrid::Ffn;
6790        use crate::moe_cache::BlockId;
6791        let Ok(content) = std::fs::read_to_string(path) else {
6792            return Ok(false);
6793        };
6794        let mut lines = content.lines();
6795        let Some(header) = lines.next() else {
6796            return Ok(false);
6797        };
6798        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
6799        if !header.starts_with(&expected) {
6800            println!(
6801                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
6802                path.display()
6803            );
6804            return Ok(false);
6805        }
6806        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
6807            std::collections::HashMap::new();
6808        for line in lines {
6809            let mut fields = line.split_whitespace();
6810            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
6811            else {
6812                continue;
6813            };
6814            let (Ok(layer), Ok(proj), Ok(ex)) =
6815                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
6816            else {
6817                continue;
6818            };
6819            by_layer
6820                .entry(layer)
6821                .or_default()
6822                .push(BlockId::new(layer, proj, ex));
6823        }
6824        let requested: usize = by_layer.values().map(Vec::len).sum();
6825        if requested == 0 {
6826            return Ok(false);
6827        }
6828        let max_block = self.max_moe_block();
6829        let mut restaged = 0usize;
6830        let mut stage_layer =
6831            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
6832                let Ffn::Moe(m) = ffn else { return Ok(()) };
6833                let Some(ids) = by_layer.get(&layer_index) else {
6834                    return Ok(());
6835                };
6836                e.with_moe_cache(max_block, |cache, eng| {
6837                    for id in ids {
6838                        if cache.restage_block(*id, m, eng)? {
6839                            restaged += 1;
6840                        }
6841                    }
6842                    Ok(())
6843                })
6844            };
6845        for (index, layer) in self.layers.iter().enumerate() {
6846            stage_layer(index as u16, &layer.ffn)?;
6847        }
6848        if let Some(mtp) = self.mtp.as_ref() {
6849            stage_layer(u16::MAX, &mtp.ffn)?;
6850        }
6851        e.freeze_moe_cache();
6852        println!(
6853            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
6854            path.display()
6855        );
6856        Ok(true)
6857    }
6858
6859    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
6860    pub fn freeze_cpu_expert_residency(
6861        &self,
6862        e: &Engine,
6863    ) -> Result<(), Box<dyn std::error::Error>> {
6864        e.freeze_moe_cache();
6865        Ok(())
6866    }
6867
6868    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
6869    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
6870    /// the model's activation exactly.
6871    ///
6872    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
6873    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
6874    /// form for anything that can land on a clamped layer.
6875    pub fn ffn_act(
6876        e: &Engine,
6877        cfg: &ModelConfig,
6878        gate: &CudaSlice<f32>,
6879        up: &CudaSlice<f32>,
6880        act: &mut CudaSlice<f32>,
6881        n: usize,
6882    ) -> Result<(), Box<dyn std::error::Error>> {
6883        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
6884    }
6885
6886    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
6887    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
6888    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
6889    #[allow(clippy::too_many_arguments)]
6890    pub(crate) fn ffn_act_scaled(
6891        e: &Engine,
6892        cfg: &ModelConfig,
6893        gate: &CudaSlice<f32>,
6894        up: &CudaSlice<f32>,
6895        gs: f32,
6896        us: f32,
6897        act: &mut CudaSlice<f32>,
6898        n: usize,
6899    ) -> Result<(), Box<dyn std::error::Error>> {
6900        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
6901    }
6902
6903    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
6904    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
6905    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
6906    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
6907    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
6908    ///                 arrays are SEPARATE and a layer can have one without the other.
6909    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
6910    /// already known live.
6911    #[allow(clippy::too_many_arguments)]
6912    pub(crate) fn ffn_act_lim(
6913        e: &Engine,
6914        cfg: &ModelConfig,
6915        gate: &CudaSlice<f32>,
6916        up: &CudaSlice<f32>,
6917        gs: f32,
6918        us: f32,
6919        limit: Option<f32>,
6920        act: &mut CudaSlice<f32>,
6921        n: usize,
6922    ) -> Result<(), Box<dyn std::error::Error>> {
6923        if let Some(m3) = cfg.m3.as_ref() {
6924            debug_assert!(
6925                limit.is_none(),
6926                "m3 swigluoai and step35 clamp are different archs"
6927            );
6928            return e.swigluoai_mul_scaled(
6929                gate,
6930                up,
6931                gs,
6932                us,
6933                m3.swiglu_alpha,
6934                m3.swiglu_limit,
6935                act,
6936                n,
6937            );
6938        }
6939        if let Some(l) = limit {
6940            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
6941        }
6942        if gs == 1.0 && us == 1.0 {
6943            return e.silu_mul(gate, up, act, n);
6944        }
6945        e.silu_mul_scaled(gate, up, gs, us, act, n)
6946    }
6947
6948    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
6949    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
6950    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
6951    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
6952    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
6953    fn moe_route(
6954        e: &Engine,
6955        logits: &CudaSlice<f32>,
6956        t: usize,
6957        n_expert: usize,
6958        n_used: usize,
6959    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6960        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
6961    }
6962
6963    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
6964    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
6965    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
6966    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
6967    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
6968    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
6969    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
6970    #[allow(clippy::too_many_arguments)]
6971    fn moe_route_sigmoid_cfg(
6972        e: &Engine,
6973        logits: &CudaSlice<f32>,
6974        t: usize,
6975        n_expert: usize,
6976        n_used: usize,
6977        m: &MoeWeights,
6978        (sf, route_norm): (f32, bool),
6979    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
6980        if sigmoid_router_enabled() {
6981            return e.moe_router_sigmoid_topk_host(
6982                logits,
6983                t,
6984                n_expert,
6985                n_used,
6986                m.active_count(),
6987                &m.exp_probs_b_dev,
6988                &m.active_experts_dev,
6989                sf,
6990                route_norm,
6991            );
6992        }
6993        let lg = e.dtoh(logits)?;
6994        Self::moe_route_sigmoid_host(
6995            &lg,
6996            t,
6997            n_expert,
6998            n_used,
6999            m.exp_probs_b.as_deref(),
7000            sf,
7001            route_norm,
7002            m.active_experts.as_deref(),
7003        )
7004    }
7005
7006    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
7007    /// the existing softmax device kernel has no mask input.
7008    fn moe_route_cfg(
7009        e: &Engine,
7010        logits: &CudaSlice<f32>,
7011        t: usize,
7012        n_expert: usize,
7013        n_used: usize,
7014        active: Option<&[bool]>,
7015    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7016        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7017        // rollback) via the single-sync pinned readback — softmax arch only.
7018        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7019            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7020        }
7021        // Host oracle (the §D bit-identity reference).
7022        let lg = e.dtoh(logits)?; // [T*n_expert] host
7023        let mut sel = vec![0u32; t * n_used];
7024        let mut w_out = vec![0f32; t * n_used];
7025        for tok in 0..t {
7026            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7027            // softmax over ALL n_expert (stable: subtract max)
7028            let maxl = row
7029                .iter()
7030                .enumerate()
7031                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7032                .map(|(_, &x)| x)
7033                .fold(f32::NEG_INFINITY, f32::max);
7034            let mut probs = vec![0f32; n_expert];
7035            let mut den = 0f32;
7036            for i in 0..n_expert {
7037                if active.is_some_and(|mask| !mask[i]) {
7038                    continue;
7039                }
7040                let x = (row[i] - maxl).exp();
7041                probs[i] = x;
7042                den += x;
7043            }
7044            for p in probs.iter_mut() {
7045                *p /= den;
7046            }
7047            // stable DESC sort: prob DESC, ascending-index tiebreak.
7048            let mut idx: Vec<usize> = (0..n_expert)
7049                .filter(|&i| active.is_none_or(|mask| mask[i]))
7050                .collect();
7051            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7052            let sl = &idx[..n_used];
7053            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7054            let mut ws: f32 = wv.iter().sum();
7055            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7056            for x in wv.iter_mut() {
7057                *x /= ws;
7058            }
7059            for j in 0..n_used {
7060                sel[tok * n_used + j] = sl[j] as u32;
7061                w_out[tok * n_used + j] = wv[j];
7062            }
7063        }
7064        Ok((sel, w_out))
7065    }
7066
7067    #[allow(clippy::too_many_arguments)]
7068    fn moe_route_sigmoid_with_input(
7069        e: &Engine,
7070        logits: &CudaSlice<f32>,
7071        input: &CudaSlice<f32>,
7072        t: usize,
7073        in_features: usize,
7074        n_expert: usize,
7075        n_used: usize,
7076        bias: Option<&[f32]>,
7077        (sf, route_norm): (f32, bool),
7078        active: Option<&[bool]>,
7079    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7080        let logit_values =
7081            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7082        let input_values =
7083            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7084        let (lg, input) = e.dtoh_pair_views(
7085            &logits.slice(0..logit_values),
7086            &input.slice(0..input_values),
7087        )?;
7088        let (sel, w) =
7089            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7090        Ok((sel, w, input))
7091    }
7092
7093    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7094    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7095    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7096    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7097    pub fn start_moe_prefetch_predictor(
7098        &self,
7099        e: &Engine,
7100        cfg: &ModelConfig,
7101    ) -> Result<(), Box<dyn std::error::Error>> {
7102        use crate::hybrid::Ffn;
7103        let Some(sig) = cfg.sigmoid_router() else {
7104            return Err("prefetch predictor requires a sigmoid-router arch".into());
7105        };
7106        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7107            .export_moe_residency()
7108            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7109            .into_iter()
7110            .collect();
7111        let mut layers = Vec::new();
7112        for (index, layer) in self.layers.iter().enumerate() {
7113            let Ffn::Moe(m) = &layer.ffn else { continue };
7114            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7115                continue;
7116            };
7117            let router = e.dtoh(data)?;
7118            let n_expert = m.gate_exps.n_expert;
7119            let n_embd = m.gate_exps.in_f;
7120            if router.len() != n_embd * n_expert {
7121                continue;
7122            }
7123            let build = |exps: &crate::model::HostExps| {
7124                (0..n_expert)
7125                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7126                    .collect::<Vec<_>>()
7127            };
7128            layers.push((
7129                index as u16,
7130                crate::cpu_experts::PredictLayerInit {
7131                    router,
7132                    bias: m.exp_probs_b.clone(),
7133                    active: m.active_experts.clone(),
7134                    n_embd,
7135                    n_used: cfg
7136                        .moe
7137                        .as_ref()
7138                        .map(|moe| moe.expert_used_count as usize)
7139                        .ok_or("prefetch predictor requires MoE config")?,
7140                    sig,
7141                    weights_n_expert: n_expert,
7142                    gate: build(&m.gate_exps),
7143                    up: build(&m.up_exps),
7144                    down: build(&m.down_exps),
7145                },
7146            ));
7147        }
7148        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7149    }
7150
7151    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7152    /// selection math to the rollback runtime, applied to host-computed logits.
7153    #[allow(clippy::too_many_arguments)]
7154    pub fn moe_route_sigmoid_host_public(
7155        logits: &[f32],
7156        t: usize,
7157        n_expert: usize,
7158        n_used: usize,
7159        bias: Option<&[f32]>,
7160        sf: f32,
7161        route_norm: bool,
7162        active: Option<&[bool]>,
7163    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7164        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7165    }
7166
7167    #[allow(clippy::too_many_arguments)]
7168    fn moe_route_sigmoid_host(
7169        lg: &[f32],
7170        t: usize,
7171        n_expert: usize,
7172        n_used: usize,
7173        bias: Option<&[f32]>,
7174        sf: f32,
7175        route_norm: bool,
7176        active: Option<&[bool]>,
7177    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7178        let active_count = active
7179            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7180            .unwrap_or(n_expert);
7181        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7182        if lg.len() != t * n_expert {
7183            return Err(format!(
7184                "sigmoid router logits length mismatch: got {}, expected {}",
7185                lg.len(),
7186                t * n_expert,
7187            )
7188            .into());
7189        }
7190        let mut sel = vec![0u32; t * n_used];
7191        let mut w_out = vec![0f32; t * n_used];
7192        for tok in 0..t {
7193            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7194            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7195            // selection score = sigmoid + bias; weight = plain sigmoid.
7196            let selsc: Vec<f32> = match bias {
7197                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7198                None => scores.clone(),
7199            };
7200            let mut idx: Vec<usize> = (0..n_expert)
7201                .filter(|&i| active.is_none_or(|mask| mask[i]))
7202                .collect();
7203            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7204            let sl = &idx[..n_used];
7205            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7206            if route_norm {
7207                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7208                for x in wv.iter_mut() {
7209                    *x = *x / ws * sf;
7210                }
7211            } else {
7212                for x in wv.iter_mut() {
7213                    *x *= sf;
7214                }
7215            }
7216            for j in 0..n_used {
7217                sel[tok * n_used + j] = sl[j] as u32;
7218                w_out[tok * n_used + j] = wv[j];
7219            }
7220        }
7221        Ok((sel, w_out))
7222    }
7223
7224    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7225    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7226    /// macro-scaled experts, and observation modes are denied by the caller.
7227    #[allow(clippy::too_many_arguments)]
7228    fn moe_ffn_sigmoid_dev(
7229        e: &Engine,
7230        m: &MoeWeights,
7231        z: &CudaSlice<f32>,
7232        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7233        logits: &CudaSlice<f32>,
7234        t: usize,
7235        cfg: &ModelConfig,
7236        il: u16,
7237        (scaling_factor, route_norm): (f32, bool),
7238    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7239        let moe = cfg.moe.as_ref().unwrap();
7240        let n_embd = cfg.n_embd as usize;
7241        let n_expert = moe.expert_count as usize;
7242        let n_used = moe.expert_used_count as usize;
7243        let n_ff_exp = moe.expert_ff_length as usize;
7244        let dev = m.dev_exps.as_ref().unwrap();
7245        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7246        debug_assert!(m.has_uniform_expert_layout());
7247        debug_assert!(!m.has_macros);
7248
7249        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7250            logits,
7251            t,
7252            n_expert,
7253            n_used,
7254            m.active_count(),
7255            &m.exp_probs_b_dev,
7256            &m.active_experts_dev,
7257            scaling_factor,
7258            route_norm,
7259        )?;
7260        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7261        if let Some(fp8) = dev.fp8_blk.as_ref() {
7262            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7263            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7264            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7265            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7266            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7267            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7268
7269            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7270            // activations with block-128 E4M3 weights. This deliberately
7271            // simple resident reference is the correctness oracle for later
7272            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7273            // load-time Q8 diagnostic representation, so one process never
7274            // crosses between numerical programs.
7275            let selected = e.dtoh_i32(&sel_d)?;
7276            let route_weights = e.dtoh(&w_d)?;
7277            let mut moe_out = e.zeros(t * n_embd)?;
7278            for tok in 0..t {
7279                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7280                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7281                for j in 0..n_used {
7282                    let pair = tok * n_used + j;
7283                    let expert = selected[pair] as usize;
7284                    let gate = Self::moe_resident_fp8_e4m3(
7285                        e,
7286                        &m.gate_exps,
7287                        &dev.gate,
7288                        &fp8.gate,
7289                        expert,
7290                        &zt,
7291                        1,
7292                    )?;
7293                    let up = Self::moe_resident_fp8_e4m3(
7294                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7295                    )?;
7296                    let mut act = e.uninit(n_ff_exp)?;
7297                    Self::ffn_act_lim(
7298                        e,
7299                        cfg,
7300                        &gate,
7301                        &up,
7302                        1.0,
7303                        1.0,
7304                        cfg.clamp_exp_at(il as u32),
7305                        &mut act,
7306                        n_ff_exp,
7307                    )?;
7308                    let act = act.slice(0..n_ff_exp);
7309                    let down = Self::moe_resident_fp8_e4m3(
7310                        e,
7311                        &m.down_exps,
7312                        &dev.down,
7313                        &fp8.down,
7314                        expert,
7315                        &act,
7316                        1,
7317                    )?;
7318                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7319                }
7320            }
7321            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7322                eprintln!(
7323                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7324                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7325                    cfg.clamp_exp_at(il as u32).is_some(),
7326                );
7327            }
7328            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7329            return Ok(moe_out);
7330        }
7331        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7332            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7333            (combined, combined)
7334        } else {
7335            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7336        };
7337        let (zq, zd) = match (t, zq8) {
7338            (1, Some((q, d))) => (q.clone(), d.clone()),
7339            _ => e.quantize_q8_1(z, t, n_embd)?,
7340        };
7341        let n_pairs = t * n_used;
7342        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7343            // The final Step layers retain the established separate gate/up -> clamp -> down
7344            // arithmetic. Pair rows are derived from token position; selected expert ids and
7345            // routing weights remain the device router's buffers throughout.
7346            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7347            let pair_tok_d = e.htod_i32(&pair_tok)?;
7348            let gate = e.moe_pairs_matvec_q8(
7349                &dev.ptr_row,
7350                0,
7351                &pair_tok_d,
7352                &sel_d,
7353                &zq,
7354                &zd,
7355                n_embd,
7356                n_ff_exp,
7357                n_expert,
7358                n_pairs,
7359                m.gate_exps.qtype,
7360                gate_row_bytes,
7361            )?;
7362            let up = e.moe_pairs_matvec_q8(
7363                &dev.ptr_row,
7364                1,
7365                &pair_tok_d,
7366                &sel_d,
7367                &zq,
7368                &zd,
7369                n_embd,
7370                n_ff_exp,
7371                n_expert,
7372                n_pairs,
7373                m.up_exps.qtype,
7374                up_row_bytes,
7375            )?;
7376            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7377            Self::ffn_act_lim(
7378                e,
7379                cfg,
7380                &gate,
7381                &up,
7382                1.0,
7383                1.0,
7384                cfg.clamp_exp_at(il as u32),
7385                &mut act,
7386                n_pairs * n_ff_exp,
7387            )?;
7388            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7389            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7390            let pair_self_d = e.htod_i32(&pair_self)?;
7391            let down = e.moe_pairs_matvec_q8(
7392                &dev.ptr_row,
7393                2,
7394                &pair_self_d,
7395                &sel_d,
7396                &aq2,
7397                &ad2,
7398                n_ff_exp,
7399                n_embd,
7400                n_expert,
7401                n_pairs,
7402                m.down_exps.qtype,
7403                m.down_exps.row_bytes,
7404            )?;
7405            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7406            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7407            let tok_off_d = e.htod_i32(&tok_off)?;
7408            let tok_ids_d = e.htod_i32(&tok_ids)?;
7409            let mut output = e.uninit(t * n_embd)?;
7410            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7411            output
7412        } else {
7413            let act = e.moe_gate_up_silu8_dev_q8_rows(
7414                &dev.ptr_row,
7415                &sel_d,
7416                &zq,
7417                &zd,
7418                t,
7419                n_embd,
7420                n_ff_exp,
7421                n_used,
7422                n_expert,
7423                m.gate_exps.qtype,
7424                m.up_exps.qtype,
7425                gate_row_bytes,
7426                up_row_bytes,
7427                &m.dev_macros,
7428            )?;
7429            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7430            let mut output = e.uninit(t * n_embd)?;
7431            e.moe_down8_fma_dev_q8_rows_g(
7432                &dev.ptr_row,
7433                &sel_d,
7434                &w_d,
7435                &aq2,
7436                &ad2,
7437                &mut output,
7438                t,
7439                n_ff_exp,
7440                n_embd,
7441                n_used,
7442                n_expert,
7443                m.down_exps.qtype,
7444                m.down_exps.row_bytes,
7445            )?;
7446            output
7447        };
7448
7449        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7450            eprintln!(
7451                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7452                cfg.clamp_exp_at(il as u32).is_some(),
7453                dev.gu_il,
7454            );
7455        }
7456        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7457        Ok(moe_out)
7458    }
7459
7460    #[allow(clippy::too_many_arguments)]
7461    fn moe_resident_fp8_e4m3(
7462        e: &Engine,
7463        exps: &crate::model::HostExps,
7464        bytes: &CudaSlice<u8>,
7465        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7466        expert: usize,
7467        x: &cudarc::driver::CudaView<f32>,
7468        m: usize,
7469    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7470        let layout = exps.expert_layout(expert);
7471        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7472        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7473        let byte_start = expert * exps.expert_stride;
7474        let scale_start = expert * scales.expert_stride;
7475        let weight = bytes.slice(byte_start..byte_start + layout.len);
7476        let scale = scales
7477            .scales
7478            .slice(scale_start..scale_start + scales.expert_stride);
7479        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7480    }
7481
7482    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7483    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7484    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7485    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7486    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7487    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7488    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7489    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7490    fn moe_ffn_pairs(
7491        e: &Engine,
7492        m: &MoeWeights,
7493        z: &CudaSlice<f32>,
7494        logits: &CudaSlice<f32>,
7495        t: usize,
7496        cfg: &ModelConfig,
7497    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7498        let moe = cfg.moe.as_ref().unwrap();
7499        let n_embd = cfg.n_embd as usize;
7500        let n_expert = moe.expert_count as usize;
7501        let n_used = moe.expert_used_count as usize;
7502        let n_ff_exp = moe.expert_ff_length as usize;
7503        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7504        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7505        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7506        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7507        debug_assert!(
7508            !cfg.swiglu_clamped_anywhere(),
7509            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7510        );
7511        let dev = m.dev_exps.as_ref().unwrap();
7512        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7513        let (rbg_d, rbu_d) = if dev.gu_il {
7514            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7515            (sxx, sxx)
7516        } else {
7517            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7518        };
7519
7520        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7521        let n_pairs = t * n_used;
7522        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7523        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7524        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7525        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7526        let pair_w: Vec<f32> = w_all.clone();
7527        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7528        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7529        let pt = e.htod_i32(&pair_tok)?;
7530        let px = e.htod_i32(&pair_ex)?;
7531        let pw = e.htod(&pair_w)?;
7532        let toff = e.htod_i32(&tok_off)?;
7533        let tids = e.htod_i32(&tok_ids)?;
7534
7535        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7536        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7537        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7538        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7539        for p in 0..n_pairs {
7540            by_ex[pair_ex[p] as usize].push(p as i32);
7541        }
7542        let mut ex_ids: Vec<i32> = Vec::new();
7543        let mut ex_off: Vec<i32> = vec![0];
7544        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7545        for (ex, list) in by_ex.iter().enumerate() {
7546            if list.is_empty() {
7547                continue;
7548            }
7549            ex_ids.push(ex as i32);
7550            ex_pairs.extend_from_slice(list);
7551            ex_off.push(ex_pairs.len() as i32);
7552        }
7553        let n_active = ex_ids.len();
7554        let exi = e.htod_i32(&ex_ids)?;
7555        let exo = e.htod_i32(&ex_off)?;
7556        let exp_d = e.htod_i32(&ex_pairs)?;
7557        let _ = &px; // pair-major twin keeps it; em path uses CSR
7558
7559        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7560        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7561        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7562        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7563        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7564        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7565        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7566        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7567        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7568        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7569        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7570        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7571        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7572        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7573        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7574        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7575        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7576        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7577        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7578        let mma_t = *MMA_T.get_or_init(|| {
7579            std::env::var("MEMRA_MOE_MMA_T")
7580                .ok()
7581                .and_then(|v| v.parse().ok())
7582                .unwrap_or(16)
7583        });
7584        let use_mma = std::env::var("MEMRA_MOE_MMA")
7585            .map(|v| v != "0")
7586            .unwrap_or(true)
7587            && t >= mma_t
7588            && q8_expert_dec_supported(m.gate_exps.qtype)
7589            && q8_expert_dec_supported(m.up_exps.qtype)
7590            && q8_expert_dec_supported(m.down_exps.qtype)
7591            && n_embd % 256 == 0
7592            && n_ff_exp % 256 == 0;
7593        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7594        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7595        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7596        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7597        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7598        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7599        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7600        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7601        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7602        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7603        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7604        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7605        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7606        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
7607        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
7608        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
7609            && q8_expert_dec_supported(m.up_exps.qtype)
7610            && q8_expert_dec_supported(m.down_exps.qtype)
7611            && n_embd % 256 == 0
7612            && n_ff_exp % 256 == 0;
7613        let f16g_mode = crate::moe_f16g_mode();
7614        let f16g = f16g_mode != 0
7615            && t >= mma_t
7616            && (f16g_mode != 3 || !mma_capable)
7617            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
7618            && f16g_proj_ok(m.up_exps.qtype, n_embd)
7619            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
7620        if use_mma || f16g {
7621            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
7622            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
7623            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
7624            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
7625            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
7626            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
7627            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
7628            let y_down = if f16g {
7629                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
7630                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
7631                // permute at the very end back to pair-id order for the scatter.
7632                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
7633                let csr_tok_d = e.htod_i32(&csr_tok)?;
7634                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
7635                let g_csr = e.moe_f16_grouped(
7636                    &dev.ptr_row,
7637                    0,
7638                    n_expert,
7639                    &exi,
7640                    &ex_off,
7641                    &exo,
7642                    &z_f16,
7643                    &z_s,
7644                    n_embd,
7645                    n_ff_exp,
7646                    n_active,
7647                    n_pairs,
7648                    m.gate_exps.qtype,
7649                    rbg_d,
7650                )?;
7651                let u_csr = e.moe_f16_grouped(
7652                    &dev.ptr_row,
7653                    1,
7654                    n_expert,
7655                    &exi,
7656                    &ex_off,
7657                    &exo,
7658                    &z_f16,
7659                    &z_s,
7660                    n_embd,
7661                    n_ff_exp,
7662                    n_active,
7663                    n_pairs,
7664                    m.up_exps.qtype,
7665                    rbu_d,
7666                )?;
7667                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
7668                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
7669                let d_csr = e.moe_f16_grouped(
7670                    &dev.ptr_row,
7671                    2,
7672                    n_expert,
7673                    &exi,
7674                    &ex_off,
7675                    &exo,
7676                    &a_f16,
7677                    &a_s,
7678                    n_ff_exp,
7679                    n_embd,
7680                    n_active,
7681                    n_pairs,
7682                    m.down_exps.qtype,
7683                    m.down_exps.row_bytes,
7684                )?;
7685                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
7686            } else {
7687                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
7688                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
7689                let gate = e.mmq_iq_experts(
7690                    &dev.ptr_row,
7691                    0,
7692                    n_expert,
7693                    &exi,
7694                    &exo,
7695                    &exp_d,
7696                    &pt,
7697                    &z_scr,
7698                    n_embd,
7699                    n_ff_exp,
7700                    n_active,
7701                    n_pairs,
7702                    t,
7703                    m.gate_exps.qtype,
7704                    rbg_d,
7705                )?;
7706                let up = e.mmq_iq_experts(
7707                    &dev.ptr_row,
7708                    1,
7709                    n_expert,
7710                    &exi,
7711                    &exo,
7712                    &exp_d,
7713                    &pt,
7714                    &z_scr,
7715                    n_embd,
7716                    n_ff_exp,
7717                    n_active,
7718                    n_pairs,
7719                    t,
7720                    m.up_exps.qtype,
7721                    rbu_d,
7722                )?;
7723                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
7724                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
7725                // registers and writes ONLY the quantized scratch — the two-pass chain
7726                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
7727                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
7728                let a_scr = if crate::moe_fuse_actq_on() {
7729                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
7730                } else {
7731                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7732                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
7733                };
7734                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7735                let pself = e.htod_i32(&pair_self)?;
7736                e.mmq_iq_experts(
7737                    &dev.ptr_row,
7738                    2,
7739                    n_expert,
7740                    &exi,
7741                    &exo,
7742                    &exp_d,
7743                    &pself,
7744                    &a_scr,
7745                    n_ff_exp,
7746                    n_embd,
7747                    n_active,
7748                    n_pairs,
7749                    n_pairs,
7750                    m.down_exps.qtype,
7751                    m.down_exps.row_bytes,
7752                )?
7753            };
7754            let mut moe_out = e.uninit(t * n_embd)?;
7755            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7756            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7757                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7758            {
7759                let n_ff_sh = gate_shexp.out_features();
7760                let sg_gate = e.matmul(gate_shexp, z, t)?;
7761                let sg_up = e.matmul(up_shexp, z, t)?;
7762                let mut sa = e.uninit(t * n_ff_sh)?;
7763                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7764                let sh = e.matmul(down_shexp, &sa, t)?;
7765                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7766                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
7767                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
7768                // i.e. the one real prefill actually takes on a resident-expert MoE model,
7769                // so the concat-prime isolation fix has to land here as well.
7770                let g = match &m.gate_inp_shexp {
7771                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7772                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7773                    }
7774                    Some(gate_inp_shexp) => {
7775                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7776                        let mut g = e.uninit(t)?;
7777                        e.sigmoid(&gs, &mut g, t)?;
7778                        g
7779                    }
7780                    None => e.htod(&vec![1.0f32; t])?,
7781                };
7782                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7783            }
7784            return Ok(moe_out);
7785        }
7786
7787        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
7788        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
7789        let dec = std::env::var("MEMRA_MOE_DEC")
7790            .map(|v| v != "0")
7791            .unwrap_or(true);
7792        let matvec = |proj,
7793                      exi: &_,
7794                      exo: &_,
7795                      exp_d: &_,
7796                      pt: &_,
7797                      aq: &_,
7798                      ad: &_,
7799                      inf,
7800                      outf,
7801                      qtype,
7802                      rb|
7803         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7804            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
7805            let dec = dec && q8_expert_dec_supported(qtype);
7806            if dec {
7807                e.moe_pairs_matvec_q8_dec(
7808                    &dev.ptr_row,
7809                    proj,
7810                    exi,
7811                    exo,
7812                    exp_d,
7813                    pt,
7814                    aq,
7815                    ad,
7816                    inf,
7817                    outf,
7818                    n_expert,
7819                    n_active,
7820                    n_pairs,
7821                    qtype,
7822                    rb,
7823                )
7824            } else {
7825                e.moe_pairs_matvec_q8_em(
7826                    &dev.ptr_row,
7827                    proj,
7828                    exi,
7829                    exo,
7830                    exp_d,
7831                    pt,
7832                    aq,
7833                    ad,
7834                    inf,
7835                    outf,
7836                    n_expert,
7837                    n_active,
7838                    n_pairs,
7839                    qtype,
7840                    rb,
7841                )
7842            }
7843        };
7844        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7845        let gate = matvec(
7846            0,
7847            &exi,
7848            &exo,
7849            &exp_d,
7850            &pt,
7851            &zq,
7852            &zd,
7853            n_embd,
7854            n_ff_exp,
7855            m.gate_exps.qtype,
7856            rbg_d,
7857        )?;
7858        let up = matvec(
7859            1,
7860            &exi,
7861            &exo,
7862            &exp_d,
7863            &pt,
7864            &zq,
7865            &zd,
7866            n_embd,
7867            n_ff_exp,
7868            m.up_exps.qtype,
7869            rbu_d,
7870        )?;
7871        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
7872        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7873        // down consumes PAIR-major activation rows: pair_tok = identity.
7874        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7875        let pself = e.htod_i32(&pair_self)?;
7876        let y_down = matvec(
7877            2,
7878            &exi,
7879            &exo,
7880            &exp_d,
7881            &pself,
7882            &aq2,
7883            &ad2,
7884            n_ff_exp,
7885            n_embd,
7886            m.down_exps.qtype,
7887            m.down_exps.row_bytes,
7888        )?;
7889        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
7890        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
7891
7892        // SHARED EXPERT epilogue — same as the other paths.
7893        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
7894        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
7895        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
7896            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7897        {
7898            let n_ff_sh = gate_shexp.out_features();
7899            // These decode-exact forms are required by the new Step resident arm. Keep the
7900            // established grouped shared-expert program for every other architecture: widening
7901            // this to Gemma changed its speculative acceptance despite green argmax gates.
7902            let step_exact = true;
7903            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
7904            let (sg_gate, sg_up) = if step_exact && t == 1 {
7905                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
7906            } else if verify_t {
7907                let mut fused = None;
7908                if crate::spec::spec_fused_t()
7909                    && (2..=4).contains(&t)
7910                    && e.uses_q8_1_fast(gate_shexp)
7911                    && e.uses_q8_1_fast(up_shexp)
7912                {
7913                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
7914                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
7915                }
7916                match fused {
7917                    Some(pair) => pair,
7918                    None => (
7919                        e.matmul_decode_exact(gate_shexp, z, t)?,
7920                        e.matmul_decode_exact(up_shexp, z, t)?,
7921                    ),
7922                }
7923            } else {
7924                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
7925            };
7926            let mut sa = e.uninit(t * n_ff_sh)?;
7927            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
7928            let sh = if verify_t {
7929                e.matmul_decode_exact(down_shexp, &sa, t)?
7930            } else {
7931                e.matmul(down_shexp, &sa, t)?
7932            };
7933            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
7934            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
7935            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
7936            // dispatch choice cannot change bits.
7937            let g = match &m.gate_inp_shexp {
7938                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
7939                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7940                }
7941                Some(gate_inp_shexp) => {
7942                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7943                    let mut g = e.uninit(t)?;
7944                    e.sigmoid(&gs, &mut g, t)?;
7945                    g
7946                }
7947                None => e.htod(&vec![1.0f32; t])?,
7948            };
7949            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7950        }
7951        Ok(moe_out)
7952    }
7953
7954    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
7955    #[allow(clippy::too_many_arguments)]
7956    #[allow(clippy::too_many_arguments)]
7957    fn moe_ffn_dev(
7958        e: &Engine,
7959        m: &MoeWeights,
7960        z: &CudaSlice<f32>,
7961        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7962        logits: &CudaSlice<f32>,
7963        t: usize,
7964        cfg: &ModelConfig,
7965        il: u16,
7966        max_block: usize,
7967    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7968        let moe = cfg.moe.as_ref().unwrap();
7969        let n_embd = cfg.n_embd as usize;
7970        let n_expert = moe.expert_count as usize;
7971        let n_used = moe.expert_used_count as usize;
7972        let n_ff_exp = moe.expert_ff_length as usize;
7973        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
7974        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
7975        // clamped layers; assert both so a future caller that skips the gate fails loudly.
7976        debug_assert!(
7977            cfg.sigmoid_router().is_none(),
7978            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
7979        );
7980        debug_assert!(
7981            !cfg.swiglu_clamped_at(il as u32),
7982            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
7983        );
7984
7985        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
7986        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
7987        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
7988        // skipped entirely for macro-free experts (every k-quant GGUF).
7989        if m.has_macros {
7990            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
7991        }
7992
7993        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
7994        let mut moe_out = e.uninit(t * n_embd)?;
7995
7996        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
7997        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
7998        if let Some(dev) = m.dev_exps.as_ref() {
7999            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
8000            // the combined stride; up's base is offset in the ptr table. Down unchanged.
8001            let (rbg_d, rbu_d) = if dev.gu_il {
8002                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8003                (sxx, sxx)
8004            } else {
8005                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8006            };
8007            let q8 = moe_q8_enabled()
8008                && q8_expert_supported(m.gate_exps.qtype)
8009                && q8_expert_supported(m.up_exps.qtype)
8010                && q8_expert_supported(m.down_exps.qtype);
8011            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
8012            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
8013            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
8014            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8015            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8016            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8017            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8018            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8019            let rows_arm = q8
8020                && t > 1
8021                && crate::spec::spec_m2()
8022                && n_ff_exp == 512
8023                && n_used <= 8
8024                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8025                    .map(|v| v.is_empty() || v == "v")
8026                    .unwrap_or(true)
8027                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8028                    .map(|v| v.is_empty() || v == "w8h2v")
8029                    .unwrap_or(true);
8030            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8031            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8032            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8033            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8034            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8035            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8036            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8037            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8038            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8039                .ok()
8040                .and_then(|v| v.parse::<i32>().ok())
8041                .unwrap_or(1);
8042            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8043            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8044            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8045            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8046            // axis. Three chain-pinning attempts did not close it (receipts,
8047            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8048            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8049            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8050            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8051            // never decode-batch-gate at B=8 on the MoE model itself.
8052            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8053            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8054            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8055            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8056            // de-admission verdict above stands until those gates are GREEN on the MoE
8057            // artifact; this door must never default on.
8058            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8059            let csr_qt = |qt: i32| {
8060                qt == crate::QT_IQ4_XS
8061                    || qt == crate::QT_IQ3_S
8062                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8063            };
8064            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8065            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8066            let csr_arm = rows_arm
8067                && csr_mode > 0
8068                && t <= csr_t_max
8069                && csr_uniform
8070                && csr_qt(m.gate_exps.qtype)
8071                && csr_qt(m.up_exps.qtype)
8072                && csr_qt(m.down_exps.qtype);
8073            if csr_arm {
8074                if csr_mode == 2 {
8075                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8076                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8077                }
8078                let n_pairs = t * n_used;
8079                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8080                let act = e.moe_gate_up_silu8_dev_q8_csr(
8081                    &dev.ptr_row,
8082                    &sel_d,
8083                    &zq,
8084                    &zd,
8085                    n_pairs,
8086                    n_embd,
8087                    n_ff_exp,
8088                    n_used,
8089                    n_expert,
8090                    m.gate_exps.qtype,
8091                    m.up_exps.qtype,
8092                    rbg_d,
8093                    rbu_d,
8094                )?;
8095                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8096                // down stays on the _rows twin — BOTH CSR down variants measured negative
8097                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8098                // 16-group rows have too little decode to amortize any dedup structure.
8099                e.moe_down8_fma_dev_q8_rows(
8100                    &dev.ptr_row,
8101                    &sel_d,
8102                    &w_d,
8103                    &aq2,
8104                    &ad2,
8105                    &mut moe_out,
8106                    t,
8107                    n_ff_exp,
8108                    n_embd,
8109                    n_used,
8110                    n_expert,
8111                    m.down_exps.qtype,
8112                    m.down_exps.row_bytes,
8113                )?;
8114                if csr_mode == 2 {
8115                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8116                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8117                        &dev.ptr_row,
8118                        &sel_d,
8119                        &zq,
8120                        &zd,
8121                        t,
8122                        n_embd,
8123                        n_ff_exp,
8124                        n_used,
8125                        n_expert,
8126                        m.gate_exps.qtype,
8127                        m.up_exps.qtype,
8128                        rbg_d,
8129                        rbu_d,
8130                        &m.dev_macros,
8131                    )?;
8132                    let mut out_r = e.uninit(t * n_embd)?;
8133                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8134                    e.moe_down8_fma_dev_q8_rows(
8135                        &dev.ptr_row,
8136                        &sel_d,
8137                        &w_d,
8138                        &aq2r,
8139                        &ad2r,
8140                        &mut out_r,
8141                        t,
8142                        n_ff_exp,
8143                        n_embd,
8144                        n_used,
8145                        n_expert,
8146                        m.down_exps.qtype,
8147                        m.down_exps.row_bytes,
8148                    )?;
8149                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8150                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8151                    let ba = a1
8152                        .iter()
8153                        .zip(&a2)
8154                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8155                        .count();
8156                    let bo = o1
8157                        .iter()
8158                        .zip(&o2)
8159                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8160                        .count();
8161                    if ba + bo > 0 {
8162                        eprintln!(
8163                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8164                            a1.len(),
8165                            o1.len()
8166                        );
8167                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8168                        let sel_h = e.dtoh_i32(&sel_d)?;
8169                        let mut shown = 0;
8170                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8171                            if x.to_bits() != y.to_bits() && shown < 4 {
8172                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8173                                let ex = sel_h[p];
8174                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8175                                eprintln!(
8176                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8177                                );
8178                                shown += 1;
8179                            }
8180                        }
8181                        std::process::exit(3);
8182                    }
8183                }
8184            } else if rows_arm {
8185                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8186                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8187                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8188                    use std::sync::atomic::{AtomicU64, Ordering};
8189                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8190                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8191                    static CALLS: AtomicU64 = AtomicU64::new(0);
8192                    let sel_h = e.dtoh_i32(&sel_d)?;
8193                    let mut u: Vec<i32> = sel_h.clone();
8194                    u.sort_unstable();
8195                    u.dedup();
8196                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8197                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8198                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8199                    if c % 480 == 0 {
8200                        let p = PAIRS.load(Ordering::Relaxed);
8201                        let q = UNIQ.load(Ordering::Relaxed);
8202                        eprintln!(
8203                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8204                            q as f64 / p as f64
8205                        );
8206                    }
8207                }
8208                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8209                let act = e.moe_gate_up_silu8_dev_q8_rows(
8210                    &dev.ptr_row,
8211                    &sel_d,
8212                    &zq,
8213                    &zd,
8214                    t,
8215                    n_embd,
8216                    n_ff_exp,
8217                    n_used,
8218                    n_expert,
8219                    m.gate_exps.qtype,
8220                    m.up_exps.qtype,
8221                    rbg_d,
8222                    rbu_d,
8223                    &m.dev_macros,
8224                )?;
8225                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8226                e.moe_down8_fma_dev_q8_rows(
8227                    &dev.ptr_row,
8228                    &sel_d,
8229                    &w_d,
8230                    &aq2,
8231                    &ad2,
8232                    &mut moe_out,
8233                    t,
8234                    n_ff_exp,
8235                    n_embd,
8236                    n_used,
8237                    n_expert,
8238                    m.down_exps.qtype,
8239                    m.down_exps.row_bytes,
8240                )?;
8241            } else {
8242                for tok in 0..t {
8243                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8244                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8245                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8246                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8247                    if q8 {
8248                        let (zq, zd) = match (t, zq8) {
8249                            (1, Some((q, d))) => (q.clone(), d.clone()),
8250                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8251                        };
8252                        let act = e.moe_gate_up_silu8_dev_q8(
8253                            &dev.ptr_row,
8254                            &selt,
8255                            &zq,
8256                            &zd,
8257                            n_embd,
8258                            n_ff_exp,
8259                            n_used,
8260                            n_expert,
8261                            m.gate_exps.qtype,
8262                            m.up_exps.qtype,
8263                            rbg_d,
8264                            rbu_d,
8265                            &m.dev_macros,
8266                        )?;
8267                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8268                        e.moe_down8_fma_dev_q8(
8269                            &dev.ptr_row,
8270                            &selt,
8271                            &wt,
8272                            &aq2,
8273                            &ad2,
8274                            &mut dst,
8275                            n_ff_exp,
8276                            n_embd,
8277                            n_used,
8278                            n_expert,
8279                            m.down_exps.qtype,
8280                            m.down_exps.row_bytes,
8281                        )?;
8282                    } else {
8283                        let act = e.moe_gate_up_silu8_dev(
8284                            &dev.ptr_row,
8285                            &selt,
8286                            &zt,
8287                            n_embd,
8288                            n_ff_exp,
8289                            n_used,
8290                            n_expert,
8291                            m.gate_exps.qtype,
8292                            m.up_exps.qtype,
8293                            rbg_d,
8294                            rbu_d,
8295                            &m.dev_macros,
8296                        )?;
8297                        e.moe_down8_fma_dev(
8298                            &dev.ptr_row,
8299                            &selt,
8300                            &wt,
8301                            &act,
8302                            &mut dst,
8303                            n_ff_exp,
8304                            n_embd,
8305                            n_used,
8306                            n_expert,
8307                            m.down_exps.qtype,
8308                            m.down_exps.row_bytes,
8309                        )?;
8310                    }
8311                }
8312            }
8313        } else {
8314            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8315            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8316            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8317            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8318            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8319            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8320            let q8 = moe_q8_enabled()
8321                && q8_expert_supported(m.gate_exps.qtype)
8322                && q8_expert_supported(m.up_exps.qtype)
8323                && q8_expert_supported(m.down_exps.qtype);
8324            e.with_moe_cache(max_block, |c, eng| {
8325                let row = c
8326                    .layer_dev_row(il, n_expert, eng)?
8327                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8328                for tok in 0..t {
8329                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8330                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8331                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8332                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8333                    if q8 {
8334                        let (zq, zd) = match (t, zq8) {
8335                            (1, Some((q, d))) => (q.clone(), d.clone()),
8336                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8337                        };
8338                        let act = eng.moe_gate_up_silu8_dev_q8(
8339                            row,
8340                            &selt,
8341                            &zq,
8342                            &zd,
8343                            n_embd,
8344                            n_ff_exp,
8345                            n_used,
8346                            n_expert,
8347                            m.gate_exps.qtype,
8348                            m.up_exps.qtype,
8349                            m.gate_exps.row_bytes,
8350                            m.up_exps.row_bytes,
8351                            &m.dev_macros,
8352                        )?;
8353                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8354                        eng.moe_down8_fma_dev_q8(
8355                            row,
8356                            &selt,
8357                            &wt,
8358                            &aq2,
8359                            &ad2,
8360                            &mut dst,
8361                            n_ff_exp,
8362                            n_embd,
8363                            n_used,
8364                            n_expert,
8365                            m.down_exps.qtype,
8366                            m.down_exps.row_bytes,
8367                        )?;
8368                    } else {
8369                        let act = eng.moe_gate_up_silu8_dev(
8370                            row,
8371                            &selt,
8372                            &zt,
8373                            n_embd,
8374                            n_ff_exp,
8375                            n_used,
8376                            n_expert,
8377                            m.gate_exps.qtype,
8378                            m.up_exps.qtype,
8379                            m.gate_exps.row_bytes,
8380                            m.up_exps.row_bytes,
8381                            &m.dev_macros,
8382                        )?;
8383                        eng.moe_down8_fma_dev(
8384                            row,
8385                            &selt,
8386                            &wt,
8387                            &act,
8388                            &mut dst,
8389                            n_ff_exp,
8390                            n_embd,
8391                            n_used,
8392                            n_expert,
8393                            m.down_exps.qtype,
8394                            m.down_exps.row_bytes,
8395                        )?;
8396                    }
8397                }
8398                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8399                c.hits += (t * 3 * n_used) as u64;
8400                Ok(())
8401            })?;
8402        }
8403
8404        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8405        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8406        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8407        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8408        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8409            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8410        {
8411            let n_ff_sh = gate_shexp.out_features();
8412            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8413            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8414            let verify_t = t > 1 && t < PRIME_MIN_T;
8415            let (sg_gate, sg_up) = if t == 1 {
8416                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8417            } else if verify_t {
8418                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8419                // rides one shared quantize + one fused2 batched launch instead of two
8420                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8421                let mut fused = None;
8422                if crate::spec::spec_fused_t()
8423                    && (2..=4).contains(&t)
8424                    && e.uses_q8_1_fast(gate_shexp)
8425                    && e.uses_q8_1_fast(up_shexp)
8426                {
8427                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8428                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8429                }
8430                match fused {
8431                    Some(pair) => pair,
8432                    None => (
8433                        e.matmul_decode_exact(gate_shexp, z, t)?,
8434                        e.matmul_decode_exact(up_shexp, z, t)?,
8435                    ),
8436                }
8437            } else {
8438                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8439            };
8440            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8441            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8442            let sh = if verify_t {
8443                e.matmul_decode_exact(down_shexp, &sa, t)?
8444            } else {
8445                e.matmul(down_shexp, &sa, t)?
8446            };
8447            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8448            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8449            // between the two arms; prefill keeps the batched cuBLASLt linear).
8450            let g = match &m.gate_inp_shexp {
8451                Some(gate_inp_shexp) => {
8452                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8453                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8454                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8455                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8456                    } else {
8457                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8458                        let mut g = e.uninit(t)?;
8459                        e.sigmoid(&gs, &mut g, t)?;
8460                        g
8461                    }
8462                }
8463                None => e.htod(&vec![1.0f32; t])?,
8464            };
8465            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8466        }
8467
8468        Ok(moe_out)
8469    }
8470
8471    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8472    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8473    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8474    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8475    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8476    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8477    /// the collected raw pointers cannot move between collection and launch (single-threaded
8478    /// decode; the lock is held only for collection, launches are stream-ordered after any
8479    /// prior same-stream staging writes).
8480    #[allow(clippy::too_many_arguments)]
8481    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8482    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8483    #[allow(clippy::too_many_arguments)]
8484    fn moe_gdec_token_q8(
8485        e: &Engine,
8486        m: &MoeWeights,
8487        il: u16,
8488        max_block: usize,
8489        zq: &CudaSlice<i8>,
8490        zd: &CudaSlice<f32>,
8491        sel: &[u32],
8492        w: &[f32],
8493        moe_out: &mut CudaSlice<f32>,
8494        tok: usize,
8495        n_embd: usize,
8496        n_ff_exp: usize,
8497        n_used: usize,
8498    ) -> Result<bool, Box<dyn std::error::Error>> {
8499        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8500        use cudarc::driver::DevicePtr;
8501        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8502            let mut g = [0u64; 8];
8503            let mut u = [0u64; 8];
8504            let mut d = [0u64; 8];
8505            for (j, &ex) in sel.iter().enumerate() {
8506                let ex = ex as u16;
8507                let (Some(sg), Some(su), Some(sd)) = (
8508                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8509                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8510                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8511                ) else {
8512                    return Ok(None);
8513                };
8514                let __s = eng.stream();
8515                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8516                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8517                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8518                g[j] = pg as u64;
8519                u[j] = pu as u64;
8520                d[j] = pd as u64;
8521            }
8522            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8523                for &ex in sel {
8524                    let ex = ex as u16;
8525                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8526                        c.note_profile_hit(BlockId::new(il, proj, ex));
8527                    }
8528                }
8529            }
8530            c.hits += (3 * n_used) as u64;
8531            Ok(Some((g, u, d)))
8532        })?;
8533        let Some((g, u, d)) = ptrs else {
8534            return Ok(false);
8535        };
8536        let mut wv = [0f32; 8];
8537        wv[..n_used].copy_from_slice(w);
8538        let act = e.moe_gate_up_silu8_q8(
8539            crate::WPtr8(g),
8540            crate::WPtr8(u),
8541            zq,
8542            zd,
8543            n_embd,
8544            n_ff_exp,
8545            n_used,
8546            m.gate_exps.qtype,
8547            m.up_exps.qtype,
8548            m.gate_exps.row_bytes,
8549            m.up_exps.row_bytes,
8550        )?;
8551        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8552        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8553        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8554        e.moe_down8_fma_q8(
8555            crate::WPtr8(d),
8556            crate::F32x8(wv),
8557            &aq2,
8558            &ad2,
8559            &mut dst,
8560            n_ff_exp,
8561            n_embd,
8562            n_used,
8563            m.down_exps.qtype,
8564            m.down_exps.row_bytes,
8565        )?;
8566        Ok(true)
8567    }
8568
8569    fn moe_gdec_token(
8570        e: &Engine,
8571        m: &MoeWeights,
8572        il: u16,
8573        max_block: usize,
8574        zt: &cudarc::driver::CudaView<f32>,
8575        sel: &[u32],
8576        w: &[f32],
8577        moe_out: &mut CudaSlice<f32>,
8578        tok: usize,
8579        n_embd: usize,
8580        n_ff_exp: usize,
8581        n_used: usize,
8582    ) -> Result<bool, Box<dyn std::error::Error>> {
8583        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8584        use cudarc::driver::DevicePtr;
8585        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8586        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8587            let mut g = [0u64; 8];
8588            let mut u = [0u64; 8];
8589            let mut d = [0u64; 8];
8590            for (j, &ex) in sel.iter().enumerate() {
8591                let ex = ex as u16;
8592                let (Some(sg), Some(su), Some(sd)) = (
8593                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8594                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8595                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8596                ) else {
8597                    return Ok(None);
8598                };
8599                let __s = eng.stream();
8600                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8601                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8602                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8603                g[j] = pg as u64;
8604                u[j] = pu as u64;
8605                d[j] = pd as u64;
8606            }
8607            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8608                for &ex in sel {
8609                    let ex = ex as u16;
8610                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8611                        c.note_profile_hit(BlockId::new(il, proj, ex));
8612                    }
8613                }
8614            }
8615            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
8616            Ok(Some((g, u, d)))
8617        })?;
8618        let Some((g, u, d)) = ptrs else {
8619            return Ok(false);
8620        };
8621        let mut wv = [0f32; 8];
8622        wv[..n_used].copy_from_slice(w);
8623        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
8624        let act = e.moe_gate_up_silu8(
8625            crate::WPtr8(g),
8626            crate::WPtr8(u),
8627            zt,
8628            n_embd,
8629            n_ff_exp,
8630            n_used,
8631            m.gate_exps.qtype,
8632            m.up_exps.qtype,
8633            m.gate_exps.row_bytes,
8634            m.up_exps.row_bytes,
8635        )?;
8636        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8637        e.moe_down8_fma_into(
8638            crate::WPtr8(d),
8639            crate::F32x8(wv),
8640            &act,
8641            &mut dst,
8642            n_ff_exp,
8643            n_embd,
8644            n_used,
8645            m.down_exps.qtype,
8646            m.down_exps.row_bytes,
8647        )?;
8648        Ok(true)
8649    }
8650
8651    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
8652    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
8653    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
8654    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
8655    fn moe_cached_gemm_q8(
8656        e: &Engine,
8657        il: u16,
8658        proj: u8,
8659        ex: usize,
8660        m: &MoeWeights,
8661        max_block: usize,
8662        aq: &CudaSlice<i8>,
8663        ad: &CudaSlice<f32>,
8664    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8665        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8666        let exps = match proj {
8667            PROJ_GATE => &m.gate_exps,
8668            PROJ_UP => &m.up_exps,
8669            _ => &m.down_exps,
8670        };
8671        let layout = exps.expert_layout(ex);
8672        let id = BlockId::new(il, proj, ex as u16);
8673        let source = exps.expert_source(ex);
8674        e.with_moe_cache(max_block, |c, eng| {
8675            let slot = c.dispatch_source(id, source, eng)?;
8676            let DispatchSlot::Resident(sl) = slot;
8677            let buf = c.slot(sl);
8678            eng.qmatvec_expert_q8(
8679                buf,
8680                0..layout.len,
8681                aq,
8682                ad,
8683                1,
8684                exps.in_f,
8685                exps.out_f,
8686                layout.qtype,
8687                layout.row_bytes,
8688            )
8689        })
8690    }
8691
8692    fn moe_cached_gemm(
8693        e: &Engine,
8694        il: u16,
8695        proj: u8,
8696        ex: usize,
8697        m: &MoeWeights,
8698        max_block: usize,
8699        x: &cudarc::driver::CudaView<f32>,
8700    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8701        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
8702        let exps = match proj {
8703            PROJ_GATE => &m.gate_exps,
8704            PROJ_UP => &m.up_exps,
8705            _ => &m.down_exps,
8706        };
8707        let layout = exps.expert_layout(ex);
8708        let id = BlockId::new(il, proj, ex as u16);
8709        let source = exps.expert_source(ex);
8710        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
8711        e.with_moe_cache(max_block, |c, eng| {
8712            let slot = c.dispatch_source(id, source, eng)?;
8713            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
8714            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
8715            let DispatchSlot::Resident(sl) = slot;
8716            let buf = c.slot(sl);
8717            eng.qmatvec_view(
8718                buf,
8719                0..layout.len,
8720                x,
8721                1,
8722                exps.in_f,
8723                exps.out_f,
8724                layout.qtype,
8725                layout.row_bytes,
8726            )
8727        })
8728    }
8729
8730    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
8731    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
8732    /// so the current forward's backend assignment and output remain unchanged.
8733    fn moe_profile_admit_expert(
8734        e: &Engine,
8735        il: u16,
8736        ex: usize,
8737        m: &MoeWeights,
8738        max_block: usize,
8739    ) -> Result<(), Box<dyn std::error::Error>> {
8740        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8741        e.with_moe_cache(max_block, |cache, eng| {
8742            for (proj, exps) in [
8743                (PROJ_GATE, &m.gate_exps),
8744                (PROJ_UP, &m.up_exps),
8745                (PROJ_DOWN, &m.down_exps),
8746            ] {
8747                let id = BlockId::new(il, proj, ex as u16);
8748                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
8749            }
8750            Ok(())
8751        })
8752    }
8753
8754    /// Read a projection from the immutable residency set when present; otherwise use one
8755    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
8756    #[allow(clippy::too_many_arguments)]
8757    fn moe_frozen_gemm(
8758        e: &Engine,
8759        il: u16,
8760        proj: u8,
8761        ex: usize,
8762        m: &MoeWeights,
8763        max_block: usize,
8764        x: &cudarc::driver::CudaView<f32>,
8765        scratch: &mut Option<CudaSlice<u8>>,
8766        scratch_len: usize,
8767    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8768        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
8769        let exps = match proj {
8770            PROJ_GATE => &m.gate_exps,
8771            PROJ_UP => &m.up_exps,
8772            _ => &m.down_exps,
8773        };
8774        let layout = exps.expert_layout(ex);
8775        let id = BlockId::new(il, proj, ex as u16);
8776        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
8777            let Some(slot) = cache.resident(id) else {
8778                return Ok(None);
8779            };
8780            let buf = cache.slot(slot);
8781            Ok(Some(eng.qmatvec_view(
8782                buf,
8783                0..layout.len,
8784                x,
8785                1,
8786                exps.in_f,
8787                exps.out_f,
8788                layout.qtype,
8789                layout.row_bytes,
8790            )?))
8791        })? {
8792            return Ok(output);
8793        }
8794        if scratch.is_none() {
8795            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
8796        }
8797        let scratch = scratch.as_mut().unwrap();
8798        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
8799        e.qmatvec_view(
8800            scratch,
8801            0..layout.len,
8802            x,
8803            1,
8804            exps.in_f,
8805            exps.out_f,
8806            layout.qtype,
8807            layout.row_bytes,
8808        )
8809    }
8810
8811    fn moe_prefetch_expert(
8812        e: &Engine,
8813        il: u16,
8814        ex: usize,
8815        m: &MoeWeights,
8816        max_block: usize,
8817        keep: &[crate::moe_cache::BlockId],
8818    ) -> Result<(), Box<dyn std::error::Error>> {
8819        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8820        e.with_moe_cache(max_block, |c, eng| {
8821            for (proj, exps) in [
8822                (PROJ_GATE, &m.gate_exps),
8823                (PROJ_UP, &m.up_exps),
8824                (PROJ_DOWN, &m.down_exps),
8825            ] {
8826                let id = BlockId::new(il, proj, ex as u16);
8827                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
8828            }
8829            Ok(())
8830        })
8831    }
8832
8833    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
8834    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
8835    fn moe_prefetch_disk_expert(
8836        e: &Engine,
8837        il: u16,
8838        ex: usize,
8839        m: &MoeWeights,
8840        max_block: usize,
8841        keep: &[crate::moe_cache::BlockId],
8842    ) -> Result<(), Box<dyn std::error::Error>> {
8843        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8844        e.with_moe_cache(max_block, |c, eng| {
8845            for (proj, exps) in [
8846                (PROJ_GATE, &m.gate_exps),
8847                (PROJ_UP, &m.up_exps),
8848                (PROJ_DOWN, &m.down_exps),
8849            ] {
8850                let source = exps.expert_source(ex);
8851                if let crate::model::ExpertSource::Disk { .. } = &source {
8852                    let id = BlockId::new(il, proj, ex as u16);
8853                    let _ = c.prefetch_source(id, source, keep, eng)?;
8854                }
8855            }
8856            Ok(())
8857        })
8858    }
8859
8860    #[inline]
8861    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
8862        let _ = m.gate_exps.prefetch_expert_pages(ex);
8863        let _ = m.up_exps.prefetch_expert_pages(ex);
8864        let _ = m.down_exps.prefetch_expert_pages(ex);
8865    }
8866}
8867
8868// ================================================================================================
8869// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
8870//
8871// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
8872// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
8873// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
8874//
8875// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
8876// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
8877// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
8878// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
8879// identical to the per-token loop regardless of expert processing order.
8880//
8881// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
8882// ================================================================================================
8883
8884impl HybridModel {
8885    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
8886    /// sequential fused q8 program over the token axis; clamped layers use the separate
8887    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
8888    #[allow(clippy::too_many_arguments)]
8889    fn moe_ffn_grouped_resident_q8(
8890        e: &Engine,
8891        m: &MoeWeights,
8892        z: &CudaSlice<f32>,
8893        t: usize,
8894        cfg: &ModelConfig,
8895        il: u16,
8896        sel_all: &[u32],
8897        w_all: &[f32],
8898        table: &CudaSlice<u64>,
8899        gu_il: bool,
8900    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8901        let moe = cfg.moe.as_ref().unwrap();
8902        let n_embd = cfg.n_embd as usize;
8903        let n_expert = moe.expert_count as usize;
8904        let n_used = moe.expert_used_count as usize;
8905        let n_ff_exp = moe.expert_ff_length as usize;
8906        let n_pairs = t * n_used;
8907        debug_assert_eq!(sel_all.len(), n_pairs);
8908        debug_assert_eq!(w_all.len(), n_pairs);
8909        debug_assert!(
8910            m.gate_exps.macros.is_none()
8911                && m.up_exps.macros.is_none()
8912                && m.down_exps.macros.is_none(),
8913            "resident grouped q8 does not fold per-expert macro scales",
8914        );
8915
8916        // The rows twins run the resident sequential program verbatim on grid.z = token:
8917        // fused gate/up/SiLU per slot, batched activation quantization, then the original
8918        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
8919        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
8920        // never enter the softmax router.
8921        if !cfg.swiglu_clamped_at(il as u32) {
8922            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8923            let sel_d = e.htod_i32(&sel)?;
8924            let w_d = e.htod(w_all)?;
8925            let (gate_row_bytes, up_row_bytes) = if gu_il {
8926                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8927                (combined, combined)
8928            } else {
8929                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8930            };
8931            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8932            let act = e.moe_gate_up_silu8_dev_q8_rows(
8933                table,
8934                &sel_d,
8935                &zq,
8936                &zd,
8937                t,
8938                n_embd,
8939                n_ff_exp,
8940                n_used,
8941                n_expert,
8942                m.gate_exps.qtype,
8943                m.up_exps.qtype,
8944                gate_row_bytes,
8945                up_row_bytes,
8946                &m.dev_macros,
8947            )?;
8948            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8949            let mut moe_out = e.uninit(t * n_embd)?;
8950            e.moe_down8_fma_dev_q8_rows_g(
8951                table,
8952                &sel_d,
8953                &w_d,
8954                &aq2,
8955                &ad2,
8956                &mut moe_out,
8957                t,
8958                n_ff_exp,
8959                n_embd,
8960                n_used,
8961                n_expert,
8962                m.down_exps.qtype,
8963                m.down_exps.row_bytes,
8964            )?;
8965
8966            if std::env::var("MEMRA_MOE_STATS").is_ok() {
8967                let mut counts = vec![0usize; n_expert];
8968                for &expert in sel_all {
8969                    counts[expert as usize] += 1;
8970                }
8971                let mut sizes: Vec<usize> =
8972                    counts.into_iter().filter(|&count| count != 0).collect();
8973                sizes.sort_unstable();
8974                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
8975                println!(
8976                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
8977                     m_e: min={} median={} mean={mean:.1} max={}",
8978                    sizes.len(),
8979                    n_expert,
8980                    sizes.first().copied().unwrap_or(0),
8981                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
8982                    sizes.last().copied().unwrap_or(0),
8983                );
8984            }
8985            return Ok(moe_out);
8986        }
8987
8988        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
8989        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
8990        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
8991        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
8992        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
8993        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
8994        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
8995
8996        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
8997        for (pair, &expert) in pair_ex.iter().enumerate() {
8998            by_expert[expert as usize].push(pair as i32);
8999        }
9000
9001        let pair_tok_d = e.htod_i32(&pair_tok)?;
9002        let pair_ex_d = e.htod_i32(&pair_ex)?;
9003        let pair_w_d = e.htod(w_all)?;
9004        let tok_off_d = e.htod_i32(&tok_off)?;
9005        let tok_ids_d = e.htod_i32(&tok_ids)?;
9006
9007        let matvec = |proj: i32,
9008                      pair_rows: &CudaSlice<i32>,
9009                      aq: &CudaSlice<i8>,
9010                      ad: &CudaSlice<f32>,
9011                      in_f: usize,
9012                      out_f: usize,
9013                      qtype: i32,
9014                      row_bytes: usize|
9015         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9016            e.moe_pairs_matvec_q8(
9017                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9018                row_bytes,
9019            )
9020        };
9021
9022        let (gate_row_bytes, up_row_bytes) = if gu_il {
9023            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9024            (combined, combined)
9025        } else {
9026            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9027        };
9028        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9029        let gate = matvec(
9030            0,
9031            &pair_tok_d,
9032            &zq,
9033            &zd,
9034            n_embd,
9035            n_ff_exp,
9036            m.gate_exps.qtype,
9037            gate_row_bytes,
9038        )?;
9039        let up = matvec(
9040            1,
9041            &pair_tok_d,
9042            &zq,
9043            &zd,
9044            n_embd,
9045            n_ff_exp,
9046            m.up_exps.qtype,
9047            up_row_bytes,
9048        )?;
9049        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9050        Self::ffn_act_lim(
9051            e,
9052            cfg,
9053            &gate,
9054            &up,
9055            1.0,
9056            1.0,
9057            cfg.clamp_exp_at(il as u32),
9058            &mut act,
9059            n_pairs * n_ff_exp,
9060        )?;
9061        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9062        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9063        let pair_self_d = e.htod_i32(&pair_self)?;
9064        let down = matvec(
9065            2,
9066            &pair_self_d,
9067            &aq2,
9068            &ad2,
9069            n_ff_exp,
9070            n_embd,
9071            m.down_exps.qtype,
9072            m.down_exps.row_bytes,
9073        )?;
9074        let mut moe_out = e.uninit(t * n_embd)?;
9075        e.moe_pairs_scatter(
9076            &down,
9077            &pair_w_d,
9078            &tok_off_d,
9079            &tok_ids_d,
9080            &mut moe_out,
9081            t,
9082            n_embd,
9083        )?;
9084
9085        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9086            let mut sizes: Vec<usize> = by_expert
9087                .iter()
9088                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9089                .collect();
9090            sizes.sort_unstable();
9091            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9092            println!(
9093                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9094                 m_e: min={} median={} mean={mean:.1} max={}",
9095                sizes.len(),
9096                n_expert,
9097                sizes.first().copied().unwrap_or(0),
9098                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9099                sizes.last().copied().unwrap_or(0),
9100            );
9101        }
9102        Ok(moe_out)
9103    }
9104
9105    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9106    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9107    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9108    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9109    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9110    #[allow(clippy::too_many_arguments)]
9111    fn shexp_split_matvec(
9112        e: &Engine,
9113        rank1: &Engine,
9114        wg: &CudaSlice<u8>,
9115        wu: &CudaSlice<u8>,
9116        wd: &CudaSlice<u8>,
9117        z: &CudaSlice<f32>,
9118        lim: Option<f32>,
9119        cfg: &ModelConfig,
9120        il: u16,
9121        n_embd: usize,
9122        n_ff_sh: usize,
9123    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9124        use cudarc::driver::DevicePtr;
9125        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9126            return Ok(None);
9127        }
9128        let hf = n_ff_sh / 2;
9129        let nd = n_embd / 2;
9130        struct Rep {
9131            wg1: CudaSlice<u8>,
9132            wu1: CudaSlice<u8>,
9133            wd1: CudaSlice<u8>,
9134        }
9135        struct SplitWs {
9136            pin_dev: usize,
9137            // e side
9138            gate0: CudaSlice<f32>,
9139            up0: CudaSlice<f32>,
9140            act: CudaSlice<f32>,
9141            sh_buf: CudaSlice<f32>,
9142            ev_z: cudarc::driver::CudaEvent,
9143            ev_act0: cudarc::driver::CudaEvent,
9144            // rank1 side
9145            z1: CudaSlice<f32>,
9146            g1: CudaSlice<f32>,
9147            u1: CudaSlice<f32>,
9148            a1h: CudaSlice<f32>,
9149            act1: CudaSlice<f32>,
9150            y1: CudaSlice<f32>,
9151            ev_act1: cudarc::driver::CudaEvent,
9152            ev_y1: cudarc::driver::CudaEvent,
9153            raw_act_e: u64,
9154            raw_sh_e: u64,
9155            raw_z1: u64,
9156            raw_a1h: u64,
9157            raw_act1: u64,
9158            raw_y1: u64,
9159        }
9160        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9161        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9162            std::sync::Mutex::new(None);
9163        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9164        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9165        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9166        let pins = e.ctx().ordinal();
9167        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9168            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9169                let _m = e.gpu.enter_main()?;
9170                (
9171                    e.htod(&vec![0.0f32; hf])?,
9172                    e.htod(&vec![0.0f32; hf])?,
9173                    e.htod(&vec![0.0f32; n_ff_sh])?,
9174                    e.htod(&vec![0.0f32; n_embd])?,
9175                    e.ctx().new_event(None)?,
9176                    e.ctx().new_event(None)?,
9177                )
9178            };
9179            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9180                let _r = rank1.gpu.enter_main()?;
9181                (
9182                    rank1.htod(&vec![0.0f32; n_embd])?,
9183                    rank1.htod(&vec![0.0f32; hf])?,
9184                    rank1.htod(&vec![0.0f32; hf])?,
9185                    rank1.htod(&vec![0.0f32; hf])?,
9186                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9187                    rank1.htod(&vec![0.0f32; nd])?,
9188                    rank1.ctx().new_event(None)?,
9189                    rank1.ctx().new_event(None)?,
9190                )
9191            };
9192            let (raw_act_e, raw_sh_e) = {
9193                let _m = e.gpu.enter_main()?;
9194                let stream = e.stream();
9195                let (a, _g0) = act.device_ptr(&stream);
9196                let (b, _g1) = sh_buf.device_ptr(&stream);
9197                (a as u64, b as u64)
9198            };
9199            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9200                let _r = rank1.gpu.enter_main()?;
9201                let rs = rank1.stream();
9202                let (a, _g0) = z1.device_ptr(&rs);
9203                let (b, _g1) = a1h.device_ptr(&rs);
9204                let (c, _g2) = act1.device_ptr(&rs);
9205                let (d, _g3) = y1.device_ptr(&rs);
9206                (a as u64, b as u64, c as u64, d as u64)
9207            };
9208            *guard = Some(SplitWs {
9209                pin_dev: pins,
9210                gate0,
9211                up0,
9212                act,
9213                sh_buf,
9214                ev_z,
9215                ev_act0,
9216                z1,
9217                g1,
9218                u1,
9219                a1h,
9220                act1,
9221                y1,
9222                ev_act1,
9223                ev_y1,
9224                raw_act_e,
9225                raw_sh_e,
9226                raw_z1,
9227                raw_a1h,
9228                raw_act1,
9229                raw_y1,
9230            });
9231        }
9232        let ws = guard.as_mut().expect("armed above");
9233        let wg_pin = {
9234            let _m = e.gpu.enter_main()?;
9235            let stream = e.stream();
9236            let (p, _g) = wg.device_ptr(&stream);
9237            p as u64
9238        };
9239        if !reps.contains_key(&wg_pin) {
9240            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9241            let mut up = |src: &CudaSlice<u8>,
9242                          off_bytes: usize,
9243                          len: usize|
9244             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9245                use cudarc::driver::sys;
9246                let sptr = {
9247                    let _m = e.gpu.enter_main()?;
9248                    let stream = e.stream();
9249                    let (p, _g) = src.device_ptr(&stream);
9250                    p as u64 + off_bytes as u64
9251                };
9252                let dst = {
9253                    let _r = rank1.gpu.enter_main()?;
9254                    rank1.alloc_u8_uninit(len)?
9255                };
9256                let dptr = {
9257                    let _r = rank1.gpu.enter_main()?;
9258                    let rs = rank1.stream();
9259                    let (p, _g) = dst.device_ptr(&rs);
9260                    p as u64
9261                };
9262                let _r = rank1.gpu.enter_main()?;
9263                let r = unsafe {
9264                    sys::cuMemcpyAsync(
9265                        dptr as sys::CUdeviceptr,
9266                        sptr as sys::CUdeviceptr,
9267                        len,
9268                        rank1.stream().cu_stream() as sys::CUstream,
9269                    )
9270                };
9271                if r != sys::CUresult::CUDA_SUCCESS {
9272                    return Err(format!("shexp split replica upload: {r:?}").into());
9273                }
9274                rank1.stream().synchronize()?;
9275                Ok(dst)
9276            };
9277            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9278            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9279            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9280            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9281        }
9282        let _ = il;
9283        // Per token, evented split flow.
9284        let raw_z = {
9285            let _m = e.gpu.enter_main()?;
9286            let stream = e.stream();
9287            let (p, _g) = z.device_ptr(&stream);
9288            ws.ev_z.record(&stream)?;
9289            p as u64
9290        };
9291        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9292        {
9293            let rep = reps.get(&wg_pin).expect("uploaded above");
9294            let _r = rank1.gpu.enter_main()?;
9295            rank1.stream().wait(&ws.ev_z)?;
9296            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9297            let SplitWs {
9298                z1, g1, u1, a1h, ..
9299            } = &mut *ws;
9300            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9301            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9302            // local place into act1[hf..] + P2P push into e's act[hf..]
9303            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9304            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9305            ws.ev_act1.record(&rank1.stream())?;
9306        }
9307        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9308        {
9309            let _m = e.gpu.enter_main()?;
9310            let SplitWs {
9311                gate0, up0, act, ..
9312            } = &mut *ws;
9313            let wg_lo = wg.slice(0..hf * n_embd * 2);
9314            let wu_lo = wu.slice(0..hf * n_embd * 2);
9315            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9316            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9317            ws.ev_act0.record(&e.stream())?;
9318        }
9319        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9320        {
9321            let rep = reps.get(&wg_pin).expect("uploaded above");
9322            let _r = rank1.gpu.enter_main()?;
9323            rank1.stream().wait(&ws.ev_act0)?;
9324            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9325            let SplitWs { act1, y1, .. } = &mut *ws;
9326            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9327            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9328            ws.ev_y1.record(&rank1.stream())?;
9329        }
9330        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9331        {
9332            let _m = e.gpu.enter_main()?;
9333            e.stream().wait(&ws.ev_act1)?;
9334            let SplitWs { act, sh_buf, .. } = &mut *ws;
9335            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9336            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9337            e.stream().wait(&ws.ev_y1)?;
9338            let mut sh = e.uninit(n_embd)?;
9339            {
9340                let mut dst = sh.slice_mut(0..n_embd);
9341                e.stream()
9342                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9343            }
9344            Ok(Some(sh))
9345        }
9346    }
9347
9348    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9349    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9350    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9351    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9352    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9353    /// the join with the exact add_scaled_rows expression: values unchanged.
9354    fn shexp_overlap_issue(
9355        e: &Engine,
9356        m: &MoeWeights,
9357        z: &CudaSlice<f32>,
9358        cfg: &ModelConfig,
9359        il: u16,
9360        n_embd: usize,
9361    ) -> Result<bool, Box<dyn std::error::Error>> {
9362        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9363            return Ok(false);
9364        }
9365        let (
9366            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9367            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9368            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9369        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9370        else {
9371            return Ok(false);
9372        };
9373        let n_ff_sh = m
9374            .gate_shexp
9375            .as_ref()
9376            .expect("matched Some above")
9377            .out_features();
9378        let lim = cfg.clamp_shexp_at(il as u32);
9379        let mut guard = SHEXP_OV_WS
9380            .lock()
9381            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9382        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9383        if guard
9384            .as_ref()
9385            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9386        {
9387            *guard = Some((
9388                pins.0,
9389                pins.1,
9390                pins.2,
9391                e.uninit(n_ff_sh)?,
9392                e.uninit(n_embd)?,
9393            ));
9394        }
9395        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9396        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9397        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9398        drop(guard);
9399        Ok(true)
9400    }
9401
9402    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9403    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9404    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9405    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9406    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9407    #[allow(clippy::too_many_arguments)]
9408    fn shexp_dev1_issue(
9409        e: &Engine,
9410        rank1: &Engine,
9411        m: &MoeWeights,
9412        z: &CudaSlice<f32>,
9413        cfg: &ModelConfig,
9414        il: u16,
9415        n_embd: usize,
9416    ) -> Result<bool, Box<dyn std::error::Error>> {
9417        use cudarc::driver::DevicePtr;
9418        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9419            return Ok(false);
9420        }
9421        let (
9422            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9423            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9424            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9425        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9426        else {
9427            return Ok(false);
9428        };
9429        let n_ff_sh = m
9430            .gate_shexp
9431            .as_ref()
9432            .expect("matched Some above")
9433            .out_features();
9434        let lim = cfg.clamp_shexp_at(il as u32);
9435        // Shared scratch, geometry-keyed.
9436        let mut ws_guard = SHEXP_D1_WS
9437            .lock()
9438            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9439        if ws_guard
9440            .as_ref()
9441            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9442        {
9443            let (act1, z1, ev_done) = {
9444                let _r1 = rank1.gpu.enter_main()?;
9445                (
9446                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9447                    rank1.htod(&vec![0.0f32; n_embd])?,
9448                    rank1.ctx().new_event(None)?,
9449                )
9450            };
9451            let (sh_root, ev_z) = {
9452                let _main = e.gpu.enter_main()?;
9453                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9454            };
9455            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9456        }
9457        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9458        let mut reps_guard = SHEXP_D1_REPS
9459            .lock()
9460            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9461        let reps = reps_guard.get_or_insert_with(Default::default);
9462        if !reps.contains_key(&il) {
9463            let (wg1, wu1, wd1) = {
9464                let _r1 = rank1.gpu.enter_main()?;
9465                (
9466                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9467                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9468                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9469                )
9470            };
9471            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9472                let s_ptr = {
9473                    let _main = e.gpu.enter_main()?;
9474                    let stream = e.stream();
9475                    let (p, _g) = src.device_ptr(&stream);
9476                    p as u64
9477                };
9478                let d_ptr = {
9479                    let _r1 = rank1.gpu.enter_main()?;
9480                    let stream = rank1.stream();
9481                    let (p, _g) = dst.device_ptr(&stream);
9482                    p as u64
9483                };
9484                let _r1 = rank1.gpu.enter_main()?;
9485                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9486            }
9487            {
9488                let _r1 = rank1.gpu.enter_main()?;
9489                rank1.stream().synchronize()?;
9490            }
9491            reps.insert(il, (wg1, wu1, wd1));
9492        }
9493        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9494        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9495        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9496        // row root-side (single store pass), rings ev_done.
9497        let (raw_z, raw_sh) = {
9498            let _main = e.gpu.enter_main()?;
9499            let stream = e.stream();
9500            let (a, _g0) = z.device_ptr(&stream);
9501            let (b, _g1) = sh_root.device_ptr(&stream);
9502            ev_z.record(&stream)?;
9503            (a as u64, b as u64)
9504        };
9505        {
9506            let _r1 = rank1.gpu.enter_main()?;
9507            rank1.stream().wait(ev_z)?;
9508            let raw_z1 = {
9509                let stream = rank1.stream();
9510                let (p, _g) = z1.device_ptr(&stream);
9511                p as u64
9512            };
9513            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9514            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9515            // down writes the ROOT-resident row over P2P via the raw-output twin of
9516            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9517            // cross-device, so launch on the raw pointer.
9518            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9519            ev_done.record(&rank1.stream())?;
9520        }
9521        Ok(true)
9522    }
9523
9524    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9525    fn shexp_dev1_apply(
9526        e: &Engine,
9527        output: &mut CudaSlice<f32>,
9528        n_embd: usize,
9529    ) -> Result<(), Box<dyn std::error::Error>> {
9530        let guard = SHEXP_D1_WS
9531            .lock()
9532            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9533        let (pin, _, _, sh_root, _, ev_done) =
9534            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9535        if pin.0 != n_embd {
9536            return Err("shexp dev1 width drifted".into());
9537        }
9538        let _main = e.gpu.enter_main()?;
9539        e.stream().wait(ev_done)?;
9540        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9541            std::sync::Mutex::new(None);
9542        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9543        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9544            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9545        }
9546        let ones = &og.as_ref().expect("armed above").1;
9547        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9548        Ok(())
9549    }
9550
9551    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9552    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9553    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9554    fn shexp_overlap_tail_ptrs(
9555        e: &Engine,
9556        m: &MoeWeights,
9557        cfg: &ModelConfig,
9558        n_embd: usize,
9559    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9560        use cudarc::driver::DevicePtr;
9561        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9562            return Ok(None);
9563        }
9564        let (
9565            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9566            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9567            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9568        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9569        else {
9570            return Ok(None);
9571        };
9572        let n_ff_sh = m
9573            .gate_shexp
9574            .as_ref()
9575            .expect("matched Some above")
9576            .out_features();
9577        let mut guard = SHEXP_OV_WS
9578            .lock()
9579            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9580        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9581        if guard
9582            .as_ref()
9583            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9584        {
9585            *guard = Some((
9586                pins.0,
9587                pins.1,
9588                pins.2,
9589                e.uninit(n_ff_sh)?,
9590                e.uninit(n_embd)?,
9591            ));
9592        }
9593        let sh_raw = {
9594            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9595            let stream = e.stream();
9596            let (p, _g) = sh.device_ptr(&stream);
9597            p as u64
9598        };
9599        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9600            std::sync::Mutex::new(None);
9601        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9602        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9603            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9604        }
9605        let ones_raw = {
9606            let stream = e.stream();
9607            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
9608            p as u64
9609        };
9610        Ok(Some((sh_raw, ones_raw)))
9611    }
9612
9613    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
9614    /// add_scaled_rows program the split path used (persistent ones row, no htod).
9615    fn shexp_overlap_apply(
9616        e: &Engine,
9617        output: &mut CudaSlice<f32>,
9618        n_embd: usize,
9619    ) -> Result<(), Box<dyn std::error::Error>> {
9620        let guard = SHEXP_OV_WS
9621            .lock()
9622            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9623        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
9624        if *ne != n_embd {
9625            return Err("shexp overlap width drifted".into());
9626        }
9627        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9628            std::sync::Mutex::new(None);
9629        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
9630        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9631            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9632        }
9633        let ones = &og.as_ref().expect("armed above").1;
9634        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
9635        Ok(())
9636    }
9637
9638    fn moe_ffn_grouped_add_shared(
9639        e: &Engine,
9640        m: &MoeWeights,
9641        z: &CudaSlice<f32>,
9642        t: usize,
9643        cfg: &ModelConfig,
9644        il: u16,
9645        moe_out: &mut CudaSlice<f32>,
9646    ) -> Result<(), Box<dyn std::error::Error>> {
9647        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
9648        // queued matmuls here rather than at the next host readback).
9649        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9650        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9651        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9652        let shexp_started = shexp_timing.then(std::time::Instant::now);
9653        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
9654        if let Some(started) = shexp_started {
9655            use std::sync::atomic::Ordering;
9656            e.stream().synchronize()?;
9657            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9658                + started.elapsed().as_nanos() as u64;
9659            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9660            if calls % 430 == 0 {
9661                eprintln!(
9662                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9663                    ns as f64 / 1.0e6,
9664                    ns as f64 / calls as f64 / 1.0e3,
9665                );
9666            }
9667        }
9668        result
9669    }
9670
9671    #[allow(clippy::too_many_arguments)]
9672    fn moe_ffn_grouped_add_shared_inner(
9673        e: &Engine,
9674        m: &MoeWeights,
9675        z: &CudaSlice<f32>,
9676        t: usize,
9677        cfg: &ModelConfig,
9678        il: u16,
9679        moe_out: &mut CudaSlice<f32>,
9680    ) -> Result<(), Box<dyn std::error::Error>> {
9681        let n_embd = cfg.n_embd as usize;
9682        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
9683            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9684        {
9685            let n_ff_sh = gate_shexp.out_features();
9686            let lim = cfg.clamp_shexp_at(il as u32);
9687            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
9688            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
9689            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
9690            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
9691            // operand pre-quantized (kernel_check-proven identities). This path measured
9692            // 167us/layer as separate matmuls + 5 allocs at decode.
9693            let fused = t == 1
9694                && lim.is_none()
9695                && cfg.m3.is_none()
9696                && e.uses_q8_1_fast(gate_shexp)
9697                && e.uses_q8_1_fast(up_shexp);
9698            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
9699            // the two matvec_bf16 launches matmul would issue).
9700            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
9701                match (gate_shexp, up_shexp) {
9702                    (
9703                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
9704                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
9705                    ) => Some((wg, wu)),
9706                    _ => None,
9707                }
9708            } else {
9709                None
9710            };
9711            let sh = if let Some((wg, wu)) = bf16_dual {
9712                // Persistent shared-expert workspace: sizes are constant across every MoE
9713                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
9714                // the four per-layer allocations. Buffers are fully overwritten each call.
9715                static SHEXP_WS: std::sync::Mutex<
9716                    Option<(
9717                        usize,
9718                        usize,
9719                        usize,
9720                        CudaSlice<f32>,
9721                        CudaSlice<f32>,
9722                        CudaSlice<f32>,
9723                        CudaSlice<f32>,
9724                    )>,
9725                > = std::sync::Mutex::new(None);
9726                let down_bf16 = match down_shexp {
9727                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9728                    _ => None,
9729                };
9730                let mut guard = SHEXP_WS
9731                    .lock()
9732                    .map_err(|_| "shexp workspace lock is poisoned")?;
9733                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9734                if guard
9735                    .as_ref()
9736                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9737                {
9738                    *guard = Some((
9739                        pins.0,
9740                        pins.1,
9741                        pins.2,
9742                        e.uninit(n_ff_sh)?,
9743                        e.uninit(n_ff_sh)?,
9744                        e.uninit(n_ff_sh)?,
9745                        e.uninit(n_embd)?,
9746                    ));
9747                }
9748                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
9749                // through to the single-device arm when ineligible.
9750                {
9751                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9752                    let split_on = *ON
9753                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
9754                    if split_on {
9755                        if let (Some(wd), Some(rank1)) = (
9756                            match down_shexp {
9757                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
9758                                _ => None,
9759                            },
9760                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
9761                        ) {
9762                            if let Some(sh) = Self::shexp_split_matvec(
9763                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
9764                            )? {
9765                                drop(guard);
9766                                let gate = match &m.gate_inp_shexp {
9767                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
9768                                        z,
9769                                        gate_inp_shexp.float_data(),
9770                                        n_embd,
9771                                        t,
9772                                    )?,
9773                                    None => e.htod(&vec![1.0f32; t])?,
9774                                };
9775                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9776                                return Ok(());
9777                            }
9778                        }
9779                    }
9780                }
9781                let (_, _, _, gate, up, act, sh_buf) =
9782                    guard.as_mut().expect("shexp workspace initialized above");
9783                if cfg.m3.is_none() {
9784                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
9785                    // per-row program + exact silu/clamped expression, bit-identical.
9786                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9787                    let _ = (&gate, &up);
9788                } else {
9789                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
9790                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
9791                }
9792                if let Some(down) = down_bf16 {
9793                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
9794                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
9795                    // exact f32acc per-row program + the exact add_scaled_rows expression
9796                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
9797                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
9798                    // accumulate consumes the same f32 the split path stored and reloaded.
9799                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9800                    let fuse_da = *FUSE_DA.get_or_init(|| {
9801                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
9802                    });
9803                    if fuse_da && m.gate_inp_shexp.is_none() {
9804                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9805                            std::sync::Mutex::new(None);
9806                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
9807                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9808                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9809                        }
9810                        let ones = &og.as_ref().expect("armed above").1;
9811                        e.matvec_bf16_down_addscale_into(
9812                            down, act, ones, moe_out, n_ff_sh, n_embd,
9813                        )?;
9814                        return Ok(());
9815                    }
9816                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
9817                    let sh = e.uninit(n_embd)?;
9818                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
9819                    let mut sh = sh;
9820                    {
9821                        let mut dst = sh.slice_mut(0..n_embd);
9822                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
9823                    }
9824                    sh
9825                } else {
9826                    e.matmul(down_shexp, act, 1)?
9827                }
9828            } else if fused {
9829                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
9830                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
9831                    Some((gate, up)) => Some((gate, up)),
9832                    None => {
9833                        match (
9834                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
9835                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
9836                        ) {
9837                            (Some(gate), Some(up)) => Some((gate, up)),
9838                            _ => None,
9839                        }
9840                    }
9841                };
9842                match pair {
9843                    Some(((gate, gs), (up, us))) => {
9844                        if e.uses_q8_1_fast(down_shexp) {
9845                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
9846                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
9847                        } else {
9848                            let mut act = e.uninit(n_ff_sh)?;
9849                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
9850                            e.matmul(down_shexp, &act, 1)?
9851                        }
9852                    }
9853                    None => {
9854                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
9855                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
9856                        let mut act = e.uninit(n_ff_sh)?;
9857                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
9858                        e.matmul(down_shexp, &act, 1)?
9859                    }
9860                }
9861            } else {
9862                let sg_gate = e.matmul(gate_shexp, z, t)?;
9863                let sg_up = e.matmul(up_shexp, z, t)?;
9864                let mut sa = e.uninit(t * n_ff_sh)?;
9865                Self::ffn_act_lim(
9866                    e,
9867                    cfg,
9868                    &sg_gate,
9869                    &sg_up,
9870                    1.0,
9871                    1.0,
9872                    lim,
9873                    &mut sa,
9874                    t * n_ff_sh,
9875                )?;
9876                e.matmul(down_shexp, &sa, t)?
9877            };
9878            let gate = match &m.gate_inp_shexp {
9879                Some(gate_inp_shexp) => {
9880                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
9881                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
9882                    } else {
9883                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
9884                        let mut gate = e.uninit(t)?;
9885                        e.sigmoid(&raw, &mut gate, t)?;
9886                        gate
9887                    }
9888                }
9889                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
9890                // synchronizes the stream — measured as the biggest per-layer host gap
9891                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
9892                // device serves every layer; larger t (prefill) keeps the plain htod.
9893                None if t == 1 => {
9894                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9895                        std::sync::Mutex::new(None);
9896                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
9897                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9898                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9899                    }
9900                    let ones = &guard.as_ref().expect("armed above").1;
9901                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
9902                    return Ok(());
9903                }
9904                None => e.htod(&vec![1.0f32; t])?,
9905            };
9906            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
9907        }
9908        Ok(())
9909    }
9910
9911    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
9912    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
9913    pub(crate) fn moe_ffn_grouped(
9914        e: &Engine,
9915        m: &MoeWeights,
9916        z: &CudaSlice<f32>,
9917        t: usize,
9918        cfg: &ModelConfig,
9919        il: u16,
9920        max_block: usize,
9921    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9922        let moe = cfg.moe.as_ref().unwrap();
9923        let n_embd = cfg.n_embd as usize;
9924        let n_expert = moe.expert_count as usize;
9925        let n_used = moe.expert_used_count as usize;
9926        let n_ff_exp = moe.expert_ff_length as usize;
9927        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
9928        let lim_exp = cfg.clamp_exp_at(il as u32);
9929
9930        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
9931        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
9932        // enters the softmax-only pairs/dev router.
9933        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
9934        if let Some(sig) = cfg.sigmoid_router() {
9935            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
9936        }
9937        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
9938            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
9939        } else {
9940            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
9941        };
9942        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
9943        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
9944        Self::trace_moe_input(e, il, t, n_embd, z)?;
9945
9946        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
9947        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
9948        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
9949        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
9950        let no_exp_macros = m.gate_exps.macros.is_none()
9951            && m.up_exps.macros.is_none()
9952            && m.down_exps.macros.is_none();
9953        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
9954            m.has_uniform_expert_layout()
9955                && no_exp_macros
9956                && moe_q8_enabled()
9957                && q8_expert_supported(m.gate_exps.qtype)
9958                && q8_expert_supported(m.up_exps.qtype)
9959                && q8_expert_supported(m.down_exps.qtype)
9960                && moe_slab_enabled()
9961                && dev.dev == e.ctx().ordinal()
9962        });
9963        if let Some(dev) = resident_q8 {
9964            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
9965                e,
9966                m,
9967                z,
9968                t,
9969                cfg,
9970                il,
9971                &sel_all,
9972                &w_all,
9973                &dev.ptr_row,
9974                dev.gu_il,
9975            )?;
9976            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
9977            return Ok(moe_out);
9978        }
9979
9980        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
9981        // For each expert e, we need: which tokens use it, their positions in z, their top-k
9982        // slot index (for bit-identical accumulation), and their weights.
9983        struct ExpertGroup {
9984            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
9985            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
9986            weights: Vec<f32>,      // renormalized weight for that token-expert pair
9987        }
9988        let mut groups: Vec<ExpertGroup> = (0..n_expert)
9989            .map(|_| ExpertGroup {
9990                tok_indices: Vec::new(),
9991                slot_indices: Vec::new(),
9992                weights: Vec::new(),
9993            })
9994            .collect();
9995
9996        for tok in 0..t {
9997            for j in 0..n_used {
9998                let ex = sel_all[tok * n_used + j] as usize;
9999                let w = w_all[tok * n_used + j];
10000                groups[ex].tok_indices.push(tok as i32);
10001                groups[ex].slot_indices.push(j as i32);
10002                groups[ex].weights.push(w);
10003            }
10004        }
10005
10006        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
10007        // Each token's 8 expert contributions land in their respective slots.
10008        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
10009        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
10010
10011        // Expert weight dimensions (used in both cache and staging paths).
10012        let g_len = m.gate_exps.max_expert_bytes();
10013        let u_len = m.up_exps.max_expert_bytes();
10014        let d_len = m.down_exps.max_expert_bytes();
10015        let moe_q8 = m.has_uniform_expert_layout()
10016            && moe_q8_enabled()
10017            && q8_expert_supported(m.gate_exps.qtype)
10018            && q8_expert_supported(m.up_exps.qtype)
10019            && q8_expert_supported(m.down_exps.qtype);
10020        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10021        // Interleaved GU slabs require the pointer-table fast path above.
10022        let slab_local = m
10023            .dev_exps
10024            .as_ref()
10025            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10026        let use_cache =
10027            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10028        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10029        // also does: a local resident slab or a live SLRU dispatch.
10030        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10031
10032        // GPU scratch for staging (only allocated without a local slab or cache).
10033        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10034            (
10035                Some(e.alloc_u8(g_len)?),
10036                Some(e.alloc_u8(u_len)?),
10037                Some(e.alloc_u8(d_len)?),
10038            )
10039        } else {
10040            (None, None, None)
10041        };
10042
10043        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10044        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10045        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10046        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10047        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10048        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10049        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10050        // at long prompts where every expert stages regardless. Order is FREE to change without
10051        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10052        // regardless of expert processing order (the whole point of the slots).
10053        let mut order: Vec<usize> = (0..n_expert)
10054            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10055            .collect();
10056        order.sort_by(|&a, &b| {
10057            groups[b]
10058                .tok_indices
10059                .len()
10060                .cmp(&groups[a].tok_indices.len())
10061                .then(a.cmp(&b))
10062        });
10063        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10064        let page_window = moe_page_prefetch_window();
10065        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10066        if worker_disk_prefetch {
10067            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10068                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10069            }
10070        }
10071        for (order_pos, &ex) in order.iter().enumerate() {
10072            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10073                Self::moe_prefetch_host_expert(order[next], m);
10074            }
10075            if worker_disk_prefetch {
10076                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10077                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10078                    let keep = [
10079                        BlockId::new(il, PROJ_GATE, ex as u16),
10080                        BlockId::new(il, PROJ_UP, ex as u16),
10081                        BlockId::new(il, PROJ_DOWN, ex as u16),
10082                    ];
10083                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10084                }
10085            }
10086            let grp = &groups[ex];
10087            let m_e = grp.tok_indices.len();
10088            m_dist.push(m_e);
10089            let gl = m.gate_exps.expert_layout(ex);
10090            let ul = m.up_exps.expert_layout(ex);
10091            let dl = m.down_exps.expert_layout(ex);
10092
10093            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10094            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10095            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10096            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10097            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10098            let dmac = m.down_exps.macro_scale(ex);
10099            let weight_d = if dmac == 1.0 {
10100                e.htod(&grp.weights)?
10101            } else {
10102                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10103                e.htod(&scaled)?
10104            };
10105
10106            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10107            let mut gathered = e.zeros(m_e * n_embd)?;
10108            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10109            let gv = gathered.slice(0..m_e * n_embd);
10110
10111            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10112            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10113            let y = if let Some(dev) = slab_local {
10114                let gate_start = ex * m.gate_exps.expert_stride;
10115                let up_start = ex * m.up_exps.expert_stride;
10116                let down_start = ex * m.down_exps.expert_stride;
10117                if grouped_q8 {
10118                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10119                    let gate = e.qmatvec_expert_q8(
10120                        &dev.gate,
10121                        gate_start..gate_start + gl.len,
10122                        &zq,
10123                        &zd,
10124                        m_e,
10125                        m.gate_exps.in_f,
10126                        m.gate_exps.out_f,
10127                        gl.qtype,
10128                        gl.row_bytes,
10129                    )?;
10130                    let up = e.qmatvec_expert_q8(
10131                        &dev.up,
10132                        up_start..up_start + ul.len,
10133                        &zq,
10134                        &zd,
10135                        m_e,
10136                        m.up_exps.in_f,
10137                        m.up_exps.out_f,
10138                        ul.qtype,
10139                        ul.row_bytes,
10140                    )?;
10141                    let mut act = e.uninit(m_e * n_ff_exp)?;
10142                    Self::ffn_act_lim(
10143                        e,
10144                        cfg,
10145                        &gate,
10146                        &up,
10147                        m.gate_exps.macro_scale(ex),
10148                        m.up_exps.macro_scale(ex),
10149                        lim_exp,
10150                        &mut act,
10151                        m_e * n_ff_exp,
10152                    )?;
10153                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10154                    e.qmatvec_expert_q8(
10155                        &dev.down,
10156                        down_start..down_start + dl.len,
10157                        &aq2,
10158                        &ad2,
10159                        m_e,
10160                        m.down_exps.in_f,
10161                        m.down_exps.out_f,
10162                        dl.qtype,
10163                        dl.row_bytes,
10164                    )?
10165                } else {
10166                    let gate = e.qmatvec_view(
10167                        &dev.gate,
10168                        gate_start..gate_start + gl.len,
10169                        &gv,
10170                        m_e,
10171                        m.gate_exps.in_f,
10172                        m.gate_exps.out_f,
10173                        gl.qtype,
10174                        gl.row_bytes,
10175                    )?;
10176                    let up = e.qmatvec_view(
10177                        &dev.up,
10178                        up_start..up_start + ul.len,
10179                        &gv,
10180                        m_e,
10181                        m.up_exps.in_f,
10182                        m.up_exps.out_f,
10183                        ul.qtype,
10184                        ul.row_bytes,
10185                    )?;
10186                    let mut act = e.uninit(m_e * n_ff_exp)?;
10187                    Self::ffn_act_lim(
10188                        e,
10189                        cfg,
10190                        &gate,
10191                        &up,
10192                        m.gate_exps.macro_scale(ex),
10193                        m.up_exps.macro_scale(ex),
10194                        lim_exp,
10195                        &mut act,
10196                        m_e * n_ff_exp,
10197                    )?;
10198                    let actv = act.slice(0..m_e * n_ff_exp);
10199                    e.qmatvec_view(
10200                        &dev.down,
10201                        down_start..down_start + dl.len,
10202                        &actv,
10203                        m_e,
10204                        m.down_exps.in_f,
10205                        m.down_exps.out_f,
10206                        dl.qtype,
10207                        dl.row_bytes,
10208                    )?
10209                }
10210            } else if use_cache {
10211                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10212                if grouped_q8 {
10213                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10214                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10215                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10216                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10217                        eng.qmatvec_expert_q8(
10218                            cache.buf(slot),
10219                            0..gl.len,
10220                            &zq,
10221                            &zd,
10222                            m_e,
10223                            m.gate_exps.in_f,
10224                            m.gate_exps.out_f,
10225                            gl.qtype,
10226                            gl.row_bytes,
10227                        )
10228                    })?;
10229                    let up = e.with_moe_cache(max_block, |cache, eng| {
10230                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10231                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10232                        eng.qmatvec_expert_q8(
10233                            cache.buf(slot),
10234                            0..ul.len,
10235                            &zq,
10236                            &zd,
10237                            m_e,
10238                            m.up_exps.in_f,
10239                            m.up_exps.out_f,
10240                            ul.qtype,
10241                            ul.row_bytes,
10242                        )
10243                    })?;
10244                    let mut act = e.uninit(m_e * n_ff_exp)?;
10245                    Self::ffn_act_lim(
10246                        e,
10247                        cfg,
10248                        &gate,
10249                        &up,
10250                        m.gate_exps.macro_scale(ex),
10251                        m.up_exps.macro_scale(ex),
10252                        lim_exp,
10253                        &mut act,
10254                        m_e * n_ff_exp,
10255                    )?;
10256                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10257                    e.with_moe_cache(max_block, |cache, eng| {
10258                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10259                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10260                        eng.qmatvec_expert_q8(
10261                            cache.buf(slot),
10262                            0..dl.len,
10263                            &aq2,
10264                            &ad2,
10265                            m_e,
10266                            m.down_exps.in_f,
10267                            m.down_exps.out_f,
10268                            dl.qtype,
10269                            dl.row_bytes,
10270                        )
10271                    })?
10272                } else {
10273                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10274                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10275                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10276                        eng.qmatvec_view(
10277                            cache.buf(slot),
10278                            0..gl.len,
10279                            &gv,
10280                            m_e,
10281                            m.gate_exps.in_f,
10282                            m.gate_exps.out_f,
10283                            gl.qtype,
10284                            gl.row_bytes,
10285                        )
10286                    })?;
10287                    let up = e.with_moe_cache(max_block, |cache, eng| {
10288                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10289                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10290                        eng.qmatvec_view(
10291                            cache.buf(slot),
10292                            0..ul.len,
10293                            &gv,
10294                            m_e,
10295                            m.up_exps.in_f,
10296                            m.up_exps.out_f,
10297                            ul.qtype,
10298                            ul.row_bytes,
10299                        )
10300                    })?;
10301                    let mut act = e.uninit(m_e * n_ff_exp)?;
10302                    Self::ffn_act_lim(
10303                        e,
10304                        cfg,
10305                        &gate,
10306                        &up,
10307                        m.gate_exps.macro_scale(ex),
10308                        m.up_exps.macro_scale(ex),
10309                        lim_exp,
10310                        &mut act,
10311                        m_e * n_ff_exp,
10312                    )?;
10313                    let actv = act.slice(0..m_e * n_ff_exp);
10314                    e.with_moe_cache(max_block, |cache, eng| {
10315                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10316                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10317                        eng.qmatvec_view(
10318                            cache.buf(slot),
10319                            0..dl.len,
10320                            &actv,
10321                            m_e,
10322                            m.down_exps.in_f,
10323                            m.down_exps.out_f,
10324                            dl.qtype,
10325                            dl.row_bytes,
10326                        )
10327                    })?
10328                }
10329            } else {
10330                let sg = scratch_g.as_mut().unwrap();
10331                let su = scratch_u.as_mut().unwrap();
10332                let sd = scratch_d.as_mut().unwrap();
10333                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10334                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10335                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10336                if grouped_q8 {
10337                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10338                    let gate = e.qmatvec_expert_q8(
10339                        sg,
10340                        0..gl.len,
10341                        &zq,
10342                        &zd,
10343                        m_e,
10344                        m.gate_exps.in_f,
10345                        m.gate_exps.out_f,
10346                        gl.qtype,
10347                        gl.row_bytes,
10348                    )?;
10349                    let up = e.qmatvec_expert_q8(
10350                        su,
10351                        0..ul.len,
10352                        &zq,
10353                        &zd,
10354                        m_e,
10355                        m.up_exps.in_f,
10356                        m.up_exps.out_f,
10357                        ul.qtype,
10358                        ul.row_bytes,
10359                    )?;
10360                    let mut act = e.uninit(m_e * n_ff_exp)?;
10361                    Self::ffn_act_lim(
10362                        e,
10363                        cfg,
10364                        &gate,
10365                        &up,
10366                        m.gate_exps.macro_scale(ex),
10367                        m.up_exps.macro_scale(ex),
10368                        lim_exp,
10369                        &mut act,
10370                        m_e * n_ff_exp,
10371                    )?;
10372                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10373                    e.qmatvec_expert_q8(
10374                        sd,
10375                        0..dl.len,
10376                        &aq2,
10377                        &ad2,
10378                        m_e,
10379                        m.down_exps.in_f,
10380                        m.down_exps.out_f,
10381                        dl.qtype,
10382                        dl.row_bytes,
10383                    )?
10384                } else {
10385                    let gate = e.qmatvec_view(
10386                        sg,
10387                        0..gl.len,
10388                        &gv,
10389                        m_e,
10390                        m.gate_exps.in_f,
10391                        m.gate_exps.out_f,
10392                        gl.qtype,
10393                        gl.row_bytes,
10394                    )?;
10395                    let up = e.qmatvec_view(
10396                        su,
10397                        0..ul.len,
10398                        &gv,
10399                        m_e,
10400                        m.up_exps.in_f,
10401                        m.up_exps.out_f,
10402                        ul.qtype,
10403                        ul.row_bytes,
10404                    )?;
10405                    let mut act = e.uninit(m_e * n_ff_exp)?;
10406                    Self::ffn_act_lim(
10407                        e,
10408                        cfg,
10409                        &gate,
10410                        &up,
10411                        m.gate_exps.macro_scale(ex),
10412                        m.up_exps.macro_scale(ex),
10413                        lim_exp,
10414                        &mut act,
10415                        m_e * n_ff_exp,
10416                    )?;
10417                    let actv = act.slice(0..m_e * n_ff_exp);
10418                    e.qmatvec_view(
10419                        sd,
10420                        0..dl.len,
10421                        &actv,
10422                        m_e,
10423                        m.down_exps.in_f,
10424                        m.down_exps.out_f,
10425                        dl.qtype,
10426                        dl.row_bytes,
10427                    )?
10428                }
10429            };
10430
10431            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10432            e.scatter_slot(
10433                &y,
10434                &tok_idx_d,
10435                &slot_idx_d,
10436                &weight_d,
10437                &mut slot_buf,
10438                &mut wbuf,
10439                n_embd,
10440                n_used,
10441                m_e,
10442            )?;
10443        }
10444
10445        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10446        let mut moe_out = e.zeros(t * n_embd)?;
10447        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10448
10449        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10450        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10451            m_dist.sort_unstable();
10452            let active = m_dist.len();
10453            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10454            let median = m_dist[active / 2];
10455            let max_m = *m_dist.last().unwrap();
10456            let min_m = m_dist[0];
10457            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10458            println!(
10459                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10460                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10461                      above_gemm_threshold(>=16)={above16}/{active}"
10462            );
10463        }
10464
10465        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10466        Ok(moe_out)
10467    }
10468
10469    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10470    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10471    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10472    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10473    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10474    /// expert-sum order identical to the sequential path.
10475    pub(crate) fn moe_ffn_lockstep(
10476        &self,
10477        e: &Engine,
10478        m: &MoeWeights,
10479        zbatch: &CudaSlice<f32>,
10480        mrows: usize,
10481        il: u16,
10482        max_block: usize,
10483    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10484        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10485        let cfg = &self.cfg;
10486        let moe = cfg.moe.as_ref().unwrap();
10487        let n_embd = cfg.n_embd as usize;
10488        let n_expert = moe.expert_count as usize;
10489        let n_used = moe.expert_used_count as usize;
10490        let n_ff_exp = moe.expert_ff_length as usize;
10491        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10492        let lim_exp = cfg.clamp_exp_at(il as u32);
10493        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10494
10495        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10496        if let Some(sig) = cfg.sigmoid_router() {
10497            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10498        }
10499        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10500            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10501        } else {
10502            Self::moe_route_cfg(
10503                e,
10504                &logits,
10505                mrows,
10506                n_expert,
10507                n_used,
10508                m.active_experts.as_deref(),
10509            )?
10510        };
10511        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10512
10513        // Residency split at whole-expert granularity against the (frozen) cache.
10514        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10515            Ok((0..n_expert)
10516                .map(|ex| {
10517                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10518                        .into_iter()
10519                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10520                })
10521                .collect())
10522        })?;
10523
10524        struct Group {
10525            rows: Vec<i32>,
10526            slots: Vec<i32>,
10527            weights: Vec<f32>,
10528        }
10529        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10530        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10531        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10532            Default::default();
10533        for row in 0..mrows {
10534            for j in 0..n_used {
10535                let ex = sel_all[row * n_used + j] as usize;
10536                let w = w_all[row * n_used + j];
10537                if resident_expert[ex] {
10538                    let group = groups.entry(ex).or_insert_with(|| Group {
10539                        rows: Vec::new(),
10540                        slots: Vec::new(),
10541                        weights: Vec::new(),
10542                    });
10543                    group.rows.push(row as i32);
10544                    group.slots.push(j as i32);
10545                    group.weights.push(w);
10546                } else {
10547                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10548                    cpu_rows[row].push((ex, w));
10549                    cpu_by_expert.entry(ex).or_default().push((row, w));
10550                }
10551            }
10552        }
10553
10554        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10555        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10556        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10557        // order per row differs from the sequential single-call chunk — part of the
10558        // documented lockstep numeric class.
10559        let host_rows = e.dtoh(zbatch)?;
10560        let rows_ok = crate::cpu_experts::rows_supported();
10561        enum CpuPart {
10562            Single { row: usize },
10563            Rows { rows: Vec<usize> },
10564        }
10565        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10566        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10567        if rows_ok {
10568            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10569                .into_iter()
10570                .filter(|(_, rows)| rows.len() >= 2)
10571                .collect();
10572            shared.sort_by_key(|(ex, _)| *ex);
10573            for (ex, mut row_weights) in shared {
10574                row_weights.sort_by_key(|(row, _)| *row);
10575                let inputs: Vec<(&[f32], f32)> = row_weights
10576                    .iter()
10577                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10578                    .collect();
10579                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10580                    .map_err(std::io::Error::other)?;
10581                for &(row, _) in &row_weights {
10582                    rows_served.insert((row, ex));
10583                }
10584                tickets.push((
10585                    CpuPart::Rows {
10586                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10587                    },
10588                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10589                ));
10590            }
10591        }
10592        for (row, selected) in cpu_rows.iter().enumerate() {
10593            let leftover: Vec<(usize, f32)> = selected
10594                .iter()
10595                .copied()
10596                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10597                .collect();
10598            if leftover.is_empty() {
10599                continue;
10600            }
10601            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10602            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10603                .map_err(std::io::Error::other)?;
10604            tickets.push((
10605                CpuPart::Single { row },
10606                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
10607            ));
10608        }
10609
10610        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
10611        let mut wbuf = e.zeros(mrows * n_used)?;
10612        let mut order: Vec<usize> = groups.keys().copied().collect();
10613        order.sort_by(|&a, &b| {
10614            groups[&b]
10615                .rows
10616                .len()
10617                .cmp(&groups[&a].rows.len())
10618                .then(a.cmp(&b))
10619        });
10620        for &ex in &order {
10621            let group = &groups[&ex];
10622            let m_e = group.rows.len();
10623            let gl = m.gate_exps.expert_layout(ex);
10624            let ul = m.up_exps.expert_layout(ex);
10625            let dl = m.down_exps.expert_layout(ex);
10626            let row_idx_d = e.htod_i32(&group.rows)?;
10627            let slot_idx_d = e.htod_i32(&group.slots)?;
10628            let dmac = m.down_exps.macro_scale(ex);
10629            let weight_d = if dmac == 1.0 {
10630                e.htod(&group.weights)?
10631            } else {
10632                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
10633                e.htod(&scaled)?
10634            };
10635            let mut gathered = e.zeros(m_e * n_embd)?;
10636            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
10637            let gv = gathered.slice(0..m_e * n_embd);
10638            let gate = e.with_moe_cache(max_block, |c, eng| {
10639                let slot = c
10640                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
10641                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10642                eng.qmatvec_view(
10643                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10644                    0..gl.len,
10645                    &gv,
10646                    m_e,
10647                    m.gate_exps.in_f,
10648                    m.gate_exps.out_f,
10649                    gl.qtype,
10650                    gl.row_bytes,
10651                )
10652            })?;
10653            let up = e.with_moe_cache(max_block, |c, eng| {
10654                let slot = c
10655                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
10656                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10657                eng.qmatvec_view(
10658                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10659                    0..ul.len,
10660                    &gv,
10661                    m_e,
10662                    m.up_exps.in_f,
10663                    m.up_exps.out_f,
10664                    ul.qtype,
10665                    ul.row_bytes,
10666                )
10667            })?;
10668            let mut act = e.zeros(m_e * n_ff_exp)?;
10669            Self::ffn_act_lim(
10670                e,
10671                cfg,
10672                &gate,
10673                &up,
10674                m.gate_exps.macro_scale(ex),
10675                m.up_exps.macro_scale(ex),
10676                lim_exp,
10677                &mut act,
10678                m_e * n_ff_exp,
10679            )?;
10680            let actv = act.slice(0..m_e * n_ff_exp);
10681            let y = e.with_moe_cache(max_block, |c, eng| {
10682                let slot = c
10683                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
10684                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
10685                eng.qmatvec_view(
10686                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
10687                    0..dl.len,
10688                    &actv,
10689                    m_e,
10690                    m.down_exps.in_f,
10691                    m.down_exps.out_f,
10692                    dl.qtype,
10693                    dl.row_bytes,
10694                )
10695            })?;
10696            e.scatter_slot(
10697                &y,
10698                &row_idx_d,
10699                &slot_idx_d,
10700                &weight_d,
10701                &mut slot_buf,
10702                &mut wbuf,
10703                n_embd,
10704                n_used,
10705                m_e,
10706            )?;
10707        }
10708        let mut moe_out = e.zeros(mrows * n_embd)?;
10709        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
10710
10711        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
10712        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
10713        for (part, ticket) in tickets {
10714            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
10715            let mut add_row = |row: usize, chunk: &[f32]| {
10716                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
10717                for (accumulator, value) in sum.iter_mut().zip(chunk) {
10718                    *accumulator += value;
10719                }
10720            };
10721            match part {
10722                CpuPart::Single { row } => add_row(row, &cpu_output),
10723                CpuPart::Rows { rows } => {
10724                    for (slot, row) in rows.into_iter().enumerate() {
10725                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
10726                    }
10727                }
10728            }
10729        }
10730        for (row, sum) in row_sums.into_iter().enumerate() {
10731            let Some(sum) = sum else { continue };
10732            let cpu_output = e.htod(&sum)?;
10733            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
10734            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
10735        }
10736
10737        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10738            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10739        {
10740            let n_ff_sh = gate_shexp.out_features();
10741            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
10742            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
10743            let mut sa = e.zeros(mrows * n_ff_sh)?;
10744            Self::ffn_act_lim(
10745                e,
10746                cfg,
10747                &sg_gate,
10748                &sg_up,
10749                1.0,
10750                1.0,
10751                lim_shexp,
10752                &mut sa,
10753                mrows * n_ff_sh,
10754            )?;
10755            let sh = e.matmul(down_shexp, &sa, mrows)?;
10756            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
10757            // decode matches the single-sequence decode chain bit-for-bit.
10758            let g = match &m.gate_inp_shexp {
10759                Some(gate_inp_shexp) => {
10760                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
10761                }
10762                None => e.htod(&vec![1.0f32; mrows])?,
10763            };
10764            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
10765        }
10766
10767        Ok(moe_out)
10768    }
10769}
10770
10771// ============================ gemma4 (R8 verified wiring) ==================================
10772// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
10773// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
10774// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
10775// gemma variants after the correctness gate).
10776impl HybridModel {
10777    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
10778    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
10779    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
10780    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
10781    ///
10782    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
10783    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
10784    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
10785    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
10786    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
10787    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
10788    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
10789    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
10790    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
10791        let g = self
10792            .cfg
10793            .gemma4
10794            .as_ref()
10795            .expect("gemma4_rope_dims on a non-gemma4 config");
10796        if g.swa_pattern[il] {
10797            g.rope_dims_swa as usize
10798        } else {
10799            g.rope_dims_global as usize
10800        }
10801    }
10802
10803    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
10804        let g = self.cfg.gemma4.as_ref().unwrap();
10805        let swa = g.swa_pattern[il];
10806        let hd = if swa {
10807            g.key_length_swa
10808        } else {
10809            g.key_length_global
10810        } as usize;
10811        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
10812        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
10813        // rows exact (softmax over one element) while every later position drifted).
10814        (
10815            hd,
10816            g.head_count_kv[il] as usize,
10817            self.cfg.n_head as usize,
10818            if swa {
10819                g.rope_base_swa
10820            } else {
10821                g.rope_base_global
10822            },
10823            1.0,
10824            swa,
10825        )
10826    }
10827
10828    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
10829    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
10830    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
10831    pub(crate) fn gemma4_suppress(
10832        &self,
10833        e: &Engine,
10834        ld: &mut CudaSlice<f32>,
10835        t: usize,
10836    ) -> Result<(), Box<dyn std::error::Error>> {
10837        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
10838            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
10839            // stage as primary, and this tail runs only after the last stage). The assert turns
10840            // that argued invariant into a checked one: any topology violating primary==head
10841            // trips here in debug instead of silently peer-reading a device-0 buffer.
10842            #[cfg(debug_assertions)]
10843            crate::debug_assert_tensor_stream_device(
10844                ids,
10845                &e.stream(),
10846                "gemma4_suppress.suppress_d",
10847            );
10848            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
10849        }
10850        Ok(())
10851    }
10852
10853    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
10854    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
10855    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
10856    /// only (v0): attends within `tokens` via the f32 sdpa.
10857    #[allow(clippy::too_many_arguments)]
10858    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
10859    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
10860    /// switching program at `t > sliding_window`. The door is the measured cause of the
10861    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
10862    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
10863    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
10864    /// published prefix KV stops depending on the total prompt length. Off by default because
10865    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
10866    fn gemma_fa_one_program() -> bool {
10867        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10868        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
10869    }
10870
10871    fn gemma4_attn_prime(
10872        &self,
10873        e: &Engine,
10874        fa: &crate::hybrid::FullAttnLayer,
10875        il: usize,
10876        h: &CudaSlice<f32>,
10877        pos_d: &CudaSlice<i32>,
10878        t: usize,
10879        cache: Option<&mut Cache>,
10880        island: Option<&CudaSlice<i32>>,
10881    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10882        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
10883        let eps = self.cfg.rms_eps;
10884        let aux = self.gemma4_aux.as_ref().unwrap();
10885        let ones = aux.ones(e);
10886        #[cfg(debug_assertions)]
10887        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
10888
10889        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
10890        // (h stays borrowed across the triple, so the cache key can't go stale).
10891        e.mmq_act_begin();
10892        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
10893        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10894            let v = e.dtoh(&q0)?;
10895            let nan = v.iter().filter(|x| x.is_nan()).count();
10896            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10897            eprintln!(
10898                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
10899                v.len()
10900            );
10901        }
10902        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
10903        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
10904        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
10905        let v0 = if swa {
10906            e.matmul(&fa.wv, h, t)?
10907        } else {
10908            e.clone_dtod(&k0)?
10909        };
10910        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
10911            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
10912                let v = e.dtoh(buf)?;
10913                let nan = v.iter().filter(|x| x.is_nan()).count();
10914                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
10915                eprintln!(
10916                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
10917                    v.len()
10918                );
10919            }
10920        }
10921
10922        let mut q = e.uninit(t * nh * hd)?;
10923        let mut k = e.uninit(t * nkv * hd)?;
10924        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
10925        let mut v = e.uninit(t * nkv * hd)?;
10926        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
10927        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
10928        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
10929        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10930        // Island primes take the mask-capable naive kernel below; keep the operands f32
10931        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
10932        let emit = island.is_none()
10933            && t >= 16
10934            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
10935            && *EMIT.get_or_init(|| {
10936                std::env::var("MEMRA_FA_EMIT")
10937                    .map(|s| s != "0")
10938                    .unwrap_or(true)
10939            });
10940        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
10941        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10942        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
10943        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
10944        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
10945        let v_f16 = emit
10946            && crate::fa_f16pv_on()
10947            && match hd {
10948                512 => true,
10949                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
10950                _ => false,
10951            };
10952        if emit {
10953            e.rms_norm_qkv_w4b(
10954                &q0,
10955                &k0,
10956                &v0,
10957                fa.q_norm.float_data(),
10958                fa.k_norm.float_data(),
10959                ones,
10960                &mut q,
10961                &mut k,
10962                &mut v,
10963                &mut vb,
10964                hd,
10965                nh * t,
10966                nkv * t,
10967                eps,
10968                v_f16,
10969            )?;
10970        } else {
10971            e.rms_norm_qkv(
10972                &q0,
10973                &k0,
10974                &v0,
10975                fa.q_norm.float_data(),
10976                fa.k_norm.float_data(),
10977                ones,
10978                &mut q,
10979                &mut k,
10980                &mut v,
10981                hd,
10982                nh * t,
10983                nkv * t,
10984                eps,
10985            )?;
10986        }
10987
10988        let ff = if swa {
10989            None
10990        } else {
10991            Some(
10992                aux.rope_freqs(e)
10993                    .expect("gemma4 global rope needs rope_freqs.weight"),
10994            )
10995        };
10996        #[cfg(debug_assertions)]
10997        if let Some(ff) = ff {
10998            crate::debug_assert_tensor_stream_device(
10999                ff,
11000                &e.stream(),
11001                "gemma4_attn_prime.rope_freqs",
11002            );
11003        }
11004        if emit {
11005            e.rope_neox2_bf16e(
11006                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
11007            )?;
11008        } else {
11009            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
11010        }
11011
11012        if let Some(cache) = cache {
11013            let kvl = cache.kv[il].as_mut().unwrap();
11014            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11015            e.append_kv_quantized_rows(
11016                &k,
11017                &v,
11018                &mut kvl.k,
11019                &mut kvl.v,
11020                kvl.len,
11021                t,
11022                kvl.kv_dim_k,
11023                kvl.kv_dim_v,
11024                kvl.k_tok_bytes,
11025                kvl.v_tok_bytes,
11026                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11027            )?;
11028            kvl.len += t;
11029        }
11030        let mut attn = e.zeros(t * nh * hd)?;
11031        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11032        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11033        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11034        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11035        if let Some(span) = island {
11036            // Masked-prefill arm: every layer routes through the island-aware naive
11037            // kernel (correctness-first, same posture as the vision tower v1). The
11038            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11039            // window, the real window beyond it.
11040            let w = if swa && t > win { win } else { 0 };
11041            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11042        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11043            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11044                if emit {
11045                    e.fa_prefill_w_pre(
11046                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11047                    )?;
11048                } else {
11049                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11050                }
11051            } else {
11052                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11053            }
11054        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11055            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11056        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11057            if emit {
11058                e.fa_prefill_hd512_pre(
11059                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11060                )?;
11061            } else {
11062                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11063            }
11064        } else {
11065            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11066        }
11067        Ok(e.matmul(&fa.wo, &attn, t)?)
11068    }
11069
11070    /// Back-compat wrapper (pure prefill, no cache).
11071    fn gemma4_attn(
11072        &self,
11073        e: &Engine,
11074        fa: &crate::hybrid::FullAttnLayer,
11075        il: usize,
11076        h: &CudaSlice<f32>,
11077        pos_d: &CudaSlice<i32>,
11078        t: usize,
11079    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11080        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11081    }
11082
11083    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11084    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11085    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11086    /// the q8z epilogue is quantize_q8_1 verbatim).
11087    fn gemma4_moe_q8(
11088        &self,
11089        e: &Engine,
11090        m: &crate::hybrid::MoeWeights,
11091        bits: &crate::hybrid::Gemma4MoeBits,
11092        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11093        router_in: &CudaSlice<f32>,
11094        t: usize,
11095    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11096        let cfg = &self.cfg;
11097        let moe = cfg.moe.as_ref().unwrap();
11098        let n_embd = cfg.n_embd as usize;
11099        let n_expert = moe.expert_count as usize;
11100        let n_used = moe.expert_used_count as usize;
11101        let n_ff_exp = moe.expert_ff_length as usize;
11102        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11103        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11104        // the pair's 12us is kernel time, not launch gaps.
11105        let logits = if crate::router_kernel_on() {
11106            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11107        } else {
11108            e.matmul(&m.gate_inp, router_in, t)?
11109        };
11110        let dev = m.dev_exps.as_ref().unwrap();
11111        let (sel_d, w_d) =
11112            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11113        let (zq, zd) = mq;
11114        if t == 1 {
11115            let selv = sel_d.slice(0..n_used);
11116            let wv = w_d.slice(0..n_used);
11117            let act = e.moe_gate_up_gelu8_dev_q8(
11118                &dev.ptr_row,
11119                &selv,
11120                zq,
11121                zd,
11122                n_embd,
11123                n_ff_exp,
11124                n_used,
11125                n_expert,
11126                m.gate_exps.qtype,
11127                m.up_exps.qtype,
11128                m.gate_exps.row_bytes,
11129                m.up_exps.row_bytes,
11130            )?;
11131            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11132            let mut moe_out = e.uninit(n_embd)?;
11133            e.moe_down8_fma_dev_q8(
11134                &dev.ptr_row,
11135                &selv,
11136                &wv,
11137                &aq2,
11138                &ad2,
11139                &mut moe_out.slice_mut(0..n_embd),
11140                n_ff_exp,
11141                n_embd,
11142                n_used,
11143                n_expert,
11144                m.down_exps.qtype,
11145                m.down_exps.row_bytes,
11146            )?;
11147            return Ok(moe_out);
11148        }
11149        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11150        let act = if csr {
11151            e.moe_gate_up_gelu8_dev_q8_csr(
11152                &dev.ptr_row,
11153                &sel_d,
11154                zq,
11155                zd,
11156                t * n_used,
11157                n_embd,
11158                n_ff_exp,
11159                n_used,
11160                n_expert,
11161                m.gate_exps.qtype,
11162                m.up_exps.qtype,
11163                m.gate_exps.row_bytes,
11164                m.up_exps.row_bytes,
11165            )?
11166        } else {
11167            e.moe_gate_up_gelu8_dev_q8_rows(
11168                &dev.ptr_row,
11169                &sel_d,
11170                zq,
11171                zd,
11172                t,
11173                n_embd,
11174                n_ff_exp,
11175                n_used,
11176                n_expert,
11177                m.gate_exps.qtype,
11178                m.up_exps.qtype,
11179                m.gate_exps.row_bytes,
11180                m.up_exps.row_bytes,
11181            )?
11182        };
11183        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11184        let mut moe_out = e.uninit(t * n_embd)?;
11185        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11186        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11187        e.moe_down8_fma_dev_q8_rows_g(
11188            &dev.ptr_row,
11189            &sel_d,
11190            &w_d,
11191            &aq2,
11192            &ad2,
11193            &mut moe_out,
11194            t,
11195            n_ff_exp,
11196            n_embd,
11197            n_used,
11198            n_expert,
11199            m.down_exps.qtype,
11200            m.down_exps.row_bytes,
11201        )?;
11202        Ok(moe_out)
11203    }
11204
11205    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11206    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11207    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11208    fn gemma4_moe(
11209        &self,
11210        e: &Engine,
11211        m: &crate::hybrid::MoeWeights,
11212        bits: &crate::hybrid::Gemma4MoeBits,
11213        moe_in: &CudaSlice<f32>,
11214        router_in: &CudaSlice<f32>,
11215        t: usize,
11216    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11217        let cfg = &self.cfg;
11218        let moe = cfg.moe.as_ref().unwrap();
11219        let n_embd = cfg.n_embd as usize;
11220        let n_expert = moe.expert_count as usize;
11221        let n_used = moe.expert_used_count as usize;
11222        let n_ff_exp = moe.expert_ff_length as usize;
11223
11224        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11225        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11226        // batched matmul only at real prefill.
11227        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11228            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11229        } else {
11230            e.matmul(&m.gate_inp, router_in, t)?
11231        };
11232
11233        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11234        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11235        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11236        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11237        if t < PRIME_MIN_T
11238            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11239            && expert_dp4a_supported(m.gate_exps.qtype)
11240            && expert_dp4a_supported(m.up_exps.qtype)
11241            && expert_dp4a_supported(m.down_exps.qtype)
11242            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11243        {
11244            let dev = m.dev_exps.as_ref().unwrap();
11245            let (sel_d, w_d) =
11246                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11247            if t == 1 {
11248                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11249                let selv = sel_d.slice(0..n_used);
11250                let wv = w_d.slice(0..n_used);
11251                let act = e.moe_gate_up_gelu8_dev_q8(
11252                    &dev.ptr_row,
11253                    &selv,
11254                    &zq,
11255                    &zd,
11256                    n_embd,
11257                    n_ff_exp,
11258                    n_used,
11259                    n_expert,
11260                    m.gate_exps.qtype,
11261                    m.up_exps.qtype,
11262                    m.gate_exps.row_bytes,
11263                    m.up_exps.row_bytes,
11264                )?;
11265                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11266                let mut moe_out = e.uninit(n_embd)?;
11267                e.moe_down8_fma_dev_q8(
11268                    &dev.ptr_row,
11269                    &selv,
11270                    &wv,
11271                    &aq2,
11272                    &ad2,
11273                    &mut moe_out.slice_mut(0..n_embd),
11274                    n_ff_exp,
11275                    n_embd,
11276                    n_used,
11277                    n_expert,
11278                    m.down_exps.qtype,
11279                    m.down_exps.row_bytes,
11280                )?;
11281                return Ok(moe_out);
11282            }
11283            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11284            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11285            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11286            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11287            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11288            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11289            let act = if csr {
11290                e.moe_gate_up_gelu8_dev_q8_csr(
11291                    &dev.ptr_row,
11292                    &sel_d,
11293                    &zq,
11294                    &zd,
11295                    t * n_used,
11296                    n_embd,
11297                    n_ff_exp,
11298                    n_used,
11299                    n_expert,
11300                    m.gate_exps.qtype,
11301                    m.up_exps.qtype,
11302                    m.gate_exps.row_bytes,
11303                    m.up_exps.row_bytes,
11304                )?
11305            } else {
11306                e.moe_gate_up_gelu8_dev_q8_rows(
11307                    &dev.ptr_row,
11308                    &sel_d,
11309                    &zq,
11310                    &zd,
11311                    t,
11312                    n_embd,
11313                    n_ff_exp,
11314                    n_used,
11315                    n_expert,
11316                    m.gate_exps.qtype,
11317                    m.up_exps.qtype,
11318                    m.gate_exps.row_bytes,
11319                    m.up_exps.row_bytes,
11320                )?
11321            };
11322            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11323            let mut moe_out = e.uninit(t * n_embd)?;
11324            e.moe_down8_fma_dev_q8_rows_g(
11325                &dev.ptr_row,
11326                &sel_d,
11327                &w_d,
11328                &aq2,
11329                &ad2,
11330                &mut moe_out,
11331                t,
11332                n_ff_exp,
11333                n_embd,
11334                n_used,
11335                n_expert,
11336                m.down_exps.qtype,
11337                m.down_exps.row_bytes,
11338            )?;
11339            return Ok(moe_out);
11340        }
11341
11342        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11343        for (i, &sx) in sel_all.iter().enumerate() {
11344            w_all[i] *= bits.per_expert_scale[sx as usize];
11345        }
11346
11347        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11348        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11349        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11350        if t >= PRIME_MIN_T
11351            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11352            && expert_dp4a_supported(m.gate_exps.qtype)
11353            && expert_dp4a_supported(m.up_exps.qtype)
11354            && expert_dp4a_supported(m.down_exps.qtype)
11355            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11356        {
11357            let dev = m.dev_exps.as_ref().unwrap();
11358            let n_pairs = t * n_used;
11359            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11360            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11361            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11362            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11363            let pt = e.htod_i32(&pair_tok)?;
11364            let pw = e.htod(&w_all)?;
11365            let toff = e.htod_i32(&tok_off)?;
11366            let tids = e.htod_i32(&tok_ids)?;
11367            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11368            for p in 0..n_pairs {
11369                by_ex[pair_ex[p] as usize].push(p as i32);
11370            }
11371            let mut ex_ids: Vec<i32> = Vec::new();
11372            let mut ex_off: Vec<i32> = vec![0];
11373            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11374            for (ex, list) in by_ex.iter().enumerate() {
11375                if list.is_empty() {
11376                    continue;
11377                }
11378                ex_ids.push(ex as i32);
11379                ex_pairs.extend_from_slice(list);
11380                ex_off.push(ex_pairs.len() as i32);
11381            }
11382            let n_active = ex_ids.len();
11383            let exi = e.htod_i32(&ex_ids)?;
11384            let exo = e.htod_i32(&ex_off)?;
11385            let exp_d = e.htod_i32(&ex_pairs)?;
11386            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11387            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11388            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11389            // ragged down k (704) needs no padding here — cublas takes any k.
11390            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11391            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11392            // Hopper default — see moe_f16g_gemma_on.
11393            if crate::moe_f16g_gemma_on()
11394                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11395                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11396                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11397            {
11398                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11399                let csr_tok_d = e.htod_i32(&csr_tok)?;
11400                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11401                let g_csr = e.moe_f16_grouped(
11402                    &dev.ptr_row,
11403                    0,
11404                    n_expert,
11405                    &exi,
11406                    &ex_off,
11407                    &exo,
11408                    &z_f16,
11409                    &z_s,
11410                    n_embd,
11411                    n_ff_exp,
11412                    n_active,
11413                    n_pairs,
11414                    m.gate_exps.qtype,
11415                    m.gate_exps.row_bytes,
11416                )?;
11417                let u_csr = e.moe_f16_grouped(
11418                    &dev.ptr_row,
11419                    1,
11420                    n_expert,
11421                    &exi,
11422                    &ex_off,
11423                    &exo,
11424                    &z_f16,
11425                    &z_s,
11426                    n_embd,
11427                    n_ff_exp,
11428                    n_active,
11429                    n_pairs,
11430                    m.up_exps.qtype,
11431                    m.up_exps.row_bytes,
11432                )?;
11433                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11434                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11435                let d_csr = e.moe_f16_grouped(
11436                    &dev.ptr_row,
11437                    2,
11438                    n_expert,
11439                    &exi,
11440                    &ex_off,
11441                    &exo,
11442                    &a_f16,
11443                    &a_s,
11444                    n_ff_exp,
11445                    n_embd,
11446                    n_active,
11447                    n_pairs,
11448                    m.down_exps.qtype,
11449                    m.down_exps.row_bytes,
11450                )?;
11451                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11452                let mut moe_out = e.uninit(t * n_embd)?;
11453                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11454                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11455                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11456                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11457                    eprintln!(
11458                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11459                        scan(&yd),
11460                        scan(&mo)
11461                    );
11462                }
11463                return Ok(moe_out);
11464            }
11465            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11466            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11467            let mma =
11468                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11469            let (gate, up) = if mma {
11470                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11471                (
11472                    e.mmq_iq_experts(
11473                        &dev.ptr_row,
11474                        0,
11475                        n_expert,
11476                        &exi,
11477                        &exo,
11478                        &exp_d,
11479                        &pt,
11480                        &z_scr,
11481                        n_embd,
11482                        n_ff_exp,
11483                        n_active,
11484                        n_pairs,
11485                        t,
11486                        m.gate_exps.qtype,
11487                        m.gate_exps.row_bytes,
11488                    )?,
11489                    e.mmq_iq_experts(
11490                        &dev.ptr_row,
11491                        1,
11492                        n_expert,
11493                        &exi,
11494                        &exo,
11495                        &exp_d,
11496                        &pt,
11497                        &z_scr,
11498                        n_embd,
11499                        n_ff_exp,
11500                        n_active,
11501                        n_pairs,
11502                        t,
11503                        m.up_exps.qtype,
11504                        m.up_exps.row_bytes,
11505                    )?,
11506                )
11507            } else {
11508                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11509                (
11510                    e.moe_pairs_matvec_q8_dec(
11511                        &dev.ptr_row,
11512                        0,
11513                        &exi,
11514                        &exo,
11515                        &exp_d,
11516                        &pt,
11517                        &zq,
11518                        &zd,
11519                        n_embd,
11520                        n_ff_exp,
11521                        n_expert,
11522                        n_active,
11523                        n_pairs,
11524                        m.gate_exps.qtype,
11525                        m.gate_exps.row_bytes,
11526                    )?,
11527                    e.moe_pairs_matvec_q8_dec(
11528                        &dev.ptr_row,
11529                        1,
11530                        &exi,
11531                        &exo,
11532                        &exp_d,
11533                        &pt,
11534                        &zq,
11535                        &zd,
11536                        n_embd,
11537                        n_ff_exp,
11538                        n_expert,
11539                        n_active,
11540                        n_pairs,
11541                        m.up_exps.qtype,
11542                        m.up_exps.row_bytes,
11543                    )?,
11544                )
11545            };
11546            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11547            let pself = e.htod_i32(&pair_self)?;
11548            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11549            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11550            // to the 256-val superblock (768) while the act quantizer's zero padding
11551            // makes every padded-k product exactly zero (weight overread bytes multiply
11552            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11553            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11554            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11555            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11556            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11557            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11558            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11559            let y_down = if mma {
11560                let in_pad = n_ff_exp.div_ceil(256) * 256;
11561                let a_scr = if crate::moe_fuse_actq_on() {
11562                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11563                } else {
11564                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11565                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11566                };
11567                e.mmq_iq_experts(
11568                    &dev.ptr_row,
11569                    2,
11570                    n_expert,
11571                    &exi,
11572                    &exo,
11573                    &exp_d,
11574                    &pself,
11575                    &a_scr,
11576                    in_pad,
11577                    n_embd,
11578                    n_active,
11579                    n_pairs,
11580                    n_pairs,
11581                    m.down_exps.qtype,
11582                    m.down_exps.row_bytes,
11583                )?
11584            } else {
11585                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11586                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11587                e.moe_pairs_matvec_q8_dec(
11588                    &dev.ptr_row,
11589                    2,
11590                    &exi,
11591                    &exo,
11592                    &exp_d,
11593                    &pself,
11594                    &aq2,
11595                    &ad2,
11596                    n_ff_exp,
11597                    n_embd,
11598                    n_expert,
11599                    n_active,
11600                    n_pairs,
11601                    m.down_exps.qtype,
11602                    m.down_exps.row_bytes,
11603                )?
11604            };
11605            let mut moe_out = e.uninit(t * n_embd)?;
11606            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11607            return Ok(moe_out);
11608        }
11609
11610        let g_len = m.gate_exps.expert_stride;
11611        let u_len = m.up_exps.expert_stride;
11612        let d_len = m.down_exps.expert_stride;
11613        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
11614        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
11615        // the spill fallback.
11616        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
11617        let (mut sg, mut su, mut sd) = if dev.is_some() {
11618            (None, None, None)
11619        } else {
11620            (
11621                Some(e.alloc_u8_uninit(g_len)?),
11622                Some(e.alloc_u8_uninit(u_len)?),
11623                Some(e.alloc_u8_uninit(d_len)?),
11624            )
11625        };
11626        let mut moe_out = e.zeros(t * n_embd)?;
11627        for tok in 0..t {
11628            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
11629            let w = &w_all[tok * n_used..(tok + 1) * n_used];
11630            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
11631            for (j, &ex) in sel.iter().enumerate() {
11632                let ex = ex as usize;
11633                let gate = match dev {
11634                    Some(d) => e.qmatvec_view(
11635                        &d.gate,
11636                        ex * g_len..(ex + 1) * g_len,
11637                        &zt,
11638                        1,
11639                        m.gate_exps.in_f,
11640                        m.gate_exps.out_f,
11641                        m.gate_exps.qtype,
11642                        m.gate_exps.row_bytes,
11643                    )?,
11644                    None => {
11645                        let sg = sg.as_mut().unwrap();
11646                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
11647                        e.qmatvec_view(
11648                            sg,
11649                            0..g_len,
11650                            &zt,
11651                            1,
11652                            m.gate_exps.in_f,
11653                            m.gate_exps.out_f,
11654                            m.gate_exps.qtype,
11655                            m.gate_exps.row_bytes,
11656                        )?
11657                    }
11658                };
11659                let up = match dev {
11660                    Some(d) => e.qmatvec_view(
11661                        &d.up,
11662                        ex * u_len..(ex + 1) * u_len,
11663                        &zt,
11664                        1,
11665                        m.up_exps.in_f,
11666                        m.up_exps.out_f,
11667                        m.up_exps.qtype,
11668                        m.up_exps.row_bytes,
11669                    )?,
11670                    None => {
11671                        let su = su.as_mut().unwrap();
11672                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
11673                        e.qmatvec_view(
11674                            su,
11675                            0..u_len,
11676                            &zt,
11677                            1,
11678                            m.up_exps.in_f,
11679                            m.up_exps.out_f,
11680                            m.up_exps.qtype,
11681                            m.up_exps.row_bytes,
11682                        )?
11683                    }
11684                };
11685                let mut act = e.uninit(n_ff_exp)?;
11686                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
11687                let actv = act.slice(0..n_ff_exp);
11688                let y = match dev {
11689                    Some(d) => e.qmatvec_view(
11690                        &d.down,
11691                        ex * d_len..(ex + 1) * d_len,
11692                        &actv,
11693                        1,
11694                        m.down_exps.in_f,
11695                        m.down_exps.out_f,
11696                        m.down_exps.qtype,
11697                        m.down_exps.row_bytes,
11698                    )?,
11699                    None => {
11700                        let sd = sd.as_mut().unwrap();
11701                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
11702                        e.qmatvec_view(
11703                            sd,
11704                            0..d_len,
11705                            &actv,
11706                            1,
11707                            m.down_exps.in_f,
11708                            m.down_exps.out_f,
11709                            m.down_exps.qtype,
11710                            m.down_exps.row_bytes,
11711                        )?
11712                    }
11713                };
11714                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
11715                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
11716            }
11717        }
11718        Ok(moe_out)
11719    }
11720
11721    /// One gemma4 trunk layer (R8): x -> x_next.
11722    fn gemma4_layer(
11723        &self,
11724        e: &Engine,
11725        il: usize,
11726        layer: &crate::hybrid::HybridLayer,
11727        x: &CudaSlice<f32>,
11728        pos_d: &CudaSlice<i32>,
11729        t: usize,
11730    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11731        let n_embd = self.cfg.n_embd as usize;
11732        let eps = self.cfg.rms_eps;
11733
11734        let mut h = e.zeros(t * n_embd)?;
11735        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
11736        let Mixer::Full(fa) = &layer.mixer else {
11737            panic!("gemma4 layer {il} not full-attn")
11738        };
11739        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
11740        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
11741        let mut cur = e.zeros(t * n_embd)?;
11742        e.rms_norm(
11743            &o,
11744            layer.post_attn_norm.float_data(),
11745            &mut cur,
11746            n_embd,
11747            t,
11748            eps,
11749        )?;
11750        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
11751    }
11752
11753    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
11754    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
11755    /// layer scale — shared verbatim by the prefill, decode and verify paths.
11756    fn gemma4_layer_tail_add(
11757        &self,
11758        e: &Engine,
11759        layer: &crate::hybrid::HybridLayer,
11760        cur: &CudaSlice<f32>,
11761        x: &CudaSlice<f32>,
11762        t: usize,
11763    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11764        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
11765    }
11766
11767    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
11768    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
11769    fn gemma4_layer_tail_add_n(
11770        &self,
11771        e: &Engine,
11772        layer: &crate::hybrid::HybridLayer,
11773        cur: &CudaSlice<f32>,
11774        x: &CudaSlice<f32>,
11775        t: usize,
11776        next_norm: Option<&CudaSlice<f32>>,
11777    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
11778        let n_embd = self.cfg.n_embd as usize;
11779        let bits = layer.gemma4.as_ref().unwrap();
11780        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
11781        let mut xn = e.uninit(t * n_embd)?;
11782        match next_norm {
11783            Some(w) => {
11784                let mut hn = e.uninit(t * n_embd)?;
11785                e.add_scale_rms_norm(
11786                    &sn,
11787                    &attn_out,
11788                    bits.layer_scale,
11789                    w,
11790                    &mut xn,
11791                    &mut hn,
11792                    n_embd,
11793                    t,
11794                    self.cfg.rms_eps,
11795                )?;
11796                Ok((xn, Some(hn)))
11797            }
11798            None => {
11799                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
11800                Ok((xn, None))
11801            }
11802        }
11803    }
11804
11805    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
11806    /// norm — returns (sn, attn_out) for the closing add+scale variants.
11807    fn gemma4_layer_tail_core(
11808        &self,
11809        e: &Engine,
11810        layer: &crate::hybrid::HybridLayer,
11811        cur: &CudaSlice<f32>,
11812        x: &CudaSlice<f32>,
11813        t: usize,
11814    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11815        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
11816    }
11817
11818    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
11819    /// means `cur` is the RAW attention output and the dense entry runs
11820    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
11821    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
11822    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
11823    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
11824    fn gemma4_layer_tail_core_pn(
11825        &self,
11826        e: &Engine,
11827        layer: &crate::hybrid::HybridLayer,
11828        cur: &CudaSlice<f32>,
11829        x: &CudaSlice<f32>,
11830        t: usize,
11831        pre_norm: Option<&CudaSlice<f32>>,
11832        defer_post_norm: bool,
11833    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11834        let n_embd = self.cfg.n_embd as usize;
11835        let eps = self.cfg.rms_eps;
11836        let bits = layer.gemma4.as_ref().unwrap();
11837
11838        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
11839        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
11840        let Some(mbits) = bits.moe_bits.as_ref() else {
11841            let crate::hybrid::Ffn::Dense {
11842                ffn_gate,
11843                ffn_up,
11844                ffn_down,
11845            } = &layer.ffn
11846            else {
11847                panic!("gemma4 dense layer without Dense ffn")
11848            };
11849            let mut attn_out = e.uninit(t * n_embd)?;
11850            let mut zsh = e.uninit(t * n_embd)?;
11851            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
11852            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
11853            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
11854            match pre_norm {
11855                Some(wa) if t == 1 => {
11856                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
11857                        cur,
11858                        wa,
11859                        x,
11860                        bits.ffn_norm.float_data(),
11861                        &mut attn_out,
11862                        &mut zsh,
11863                        n_embd,
11864                        t,
11865                        eps,
11866                    )?);
11867                }
11868                Some(wa) => e.rms_pre_add_rms_norm(
11869                    cur,
11870                    wa,
11871                    x,
11872                    bits.ffn_norm.float_data(),
11873                    &mut attn_out,
11874                    &mut zsh,
11875                    n_embd,
11876                    t,
11877                    eps,
11878                )?,
11879                None => e.add_rms_norm(
11880                    cur,
11881                    x,
11882                    bits.ffn_norm.float_data(),
11883                    &mut attn_out,
11884                    &mut zsh,
11885                    n_embd,
11886                    t,
11887                    eps,
11888                )?,
11889            }
11890            let n_ff = ffn_gate.out_features();
11891            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
11892            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
11893            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
11894            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
11895            // rescue segment C — the megakernel front is closed for the dense tail.
11896            let (gate, up) = if t == 1 {
11897                let (zq, zd) = match zpair {
11898                    Some(p) => p,
11899                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
11900                };
11901                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
11902                    Some(p) => p,
11903                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
11904                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
11905                        Some(p) => p,
11906                        None => (
11907                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
11908                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
11909                        ),
11910                    },
11911                }
11912            } else {
11913                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
11914                // launch for the verify's gate+up — the up segment's blocks fill SMs as
11915                // the gate segment drains (the launch-tail mechanism behind the b-tier
11916                // plateau; first positive after six falsified in-kernel variants).
11917                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11918                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
11919                let fused = if f2b {
11920                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
11921                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
11922                } else {
11923                    None
11924                };
11925                match fused {
11926                    Some(p) => p,
11927                    None => {
11928                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
11929                        e.mmq_act_begin();
11930                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
11931                    }
11932                }
11933            };
11934            let mut act = e.uninit(t * n_ff)?;
11935            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
11936            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
11937            let f0 = if e.uses_q8_1_fast(ffn_down) {
11938                let upv = e.view(&up, t * n_ff);
11939                let up_all = upv.slice(0..t * n_ff);
11940                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
11941                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
11942            } else {
11943                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
11944                e.matmul(ffn_down, &act, t)?
11945            };
11946            if defer_post_norm {
11947                return Ok((f0, attn_out));
11948            }
11949            let mut sn = e.uninit(t * n_embd)?;
11950            e.rms_norm(
11951                &f0,
11952                bits.post_ffw_norm.float_data(),
11953                &mut sn,
11954                n_embd,
11955                t,
11956                eps,
11957            )?;
11958            return Ok((sn, attn_out));
11959        };
11960
11961        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
11962        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
11963        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
11964        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
11965        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
11966        let mut attn_out = e.uninit(t * n_embd)?;
11967        let mut router_in = e.uninit(t * n_embd)?;
11968        let fast_moe = match &layer.ffn {
11969            crate::hybrid::Ffn::Moe(m) => {
11970                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11971                    && expert_dp4a_supported(m.gate_exps.qtype)
11972                    && expert_dp4a_supported(m.up_exps.qtype)
11973                    && expert_dp4a_supported(m.down_exps.qtype)
11974                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11975            }
11976            _ => false,
11977        };
11978        let q8z = t < PRIME_MIN_T && fast_moe;
11979        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
11980            let (z0, m2) = e.add_rms_norm3_q8z(
11981                cur,
11982                x,
11983                bits.ffn_norm.float_data(),
11984                &mbits.router_scale_pre,
11985                mbits.pre_ffw_norm_2.float_data(),
11986                &mut attn_out,
11987                &mut router_in,
11988                n_embd,
11989                t,
11990                eps,
11991            )?;
11992            (None, Some(z0), Some(m2))
11993        } else {
11994            let mut zsh = e.uninit(t * n_embd)?;
11995            let mut moe_in = e.uninit(t * n_embd)?;
11996            e.add_rms_norm3(
11997                cur,
11998                x,
11999                bits.ffn_norm.float_data(),
12000                &mbits.router_scale_pre,
12001                mbits.pre_ffw_norm_2.float_data(),
12002                &mut attn_out,
12003                &mut zsh,
12004                &mut router_in,
12005                &mut moe_in,
12006                n_embd,
12007                t,
12008                eps,
12009            )?;
12010            (Some((zsh, moe_in)), None, None)
12011        };
12012        let attn_out2 = attn_out;
12013        #[allow(unused_variables)]
12014        let attn_out = &attn_out2;
12015        let n_ff = mbits.shared_gate.out_features();
12016        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12017            if t == 1 {
12018                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12019                    Some(p) => p,
12020                    None => match e.matmul_nvfp4_fused2(
12021                        &mbits.shared_gate,
12022                        &mbits.shared_up,
12023                        zq,
12024                        zd,
12025                        1,
12026                    )? {
12027                        Some(p) => p,
12028                        None => {
12029                            let h0 = e.zeros(0)?;
12030                            (
12031                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12032                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12033                            )
12034                        }
12035                    },
12036                }
12037            } else {
12038                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12039                let h0 = e.zeros(0)?;
12040                (
12041                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12042                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12043                )
12044            }
12045        } else {
12046            let (zsh, _) = zsh_f32.as_ref().unwrap();
12047            (
12048                e.matmul(&mbits.shared_gate, zsh, t)?,
12049                e.matmul(&mbits.shared_up, zsh, t)?,
12050            )
12051        };
12052        let mut act = e.uninit(t * n_ff)?;
12053        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12054        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12055        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12056            panic!("gemma4 layer not MoE")
12057        };
12058        let moe0 = match (&moe_q8, &zsh_f32) {
12059            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12060            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12061            _ => unreachable!(),
12062        };
12063        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12064        let mut mlp = e.uninit(t * n_embd)?;
12065        let mut moe = e.uninit(t * n_embd)?;
12066        e.rms_norm2x(
12067            &mlp0,
12068            &moe0,
12069            mbits.post_ffw_norm_1.float_data(),
12070            mbits.post_ffw_norm_2.float_data(),
12071            &mut mlp,
12072            &mut moe,
12073            n_embd,
12074            t,
12075            eps,
12076        )?;
12077
12078        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12079        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12080        let mut sum = e.uninit(t * n_embd)?;
12081        let mut sn = e.uninit(t * n_embd)?;
12082        e.add_rms_norm(
12083            &mlp,
12084            &moe,
12085            bits.post_ffw_norm.float_data(),
12086            &mut sum,
12087            &mut sn,
12088            n_embd,
12089            t,
12090            eps,
12091        )?;
12092        Ok((sn, attn_out2))
12093    }
12094
12095    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12096    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12097    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12098    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12099    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12100    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12101    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12102    /// decode == verify == graph parity holds by construction at either seam value.
12103    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12104    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12105        &self,
12106        e: &Engine,
12107        layer: &crate::hybrid::HybridLayer,
12108        o: &CudaSlice<f32>,
12109        x: &CudaSlice<f32>,
12110        t: usize,
12111        next_norm: Option<&CudaSlice<f32>>,
12112    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12113    {
12114        let n_embd = self.cfg.n_embd as usize;
12115        let eps = self.cfg.rms_eps;
12116        let bits = layer.gemma4.as_ref().unwrap();
12117        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12118            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12119                e,
12120                layer,
12121                o,
12122                x,
12123                t,
12124                Some(layer.post_attn_norm.float_data()),
12125                true,
12126            )?;
12127            let mut xn = e.uninit(t * n_embd)?;
12128            return match next_norm {
12129                Some(w) => {
12130                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12131                        &f0,
12132                        bits.post_ffw_norm.float_data(),
12133                        &attn_out,
12134                        bits.layer_scale,
12135                        w,
12136                        &mut xn,
12137                        n_embd,
12138                        t,
12139                        eps,
12140                    )?;
12141                    Ok((xn, Some(pair)))
12142                }
12143                None => {
12144                    let mut sn = e.uninit(t * n_embd)?;
12145                    e.rms_norm(
12146                        &f0,
12147                        bits.post_ffw_norm.float_data(),
12148                        &mut sn,
12149                        n_embd,
12150                        t,
12151                        eps,
12152                    )?;
12153                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12154                    Ok((xn, None))
12155                }
12156            };
12157        }
12158        let mut cur = e.uninit(t * n_embd)?;
12159        e.rms_norm(
12160            o,
12161            layer.post_attn_norm.float_data(),
12162            &mut cur,
12163            n_embd,
12164            t,
12165            eps,
12166        )?;
12167        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12168    }
12169
12170    pub(crate) fn gemma4_layer_tail_add_nq(
12171        &self,
12172        e: &Engine,
12173        layer: &crate::hybrid::HybridLayer,
12174        cur: &CudaSlice<f32>,
12175        x: &CudaSlice<f32>,
12176        t: usize,
12177        next_norm: Option<&CudaSlice<f32>>,
12178    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12179    {
12180        let n_embd = self.cfg.n_embd as usize;
12181        let bits = layer.gemma4.as_ref().unwrap();
12182        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12183        let mut xn = e.uninit(t * n_embd)?;
12184        match next_norm {
12185            Some(w) => {
12186                let pair = e.add_scale_rms_norm_q8_1(
12187                    &sn,
12188                    &attn_out,
12189                    bits.layer_scale,
12190                    w,
12191                    &mut xn,
12192                    n_embd,
12193                    t,
12194                    self.cfg.rms_eps,
12195                )?;
12196                Ok((xn, Some(pair)))
12197            }
12198            None => {
12199                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12200                Ok((xn, None))
12201            }
12202        }
12203    }
12204
12205    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12206    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12207    fn gemma4_forward(
12208        &self,
12209        e: &Engine,
12210        tokens: &[u32],
12211        last_only: bool,
12212    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12213        // E4B routes to its own forward regardless of the caller's entry point (forward /
12214        // forward_last / prime paths all funnel here for gemma4).
12215        if self.is_gemma4_e4b() {
12216            return self.gemma4_e4b_forward(e, tokens, last_only);
12217        }
12218        let n_embd = self.cfg.n_embd as usize;
12219        let t = tokens.len();
12220        let pos: Vec<i32> = (0..t as i32).collect();
12221        let pos_d = e.htod_i32(&pos)?;
12222
12223        let mut x = self.embed(e, tokens)?;
12224        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12225        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12226        // the bring-up bisect vs llama-eval-callback node stats.
12227        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12228        let stat =
12229            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12230                let h = e.dtoh(x)?;
12231                let bad = h.iter().filter(|v| !v.is_finite()).count();
12232                let mx = h
12233                    .iter()
12234                    .filter(|v| v.is_finite())
12235                    .fold(0.0f32, |m, v| m.max(v.abs()));
12236                eprintln!(
12237                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12238                    &h[..3]
12239                );
12240                Ok(())
12241            };
12242        if probe {
12243            stat(e, &x, "embed")?;
12244        }
12245        for (il, layer) in self.layers.iter().enumerate() {
12246            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12247            if probe {
12248                stat(e, &x, &format!("L{il}"))?;
12249            }
12250        }
12251        let mut hn = e.zeros(t * n_embd)?;
12252        e.rms_norm(
12253            &x,
12254            self.output_norm.float_data(),
12255            &mut hn,
12256            n_embd,
12257            t,
12258            self.cfg.rms_eps,
12259        )?;
12260        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12261        let n_vocab = self.output.out_features();
12262        let logits = if last_only {
12263            let hv = e.view(&hn, t * n_embd);
12264            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12265            let mut hlast = e.zeros(n_embd)?;
12266            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12267            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12268            e.softcap(&mut ld, cap, n_vocab)?;
12269            self.gemma4_suppress(e, &mut ld, 1)?;
12270            e.dtoh(&ld)?
12271        } else {
12272            let mut ld = e.matmul(&self.output, &hn, t)?;
12273            e.softcap(&mut ld, cap, t * n_vocab)?;
12274            self.gemma4_suppress(e, &mut ld, t)?;
12275            e.dtoh(&ld)?
12276        };
12277        Ok(logits)
12278    }
12279
12280    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12281    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12282    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12283    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12284    pub(crate) fn gemma4_prime(
12285        &self,
12286        e: &Engine,
12287        tokens: &[u32],
12288        cache: &mut Cache,
12289        overlay: Option<&crate::vision::EmbedOverlay>,
12290    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12291        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12292        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12293        // whole worker process on this line. The worker now primes gemma4 monolithically and
12294        // routes continuation suffixes tokenwise; this is the per-request backstop.
12295        if cache.pos != 0 {
12296            return Err(
12297                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12298                        — prime the full prompt in one call or decode tokenwise"
12299                    .into(),
12300            );
12301        }
12302        let n_embd = self.cfg.n_embd as usize;
12303        let eps = self.cfg.rms_eps;
12304        let t = tokens.len();
12305        let pos: Vec<i32> = (0..t as i32).collect();
12306        let pos_d = e.htod_i32(&pos)?;
12307        let mut x = self.embed(e, tokens)?;
12308        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12309        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12310        // sqrt(n_embd) text scale — the reference scales token batches only
12311        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12312        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12313        // bidirectional within itself, causal+SWA everywhere else, matching the
12314        // reference's llama_set_causal_attn(false) image batch exactly.
12315        let island: Option<CudaSlice<i32>> = match overlay {
12316            Some(ov) => {
12317                let mut span_id = vec![-1i32; t];
12318                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12319                    if pos + n_rows > t {
12320                        return Err(format!(
12321                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12322                            pos + n_rows
12323                        )
12324                        .into());
12325                    }
12326                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12327                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12328                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12329                        *s = i as i32;
12330                    }
12331                }
12332                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12333                // keep the plain causal mask. Exists only so the decisive probe can show
12334                // the island mask itself changes the answer; never on in serving.
12335                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12336                    None
12337                } else {
12338                    Some(e.htod_i32(&span_id)?)
12339                }
12340            }
12341            None => None,
12342        };
12343        for (il, layer) in self.layers.iter().enumerate() {
12344            let mut h = e.zeros(t * n_embd)?;
12345            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12346            let Mixer::Full(fa) = &layer.mixer else {
12347                panic!("gemma4 layer not full-attn")
12348            };
12349            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12350            if trace {
12351                let v = e.dtoh(&h)?;
12352                let nan = v.iter().filter(|x| x.is_nan()).count();
12353                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12354            }
12355            let o =
12356                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12357            if trace {
12358                let v = e.dtoh(&o)?;
12359                let nan = v.iter().filter(|x| x.is_nan()).count();
12360                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12361            }
12362            let mut cur = e.zeros(t * n_embd)?;
12363            e.rms_norm(
12364                &o,
12365                layer.post_attn_norm.float_data(),
12366                &mut cur,
12367                n_embd,
12368                t,
12369                eps,
12370            )?;
12371            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12372            self.dflash_tap(e, cache, il, &x, t)?;
12373            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12374            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12375                let h = e.dtoh(&x)?;
12376                let nan = h.iter().filter(|v| v.is_nan()).count();
12377                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12378                eprintln!(
12379                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12380                    h.len()
12381                );
12382                if nan > 0 {
12383                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12384                }
12385            }
12386        }
12387        cache.pos += t;
12388        let hiddens = e.clone_dtod(&x)?;
12389        let xv = e.view(&x, t * n_embd);
12390        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12391        let mut h_seed = e.zeros(n_embd)?;
12392        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12393        let mut hn = e.uninit(n_embd)?;
12394        e.rms_norm(
12395            &h_seed,
12396            self.output_norm.float_data(),
12397            &mut hn,
12398            n_embd,
12399            1,
12400            eps,
12401        )?;
12402        let mut ld = e.matmul(&self.output, &hn, 1)?;
12403        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12404        e.softcap(&mut ld, cap, self.output.out_features())?;
12405        self.gemma4_suppress(e, &mut ld, 1)?;
12406        let logits = e.dtoh(&ld)?;
12407        Ok((logits, h_seed, hiddens))
12408    }
12409
12410    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12411    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12412    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12413    /// fused norm emits q8 directly — the f32 h never materializes).
12414    fn gemma4_decode_attn(
12415        &self,
12416        e: &Engine,
12417        fa: &crate::hybrid::FullAttnLayer,
12418        il: usize,
12419        hq: &CudaSlice<i8>,
12420        hdq: &CudaSlice<f32>,
12421        pos_d: &CudaSlice<i32>,
12422        cache: &mut Cache,
12423    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12424        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12425        let eps = self.cfg.rms_eps;
12426        let aux = self.gemma4_aux.as_ref().unwrap();
12427        let ones = aux.ones(e);
12428        #[cfg(debug_assertions)]
12429        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12430        let (hq, hdq) = (hq, hdq);
12431        let h0 = e.zeros(0)?;
12432        let h = &h0;
12433        let (q0, k0, v0) = if swa {
12434            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12435                Some(t3) => t3,
12436                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12437                // match — fuse the uniform (q,k) pair and take v as its own single.
12438                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12439                    Some((q0, k0)) => {
12440                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12441                        (q0, k0, v0)
12442                    }
12443                    None => (
12444                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12445                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12446                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12447                    ),
12448                },
12449            }
12450        } else {
12451            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12452                Some(p) => p,
12453                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12454                    Some(p) => p,
12455                    None => (
12456                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12457                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12458                    ),
12459                },
12460            };
12461            let v0 = e.clone_dtod(&k0)?;
12462            (q0, k0, v0)
12463        };
12464        let mut q = e.uninit(nh * hd)?;
12465        let mut k = e.uninit(nkv * hd)?;
12466        let mut v = e.uninit(nkv * hd)?;
12467        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12468        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12469        let ff = if swa {
12470            None
12471        } else {
12472            Some(
12473                aux.rope_freqs(e)
12474                    .expect("gemma4 global rope needs rope_freqs.weight"),
12475            )
12476        };
12477        #[cfg(debug_assertions)]
12478        if let Some(ff) = ff {
12479            crate::debug_assert_tensor_stream_device(
12480                ff,
12481                &e.stream(),
12482                "gemma4_decode_attn.rope_freqs",
12483            );
12484        }
12485        let kvl = cache.kv[il].as_mut().unwrap();
12486        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12487        if crate::Engine::qkv_append_on() {
12488            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12489            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12490            // twin of the dc fold — bit-identical bodies, one launch per layer.
12491            e.rms_norm_qkv_rope_append(
12492                &q0,
12493                &k0,
12494                &v0,
12495                fa.q_norm.float_data(),
12496                fa.k_norm.float_data(),
12497                ones,
12498                &mut q,
12499                &mut k,
12500                &mut v,
12501                hd,
12502                self.gemma4_rope_dims(il),
12503                nh,
12504                nkv,
12505                pos_d,
12506                nh,
12507                nkv,
12508                base,
12509                1.0,
12510                ff,
12511                eps,
12512                &mut kvl.k,
12513                &mut kvl.v,
12514                kvl.len,
12515                kvl.k_tok_bytes,
12516                kvl.v_tok_bytes,
12517                kv_fp8,
12518            )?;
12519        } else {
12520            e.rms_norm_qkv_rope(
12521                &q0,
12522                &k0,
12523                &v0,
12524                fa.q_norm.float_data(),
12525                fa.k_norm.float_data(),
12526                ones,
12527                &mut q,
12528                &mut k,
12529                &mut v,
12530                hd,
12531                self.gemma4_rope_dims(il),
12532                nh,
12533                nkv,
12534                pos_d,
12535                nh,
12536                nkv,
12537                base,
12538                1.0,
12539                ff,
12540                eps,
12541            )?;
12542            e.append_kv_quantized(
12543                &k,
12544                &v,
12545                &mut kvl.k,
12546                &mut kvl.v,
12547                kvl.len,
12548                kvl.kv_dim_k,
12549                kvl.kv_dim_v,
12550                kvl.k_tok_bytes,
12551                kvl.v_tok_bytes,
12552                kv_fp8,
12553            )?;
12554        }
12555        kvl.len += 1;
12556        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12557        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12558        // positional). Globals attend the full history.
12559        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12560        let mut attn = e.uninit(nh * hd)?;
12561        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12562        if !swa
12563            && hd == 512
12564            && kvl.len >= crate::fa512_min_tkv()
12565            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12566        {
12567            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12568            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12569            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12570            let base = kvl.len as i32;
12571            e.i32_set_k(&mut kvl.len_d, base)?;
12572            e.fa_decode_rows(
12573                &q,
12574                &kp,
12575                &vp,
12576                &mut attn,
12577                hd,
12578                nh,
12579                nkv,
12580                kvl.len - 1,
12581                1,
12582                scale,
12583                kvl.k_tok_bytes,
12584                kvl.v_tok_bytes,
12585                Some((&kvl.len_d, -1)),
12586                false,
12587                false,
12588                None,
12589            )?;
12590            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12591        }
12592        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12593        if swa
12594            && kvl.len > win
12595            && hd == 256
12596            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12597        {
12598            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12599            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12600            let base = kvl.len as i32;
12601            e.i32_set_k(&mut kvl.len_d, base)?;
12602            e.fa_decode_rows_w(
12603                &q,
12604                &kp,
12605                &vp,
12606                &mut attn,
12607                hd,
12608                nh,
12609                nkv,
12610                &kvl.len_d,
12611                -1,
12612                1,
12613                scale,
12614                win,
12615                kvl.k_tok_bytes,
12616                kvl.v_tok_bytes,
12617                None,
12618            )?;
12619            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12620        }
12621        let (off_tok, t_kv) = if swa && kvl.len > win {
12622            (kvl.len - win, win)
12623        } else {
12624            (0, kvl.len)
12625        };
12626        let k_view = e.view_u8_range(
12627            &kvl.k,
12628            off_tok * kvl.k_tok_bytes,
12629            (off_tok + t_kv) * kvl.k_tok_bytes,
12630        );
12631        let v_view = e.view_u8_range(
12632            &kvl.v,
12633            off_tok * kvl.v_tok_bytes,
12634            (off_tok + t_kv) * kvl.v_tok_bytes,
12635        );
12636        e.fa_decode_kvmod(
12637            &q,
12638            &k_view,
12639            &v_view,
12640            &mut attn,
12641            hd,
12642            nh,
12643            nkv,
12644            t_kv,
12645            scale,
12646            kvl.k_tok_bytes,
12647            kvl.v_tok_bytes,
12648            swa && crate::Engine::wkv_on(),
12649        )?;
12650        Ok(e.matmul(&fa.wo, &attn, 1)?)
12651    }
12652
12653    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
12654    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
12655    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
12656    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
12657    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
12658    /// in-graph; the driver gates).
12659    #[allow(clippy::too_many_arguments)]
12660    pub fn gemma4_decode_step_dc(
12661        &self,
12662        e: &Engine,
12663        token_d: &CudaSlice<u32>,
12664        pos_d: &mut CudaSlice<i32>,
12665        embd_gpu: &CudaSlice<u8>,
12666        embd_qt: i32,
12667        embd_rb: usize,
12668        cache: &mut Cache,
12669        n_vocab: usize,
12670        cap_bucket_max: Option<(usize, usize)>,
12671    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
12672        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
12673        self.gemma4_decode_step_dc_into(
12674            e,
12675            token_d,
12676            pos_d,
12677            embd_gpu,
12678            embd_qt,
12679            embd_rb,
12680            cache,
12681            n_vocab,
12682            cap_bucket_max,
12683            &mut tok_out,
12684        )?;
12685        Ok(tok_out)
12686    }
12687
12688    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
12689    /// every replay; pass `token_d` itself for the self-feeding graph loop).
12690    #[allow(clippy::too_many_arguments)]
12691    pub fn gemma4_decode_step_dc_into(
12692        &self,
12693        e: &Engine,
12694        token_d: &CudaSlice<u32>,
12695        pos_d: &mut CudaSlice<i32>,
12696        embd_gpu: &CudaSlice<u8>,
12697        embd_qt: i32,
12698        embd_rb: usize,
12699        cache: &mut Cache,
12700        n_vocab: usize,
12701        cap_bucket_max: Option<(usize, usize)>,
12702        tok_out: &mut CudaSlice<u32>,
12703    ) -> Result<(), Box<dyn std::error::Error>> {
12704        let n_embd = self.cfg.n_embd as usize;
12705        let eps = self.cfg.rms_eps;
12706        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
12707        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
12708        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12709        let n_layers = self.layers.len();
12710        for (il, layer) in self.layers.iter().enumerate() {
12711            let (hq, hdq) = match h_carry.take() {
12712                Some(p) => p,
12713                None => {
12714                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
12715                }
12716            };
12717            let Mixer::Full(fa) = &layer.mixer else {
12718                panic!("gemma4 layer {il} not full-attn")
12719            };
12720            let o =
12721                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
12722            let next_norm = if il + 1 < n_layers {
12723                Some(self.layers[il + 1].attn_norm.float_data())
12724            } else {
12725                None
12726            };
12727            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
12728            x = xn;
12729            h_carry = hn;
12730        }
12731        let mut hn = e.uninit(n_embd)?;
12732        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
12733        let mut logits = e.matmul(&self.output, &hn, 1)?;
12734        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
12735        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
12736        e.inc_seqlen(pos_d)?;
12737        if cap_bucket_max.is_none() {
12738            cache.pos += 1;
12739        }
12740        Ok(())
12741    }
12742
12743    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
12744    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
12745    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
12746    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
12747
12748    /// Build the slot set (call OUTSIDE any capture).
12749    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
12750        let n_embd = self.cfg.n_embd as usize;
12751        let n_vocab = self.output.out_features();
12752        let n_layers = self.layers.len();
12753        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
12754        for il in 0..n_layers {
12755            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
12756            qmax = qmax.max(nh * hd);
12757            kvmax = kvmax.max(nkv * hd);
12758            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
12759                ffmax = ffmax.max(ffn_gate.out_features());
12760            }
12761        }
12762        Ok(G4DcSlots {
12763            x: e.uninit(n_embd)?,
12764            xn: e.uninit(n_embd)?,
12765            cur: e.uninit(n_embd)?,
12766            hq: e.alloc_i8_uninit(n_embd)?,
12767            hd_: e.uninit(n_embd / 32)?,
12768            q0: e.uninit(qmax)?,
12769            k0: e.uninit(kvmax)?,
12770            v0: e.uninit(kvmax)?,
12771            q: e.uninit(qmax)?,
12772            k: e.uninit(kvmax)?,
12773            v: e.uninit(kvmax)?,
12774            attn: e.uninit(qmax)?,
12775            o: e.uninit(n_embd)?,
12776            attn_out: e.uninit(n_embd)?,
12777            zsh: e.uninit(n_embd)?,
12778            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
12779            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
12780            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
12781            zd: e.uninit(n_embd.max(qmax) / 32)?,
12782            gate: e.uninit(ffmax)?,
12783            up: e.uninit(ffmax)?,
12784            act: e.uninit(ffmax)?,
12785            actq: e.alloc_i8_uninit(ffmax)?,
12786            actd: e.uninit(ffmax / 32)?,
12787            f0: e.uninit(n_embd)?,
12788            sn: e.uninit(n_embd)?,
12789            hn: e.uninit(n_embd)?,
12790            logits: e.uninit(n_vocab)?,
12791        })
12792    }
12793
12794    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
12795    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
12796    fn g4_matvec_m1_into(
12797        &self,
12798        e: &Engine,
12799        w: &crate::model::GpuTensor,
12800        aq: &CudaSlice<i8>,
12801        ad: &CudaSlice<f32>,
12802        y: &mut CudaSlice<f32>,
12803    ) -> Result<(), Box<dyn std::error::Error>> {
12804        use crate::model::GpuTensor;
12805        let (bytes, qtype, row_bytes, scale, rp) = match w {
12806            GpuTensor::Quant {
12807                bytes,
12808                qtype,
12809                row_bytes,
12810                scale,
12811                rp,
12812                ..
12813            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12814            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
12815        };
12816        let (mbytes, mrp) = match w {
12817            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12818            _ => (bytes, rp),
12819        };
12820        e.qmatvec_mmvq_into(
12821            mbytes,
12822            aq,
12823            ad,
12824            1,
12825            w.in_features(),
12826            w.out_features(),
12827            qtype,
12828            row_bytes,
12829            scale,
12830            mrp,
12831            y,
12832        )
12833    }
12834
12835    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
12836    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
12837    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
12838    #[allow(clippy::too_many_arguments)]
12839    pub fn gemma4_decode_step_dc_slotted(
12840        &self,
12841        e: &Engine,
12842        token_d: &CudaSlice<u32>,
12843        pos_d: &mut CudaSlice<i32>,
12844        embd_gpu: &CudaSlice<u8>,
12845        embd_qt: i32,
12846        embd_rb: usize,
12847        cache: &mut Cache,
12848        n_vocab: usize,
12849        cap_bucket_max: Option<(usize, usize)>,
12850        sl: &mut G4DcSlots,
12851        tok_out: &mut CudaSlice<u32>,
12852        ring: Option<(&mut CudaSlice<u32>, usize)>,
12853    ) -> Result<(), Box<dyn std::error::Error>> {
12854        let n_embd = self.cfg.n_embd as usize;
12855        let eps = self.cfg.rms_eps;
12856        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
12857        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
12858        let n_layers = self.layers.len();
12859        let mut has_carry = false;
12860        for il in 0..n_layers {
12861            if !has_carry {
12862                e.rms_norm_q8_1_into(
12863                    &sl.x,
12864                    self.layers[il].attn_norm.float_data(),
12865                    n_embd,
12866                    1,
12867                    eps,
12868                    &mut sl.hq,
12869                    &mut sl.hd_,
12870                )?;
12871            }
12872            has_carry = true;
12873            let layer = &self.layers[il];
12874            let Mixer::Full(fa) = &layer.mixer else {
12875                panic!("gemma4 layer {il} not full-attn")
12876            };
12877            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
12878            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
12879            // the standalone norm only survives on the unfused seam arm.
12880            if !Engine::g4_pnfold_on() {
12881                e.rms_norm(
12882                    &sl.o,
12883                    layer.post_attn_norm.float_data(),
12884                    &mut sl.cur,
12885                    n_embd,
12886                    1,
12887                    eps,
12888                )?;
12889            }
12890            let next_norm = if il + 1 < n_layers {
12891                Some(self.layers[il + 1].attn_norm.float_data())
12892            } else {
12893                None
12894            };
12895            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
12896            std::mem::swap(&mut sl.x, &mut sl.xn);
12897        }
12898        e.rms_norm(
12899            &sl.x,
12900            self.output_norm.float_data(),
12901            &mut sl.hn,
12902            n_embd,
12903            1,
12904            eps,
12905        )?;
12906        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
12907        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
12908        {
12909            let (zq, zd) = (&sl.zq, &sl.zd);
12910            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
12911            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
12912            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
12913        }
12914        self.gemma4_suppress(e, &mut sl.logits, 1)?;
12915        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
12916        if let Some((ring, base)) = ring {
12917            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
12918            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
12919            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
12920            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
12921        }
12922        e.inc_seqlen(pos_d)?;
12923        if cap_bucket_max.is_none() {
12924            cache.pos += 1;
12925        }
12926        Ok(())
12927    }
12928
12929    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
12930    #[allow(clippy::too_many_arguments)]
12931    fn gemma4_decode_attn_dc_slotted(
12932        &self,
12933        e: &Engine,
12934        fa: &crate::hybrid::FullAttnLayer,
12935        il: usize,
12936        pos_d: &CudaSlice<i32>,
12937        cache: &mut Cache,
12938        cap_bucket_max: Option<(usize, usize)>,
12939        sl: &mut G4DcSlots,
12940    ) -> Result<(), Box<dyn std::error::Error>> {
12941        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12942        let eps = self.cfg.rms_eps;
12943        let aux = self.gemma4_aux.as_ref().unwrap();
12944        let ones = aux.ones(e);
12945        #[cfg(debug_assertions)]
12946        crate::debug_assert_tensor_stream_device(
12947            ones,
12948            &e.stream(),
12949            "gemma4_decode_attn_dc_slotted.ones",
12950        );
12951        {
12952            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
12953            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
12954            if swa {
12955                if !e.matmul_q4_fused3_into(
12956                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
12957                )? {
12958                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
12959                    // (q,k) pair, v through the generic m1 slot matvec — the same two
12960                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
12961                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12962                    {
12963                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
12964                    } else {
12965                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
12966                    }
12967                }
12968            } else {
12969                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12970                    && !e
12971                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
12972                {
12973                    return Err("slotted step: fused2 unavailable".into());
12974                }
12975                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
12976                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
12977            }
12978        }
12979        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
12980        // kernel-for-kernel (graph stream-identity gate).
12981        let ff = if swa {
12982            None
12983        } else {
12984            Some(
12985                aux.rope_freqs(e)
12986                    .expect("gemma4 global rope needs rope_freqs.weight"),
12987            )
12988        };
12989        #[cfg(debug_assertions)]
12990        if let Some(ff) = ff {
12991            crate::debug_assert_tensor_stream_device(
12992                ff,
12993                &e.stream(),
12994                "gemma4_decode_attn_dc_slotted.rope_freqs",
12995            );
12996        }
12997        let kvl = cache.kv[il].as_mut().unwrap();
12998        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12999        if crate::Engine::qkv_append_on() {
13000            // append fold (2026-07-23): mirrors dc_into.
13001            e.rms_norm_qkv_rope_append_dc(
13002                &sl.q0,
13003                &sl.k0,
13004                &sl.v0,
13005                fa.q_norm.float_data(),
13006                fa.k_norm.float_data(),
13007                ones,
13008                &mut sl.q,
13009                &mut sl.k,
13010                &mut sl.v,
13011                hd,
13012                self.gemma4_rope_dims(il),
13013                nh,
13014                nkv,
13015                pos_d,
13016                nh,
13017                nkv,
13018                base,
13019                1.0,
13020                ff,
13021                eps,
13022                &mut kvl.k,
13023                &mut kvl.v,
13024                &kvl.len_d,
13025                kvl.k_tok_bytes,
13026                kvl.v_tok_bytes,
13027                kv_fp8,
13028            )?;
13029        } else {
13030            e.rms_norm_qkv_rope(
13031                &sl.q0,
13032                &sl.k0,
13033                &sl.v0,
13034                fa.q_norm.float_data(),
13035                fa.k_norm.float_data(),
13036                ones,
13037                &mut sl.q,
13038                &mut sl.k,
13039                &mut sl.v,
13040                hd,
13041                self.gemma4_rope_dims(il),
13042                nh,
13043                nkv,
13044                pos_d,
13045                nh,
13046                nkv,
13047                base,
13048                1.0,
13049                ff,
13050                eps,
13051            )?;
13052            e.append_kv_quantized_dc(
13053                &sl.k,
13054                &sl.v,
13055                &mut kvl.k,
13056                &mut kvl.v,
13057                &kvl.len_d,
13058                kvl.kv_dim_k,
13059                kvl.kv_dim_v,
13060                kvl.k_tok_bytes,
13061                kvl.v_tok_bytes,
13062                kv_fp8,
13063            )?;
13064        }
13065        e.inc_seqlen(&mut kvl.len_d)?;
13066        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13067        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13068        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13069        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13070        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13071        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13072        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13073        // the dc_into arm branch-for-branch (stream gate).
13074        let mut fa_q8 = false;
13075        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13076            e.fa_decode_rows(
13077                &sl.q,
13078                &k_view,
13079                &v_view,
13080                &mut sl.attn,
13081                hd,
13082                nh,
13083                nkv,
13084                b_glob - 1,
13085                1,
13086                scale,
13087                kvl.k_tok_bytes,
13088                kvl.v_tok_bytes,
13089                Some((&kvl.len_d, -1)),
13090                false,
13091                false,
13092                Some((&mut sl.zq, &mut sl.zd)),
13093            )?;
13094            fa_q8 = true;
13095        } else if swa && b_swa > win && hd == 256 && rows_on {
13096            e.fa_decode_rows_w(
13097                &sl.q,
13098                &k_view,
13099                &v_view,
13100                &mut sl.attn,
13101                hd,
13102                nh,
13103                nkv,
13104                &kvl.len_d,
13105                -1,
13106                1,
13107                scale,
13108                win,
13109                kvl.k_tok_bytes,
13110                kvl.v_tok_bytes,
13111                Some((&mut sl.zq, &mut sl.zd)),
13112            )?;
13113            fa_q8 = true;
13114        } else {
13115            let b = if swa { b_swa } else { b_glob };
13116            e.fa_decode_dc(
13117                &sl.q,
13118                &k_view,
13119                &v_view,
13120                &mut sl.attn,
13121                hd,
13122                nh,
13123                nkv,
13124                &kvl.len_d,
13125                b,
13126                scale,
13127                kvl.k_tok_bytes,
13128                kvl.v_tok_bytes,
13129                swa && crate::Engine::wkv_on(),
13130            )?;
13131        }
13132        if !fa_q8 {
13133            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13134            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13135        }
13136        {
13137            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13138            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13139            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13140        }
13141        Ok(())
13142    }
13143
13144    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13145    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13146    fn gemma4_layer_tail_slotted(
13147        &self,
13148        e: &Engine,
13149        layer: &crate::hybrid::HybridLayer,
13150        next_norm: Option<&CudaSlice<f32>>,
13151        sl: &mut G4DcSlots,
13152    ) -> Result<(), Box<dyn std::error::Error>> {
13153        let n_embd = self.cfg.n_embd as usize;
13154        let eps = self.cfg.rms_eps;
13155        let bits = layer.gemma4.as_ref().unwrap();
13156        let crate::hybrid::Ffn::Dense {
13157            ffn_gate,
13158            ffn_up,
13159            ffn_down,
13160        } = &layer.ffn
13161        else {
13162            return Err("slotted tail: dense ffn only".into());
13163        };
13164        let pnfold = Engine::g4_pnfold_on();
13165        if pnfold {
13166            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13167            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13168            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13169            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13170            e.rms_pre_add_rms_norm_q8z_into(
13171                or,
13172                layer.post_attn_norm.float_data(),
13173                xr,
13174                bits.ffn_norm.float_data(),
13175                &mut sl.attn_out,
13176                &mut sl.zsh,
13177                n_embd,
13178                1,
13179                eps,
13180                &mut sl.zq,
13181                &mut sl.zd,
13182            )?;
13183        } else {
13184            e.add_rms_norm(
13185                &sl.cur,
13186                &sl.x,
13187                bits.ffn_norm.float_data(),
13188                &mut sl.attn_out,
13189                &mut sl.zsh,
13190                n_embd,
13191                1,
13192                eps,
13193            )?;
13194        }
13195        let n_ff = ffn_gate.out_features();
13196        if !pnfold {
13197            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13198            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13199        }
13200        {
13201            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13202            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13203            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13204                && !e.matmul_nvfp4_fused2_into(
13205                    ffn_gate,
13206                    ffn_up,
13207                    zq,
13208                    zd,
13209                    &mut sl.gate,
13210                    &mut sl.up,
13211                )?
13212            {
13213                return Err("slotted tail: ffn fused2 unavailable".into());
13214            }
13215        }
13216        debug_assert!(e.uses_q8_1_fast(ffn_down));
13217        {
13218            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13219            let upv = e.view(upr, n_ff);
13220            let up_all = upv.slice(0..n_ff);
13221            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13222            e.gelu_tanh_mul_q8_1_into(
13223                gr,
13224                &up_all,
13225                &mut sl.act,
13226                n_ff,
13227                1,
13228                &mut sl.actq,
13229                &mut sl.actd,
13230            )?;
13231        }
13232        {
13233            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13234            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13235            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13236        }
13237        if pnfold {
13238            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13239            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13240            if let Some(w) = next_norm {
13241                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13242                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13243                e.rms_pre_add_scale_rms_norm_q8_1_into(
13244                    f0r,
13245                    bits.post_ffw_norm.float_data(),
13246                    aor,
13247                    bits.layer_scale,
13248                    w,
13249                    &mut sl.xn,
13250                    n_embd,
13251                    1,
13252                    eps,
13253                    &mut sl.hq,
13254                    &mut sl.hd_,
13255                )?;
13256                return Ok(());
13257            }
13258        }
13259        e.rms_norm(
13260            &sl.f0,
13261            bits.post_ffw_norm.float_data(),
13262            &mut sl.sn,
13263            n_embd,
13264            1,
13265            eps,
13266        )?;
13267        match next_norm {
13268            Some(w) => {
13269                e.add_scale_rms_norm_q8_1_into(
13270                    &sl.sn,
13271                    &sl.attn_out,
13272                    bits.layer_scale,
13273                    w,
13274                    &mut sl.xn,
13275                    n_embd,
13276                    1,
13277                    eps,
13278                    &mut sl.hq,
13279                    &mut sl.hd_,
13280                )?;
13281            }
13282            None => {
13283                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13284            }
13285        }
13286        Ok(())
13287    }
13288
13289    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13290    #[allow(clippy::too_many_arguments)]
13291    fn gemma4_decode_attn_dc(
13292        &self,
13293        e: &Engine,
13294        fa: &crate::hybrid::FullAttnLayer,
13295        il: usize,
13296        hq: &CudaSlice<i8>,
13297        hdq: &CudaSlice<f32>,
13298        pos_d: &CudaSlice<i32>,
13299        cache: &mut Cache,
13300        cap_bucket_max: Option<(usize, usize)>,
13301    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13302        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13303        let eps = self.cfg.rms_eps;
13304        let aux = self.gemma4_aux.as_ref().unwrap();
13305        let ones = aux.ones(e);
13306        #[cfg(debug_assertions)]
13307        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13308        let (q0, k0, v0) = if swa {
13309            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13310                Some(t3) => t3,
13311                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13312                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13313                    Some((q0, k0)) => {
13314                        let h0 = e.zeros(0)?;
13315                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13316                        (q0, k0, v0)
13317                    }
13318                    None => {
13319                        let h0 = e.zeros(0)?;
13320                        (
13321                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13322                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13323                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13324                        )
13325                    }
13326                },
13327            }
13328        } else {
13329            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13330                Some(p) => p,
13331                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13332                    Some(p) => p,
13333                    None => {
13334                        let h0 = e.zeros(0)?;
13335                        (
13336                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13337                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13338                        )
13339                    }
13340                },
13341            };
13342            let v0 = e.clone_dtod(&k0)?;
13343            (q0, k0, v0)
13344        };
13345        let mut q = e.uninit(nh * hd)?;
13346        let mut k = e.uninit(nkv * hd)?;
13347        let mut v = e.uninit(nkv * hd)?;
13348        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13349        let ff = if swa {
13350            None
13351        } else {
13352            Some(
13353                aux.rope_freqs(e)
13354                    .expect("gemma4 global rope needs rope_freqs.weight"),
13355            )
13356        };
13357        #[cfg(debug_assertions)]
13358        if let Some(ff) = ff {
13359            crate::debug_assert_tensor_stream_device(
13360                ff,
13361                &e.stream(),
13362                "gemma4_decode_attn_dc.rope_freqs",
13363            );
13364        }
13365        let kvl = cache.kv[il].as_mut().unwrap();
13366        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13367        if crate::Engine::qkv_append_on() {
13368            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13369            e.rms_norm_qkv_rope_append_dc(
13370                &q0,
13371                &k0,
13372                &v0,
13373                fa.q_norm.float_data(),
13374                fa.k_norm.float_data(),
13375                ones,
13376                &mut q,
13377                &mut k,
13378                &mut v,
13379                hd,
13380                self.gemma4_rope_dims(il),
13381                nh,
13382                nkv,
13383                pos_d,
13384                nh,
13385                nkv,
13386                base,
13387                1.0,
13388                ff,
13389                eps,
13390                &mut kvl.k,
13391                &mut kvl.v,
13392                &kvl.len_d,
13393                kvl.k_tok_bytes,
13394                kvl.v_tok_bytes,
13395                kv_fp8,
13396            )?;
13397        } else {
13398            e.rms_norm_qkv_rope(
13399                &q0,
13400                &k0,
13401                &v0,
13402                fa.q_norm.float_data(),
13403                fa.k_norm.float_data(),
13404                ones,
13405                &mut q,
13406                &mut k,
13407                &mut v,
13408                hd,
13409                self.gemma4_rope_dims(il),
13410                nh,
13411                nkv,
13412                pos_d,
13413                nh,
13414                nkv,
13415                base,
13416                1.0,
13417                ff,
13418                eps,
13419            )?;
13420            e.append_kv_quantized_dc(
13421                &k,
13422                &v,
13423                &mut kvl.k,
13424                &mut kvl.v,
13425                &kvl.len_d,
13426                kvl.kv_dim_k,
13427                kvl.kv_dim_v,
13428                kvl.k_tok_bytes,
13429                kvl.v_tok_bytes,
13430                kv_fp8,
13431            )?;
13432        }
13433        e.inc_seqlen(&mut kvl.len_d)?;
13434        let mut attn = e.uninit(nh * hd)?;
13435        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13436        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13437        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13438        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13439        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13440        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13441        // (gemma4_e4b_attn, +0.65% valid window).
13442        match cap_bucket_max {
13443            None => {
13444                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13445                // decode (SWA layers attend the last `sliding_window` keys); the device
13446                // counters carry only the append slot + the graph seam.
13447                kvl.len += 1;
13448                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13449                if !swa
13450                    && hd == 512
13451                    && kvl.len >= crate::fa512_min_tkv()
13452                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13453                {
13454                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13455                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13456                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13457                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13458                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13459                    e.fa_decode_rows(
13460                        &q,
13461                        &kp,
13462                        &vp,
13463                        &mut attn,
13464                        hd,
13465                        nh,
13466                        nkv,
13467                        kvl.len - 1,
13468                        1,
13469                        scale,
13470                        kvl.k_tok_bytes,
13471                        kvl.v_tok_bytes,
13472                        Some((&kvl.len_d, -1)),
13473                        false,
13474                        false,
13475                        Some((&mut aq8, &mut ad8)),
13476                    )?;
13477                    fa_q8 = Some((aq8, ad8));
13478                } else if swa
13479                    && kvl.len > win
13480                    && hd == 256
13481                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13482                {
13483                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13484                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13485                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13486                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13487                    e.fa_decode_rows_w(
13488                        &q,
13489                        &kp,
13490                        &vp,
13491                        &mut attn,
13492                        hd,
13493                        nh,
13494                        nkv,
13495                        &kvl.len_d,
13496                        -1,
13497                        1,
13498                        scale,
13499                        win,
13500                        kvl.k_tok_bytes,
13501                        kvl.v_tok_bytes,
13502                        Some((&mut aq8, &mut ad8)),
13503                    )?;
13504                    fa_q8 = Some((aq8, ad8));
13505                } else {
13506                    let (off_tok, t_kv) = if swa && kvl.len > win {
13507                        (kvl.len - win, win)
13508                    } else {
13509                        (0, kvl.len)
13510                    };
13511                    let k_view = e.view_u8_range(
13512                        &kvl.k,
13513                        off_tok * kvl.k_tok_bytes,
13514                        (off_tok + t_kv) * kvl.k_tok_bytes,
13515                    );
13516                    let v_view = e.view_u8_range(
13517                        &kvl.v,
13518                        off_tok * kvl.v_tok_bytes,
13519                        (off_tok + t_kv) * kvl.v_tok_bytes,
13520                    );
13521                    e.fa_decode_kvmod(
13522                        &q,
13523                        &k_view,
13524                        &v_view,
13525                        &mut attn,
13526                        hd,
13527                        nh,
13528                        nkv,
13529                        t_kv,
13530                        scale,
13531                        kvl.k_tok_bytes,
13532                        kvl.v_tok_bytes,
13533                        swa && crate::Engine::wkv_on(),
13534                    )?;
13535                }
13536            }
13537            Some((b_swa, b_glob)) => {
13538                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13539                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13540                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13541                // the RUNG max for the rows family (kernels derive per-replay splits from
13542                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13543                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13544                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13545                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13546                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13547                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13548                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13549                    e.fa_decode_rows(
13550                        &q,
13551                        &k_view,
13552                        &v_view,
13553                        &mut attn,
13554                        hd,
13555                        nh,
13556                        nkv,
13557                        b_glob - 1,
13558                        1,
13559                        scale,
13560                        kvl.k_tok_bytes,
13561                        kvl.v_tok_bytes,
13562                        Some((&kvl.len_d, -1)),
13563                        false,
13564                        false,
13565                        Some((&mut aq8, &mut ad8)),
13566                    )?;
13567                    fa_q8 = Some((aq8, ad8));
13568                } else if swa && b_swa > win && hd == 256 && rows_on {
13569                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13570                    e.fa_decode_rows_w(
13571                        &q,
13572                        &k_view,
13573                        &v_view,
13574                        &mut attn,
13575                        hd,
13576                        nh,
13577                        nkv,
13578                        &kvl.len_d,
13579                        -1,
13580                        1,
13581                        scale,
13582                        win,
13583                        kvl.k_tok_bytes,
13584                        kvl.v_tok_bytes,
13585                        Some((&mut aq8, &mut ad8)),
13586                    )?;
13587                    fa_q8 = Some((aq8, ad8));
13588                } else {
13589                    let b = if swa { b_swa } else { b_glob };
13590                    e.fa_decode_dc(
13591                        &q,
13592                        &k_view,
13593                        &v_view,
13594                        &mut attn,
13595                        hd,
13596                        nh,
13597                        nkv,
13598                        &kvl.len_d,
13599                        b,
13600                        scale,
13601                        kvl.k_tok_bytes,
13602                        kvl.v_tok_bytes,
13603                        swa && crate::Engine::wkv_on(),
13604                    )?;
13605                }
13606            }
13607        }
13608        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
13609        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
13610        if let Some((aq8, ad8)) = fa_q8 {
13611            let mut y = e.uninit(fa.wo.out_features())?;
13612            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
13613            return Ok(y);
13614        }
13615        Ok(e.matmul(&fa.wo, &attn, 1)?)
13616    }
13617
13618    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
13619    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
13620    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
13621    /// views in-graph); caller gates and falls back to the dc-eager loop.
13622    pub fn gemma4_generate_graph(
13623        &self,
13624        e: &Engine,
13625        prompt_pos: usize,
13626        first_token: u32,
13627        cache: &mut Cache,
13628        max_new: usize,
13629        eos: &[u32],
13630        mut on_token: impl FnMut(u32) -> bool,
13631    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
13632        if self.is_gemma4_e4b() {
13633            return Err(
13634                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
13635                    .into(),
13636            );
13637        }
13638        use crate::decode::StopReason;
13639        let n_vocab = self.output.out_features();
13640        let n_embd = self.cfg.n_embd as usize;
13641        let embd_gpu = self
13642            .embd_gpu
13643            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13644        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13645        for kvl in cache.kv.iter_mut().flatten() {
13646            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
13647        }
13648        let mut token_d = e.stream().clone_htod(&[first_token])?;
13649        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
13650        let g4 = self.cfg.gemma4.as_ref().unwrap();
13651        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
13652        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
13653        let nkv_s = g4
13654            .head_count_kv
13655            .iter()
13656            .zip(g4.swa_pattern.iter())
13657            .find(|p| *p.1)
13658            .map(|p| *p.0 as usize)
13659            .unwrap_or(8);
13660        let nkv_g = g4
13661            .head_count_kv
13662            .iter()
13663            .zip(g4.swa_pattern.iter())
13664            .find(|p| !*p.1)
13665            .map(|p| *p.0 as usize)
13666            .unwrap_or(2);
13667        let mut graphs: std::collections::HashMap<
13668            ((bool, usize), (bool, usize), bool, bool),
13669            (
13670                cudarc::driver::CudaGraph,
13671                Vec<Box<dyn std::any::Any + Send>>,
13672            ),
13673        > = Default::default();
13674        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
13675        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
13676        let mut slots = self.g4_dc_slots(e)?;
13677        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
13678        // baked at the door entry (the modulo keeps every capture valid indefinitely).
13679        const RING: usize = 64;
13680        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
13681        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
13682        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
13683        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
13684        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
13685        const DRAIN: usize = 1;
13686        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
13687        let ring_base = prompt_pos;
13688        let mut out = Vec::with_capacity(max_new);
13689        let mut reason = StopReason::MaxNew;
13690        let mut next = first_token;
13691        let mut captures = 0usize;
13692        for _ in 0..max_new {
13693            out.push(next);
13694            if eos.contains(&next) {
13695                reason = StopReason::Eos;
13696                break;
13697            }
13698            if !on_token(next) {
13699                reason = StopReason::Callback;
13700                break;
13701            }
13702            let t_kv = cache.pos + 1;
13703            // Bucket key per ARM (graph arc step 3):
13704            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
13705            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
13706            //    the component collapses to a single marker).
13707            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
13708            //    at/above it — the kernel derives splits from len_d per replay, so buckets
13709            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
13710            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13711            let f512 = crate::fa512_min_tkv();
13712            let key_s = if t_kv > win {
13713                (true, usize::MAX)
13714            } else {
13715                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
13716            };
13717            let (key_g, rung_end) = if t_kv >= f512 {
13718                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
13719                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
13720                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
13721                ((true, end), end)
13722            } else {
13723                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
13724            };
13725            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
13726            if !graphs.contains_key(&key) {
13727                let bucket_max = (t_kv, rung_end);
13728                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
13729                let snap = cache.snapshot(e)?;
13730                let pos_save = e.dtoh_i32_one(&pos_d)?;
13731                let len_save: Vec<Option<i32>> = cache
13732                    .kv
13733                    .iter()
13734                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
13735                    .collect();
13736                let tok_save = e.dtoh_u32_one(&token_d)?;
13737                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
13738                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
13739                // regression class, and this door's measured -8.8%. The keeper pins warmup
13740                // transients so the captured graph holds kernel nodes only.
13741                let graph = {
13742                    let tok_ref = &mut token_d;
13743                    let pos_ref = &mut pos_d;
13744                    let cache_ref = &mut *cache;
13745                    let slots_ref = &mut slots;
13746                    let ring_ref = &mut ring;
13747                    e.capture_graph_retained_flags(
13748                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
13749                        |e| {
13750                        // self-feeding: the argmax writes token_d itself.
13751                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
13752                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
13753                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
13754                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
13755                                                           cache_ref, n_vocab, Some(bucket_max),
13756                                                           sl, tok_ref, Some((rg, ring_base)))
13757                    })?
13758                };
13759                cache.rollback(e, &snap, 0)?;
13760                e.set_i32_one(&mut pos_d, pos_save)?;
13761                for (il, ls) in len_save.iter().enumerate() {
13762                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
13763                        e.set_i32_one(&mut kvl.len_d, *v)?;
13764                    }
13765                }
13766                e.set_u32_one(&mut token_d, tok_save)?;
13767                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
13768                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
13769                        eprintln!("[graph-census] {c:?}");
13770                    }
13771                }
13772                graphs.insert(key, graph);
13773                captures += 1;
13774            }
13775            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
13776            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
13777            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
13778            // the budget; capture warmups already emitted their tokens through the ring.
13779            let mut chunk = 1usize;
13780            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
13781                .ok()
13782                .and_then(|v| v.parse().ok())
13783                .unwrap_or(DRAIN);
13784            while chunk < drain_cap && out.len() + chunk < max_new {
13785                let t_next = cache.pos + 1 + chunk;
13786                let key_s2 = if t_next > win {
13787                    (true, usize::MAX)
13788                } else {
13789                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
13790                };
13791                let key_g2 = if t_next >= f512 {
13792                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
13793                } else {
13794                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
13795                };
13796                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
13797                    break;
13798                }
13799                chunk += 1;
13800            }
13801            let g = &graphs.get(&key).unwrap().0;
13802            for _ in 0..chunk {
13803                g.launch()?;
13804            }
13805            e.stream().synchronize()?;
13806            let ringh = e.dtoh_u32(&ring)?;
13807            for j in 0..chunk {
13808                let pos_j = cache.pos + j;
13809                let tok_j = ringh[(pos_j - ring_base) % RING];
13810                cache.pos += 0; // advanced below in one shot
13811                if j + 1 == chunk {
13812                    next = tok_j;
13813                } else {
13814                    out.push(tok_j);
13815                    if eos.contains(&tok_j) || !on_token(tok_j) {
13816                        reason = if eos.contains(&tok_j) {
13817                            StopReason::Eos
13818                        } else {
13819                            StopReason::Callback
13820                        };
13821                        // roll device/host state back to the stop point.
13822                        let keep = cache.pos + j + 1;
13823                        e.set_i32_one(&mut pos_d, keep as i32)?;
13824                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13825                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
13826                            kvl.len = keep;
13827                        }
13828                        cache.pos = keep;
13829                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13830                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13831                        }
13832                        return Ok((out, reason));
13833                    }
13834                }
13835            }
13836            cache.pos += chunk;
13837            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
13838                kvl.len += chunk;
13839            }
13840        }
13841        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
13842            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
13843        }
13844        Ok((out, reason))
13845    }
13846
13847    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
13848    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
13849    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
13850    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
13851    /// logits (host) + advances cache.pos by t.
13852    pub(crate) fn gemma4_decode_step_t(
13853        &self,
13854        e: &Engine,
13855        tokens: &[u32],
13856        pos0: usize,
13857        cache: &mut Cache,
13858    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13859        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
13860    }
13861
13862    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
13863    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
13864    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
13865    pub(crate) fn gemma4_decode_step_t_am(
13866        &self,
13867        e: &Engine,
13868        tokens: &[u32],
13869        pos0: usize,
13870        cache: &mut Cache,
13871    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13872        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13873        let t = tokens.len();
13874        let n_vocab = self.output.out_features();
13875        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
13876        for i in 0..t {
13877            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
13878        }
13879        Ok((e.dtoh_u32(&toks)?, hn))
13880    }
13881
13882    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
13883    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
13884    pub(crate) fn gemma4_decode_step_t_am_dev(
13885        &self,
13886        e: &Engine,
13887        tok_d: &CudaSlice<u32>,
13888        t: usize,
13889        pos0: usize,
13890        cache: &mut Cache,
13891    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13892        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
13893        let n_vocab = self.output.out_features();
13894        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13895        for i in 0..t {
13896            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13897        }
13898        Ok((vam, hn))
13899    }
13900
13901    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
13902    /// llama's h_nextn convention).
13903    pub(crate) fn gemma4_decode_step_t_h(
13904        &self,
13905        e: &Engine,
13906        tokens: &[u32],
13907        pos0: usize,
13908        cache: &mut Cache,
13909    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13910        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
13911        let t = tokens.len();
13912        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
13913        e.softcap(&mut ld, cap, t * self.output.out_features())?;
13914        Ok((e.dtoh(&ld)?, hn))
13915    }
13916
13917    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
13918    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
13919    pub(crate) fn verify_stream_scratch(
13920        &self,
13921        e: &Engine,
13922        cap: usize,
13923    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
13924        Ok(VerifyStreamScratch {
13925            pos_d: e.htod_i32(&vec![0i32; cap])?,
13926            row_ctrs: (0..cap)
13927                .map(|_| e.htod_i32(&[0]))
13928                .collect::<Result<_, _>>()?,
13929        })
13930    }
13931
13932    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
13933    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
13934    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
13935    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
13936    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
13937    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
13938    /// sync, exactly the turnaround the burst exists to remove.
13939    pub(crate) fn gemma4_verify_t_am_stream(
13940        &self,
13941        e: &Engine,
13942        tok_d: &CudaSlice<u32>,
13943        t: usize,
13944        ctr: &CudaSlice<i32>,
13945        hint: usize,
13946        cache: &mut Cache,
13947        scr: &mut VerifyStreamScratch,
13948    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13949        let n_embd = self.cfg.n_embd as usize;
13950        let eps = self.cfg.rms_eps;
13951        assert!(t <= scr.row_ctrs.len() && t <= 64);
13952        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
13953        for i in 0..t {
13954            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
13955        }
13956        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
13957        let embd_gpu = self
13958            .embd_gpu
13959            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
13960        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
13961        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
13962        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
13963        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13964        let n_layers = self.layers.len();
13965        for (il, layer) in self.layers.iter().enumerate() {
13966            let (hq, hdq) = match h_carry.take() {
13967                Some(p) => p,
13968                None => {
13969                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
13970                }
13971            };
13972            let Mixer::Full(fa) = &layer.mixer else {
13973                panic!("gemma4 layer {il} not full-attn")
13974            };
13975            let o = self
13976                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
13977            let next_norm = if il + 1 < n_layers {
13978                Some(self.layers[il + 1].attn_norm.float_data())
13979            } else {
13980                None
13981            };
13982            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
13983            x = xn;
13984            h_carry = hn;
13985            self.dflash_tap(e, cache, il, &x, t)?;
13986        }
13987        let mut hn = e.uninit(t * n_embd)?;
13988        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
13989        let ld = e.matmul(&self.output, &hn, t)?;
13990        let n_vocab = self.output.out_features();
13991        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
13992        for i in 0..t {
13993            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
13994        }
13995        Ok((vam, hn))
13996    }
13997
13998    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
13999    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
14000    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
14001    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
14002    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
14003    /// kernel later if it shows in the profile).
14004    pub(crate) fn dflash_tap(
14005        &self,
14006        e: &Engine,
14007        cache: &mut Cache,
14008        il: usize,
14009        x: &CudaSlice<f32>,
14010        t: usize,
14011    ) -> Result<(), Box<dyn std::error::Error>> {
14012        let Some(taps) = cache.dflash_taps.as_mut() else {
14013            return Ok(());
14014        };
14015        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14016            return Ok(());
14017        };
14018        let h = taps.hidden;
14019        let n_taps = taps.layer_ids.len();
14020        let base = taps.base;
14021        debug_assert!(
14022            base + t <= taps.t,
14023            "tap window {base}+{t} exceeds sink {}",
14024            taps.t
14025        );
14026        let xv = e.view(x, t * h);
14027        for r in 0..t {
14028            let row = xv.slice(r * h..(r + 1) * h);
14029            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14030        }
14031        Ok(())
14032    }
14033
14034    fn gemma4_verify_trunk(
14035        &self,
14036        e: &Engine,
14037        tokens: &[u32],
14038        pos0: usize,
14039        cache: &mut Cache,
14040        tok_dev: Option<&CudaSlice<u32>>,
14041    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14042        let n_embd = self.cfg.n_embd as usize;
14043        let eps = self.cfg.rms_eps;
14044        let t = tokens.len();
14045        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14046        let pos_d = e.htod_i32(&pos)?;
14047        let mut x = match tok_dev {
14048            Some(td) => {
14049                let embd_gpu = self
14050                    .embd_gpu
14051                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14052                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14053                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14054            }
14055            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14056        };
14057        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14058        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14059        let n_layers = self.layers.len();
14060        for (il, layer) in self.layers.iter().enumerate() {
14061            let (hq, hdq) = match h_carry.take() {
14062                Some(p) => p,
14063                None => {
14064                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14065                }
14066            };
14067            let Mixer::Full(fa) = &layer.mixer else {
14068                panic!("gemma4 layer {il} not full-attn")
14069            };
14070            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14071            let next_norm = if il + 1 < n_layers {
14072                Some(self.layers[il + 1].attn_norm.float_data())
14073            } else {
14074                None
14075            };
14076            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14077            x = xn;
14078            h_carry = hn;
14079            self.dflash_tap(e, cache, il, &x, t)?;
14080        }
14081        let mut hn = e.uninit(t * n_embd)?;
14082        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14083        let mut ld = e.matmul(&self.output, &hn, t)?;
14084        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14085        cache.pos += t;
14086        Ok((ld, hn))
14087    }
14088
14089    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14090    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14091    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14092    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14093    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14094    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14095    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14096    #[allow(clippy::too_many_arguments)]
14097    fn gemma4_verify_attn_stream(
14098        &self,
14099        e: &Engine,
14100        fa: &crate::hybrid::FullAttnLayer,
14101        il: usize,
14102        hq: &CudaSlice<i8>,
14103        hdq: &CudaSlice<f32>,
14104        pos_d: &CudaSlice<i32>,
14105        t: usize,
14106        cache: &mut Cache,
14107        hint: usize,
14108        row_ctrs: &[CudaSlice<i32>],
14109    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14110        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14111        let eps = self.cfg.rms_eps;
14112        let aux = self.gemma4_aux.as_ref().unwrap();
14113        let ones = aux.ones(e);
14114        #[cfg(debug_assertions)]
14115        crate::debug_assert_tensor_stream_device(
14116            ones,
14117            &e.stream(),
14118            "gemma4_verify_attn_stream.ones",
14119        );
14120        let h0 = e.zeros(0)?;
14121        let h = &h0;
14122        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14123        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14124        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14125        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14126        let fused_qkv = if f2b {
14127            if swa {
14128                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14129                    .map(|(a, b, c)| (a, b, Some(c)))
14130            } else {
14131                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14132                    .map(|(a, b)| (a, b, None))
14133            }
14134        } else {
14135            None
14136        };
14137        let (q0, k0, v0) = match fused_qkv {
14138            Some((a, b, cv)) => {
14139                let v = match cv {
14140                    Some(c) => c,
14141                    None => e.clone_dtod(&b)?,
14142                };
14143                (a, b, v)
14144            }
14145            None => {
14146                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14147                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14148                let v0 = if swa {
14149                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14150                } else {
14151                    e.clone_dtod(&k0)?
14152                };
14153                (q0, k0, v0)
14154            }
14155        };
14156        let mut q = e.uninit(t * nh * hd)?;
14157        let mut k = e.uninit(t * nkv * hd)?;
14158        let mut v = e.uninit(t * nkv * hd)?;
14159        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14160        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14161        let ff = if swa {
14162            None
14163        } else {
14164            Some(
14165                aux.rope_freqs(e)
14166                    .expect("gemma4 global rope needs rope_freqs.weight"),
14167            )
14168        };
14169        #[cfg(debug_assertions)]
14170        if let Some(ff) = ff {
14171            crate::debug_assert_tensor_stream_device(
14172                ff,
14173                &e.stream(),
14174                "gemma4_verify_attn_stream.rope_freqs",
14175            );
14176        }
14177        e.rms_norm_qkv_rope(
14178            &q0,
14179            &k0,
14180            &v0,
14181            fa.q_norm.float_data(),
14182            fa.k_norm.float_data(),
14183            ones,
14184            &mut q,
14185            &mut k,
14186            &mut v,
14187            hd,
14188            self.gemma4_rope_dims(il),
14189            nh * t,
14190            nkv * t,
14191            pos_d,
14192            nh,
14193            nkv,
14194            base,
14195            1.0,
14196            ff,
14197            eps,
14198        )?;
14199        let kvl = cache.kv[il].as_mut().unwrap();
14200        // append at the DEVICE slot; the counter advances by t on-device.
14201        e.append_kv_quantized_rows_dc(
14202            &k,
14203            &v,
14204            &mut kvl.k,
14205            &mut kvl.v,
14206            &kvl.len_d,
14207            t,
14208            kvl.kv_dim_k,
14209            kvl.kv_dim_v,
14210            kvl.k_tok_bytes,
14211            kvl.v_tok_bytes,
14212            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14213        )?;
14214        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14215        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14216        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14217        let mut attn = e.uninit(t * nh * hd)?;
14218        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14219        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14220        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14221        // and a stable window regime — the same rung/regime keys as the draft graph).
14222        if swa && hint + 1 >= win {
14223            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14224            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14225            e.fa_decode_rows_w(
14226                &q,
14227                &k_view,
14228                &v_view,
14229                &mut attn,
14230                hd,
14231                nh,
14232                nkv,
14233                &kvl.len_d,
14234                0,
14235                t,
14236                scale,
14237                win,
14238                kvl.k_tok_bytes,
14239                kvl.v_tok_bytes,
14240                None,
14241            )?;
14242        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14243            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14244            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14245            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14246            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14247            // for every row.
14248            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14249            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14250            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14251            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14252            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14253            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14254            // any bucket >= the live length is exact.
14255            let bucket = (hint + t + 2)
14256                .next_power_of_two()
14257                .min(crate::fa512_min_tkv().saturating_sub(1));
14258            let qv = e.view(&q, t * nh * hd);
14259            for i in 0..t {
14260                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14261                let mut q_one = e.uninit(nh * hd)?;
14262                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14263                let mut a_one = e.uninit(nh * hd)?;
14264                e.fa_decode_dc(
14265                    &q_one,
14266                    &k_view,
14267                    &v_view,
14268                    &mut a_one,
14269                    hd,
14270                    nh,
14271                    nkv,
14272                    &row_ctrs[i],
14273                    bucket,
14274                    scale,
14275                    kvl.k_tok_bytes,
14276                    kvl.v_tok_bytes,
14277                    false,
14278                )?;
14279                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14280            }
14281        } else if hd == 512 {
14282            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14283            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14284            e.fa_decode_rows(
14285                &q,
14286                &k_view,
14287                &v_view,
14288                &mut attn,
14289                hd,
14290                nh,
14291                nkv,
14292                hint,
14293                t,
14294                scale,
14295                kvl.k_tok_bytes,
14296                kvl.v_tok_bytes,
14297                Some((&kvl.len_d, 0)),
14298                false,
14299                false,
14300                None,
14301            )?;
14302        } else {
14303            // hd256 under-window: v4 device-len rows twin.
14304            e.fa_decode_rows_dc(
14305                &q,
14306                &k_view,
14307                &v_view,
14308                &mut attn,
14309                hd,
14310                nh,
14311                nkv,
14312                &kvl.len_d,
14313                hint + t,
14314                t,
14315                scale,
14316                kvl.k_tok_bytes,
14317                kvl.v_tok_bytes,
14318                0,
14319                swa && crate::Engine::wkv_on(),
14320            )?;
14321        }
14322        Ok(e.matmul(&fa.wo, &attn, t)?)
14323    }
14324
14325    fn gemma4_verify_attn(
14326        &self,
14327        e: &Engine,
14328        fa: &crate::hybrid::FullAttnLayer,
14329        il: usize,
14330        hq: &CudaSlice<i8>,
14331        hdq: &CudaSlice<f32>,
14332        pos_d: &CudaSlice<i32>,
14333        t: usize,
14334        cache: &mut Cache,
14335    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14336        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14337        let eps = self.cfg.rms_eps;
14338        let aux = self.gemma4_aux.as_ref().unwrap();
14339        let ones = aux.ones(e);
14340        #[cfg(debug_assertions)]
14341        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14342        let n_embd = self.cfg.n_embd as usize;
14343        let _ = n_embd;
14344
14345        let h0 = e.zeros(0)?;
14346        let h = &h0;
14347        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14348        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14349        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14350        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14351        let fused_qkv = if f2b {
14352            if swa {
14353                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14354                    .map(|(a, b, c)| (a, b, Some(c)))
14355            } else {
14356                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14357                    .map(|(a, b)| (a, b, None))
14358            }
14359        } else {
14360            None
14361        };
14362        let (q0, k0, v0) = match fused_qkv {
14363            Some((a, b, cv)) => {
14364                let v = match cv {
14365                    Some(c) => c,
14366                    None => e.clone_dtod(&b)?,
14367                };
14368                (a, b, v)
14369            }
14370            None => {
14371                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14372                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14373                let v0 = if swa {
14374                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14375                } else {
14376                    e.clone_dtod(&k0)?
14377                };
14378                (q0, k0, v0)
14379            }
14380        };
14381        let mut q = e.uninit(t * nh * hd)?;
14382        let mut k = e.uninit(t * nkv * hd)?;
14383        let mut v = e.uninit(t * nkv * hd)?;
14384        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14385        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14386        let ff = if swa {
14387            None
14388        } else {
14389            Some(
14390                aux.rope_freqs(e)
14391                    .expect("gemma4 global rope needs rope_freqs.weight"),
14392            )
14393        };
14394        #[cfg(debug_assertions)]
14395        if let Some(ff) = ff {
14396            crate::debug_assert_tensor_stream_device(
14397                ff,
14398                &e.stream(),
14399                "gemma4_verify_attn.rope_freqs",
14400            );
14401        }
14402        e.rms_norm_qkv_rope(
14403            &q0,
14404            &k0,
14405            &v0,
14406            fa.q_norm.float_data(),
14407            fa.k_norm.float_data(),
14408            ones,
14409            &mut q,
14410            &mut k,
14411            &mut v,
14412            hd,
14413            self.gemma4_rope_dims(il),
14414            nh * t,
14415            nkv * t,
14416            pos_d,
14417            nh,
14418            nkv,
14419            base,
14420            1.0,
14421            ff,
14422            eps,
14423        )?;
14424        let kvl = cache.kv[il].as_mut().unwrap();
14425        let base_len = kvl.len;
14426        e.append_kv_quantized_rows(
14427            &k,
14428            &v,
14429            &mut kvl.k,
14430            &mut kvl.v,
14431            base_len,
14432            t,
14433            kvl.kv_dim_k,
14434            kvl.kv_dim_v,
14435            kvl.k_tok_bytes,
14436            kvl.v_tok_bytes,
14437            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14438        )?;
14439        kvl.len += t;
14440        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14441        let mut attn = e.uninit(t * nh * hd)?;
14442        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14443        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14444        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14445            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14446            // decode rides the SAME symbol at t=1 (parity law).
14447            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14448        if rows_ok && (!swa || base_len + t <= win) {
14449            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14450            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14451            if hd == 512 {
14452                // device-len twin: sync the counter to the verify base (async arg-store).
14453                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14454                e.fa_decode_rows(
14455                    &q,
14456                    &k_view,
14457                    &v_view,
14458                    &mut attn,
14459                    hd,
14460                    nh,
14461                    nkv,
14462                    base_len,
14463                    t,
14464                    scale,
14465                    kvl.k_tok_bytes,
14466                    kvl.v_tok_bytes,
14467                    Some((&kvl.len_d, 0)),
14468                    false,
14469                    swa && crate::Engine::wkv_on(),
14470                    None,
14471                )?;
14472            } else {
14473                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14474                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14475                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14476                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14477                e.fa_decode_rows_dc(
14478                    &q,
14479                    &k_view,
14480                    &v_view,
14481                    &mut attn,
14482                    hd,
14483                    nh,
14484                    nkv,
14485                    &kvl.len_d,
14486                    base_len + t,
14487                    t,
14488                    scale,
14489                    kvl.k_tok_bytes,
14490                    kvl.v_tok_bytes,
14491                    0,
14492                    swa && crate::Engine::wkv_on(),
14493                )?;
14494            }
14495            return Ok(e.matmul(&fa.wo, &attn, t)?);
14496        }
14497        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14498        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14499        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14500        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14501        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14502        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14503        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14504        if hd == 256
14505            && swa
14506            && base_len + 1 >= win
14507            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14508        {
14509            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14510            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14511            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14512            e.fa_decode_rows_w(
14513                &q,
14514                &k_view,
14515                &v_view,
14516                &mut attn,
14517                hd,
14518                nh,
14519                nkv,
14520                &kvl.len_d,
14521                0,
14522                t,
14523                scale,
14524                win,
14525                kvl.k_tok_bytes,
14526                kvl.v_tok_bytes,
14527                None,
14528            )?;
14529            return Ok(e.matmul(&fa.wo, &attn, t)?);
14530        }
14531        for i in 0..t {
14532            let avail = base_len + i + 1;
14533            let (off_tok, t_kv) = if swa && avail > win {
14534                (avail - win, win)
14535            } else {
14536                (0, avail)
14537            };
14538            let k_view = e.view_u8_range(
14539                &kvl.k,
14540                off_tok * kvl.k_tok_bytes,
14541                (off_tok + t_kv) * kvl.k_tok_bytes,
14542            );
14543            let v_view = e.view_u8_range(
14544                &kvl.v,
14545                off_tok * kvl.v_tok_bytes,
14546                (off_tok + t_kv) * kvl.v_tok_bytes,
14547            );
14548            let qi = e.view(&q, t * nh * hd);
14549            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14550            let mut q_one = e.uninit(nh * hd)?;
14551            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14552            let mut a_one = e.uninit(nh * hd)?;
14553            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14554            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14555            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14556            if swa
14557                && avail > win
14558                && hd == 256
14559                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14560            {
14561                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14562                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14563                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14564                e.fa_decode_rows_w(
14565                    &q_one,
14566                    &kp,
14567                    &vp,
14568                    &mut a_one,
14569                    hd,
14570                    nh,
14571                    nkv,
14572                    &kvl.len_d,
14573                    0,
14574                    1,
14575                    scale,
14576                    win,
14577                    kvl.k_tok_bytes,
14578                    kvl.v_tok_bytes,
14579                    None,
14580                )?;
14581            } else if !swa
14582                && hd == 512
14583                && avail >= crate::fa512_min_tkv()
14584                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14585            {
14586                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14587                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14588                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14589                e.fa_decode_rows(
14590                    &q_one,
14591                    &kp,
14592                    &vp,
14593                    &mut a_one,
14594                    hd,
14595                    nh,
14596                    nkv,
14597                    avail - 1,
14598                    1,
14599                    scale,
14600                    kvl.k_tok_bytes,
14601                    kvl.v_tok_bytes,
14602                    Some((&kvl.len_d, 0)),
14603                    false,
14604                    false,
14605                    None,
14606                )?;
14607            } else {
14608                e.fa_decode_kvmod(
14609                    &q_one,
14610                    &k_view,
14611                    &v_view,
14612                    &mut a_one,
14613                    hd,
14614                    nh,
14615                    nkv,
14616                    t_kv,
14617                    scale,
14618                    kvl.k_tok_bytes,
14619                    kvl.v_tok_bytes,
14620                    swa && crate::Engine::wkv_on(),
14621                )?;
14622            }
14623            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14624        }
14625        Ok(e.matmul(&fa.wo, &attn, t)?)
14626    }
14627
14628    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
14629    /// h_seed = pre-output_norm hidden). Advances cache.pos.
14630    pub(crate) fn gemma4_decode_step_h(
14631        &self,
14632        e: &Engine,
14633        token: u32,
14634        cache: &mut Cache,
14635    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14636        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
14637        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
14638        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
14639        // unsplit rather than guessing a fence.
14640        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
14641            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
14642        }
14643        if crate::pp::pp_cuts(self.layers.len()).is_some() {
14644            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
14645        }
14646        let n_embd = self.cfg.n_embd as usize;
14647        let eps = self.cfg.rms_eps;
14648        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14649        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14650        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14651        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
14652        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
14653        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14654        let n_layers = self.layers.len();
14655        for (il, layer) in self.layers.iter().enumerate() {
14656            let (hq, hdq) = match h_carry.take() {
14657                Some(p) => p,
14658                None => {
14659                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
14660                }
14661            };
14662            let Mixer::Full(fa) = &layer.mixer else {
14663                panic!("gemma4 layer {il} not full-attn")
14664            };
14665            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
14666            let next_norm = if il + 1 < n_layers {
14667                Some(self.layers[il + 1].attn_norm.float_data())
14668            } else {
14669                None
14670            };
14671            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14672            x = xn;
14673            h_carry = hn;
14674        }
14675        let mut hn = e.uninit(n_embd)?;
14676        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14677        let h_seed = e.clone_dtod(&x)?;
14678        let mut ld = e.matmul(&self.output, &hn, 1)?;
14679        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14680        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
14681        self.gemma4_suppress(e, &mut ld, 1)?;
14682        let logits = e.dtoh(&ld)?;
14683        cache.pos += 1;
14684        Ok((logits, h_seed))
14685    }
14686
14687    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
14688    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
14689    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
14690    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
14691    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
14692    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
14693    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
14694    fn gemma4_decode_layers(
14695        &self,
14696        e: &Engine,
14697        mut x: CudaSlice<f32>,
14698        lo: usize,
14699        hi: usize,
14700        pos_d: &CudaSlice<i32>,
14701        cache: &mut Cache,
14702    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14703        let n_embd = self.cfg.n_embd as usize;
14704        let eps = self.cfg.rms_eps;
14705        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14706        for il in lo..hi {
14707            let layer = &self.layers[il];
14708            let (hq, hdq) = match h_carry.take() {
14709                Some(p) => p,
14710                // range head: il == lo — norm against THIS layer's attn_norm.
14711                None => {
14712                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
14713                }
14714            };
14715            let Mixer::Full(fa) = &layer.mixer else {
14716                panic!("gemma4 layer {il} not full-attn")
14717            };
14718            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
14719            let next_norm = if il + 1 < hi {
14720                Some(self.layers[il + 1].attn_norm.float_data())
14721            } else {
14722                None
14723            };
14724            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
14725            x = xn;
14726            h_carry = hn;
14727        }
14728        Ok(x)
14729    }
14730
14731    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
14732    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
14733    /// boundary handoff — same choreography as the generic arm (decode.rs), same
14734    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
14735    /// stage 1 = layers [split, n) + output_norm + softcapped head.
14736    /// Each stage uploads its own copy of the step's position scalar on its own stream.
14737    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
14738    fn gemma4_decode_step_h_pp2(
14739        &self,
14740        e: &Engine,
14741        token: u32,
14742        cache: &mut Cache,
14743        split: usize,
14744    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14745        if crate::pp::pp2_streams_off() {
14746            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
14747        }
14748        let rt = crate::pp::Pp2Rt::get(e)?;
14749        let e0 = rt.engine(0, e);
14750        let e1 = rt.engine(1, e);
14751        let n_embd = self.cfg.n_embd as usize;
14752        let eps = self.cfg.rms_eps;
14753        let pos = cache.pos as i32;
14754
14755        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
14756        let slot = {
14757            let _st0 = rt.enter(0);
14758            let pos_d = e0.htod_i32(&[pos])?;
14759            #[cfg(debug_assertions)]
14760            crate::debug_assert_tensor_stream_device(
14761                &pos_d,
14762                &e0.stream(),
14763                "gemma4_decode_step_h_pp2.stage0.pos_d",
14764            );
14765            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
14766            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14767            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
14768            rt.tx(0, &x, n_embd)?
14769        };
14770
14771        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
14772        let _st1 = rt.enter(1);
14773        let pos_d = e1.htod_i32(&[pos])?;
14774        #[cfg(debug_assertions)]
14775        crate::debug_assert_tensor_stream_device(
14776            &pos_d,
14777            &e1.stream(),
14778            "gemma4_decode_step_h_pp2.stage1.pos_d",
14779        );
14780        let x = rt.rx(0, slot, n_embd)?;
14781        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
14782
14783        let mut hn = e1.uninit(n_embd)?;
14784        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14785        let h_seed = e1.clone_dtod(&x)?;
14786        let mut ld = e1.matmul(&self.output, &hn, 1)?;
14787        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14788        e1.softcap(&mut ld, cap, self.output.out_features())?;
14789        self.gemma4_suppress(e1, &mut ld, 1)?;
14790        let logits = e1.dtoh(&ld)?;
14791        cache.pos += 1;
14792        Ok((logits, h_seed))
14793    }
14794
14795    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
14796    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
14797    fn gemma4_decode_step_h_pp2_samestream(
14798        &self,
14799        e: &Engine,
14800        token: u32,
14801        cache: &mut Cache,
14802        split: usize,
14803    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14804        let n_embd = self.cfg.n_embd as usize;
14805        let eps = self.cfg.rms_eps;
14806        let pos_d = e.htod_i32(&[cache.pos as i32])?;
14807
14808        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
14809        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
14810        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
14811        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
14812
14813        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
14814        let boundary_tx = e.clone_dtod(&x)?;
14815        let boundary_rx = e.clone_dtod(&boundary_tx)?;
14816
14817        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
14818        let x =
14819            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
14820
14821        let mut hn = e.uninit(n_embd)?;
14822        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
14823        let h_seed = e.clone_dtod(&x)?;
14824        let mut ld = e.matmul(&self.output, &hn, 1)?;
14825        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14826        e.softcap(&mut ld, cap, self.output.out_features())?;
14827        self.gemma4_suppress(e, &mut ld, 1)?;
14828        let logits = e.dtoh(&ld)?;
14829        cache.pos += 1;
14830        Ok((logits, h_seed))
14831    }
14832}
14833
14834// ============================ step35 (Step-3.7-Flash) ==================================
14835// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
14836// FAMILY and not a few branches inside the generic `full_attn*` chain:
14837//
14838//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
14839//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
14840//      shapes and the FA head counts would be wrong on 33 of 45 layers.
14841//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
14842//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
14843//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
14844//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
14845//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
14846//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
14847//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
14848//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
14849//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
14850//
14851// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
14852impl HybridModel {
14853    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
14854    /// synthesize a drafter or trunk layer from a neighboring class.
14855    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
14856        let geometry = self
14857            .cfg
14858            .layer_geometry(il as u32)
14859            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
14860        debug_assert_eq!(
14861            geometry.attention_gate,
14862            memra_gguf::config::AttentionGateKind::SeparateHead
14863        );
14864        geometry
14865    }
14866
14867    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
14868    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
14869    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
14870    ///
14871    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
14872    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
14873    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
14874    /// `cache`:
14875    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
14876    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
14877    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
14878    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
14879    ///     contract, lane/chunkinv-flip).
14880    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
14881    ///     q/k/v, no cache side effect.
14882    ///
14883    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
14884    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
14885    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
14886    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
14887    /// still contains must be masked per query. memra's window convention
14888    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
14889    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
14890    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
14891    ///
14892    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
14893    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
14894    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
14895    ///
14896    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
14897    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
14898    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
14899    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
14900    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
14901    /// hidden rows, and the generated text — a function of the chunk size:
14902    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
14903    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
14904    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
14905    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
14906    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
14907    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
14908    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
14909    ///   one-token change in a documented machine-config knob changed the answer.
14910    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
14911    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
14912    /// the same rows moves the logits by ~1.8.
14913    ///
14914    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
14915    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
14916    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
14917    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
14918    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
14919    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
14920    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
14921    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
14922    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
14923    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
14924    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
14925    /// those with t_kv <= win = 512.
14926    #[allow(clippy::too_many_arguments)]
14927    fn step35_attn_pre_wo(
14928        &self,
14929        e: &Engine,
14930        fa: &FullAttnLayer,
14931        mut g3: Vec<CudaSlice<f32>>,
14932        hg: Option<&CudaSlice<f32>>,
14933        gt_pre: Option<&CudaSlice<f32>>,
14934        pos_d: &CudaSlice<i32>,
14935        t: usize,
14936        cache: Option<&mut Cache>,
14937        il: usize,
14938        seq_end: usize,
14939    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14940        let geometry = self.step35_geom(il);
14941        let hd = geometry.head_dim_k as usize;
14942        let nkv = geometry.n_head_kv as usize;
14943        let nh = geometry.n_head as usize;
14944        let rbase = geometry.rope_base;
14945        let scale = geometry.attention_scale();
14946        let swa = geometry.window.is_some();
14947        let eps = self.cfg.rms_eps;
14948        let win = geometry.window.unwrap_or(0) as usize;
14949        let n_rot = geometry.n_rot as usize;
14950
14951        let v = g3.pop().unwrap();
14952        let k0 = g3.pop().unwrap();
14953        let q0 = g3.pop().unwrap();
14954
14955        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
14956        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
14957        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
14958        let mut q = e.uninit(t * nh * hd)?;
14959        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
14960        let mut k = e.uninit(t * nkv * hd)?;
14961        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
14962        let ff = if geometry.rope_factors {
14963            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
14964        } else {
14965            None
14966        };
14967        #[cfg(debug_assertions)]
14968        if let Some(ff) = ff {
14969            crate::debug_assert_tensor_stream_device(
14970                ff,
14971                &e.stream(),
14972                "step35_attn_pre_wo.rope_freqs",
14973            );
14974        }
14975        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
14976
14977        let mut attn = e.uninit(t * nh * hd)?;
14978        match cache {
14979            Some(cache) => {
14980                let base_len = cache.kv[il].as_ref().unwrap().len;
14981                // Read per layer call, never in a measured default.
14982                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
14983                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
14984                let off = if swa {
14985                    let raw = base_len.saturating_sub(win - 1);
14986                    if legacy_tkv || legacy_calllocal {
14987                        raw
14988                    } else {
14989                        raw & !31usize
14990                    }
14991                } else {
14992                    0
14993                };
14994                {
14995                    let kvl = cache.kv[il].as_mut().unwrap();
14996                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
14997                    let write_row = e.prepare_kv_append(kvl, off, t)?;
14998                    e.append_kv_quantized_rows(
14999                        &k,
15000                        &v,
15001                        &mut kvl.k,
15002                        &mut kvl.v,
15003                        write_row,
15004                        t,
15005                        kvl.kv_dim_k,
15006                        kvl.kv_dim_v,
15007                        kvl.k_tok_bytes,
15008                        kvl.v_tok_bytes,
15009                        crate::Engine::kv_fp8_on(),
15010                    )?;
15011                    kvl.len += t;
15012                    let new_len = kvl.len as i32;
15013                    e.set_i32_one(&mut kvl.len_d, new_len)?;
15014                }
15015                let kvl = cache.kv[il].as_ref().unwrap();
15016                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15017                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15018                // unaligned view offset here. Both halves are load-bearing for the canaries:
15019                // on the FA default the predicate arms agree bitwise wherever they can differ
15020                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15021                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15022                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15023                // on the current FA path: its tile grid starts at the chunk/call boundary.
15024                // SWA: trim the view to the oldest key any query in this chunk can reach —
15025                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15026                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15027                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15028                // the VIEW START — so an unaligned off regroups the same absolute keys into
15029                // different tiles at different chunk sizes = different (m,l) rounding =
15030                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15031                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15032                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15033                // size; the <=31 extra leading keys are older than EVERY query's window
15034                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15035                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15036                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15037                // the floor arm's bits do not move either (gated: G2f, battery 2).
15038                let t_kv = base_len + t - off;
15039                let physical = kvl.physical_rows(off, off + t_kv)?;
15040                let k_view = e.view_u8_range(
15041                    &kvl.k,
15042                    physical.start * kvl.k_tok_bytes,
15043                    physical.end * kvl.k_tok_bytes,
15044                );
15045                let v_view = e.view_u8_range(
15046                    &kvl.v,
15047                    physical.start * kvl.v_tok_bytes,
15048                    physical.end * kvl.v_tok_bytes,
15049                );
15050                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15051                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15052                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15053                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15054                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15055                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15056                // construction, so the invariance assertion MUST break under it (the seam whose
15057                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15058                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15059                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15060                // cached (probes flip it in-process). Never on in a measured default run.
15061                let swa_naive = if legacy_tkv {
15062                    t_kv > win
15063                } else {
15064                    seq_end > win
15065                };
15066                if swa && swa_naive {
15067                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15068                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15069                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15070                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15071                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15072                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15073                    // identically to the unwindowed one modulo the mask, which is the point.
15074                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15075                    // selected on `seq_end` like every arm here, so the class is uniform for
15076                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15077                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15078                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15079                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15080                        e.sdpa_naive_w_quantized_view(
15081                            &q,
15082                            &k_view,
15083                            &v_view,
15084                            &mut attn,
15085                            hd,
15086                            nh,
15087                            nkv,
15088                            t,
15089                            t_kv,
15090                            scale,
15091                            true,
15092                            win,
15093                            kvl.k_tok_bytes,
15094                            kvl.v_tok_bytes,
15095                        )?;
15096                    } else {
15097                        e.fa_prefill_view_ws_w_hd128(
15098                            &q,
15099                            &k_view,
15100                            &v_view,
15101                            &mut attn,
15102                            hd,
15103                            nh,
15104                            nkv,
15105                            t,
15106                            t_kv,
15107                            scale,
15108                            true,
15109                            win,
15110                            kvl.k_tok_bytes,
15111                            kvl.v_tok_bytes,
15112                        )?;
15113                    }
15114                } else if std::env::var("MEMRA_NOFA").is_ok() {
15115                    e.sdpa_naive_quantized_view(
15116                        &q,
15117                        &k_view,
15118                        &v_view,
15119                        &mut attn,
15120                        hd,
15121                        nh,
15122                        nkv,
15123                        t,
15124                        t_kv,
15125                        scale,
15126                        true,
15127                        kvl.k_tok_bytes,
15128                        kvl.v_tok_bytes,
15129                    )?;
15130                } else {
15131                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15132                    // reach past the window, so the window mask is a no-op under causal and every
15133                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15134                    // request either way, which is what makes the chunk size arithmetic-free.
15135                    e.fa_prefill_view_ws(
15136                        &q,
15137                        &k_view,
15138                        &v_view,
15139                        &mut attn,
15140                        hd,
15141                        nh,
15142                        nkv,
15143                        t,
15144                        t_kv,
15145                        scale,
15146                        true,
15147                        kvl.k_tok_bytes,
15148                        kvl.v_tok_bytes,
15149                        crate::Engine::kv_fp8_on(),
15150                    )?;
15151                }
15152            }
15153            None => {
15154                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15155                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15156                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15157                // seq_end here too or it re-opens the same door.
15158                debug_assert_eq!(
15159                    seq_end, t,
15160                    "step35 cacheless prefill is monolithic (seq_end == t)"
15161                );
15162                if swa && seq_end > win {
15163                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15164                } else if std::env::var("MEMRA_NOFA").is_ok() {
15165                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15166                } else {
15167                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15168                }
15169            }
15170        }
15171
15172        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15173        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15174        let gw = fa
15175            .attn_gate
15176            .as_ref()
15177            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15178        let gt_owned = if gt_pre.is_none() {
15179            Some(e.matmul(
15180                gw,
15181                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15182                t,
15183            )?)
15184        } else {
15185            None
15186        };
15187        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15188        let mut ag = e.uninit(t * nh * hd)?;
15189        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15190        Ok(ag)
15191    }
15192
15193    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15194    /// `forward_last`, t2probe). Post-`wo`.
15195    pub(crate) fn step35_attn(
15196        &self,
15197        e: &Engine,
15198        fa: &FullAttnLayer,
15199        h: &CudaSlice<f32>,
15200        pos_d: &CudaSlice<i32>,
15201        t: usize,
15202        il: usize,
15203    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15204        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15205            Some(g3) => g3,
15206            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15207        };
15208        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15209        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15210        self.step35_o(e, fa, &ag, t)
15211    }
15212
15213    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15214    /// resident quantized cache, attend through the cache view). Post-`wo`.
15215    ///
15216    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15217    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15218    /// own extent.
15219    #[allow(clippy::too_many_arguments)]
15220    pub(crate) fn step35_attn_prime(
15221        &self,
15222        e: &Engine,
15223        fa: &FullAttnLayer,
15224        h: &CudaSlice<f32>,
15225        hx: Option<&CudaSlice<u8>>,
15226        pos_d: &CudaSlice<i32>,
15227        t: usize,
15228        cache: &mut Cache,
15229        il: usize,
15230        seq_end: usize,
15231    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15232        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15233            if hx.is_some() {
15234                return Err(
15235                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15236                     pre-quantized prime path"
15237                        .into(),
15238                );
15239            }
15240            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15241        }
15242        let g3 = if fa.step_tp_qkv.is_some() {
15243            if hx.is_some() {
15244                return Err(
15245                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15246                     pre-quantized prime path"
15247                        .into(),
15248                );
15249            }
15250            self.step35_tp_qkv(e, fa, h, t)?
15251                .expect("Step Q/K/V TP disappeared after the presence check")
15252        } else {
15253            match hx {
15254                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15255                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15256            }
15257        };
15258        let ag =
15259            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15260        self.step35_o(e, fa, &ag, t)
15261    }
15262
15263    fn ensure_step_tp_kv_cache(
15264        &self,
15265        e: &Engine,
15266        fa: &FullAttnLayer,
15267        il: usize,
15268        cache: &mut Cache,
15269    ) -> Result<bool, Box<dyn std::error::Error>> {
15270        let tp = fa
15271            .step_tp_qkv
15272            .as_ref()
15273            .ok_or("Step TP cache hydration lost its resident projections")?;
15274        let geometry = self.step35_geom(il);
15275        let window = geometry.window.map(|window| window as usize);
15276        let ranks = tp.runtime.devices().len();
15277        let head_dim = geometry.head_dim_k as usize;
15278        let kv_heads = geometry.n_head_kv as usize;
15279        let max_ctx = cache.max_ctx;
15280
15281        if cache.tp_kv[il].is_some() {
15282            return Ok(false);
15283        }
15284        let local = cache.kv[il]
15285            .as_ref()
15286            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15287        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15288            return Err(format!(
15289                "Step TP layer {il} local KV geometry k={} v={} != {}",
15290                local.kv_dim_k,
15291                local.kv_dim_v,
15292                kv_heads * head_dim
15293            )
15294            .into());
15295        }
15296        let resident_start = window
15297            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15298            .unwrap_or(0);
15299        let resident_rows = local.len - resident_start;
15300        let physical = local.physical_rows(resident_start, local.len)?;
15301        let k_rows = if resident_rows == 0 {
15302            Vec::new()
15303        } else {
15304            e.dtoh_u8_view(&e.view_u8_range(
15305                &local.k,
15306                physical.start * local.k_tok_bytes,
15307                physical.end * local.k_tok_bytes,
15308            ))?
15309        };
15310        let v_rows = if resident_rows == 0 {
15311            Vec::new()
15312        } else {
15313            e.dtoh_u8_view(&e.view_u8_range(
15314                &local.v,
15315                physical.start * local.v_tok_bytes,
15316                physical.end * local.v_tok_bytes,
15317            ))?
15318        };
15319        let mut distributed = match window {
15320            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15321                kv_heads * head_dim,
15322                kv_heads * head_dim,
15323                max_ctx,
15324                window,
15325            )?,
15326            None => tp.runtime.allocate_tp_kv_cache(
15327                kv_heads * head_dim,
15328                kv_heads * head_dim,
15329                max_ctx,
15330            )?,
15331        };
15332        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15333            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15334        {
15335            return Err(format!(
15336                "Step TP layer {il} distributed/local KV token bytes disagree: \
15337                 k={}x{ranks}/{} v={}x{ranks}/{}",
15338                distributed.k_tok_bytes(),
15339                local.k_tok_bytes,
15340                distributed.v_tok_bytes(),
15341                local.v_tok_bytes,
15342            )
15343            .into());
15344        }
15345        tp.runtime.hydrate_tp_kv_cache_from(
15346            &mut distributed,
15347            local.len,
15348            resident_start,
15349            &k_rows,
15350            &v_rows,
15351        )?;
15352        cache.tp_kv[il] = Some(distributed);
15353        Ok(true)
15354    }
15355
15356    #[allow(clippy::too_many_arguments)]
15357    fn step35_tp_prefill_attn_resident(
15358        &self,
15359        e: &Engine,
15360        fa: &FullAttnLayer,
15361        il: usize,
15362        h: &CudaSlice<f32>,
15363        pos_d: &CudaSlice<i32>,
15364        tokens: usize,
15365        cache: &mut Cache,
15366        seq_end: usize,
15367    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15368        let tp = fa
15369            .step_tp_qkv
15370            .as_ref()
15371            .ok_or("Step TP prefill lost its resident projections")?;
15372        let attention = tp
15373            .attention
15374            .as_ref()
15375            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15376        let ranks = tp.runtime.devices().len();
15377        if !step_tp_prefill_shape(
15378            true,
15379            tokens,
15380            ranks,
15381            tp.runtime.native_p2p(),
15382            true,
15383            crate::Engine::kv_fp8_on(),
15384        ) {
15385            return Err(format!(
15386                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP2/TP4 native P2P, \
15387                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15388                 native_p2p={} fp8_kv={}",
15389                tp.runtime.native_p2p(),
15390                crate::Engine::kv_fp8_on(),
15391            )
15392            .into());
15393        }
15394        for seam in [
15395            "MEMRA_STEP35_SWA_TKV",
15396            "MEMRA_PRIME_CALLLOCAL",
15397            "MEMRA_PRIME_F32CHUNK0",
15398        ] {
15399            if std::env::var(seam).as_deref() == Ok("1") {
15400                return Err(format!(
15401                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15402                )
15403                .into());
15404            }
15405        }
15406
15407        let geometry = self.step35_geom(il);
15408        let window = geometry.window.map(|window| window as usize);
15409        let head_dim = geometry.head_dim_k as usize;
15410        let heads = geometry.n_head as usize;
15411        let kv_heads = geometry.n_head_kv as usize;
15412        if heads % ranks != 0 || kv_heads % ranks != 0 {
15413            return Err(format!(
15414                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15415            )
15416            .into());
15417        }
15418        let local_heads = heads / ranks;
15419        let local_kv_heads = kv_heads / ranks;
15420        let local_kv_dim = local_kv_heads * head_dim;
15421        let hidden = self.cfg.n_embd as usize;
15422        let expected_input = tokens
15423            .checked_mul(hidden)
15424            .ok_or("Step TP prefill input size overflow")?;
15425        if h.len() < expected_input {
15426            return Err(format!(
15427                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15428                h.len()
15429            )
15430            .into());
15431        }
15432        let positions = e.dtoh_i32(pos_d)?;
15433        if positions.len() != tokens {
15434            return Err(format!(
15435                "rank-local Step prefill positions {} != tokens {tokens}",
15436                positions.len()
15437            )
15438            .into());
15439        }
15440
15441        let mut active_input = e.uninit(expected_input)?;
15442        e.copy_view_into(
15443            &mut active_input,
15444            0,
15445            &h.slice(0..expected_input),
15446            expected_input,
15447        )?;
15448        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15449        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15450        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15451        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15452        // layer-count-amplified arm of the boot flake.
15453        e.stream().synchronize()?;
15454        tp.runtime
15455            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15456        let q_raw = tp
15457            .runtime
15458            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15459        let k_raw = tp
15460            .runtime
15461            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15462        let v_raw = tp
15463            .runtime
15464            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15465        let mut q = Vec::with_capacity(ranks);
15466        let mut k = Vec::with_capacity(ranks);
15467        for rank in 0..ranks {
15468            let engine = tp
15469                .runtime
15470                .rank_engine(rank)
15471                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15472            let _main = engine.gpu.enter_main()?;
15473            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15474            engine.rms_norm(
15475                &q_raw[rank],
15476                &attention.q_norm[rank],
15477                &mut q_rank,
15478                head_dim,
15479                tokens * local_heads,
15480                self.cfg.rms_eps,
15481            )?;
15482            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15483            engine.rms_norm(
15484                &k_raw[rank],
15485                &attention.k_norm[rank],
15486                &mut k_rank,
15487                head_dim,
15488                tokens * local_kv_heads,
15489                self.cfg.rms_eps,
15490            )?;
15491            let position = engine.htod_i32(&positions)?;
15492            let rope_freqs = if geometry.rope_factors {
15493                self.step35_aux
15494                    .as_ref()
15495                    .and_then(|aux| aux.rope_freqs(engine))
15496            } else {
15497                None
15498            };
15499            engine.rope_neox2(
15500                &mut q_rank,
15501                &mut k_rank,
15502                &position,
15503                head_dim,
15504                geometry.n_rot as usize,
15505                local_heads,
15506                local_kv_heads,
15507                tokens,
15508                geometry.rope_base,
15509                1.0,
15510                rope_freqs,
15511            )?;
15512            q.push(q_rank);
15513            k.push(k_rank);
15514        }
15515
15516        let gate_weight = fa
15517            .attn_gate
15518            .as_ref()
15519            .ok_or("step35 layer is missing attn_gate.weight")?;
15520        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15521        if gate.len() != tokens * heads {
15522            return Err(format!(
15523                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15524                gate.len()
15525            )
15526            .into());
15527        }
15528
15529        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15530        let base_len = cache.kv[il]
15531            .as_ref()
15532            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15533            .len;
15534        let distributed = cache.tp_kv[il]
15535            .as_ref()
15536            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15537        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15538            return Err(format!(
15539                "Step TP layer {il} cache lengths diverged before prefill: \
15540                 local={base_len} distributed={}/{}",
15541                distributed.committed_len(),
15542                distributed.staged_len()
15543            )
15544            .into());
15545        }
15546        let target_len = base_len
15547            .checked_add(tokens)
15548            .ok_or("Step TP prefill cache length overflow")?;
15549        if target_len > cache.max_ctx {
15550            return Err(format!(
15551                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15552                cache.max_ctx
15553            )
15554            .into());
15555        }
15556        if seq_end < target_len {
15557            return Err(format!(
15558                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15559            )
15560            .into());
15561        }
15562
15563        let transaction = cache.tp_kv[il]
15564            .as_mut()
15565            .expect("distributed cache checked above")
15566            .begin_transaction()?;
15567        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15568            cache.tp_kv[il]
15569                .as_mut()
15570                .expect("distributed cache checked above"),
15571            transaction,
15572            &k,
15573            &v_raw,
15574            tokens,
15575        ) {
15576            let _ = tp.runtime.rollback_tp_kv_transaction(
15577                cache.tp_kv[il]
15578                    .as_mut()
15579                    .expect("distributed cache checked above"),
15580                transaction,
15581            );
15582            return Err(error);
15583        }
15584
15585        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15586            let distributed = cache.tp_kv[il]
15587                .as_ref()
15588                .expect("distributed cache checked above");
15589            let staged_len = distributed.staged_len();
15590            let view_start = window
15591                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15592                .unwrap_or(0);
15593            let physical = distributed.physical_range(view_start, staged_len)?;
15594            let t_kv = staged_len - view_start;
15595            let swa_naive = window.is_some_and(|window| seq_end > window);
15596            let mut gated = Vec::with_capacity(ranks);
15597            for rank in 0..ranks {
15598                let engine = tp
15599                    .runtime
15600                    .rank_engine(rank)
15601                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15602                let _main = engine.gpu.enter_main()?;
15603                let rank_cache = distributed
15604                    .rank(rank)
15605                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15606                let k_view = engine.view_u8_range(
15607                    rank_cache.k(),
15608                    physical.start * distributed.k_tok_bytes(),
15609                    physical.end * distributed.k_tok_bytes(),
15610                );
15611                let v_view = engine.view_u8_range(
15612                    rank_cache.v(),
15613                    physical.start * distributed.v_tok_bytes(),
15614                    physical.end * distributed.v_tok_bytes(),
15615                );
15616                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
15617                if swa_naive {
15618                    let window = window.expect("SWA predicate requires a window");
15619                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15620                        engine.sdpa_naive_w_quantized_view(
15621                            &q[rank],
15622                            &k_view,
15623                            &v_view,
15624                            &mut attention_out,
15625                            head_dim,
15626                            local_heads,
15627                            local_kv_heads,
15628                            tokens,
15629                            t_kv,
15630                            geometry.attention_scale(),
15631                            true,
15632                            window,
15633                            distributed.k_tok_bytes(),
15634                            distributed.v_tok_bytes(),
15635                        )?;
15636                    } else {
15637                        engine.fa_prefill_view_ws_w_hd128(
15638                            &q[rank],
15639                            &k_view,
15640                            &v_view,
15641                            &mut attention_out,
15642                            head_dim,
15643                            local_heads,
15644                            local_kv_heads,
15645                            tokens,
15646                            t_kv,
15647                            geometry.attention_scale(),
15648                            true,
15649                            window,
15650                            distributed.k_tok_bytes(),
15651                            distributed.v_tok_bytes(),
15652                        )?;
15653                    }
15654                } else if std::env::var("MEMRA_NOFA").is_ok() {
15655                    engine.sdpa_naive_quantized_view(
15656                        &q[rank],
15657                        &k_view,
15658                        &v_view,
15659                        &mut attention_out,
15660                        head_dim,
15661                        local_heads,
15662                        local_kv_heads,
15663                        tokens,
15664                        t_kv,
15665                        geometry.attention_scale(),
15666                        true,
15667                        distributed.k_tok_bytes(),
15668                        distributed.v_tok_bytes(),
15669                    )?;
15670                } else {
15671                    engine.fa_prefill_view_ws(
15672                        &q[rank],
15673                        &k_view,
15674                        &v_view,
15675                        &mut attention_out,
15676                        head_dim,
15677                        local_heads,
15678                        local_kv_heads,
15679                        tokens,
15680                        t_kv,
15681                        geometry.attention_scale(),
15682                        true,
15683                        distributed.k_tok_bytes(),
15684                        distributed.v_tok_bytes(),
15685                        false,
15686                    )?;
15687                }
15688
15689                let gate_start = rank * local_heads;
15690                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
15691                for token in 0..tokens {
15692                    let start = token * heads + gate_start;
15693                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
15694                }
15695                let gate_rank = engine.htod(&gate_rank)?;
15696                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
15697                engine.attn_head_gate(
15698                    &attention_out,
15699                    &gate_rank,
15700                    &mut gated_rank,
15701                    None,
15702                    head_dim,
15703                    local_heads,
15704                    tokens,
15705                )?;
15706                gated.push(gated_rank);
15707            }
15708            for rank in 1..ranks {
15709                let engine = tp
15710                    .runtime
15711                    .rank_engine(rank)
15712                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15713                let _main = engine.gpu.enter_main()?;
15714                engine.stream().synchronize()?;
15715            }
15716
15717            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
15718                let output = tp
15719                    .runtime
15720                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
15721                let k_shadow =
15722                    tp.runtime
15723                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
15724                let v_shadow =
15725                    tp.runtime
15726                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
15727                let root = tp
15728                    .runtime
15729                    .rank_engine(0)
15730                    .ok_or("Step TP prefill lost its root engine")?;
15731                let _main = root.gpu.enter_main()?;
15732                root.stream().synchronize()?;
15733                (output, k_shadow, v_shadow)
15734            } else {
15735                let attention = tp.runtime.gather_native_column_shards(
15736                    &gated,
15737                    tokens,
15738                    local_heads * head_dim,
15739                )?;
15740                let output = tp
15741                    .runtime
15742                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
15743                let k_shadow = tp
15744                    .runtime
15745                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
15746                let v_shadow =
15747                    tp.runtime
15748                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
15749                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
15750            };
15751            let local = cache.kv[il]
15752                .as_mut()
15753                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
15754            if local.len != base_len {
15755                return Err(format!(
15756                    "Step TP layer {il} local cache changed during prefill: \
15757                     len={} base={base_len}",
15758                    local.len
15759                )
15760                .into());
15761            }
15762            let retain_from = window
15763                .map(|window| {
15764                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
15765                    let rollback_retain =
15766                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
15767                    staged_retain.min(rollback_retain)
15768                })
15769                .unwrap_or(0);
15770            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
15771            e.append_kv_quantized_rows(
15772                &k_shadow,
15773                &v_shadow,
15774                &mut local.k,
15775                &mut local.v,
15776                write_row,
15777                tokens,
15778                local.kv_dim_k,
15779                local.kv_dim_v,
15780                local.k_tok_bytes,
15781                local.v_tok_bytes,
15782                false,
15783            )?;
15784            local.len = staged_len;
15785            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
15786            Ok(output)
15787        })();
15788
15789        let output = match staged {
15790            Ok(output) => output,
15791            Err(error) => {
15792                let _ = tp.runtime.rollback_tp_kv_transaction(
15793                    cache.tp_kv[il]
15794                        .as_mut()
15795                        .expect("distributed cache checked above"),
15796                    transaction,
15797                );
15798                if let Some(local) = cache.kv[il].as_mut() {
15799                    local.len = base_len;
15800                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
15801                }
15802                return Err(error);
15803            }
15804        };
15805        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
15806            cache.tp_kv[il]
15807                .as_mut()
15808                .expect("distributed cache checked above"),
15809            transaction,
15810            tokens,
15811        ) {
15812            let _ = tp.runtime.rollback_tp_kv_transaction(
15813                cache.tp_kv[il]
15814                    .as_mut()
15815                    .expect("distributed cache checked above"),
15816                transaction,
15817            );
15818            let local = cache.kv[il].as_mut().expect("local cache checked above");
15819            local.len = base_len;
15820            e.set_i32_one(&mut local.len_d, base_len as i32)?;
15821            return Err(error);
15822        }
15823
15824        let committed = cache.tp_kv[il]
15825            .as_ref()
15826            .expect("distributed cache checked above")
15827            .committed_len();
15828        let local_len = cache.kv[il]
15829            .as_ref()
15830            .expect("local cache checked above")
15831            .len;
15832        if committed != local_len {
15833            return Err(format!(
15834                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
15835            )
15836            .into());
15837        }
15838        eprintln!(
15839            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
15840             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
15841             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
15842             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
15843             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
15844             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
15845             output={} performance_claim=false",
15846            tp.layer,
15847            tp.devices,
15848            hydrated,
15849            if window.is_some() {
15850                "rank-local-swa-ring"
15851            } else {
15852                "rank-local-global"
15853            },
15854            tp.runtime.transport_label(),
15855            tp.runtime.bulk_p2p(),
15856            if tp.runtime.bulk_p2p() {
15857                "root-device"
15858            } else {
15859                "root-readback"
15860            },
15861        );
15862        Ok(output)
15863    }
15864
15865    fn step35_tp_decode_attn_resident(
15866        &self,
15867        e: &Engine,
15868        fa: &FullAttnLayer,
15869        il: usize,
15870        h: &CudaSlice<f32>,
15871        pos_d: &CudaSlice<i32>,
15872        cache: &mut Cache,
15873    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15874        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
15875        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
15876        // nvfp4-dev-routes counter.
15877        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15878        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15879        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15880        let started = timing.then(std::time::Instant::now);
15881        let result = if crate::tp::step_tp_decode_v2_enabled()? {
15882            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
15883        } else {
15884            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
15885        };
15886        if let Some(started) = started {
15887            use std::sync::atomic::Ordering;
15888            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
15889                + started.elapsed().as_nanos() as u64;
15890            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
15891            if calls % 430 == 0 {
15892                eprintln!(
15893                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
15894                    ns as f64 / 1.0e6,
15895                    ns as f64 / calls as f64 / 1.0e3,
15896                );
15897            }
15898        }
15899        result
15900    }
15901
15902    #[allow(clippy::too_many_arguments)]
15903    fn step35_tp_decode_attn_resident_inner(
15904        &self,
15905        e: &Engine,
15906        fa: &FullAttnLayer,
15907        il: usize,
15908        h: &CudaSlice<f32>,
15909        pos_d: &CudaSlice<i32>,
15910        cache: &mut Cache,
15911    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15912        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
15913        // drains every stream so queued async work is billed to the phase that queued it — the
15914        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
15915        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
15916        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15917        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15918        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15919        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15920        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15921        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15922        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15923        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15924        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
15925        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
15926        fn lap(
15927            runtime: &crate::tp::TpE4m3HostBounce,
15928            e: &Engine,
15929            timer: &std::sync::atomic::AtomicU64,
15930            started: &mut Option<std::time::Instant>,
15931        ) -> Result<(), Box<dyn std::error::Error>> {
15932            let Some(start) = started.as_mut() else {
15933                return Ok(());
15934            };
15935            for rank in 0..runtime.devices().len() {
15936                if let Some(engine) = runtime.rank_engine(rank) {
15937                    let _main = engine.gpu.enter_main()?;
15938                    engine.stream().synchronize()?;
15939                }
15940            }
15941            e.stream().synchronize()?;
15942            timer.fetch_add(
15943                start.elapsed().as_nanos() as u64,
15944                std::sync::atomic::Ordering::Relaxed,
15945            );
15946            *start = std::time::Instant::now();
15947            Ok(())
15948        }
15949        let tp = fa
15950            .step_tp_qkv
15951            .as_ref()
15952            .ok_or("Step TP decode lost its resident projections")?;
15953        let attention = tp
15954            .attention
15955            .as_ref()
15956            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
15957        if !tp.runtime.native_p2p() {
15958            return Err("rank-local Step attention requires native P2P".into());
15959        }
15960        if crate::Engine::kv_fp8_on() {
15961            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
15962        }
15963
15964        let geometry = self.step35_geom(il);
15965        let window = geometry.window.map(|window| window as usize);
15966        let ranks = tp.runtime.devices().len();
15967        let head_dim = geometry.head_dim_k as usize;
15968        let heads = geometry.n_head as usize;
15969        let kv_heads = geometry.n_head_kv as usize;
15970        if heads % ranks != 0 || kv_heads % ranks != 0 {
15971            return Err(format!(
15972                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15973            )
15974            .into());
15975        }
15976        let local_heads = heads / ranks;
15977        let local_kv_heads = kv_heads / ranks;
15978        let local_kv_dim = local_kv_heads * head_dim;
15979        let max_ctx = cache.max_ctx;
15980
15981        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15982
15983        let base_len = cache.kv[il]
15984            .as_ref()
15985            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15986            .len;
15987        let distributed = cache.tp_kv[il]
15988            .as_ref()
15989            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15990        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15991            return Err(format!(
15992                "Step TP layer {il} cache lengths diverged before decode: \
15993                 local={base_len} distributed={}/{}",
15994                distributed.committed_len(),
15995                distributed.staged_len()
15996            )
15997            .into());
15998        }
15999
16000        let mut lap_start = timing.then(std::time::Instant::now);
16001        let positions = e.dtoh_i32(pos_d)?;
16002        if positions.len() != 1 {
16003            return Err(format!(
16004                "rank-local Step decode requires one position, got {}",
16005                positions.len()
16006            )
16007            .into());
16008        }
16009        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
16010        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
16011            attention.decode_input.as_ref()
16012        {
16013            let mut decode_input = decode_input
16014                .lock()
16015                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16016            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16017            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16018            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16019            e.stream().synchronize()?;
16020            tp.runtime
16021                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16022            let q_raw = tp
16023                .runtime
16024                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16025            let k_raw = tp
16026                .runtime
16027                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16028            let v_raw = tp
16029                .runtime
16030                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16031            (q_raw, k_raw, v_raw, "root-device-replicated")
16032        } else {
16033            let activation = e.dtoh(h)?;
16034            let q_raw =
16035                tp.runtime
16036                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16037            let k_raw =
16038                tp.runtime
16039                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16040            let v_raw =
16041                tp.runtime
16042                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16043            (q_raw, k_raw, v_raw, "host-replicated")
16044        };
16045        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16046        let mut q = Vec::with_capacity(ranks);
16047        let mut k = Vec::with_capacity(ranks);
16048        for rank in 0..ranks {
16049            let engine = tp
16050                .runtime
16051                .rank_engine(rank)
16052                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16053            let _main = engine.gpu.enter_main()?;
16054            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16055            engine.rms_norm(
16056                &q_raw[rank],
16057                &attention.q_norm[rank],
16058                &mut q_rank,
16059                head_dim,
16060                local_heads,
16061                self.cfg.rms_eps,
16062            )?;
16063            let mut k_rank = engine.uninit(local_kv_dim)?;
16064            engine.rms_norm(
16065                &k_raw[rank],
16066                &attention.k_norm[rank],
16067                &mut k_rank,
16068                head_dim,
16069                local_kv_heads,
16070                self.cfg.rms_eps,
16071            )?;
16072            let position = engine.htod_i32(&positions)?;
16073            let rope_freqs = if geometry.rope_factors {
16074                self.step35_aux
16075                    .as_ref()
16076                    .and_then(|aux| aux.rope_freqs(engine))
16077            } else {
16078                None
16079            };
16080            engine.rope_neox2(
16081                &mut q_rank,
16082                &mut k_rank,
16083                &position,
16084                head_dim,
16085                geometry.n_rot as usize,
16086                local_heads,
16087                local_kv_heads,
16088                1,
16089                geometry.rope_base,
16090                1.0,
16091                rope_freqs,
16092            )?;
16093            q.push(q_rank);
16094            k.push(k_rank);
16095        }
16096        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16097
16098        let gate_weight = fa
16099            .attn_gate
16100            .as_ref()
16101            .ok_or("step35 layer is missing attn_gate.weight")?;
16102        let gate = e.matmul(gate_weight, h, 1)?;
16103        let gate = e.dtoh(&gate)?;
16104        if gate.len() != heads {
16105            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16106        }
16107        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16108
16109        let transaction = cache.tp_kv[il]
16110            .as_mut()
16111            .expect("distributed cache checked above")
16112            .begin_transaction()?;
16113        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16114            cache.tp_kv[il]
16115                .as_mut()
16116                .expect("distributed cache checked above"),
16117            transaction,
16118            &k,
16119            &v_raw,
16120            1,
16121        ) {
16122            let _ = tp.runtime.rollback_tp_kv_transaction(
16123                cache.tp_kv[il]
16124                    .as_mut()
16125                    .expect("distributed cache checked above"),
16126                transaction,
16127            );
16128            return Err(error);
16129        }
16130        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16131
16132        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16133            let distributed = cache.tp_kv[il]
16134                .as_ref()
16135                .expect("distributed cache checked above");
16136            let staged_len = distributed.staged_len();
16137            let view_start = window
16138                .map(|window| staged_len.saturating_sub(window))
16139                .unwrap_or(0);
16140            let physical = distributed.physical_range(view_start, staged_len)?;
16141            let t_kv = staged_len - view_start;
16142            let mut gated = Vec::with_capacity(ranks);
16143            for rank in 0..ranks {
16144                let engine = tp
16145                    .runtime
16146                    .rank_engine(rank)
16147                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16148                let _main = engine.gpu.enter_main()?;
16149                let rank_cache = distributed
16150                    .rank(rank)
16151                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16152                let k_view = engine.view_u8_range(
16153                    rank_cache.k(),
16154                    physical.start * distributed.k_tok_bytes(),
16155                    physical.end * distributed.k_tok_bytes(),
16156                );
16157                let v_view = engine.view_u8_range(
16158                    rank_cache.v(),
16159                    physical.start * distributed.v_tok_bytes(),
16160                    physical.end * distributed.v_tok_bytes(),
16161                );
16162                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16163                engine.fa_decode_kvmod(
16164                    &q[rank],
16165                    &k_view,
16166                    &v_view,
16167                    &mut attention_out,
16168                    head_dim,
16169                    local_heads,
16170                    local_kv_heads,
16171                    t_kv,
16172                    geometry.attention_scale(),
16173                    distributed.k_tok_bytes(),
16174                    distributed.v_tok_bytes(),
16175                    false,
16176                )?;
16177                let gate_start = rank * local_heads;
16178                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16179                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16180                engine.attn_head_gate(
16181                    &attention_out,
16182                    &gate_rank,
16183                    &mut gated_rank,
16184                    None,
16185                    head_dim,
16186                    local_heads,
16187                    1,
16188                )?;
16189                gated.push(gated_rank);
16190            }
16191            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16192
16193            let gathered =
16194                tp.runtime
16195                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16196            let output = tp
16197                .runtime
16198                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16199            let output = e.htod(&output)?;
16200            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16201
16202            let k_shadow = tp
16203                .runtime
16204                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16205            let v_shadow = tp
16206                .runtime
16207                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16208            let k_shadow = e.htod(&k_shadow)?;
16209            let v_shadow = e.htod(&v_shadow)?;
16210            let local = cache.kv[il]
16211                .as_mut()
16212                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16213            if local.len != base_len || base_len + 1 > max_ctx {
16214                return Err(format!(
16215                    "Step TP layer {il} local cache changed during decode: \
16216                     len={} base={base_len} max={max_ctx}",
16217                    local.len
16218                )
16219                .into());
16220            }
16221            let retain_from = window
16222                .map(|window| {
16223                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16224                    let rollback_retain =
16225                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16226                    staged_retain.min(rollback_retain)
16227                })
16228                .unwrap_or(0);
16229            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16230            e.append_kv_quantized(
16231                &k_shadow,
16232                &v_shadow,
16233                &mut local.k,
16234                &mut local.v,
16235                write_row,
16236                local.kv_dim_k,
16237                local.kv_dim_v,
16238                local.k_tok_bytes,
16239                local.v_tok_bytes,
16240                false,
16241            )?;
16242            local.len = base_len + 1;
16243            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16244            Ok(output)
16245        })();
16246
16247        let output = match staged {
16248            Ok(output) => output,
16249            Err(error) => {
16250                let _ = tp.runtime.rollback_tp_kv_transaction(
16251                    cache.tp_kv[il]
16252                        .as_mut()
16253                        .expect("distributed cache checked above"),
16254                    transaction,
16255                );
16256                if let Some(local) = cache.kv[il].as_mut() {
16257                    local.len = base_len;
16258                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16259                }
16260                return Err(error);
16261            }
16262        };
16263        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16264            cache.tp_kv[il]
16265                .as_mut()
16266                .expect("distributed cache checked above"),
16267            transaction,
16268            1,
16269        ) {
16270            let _ = tp.runtime.rollback_tp_kv_transaction(
16271                cache.tp_kv[il]
16272                    .as_mut()
16273                    .expect("distributed cache checked above"),
16274                transaction,
16275            );
16276            let local = cache.kv[il].as_mut().expect("local cache checked above");
16277            local.len = base_len;
16278            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16279            return Err(error);
16280        }
16281
16282        let committed = cache.tp_kv[il]
16283            .as_ref()
16284            .expect("distributed cache checked above")
16285            .committed_len();
16286        let local_len = cache.kv[il]
16287            .as_ref()
16288            .expect("local cache checked above")
16289            .len;
16290        if committed != local_len {
16291            return Err(format!(
16292                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16293            )
16294            .into());
16295        }
16296        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16297        if timing {
16298            use std::sync::atomic::Ordering;
16299            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16300            if calls % 430 == 0 {
16301                let avg = |t: &std::sync::atomic::AtomicU64| {
16302                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16303                };
16304                eprintln!(
16305                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16306                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16307                    avg(&T_POS),
16308                    avg(&T_QKV),
16309                    avg(&T_NORMROPE),
16310                    avg(&T_GATE),
16311                    avg(&T_APPEND),
16312                    avg(&T_ATTN),
16313                    avg(&T_OPROJ),
16314                    avg(&T_SHADOW),
16315                );
16316            }
16317        }
16318        eprintln!(
16319            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16320             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16321             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16322             attention_scope={} input_path={} kv_physical_rows={} \
16323             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16324             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16325             bulk_p2p={} output=root-readback performance_claim=false",
16326            tp.layer,
16327            tp.devices,
16328            hydrated,
16329            if window.is_some() {
16330                "rank-local-swa-ring"
16331            } else {
16332                "rank-local-global"
16333            },
16334            input_path,
16335            cache.tp_kv[il]
16336                .as_ref()
16337                .expect("distributed cache checked above")
16338                .physical_capacity(),
16339            tp.runtime.transport_label(),
16340            tp.runtime.bulk_p2p(),
16341        );
16342        Ok(output)
16343    }
16344
16345    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16346    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16347    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16348    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16349    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16350    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16351    #[allow(clippy::too_many_arguments)]
16352    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16353    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16354    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16355    /// the resident fused TP2 class (caller falls back to the per-row walk).
16356    pub(crate) fn step35_verify_qkv_precompute(
16357        &self,
16358        e: &Engine,
16359        il: usize,
16360        h_t: &CudaSlice<f32>,
16361        t: usize,
16362    ) -> Result<bool, Box<dyn std::error::Error>> {
16363        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16364            return Ok(false);
16365        };
16366        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16367            return Ok(false);
16368        };
16369        let Some(attention) = tp.attention.as_ref() else {
16370            return Ok(false);
16371        };
16372        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16373            return Ok(false);
16374        }
16375        let geometry = self.step35_geom(il);
16376        let heads = geometry.n_head as usize;
16377        let ws_index = tp
16378            .runtime
16379            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16380        let gate_shards = attention
16381            .gate_shards_bf16
16382            .as_deref()
16383            .map(crate::tp::StepTpGateShards::Bf16);
16384        tp.runtime.decode_v2_input_qkv_tcol(
16385            ws_index,
16386            e,
16387            h_t,
16388            t,
16389            &tp.q,
16390            &tp.k,
16391            &tp.v,
16392            gate_shards,
16393        )?;
16394        Ok(true)
16395    }
16396
16397    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16398    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16399    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16400    /// flag confirmed the defer engaged for every column.
16401    pub(crate) fn step35_verify_oproj_tcol(
16402        &self,
16403        e: &Engine,
16404        il: usize,
16405        t: usize,
16406    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16407        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16408            return Err("tcol o_proj join expects full attention".into());
16409        };
16410        let tp = fa
16411            .step_tp_qkv
16412            .as_ref()
16413            .ok_or("tcol o_proj join lost its resident projections")?;
16414        let heads = self.step35_geom(il).n_head as usize;
16415        let ws_index = tp
16416            .runtime
16417            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16418        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16419    }
16420
16421    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16422    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16423    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16424    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16425    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16426    /// walk runs the ordinary per-column program.
16427    pub(crate) fn step35_spec_fa2_precheck(
16428        &self,
16429        cache: &Cache,
16430        il: usize,
16431        pos0: usize,
16432    ) -> Result<bool, Box<dyn std::error::Error>> {
16433        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16434        // a silently-vacuous door is indistinguishable from a slow one without this.
16435        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16436            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16437            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16438            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16439                let mut seen = SEEN.lock().unwrap();
16440                if !seen.iter().any(|c| *c == clause) {
16441                    // leak: bounded by the clause-id set
16442                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16443                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16444                }
16445            }
16446            false
16447        }
16448        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16449        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16450        if let Some(only) =
16451            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16452        {
16453            if *only != il {
16454                return Ok(false);
16455            }
16456        }
16457        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16458            return Ok(nope("mixer", il, pos0));
16459        };
16460        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16461            return Ok(nope("step_tp", il, pos0));
16462        };
16463        let Some(attention) = tp.attention.as_ref() else {
16464            return Ok(nope("attention", il, pos0));
16465        };
16466        if !tp.runtime.native_p2p()
16467            || crate::Engine::kv_fp8_on()
16468            || !crate::tp::step_tp_dcw_enabled()?
16469            || !crate::tp::step_tp_qkv_fused_enabled()?
16470            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16471        {
16472            return Ok(nope("runtime-doors", il, pos0));
16473        }
16474        let geometry = self.step35_geom(il);
16475        let head_dim = geometry.head_dim_k as usize;
16476        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16477            return Ok(nope("fa-class", il, pos0));
16478        }
16479        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16480            return Ok(nope("tp-kv", il, pos0));
16481        };
16482        if distributed.staged_len() != pos0 {
16483            return Ok(nope("staged-len", il, pos0));
16484        }
16485        // Both appends must land without a ring rebase (rebase columns take the
16486        // host-row path, which cannot stash).
16487        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16488        if would_rebase {
16489            return Ok(nope("rebase", il, pos0));
16490        }
16491        let window = geometry.window.map(|w| w as usize);
16492        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16493        // shift by one key, so one shared tile grid cannot reproduce both rows'
16494        // per-column FP grouping) — and drifted verify logits change accept decisions,
16495        // breaking the spec==target contract. Engage only when BOTH rows' views start
16496        // at 0 (global, or SWA still inside its window): bitwise per row under the
16497        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16498        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16499        if let Some(w) = window {
16500            if pos0 + 2 > w {
16501                return Ok(nope("swa-capped", il, pos0));
16502            }
16503        }
16504        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16505        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16506        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16507        let (t0, t1) = (pos0 + 1, pos0 + 2);
16508        if t0 < 96 {
16509            return Ok(nope("dcw-floor", il, pos0));
16510        }
16511        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16512            return Ok(nope("vec-floor", il, pos0));
16513        }
16514        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16515        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16516        // the two rows' own launches — the joined kernel derives one grid from T1 and
16517        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16518        // the combine's merge rounding. Boundary rounds fall back per column.
16519        let ranks = tp.runtime.devices().len();
16520        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16521        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16522        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16523        if sp0 != sp1 {
16524            return Ok(nope("partition-sp", il, pos0));
16525        }
16526        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16527        if ns0 != ns1 {
16528            return Ok(nope("partition-ns", il, pos0));
16529        }
16530        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16531            return Ok(nope("partition-per", il, pos0));
16532        }
16533        Ok(true)
16534    }
16535
16536    /// T-ROW fa precheck (the rows kernel supersedes the dcw2 pair-join): every dynamic
16537    /// input of the engine-side dcw decision must hold for EVERY row — stashing is
16538    /// unrecoverable — plus the rows-launcher guards (big-rig ladder, no env split
16539    /// overrides). No partition or capped-SWA clauses: each row derives its OWN geometry.
16540    pub(crate) fn step35_fa_rows_precheck(
16541        &self,
16542        cache: &Cache,
16543        il: usize,
16544        pos0: usize,
16545        t: usize,
16546    ) -> Result<bool, Box<dyn std::error::Error>> {
16547        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16548            return Ok(false);
16549        };
16550        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16551            return Ok(false);
16552        };
16553        let Some(attention) = tp.attention.as_ref() else {
16554            return Ok(false);
16555        };
16556        if !tp.runtime.native_p2p()
16557            || crate::Engine::kv_fp8_on()
16558            || !crate::tp::step_tp_dcw_enabled()?
16559            || !crate::tp::step_tp_qkv_fused_enabled()?
16560            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16561        {
16562            return Ok(false);
16563        }
16564        let geometry = self.step35_geom(il);
16565        let head_dim = geometry.head_dim_k as usize;
16566        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16567            return Ok(false);
16568        }
16569        if crate::fa_sm_count() < 128
16570            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16571            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16572            || std::env::var("MEMRA_FA_SP16").is_ok()
16573            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16574        {
16575            return Ok(false);
16576        }
16577        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16578            return Ok(false);
16579        };
16580        if distributed.staged_len() != pos0 {
16581            return Ok(false);
16582        }
16583        let (_, would_rebase) = distributed.peek_append_ring(t)?;
16584        if would_rebase {
16585            return Ok(false);
16586        }
16587        // Row 0 sees the smallest view: its post-append effective t_kv must clear both
16588        // the dcw floor and the vec-class floor (later rows only grow).
16589        let window = geometry.window.map(|w| w as usize);
16590        let t0 = window.map(|w| (pos0 + 1).min(w)).unwrap_or(pos0 + 1);
16591        if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16592            return Ok(false);
16593        }
16594        Ok(true)
16595    }
16596
16597    /// T-ROW fa join for the verify walk (same-session rows: shared ring/len with
16598    /// len_back = t-1-r). Tables stage once per (layer, rank, ring, t) and live on the
16599    /// owning rank.
16600    pub(crate) fn step35_verify_fa_rows_join(
16601        &self,
16602        e: &Engine,
16603        il: usize,
16604        cache: &Cache,
16605        pos0: usize,
16606        t: usize,
16607    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16608        use cudarc::driver::DevicePtr;
16609        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16610            return Err("fa rows join expects full attention".into());
16611        };
16612        let tp = fa
16613            .step_tp_qkv
16614            .as_ref()
16615            .ok_or("fa rows join lost its resident projections")?;
16616        let geometry = self.step35_geom(il);
16617        let heads = geometry.n_head as usize;
16618        let head_dim = geometry.head_dim_k as usize;
16619        let window = geometry.window.map(|w| w as usize);
16620        let distributed = cache.tp_kv[il]
16621            .as_ref()
16622            .ok_or("fa rows join lost its distributed KV cache")?;
16623        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16624        // Host mirror of the kernel's big-rig ladder (launcher-guarded identical).
16625        let ladder = |t_kv: usize| -> usize {
16626            if t_kv <= 2048 {
16627                16
16628            } else if t_kv <= 16384 {
16629                64
16630            } else {
16631                128
16632            }
16633        };
16634        let mut max_ns = 1usize;
16635        for r in 0..t {
16636            let t_kv = window
16637                .map(|w| (pos0 + r + 1).min(w))
16638                .unwrap_or(pos0 + r + 1);
16639            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16640        }
16641        // Per-(layer, rank, ring, t) table cache: ring/len/base pointers are stable for
16642        // the life of the hydrated cache, and len_back is static for same-session rows.
16643        static TABS: std::sync::Mutex<
16644            Option<std::collections::HashMap<(usize, usize, u64, usize), CudaSlice<u64>>>,
16645        > = std::sync::Mutex::new(None);
16646        let ranks = tp.runtime.devices().len();
16647        let mut keys = Vec::with_capacity(ranks);
16648        let mut guard = TABS.lock().map_err(|_| "fa rows table lock is poisoned")?;
16649        {
16650            let map = guard.get_or_insert_with(Default::default);
16651            for rank in 0..ranks {
16652                let engine = tp
16653                    .runtime
16654                    .rank_engine(rank)
16655                    .ok_or("fa rows join lost a rank engine")?;
16656                let rank_cache = distributed
16657                    .rank(rank)
16658                    .ok_or("fa rows join lost a KV cache rank")?;
16659                let _main = engine.gpu.enter_main()?;
16660                let s = engine.stream();
16661                let (kp, _g0) = rank_cache.k().device_ptr(&s);
16662                let (vp, _g1) = rank_cache.v().device_ptr(&s);
16663                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16664                let bp = match rank_cache.base_d() {
16665                    Some(b) => {
16666                        let (p, _g) = b.device_ptr(&s);
16667                        p as u64
16668                    }
16669                    None => 0u64,
16670                };
16671                // base_d arms lazily (None -> Some), so the key must cover it — a
16672                // stale null-base entry reads base=0 after a real rebase.
16673                let key = (il, rank, (kp as u64) ^ bp.rotate_left(32), t);
16674                if !map.contains_key(&key) {
16675                    let mut host = Vec::with_capacity(t * 6);
16676                    for r in 0..t {
16677                        host.extend_from_slice(&[
16678                            kp as u64,
16679                            vp as u64,
16680                            lp as u64,
16681                            bp,
16682                            0u64,
16683                            (t - 1 - r) as u64,
16684                        ]);
16685                    }
16686                    map.insert(key, engine.stream().clone_htod(&host)?);
16687                }
16688                keys.push(key);
16689            }
16690        }
16691        let map = guard.as_ref().expect("armed above");
16692        let tabs: Vec<&CudaSlice<u64>> = keys
16693            .iter()
16694            .map(|k| map.get(k).expect("inserted above"))
16695            .collect();
16696        let ws_index = tp
16697            .runtime
16698            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16699        tp.runtime.decode_v2_fa_rows_join(
16700            ws_index,
16701            e,
16702            &tp.o,
16703            &tabs,
16704            t,
16705            head_dim,
16706            window.unwrap_or(0),
16707            max_ns,
16708            geometry.attention_scale(),
16709            k_tok_bytes,
16710            v_tok_bytes,
16711        )
16712    }
16713
16714    /// Multi-session t-row fa precheck (the batched serving walk): the static doors of
16715    /// the rows kernel plus per-SESSION dynamic checks — every row's own cache must be
16716    /// hydrated, in sync, rebase-free and above both floors.
16717    pub(crate) fn step35_batch_fa_rows_precheck(
16718        &self,
16719        caches: &[&mut Cache],
16720        row_to_cache: impl Fn(usize) -> usize,
16721        positions: &[i32],
16722        il: usize,
16723    ) -> Result<bool, Box<dyn std::error::Error>> {
16724        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16725            return Ok(false);
16726        };
16727        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16728            return Ok(false);
16729        };
16730        let Some(attention) = tp.attention.as_ref() else {
16731            return Ok(false);
16732        };
16733        if !tp.runtime.native_p2p()
16734            || crate::Engine::kv_fp8_on()
16735            || !crate::tp::step_tp_dcw_enabled()?
16736            || !crate::tp::step_tp_qkv_fused_enabled()?
16737            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16738        {
16739            return Ok(false);
16740        }
16741        let geometry = self.step35_geom(il);
16742        let head_dim = geometry.head_dim_k as usize;
16743        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16744            return Ok(false);
16745        }
16746        if crate::fa_sm_count() < 128
16747            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16748            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16749            || std::env::var("MEMRA_FA_SP16").is_ok()
16750            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16751        {
16752            return Ok(false);
16753        }
16754        let window = geometry.window.map(|w| w as usize);
16755        for (r, &pos) in positions.iter().enumerate() {
16756            let cache = &caches[row_to_cache(r)];
16757            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16758                return Ok(false);
16759            };
16760            if distributed.staged_len() != pos as usize {
16761                return Ok(false);
16762            }
16763            if distributed.peek_append_ring(1)?.1 {
16764                return Ok(false);
16765            }
16766            let t0 = window
16767                .map(|w| (pos as usize + 1).min(w))
16768                .unwrap_or(pos as usize + 1);
16769            if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16770                return Ok(false);
16771            }
16772        }
16773        Ok(true)
16774    }
16775
16776    /// FULL t-row attention pass for the VERIFY walk (same-session rows): rope/append +
16777    /// fa + combine + o_proj join in 3 launches/rank/layer. Row r appends at slot
16778    /// len-base+r and one last block advances len by t; the fa rows read len_back =
16779    /// t-1-r. Returns None when the fused-rope class does not hold (the walk keeps the
16780    /// per-column stash flow). Caller has passed `step35_fa_rows_precheck`.
16781    pub(crate) fn step35_verify_rope_fa_pass(
16782        &self,
16783        e: &Engine,
16784        il: usize,
16785        cache: &Cache,
16786        pos0: usize,
16787        t: usize,
16788        stage_pos: bool,
16789    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16790        use cudarc::driver::DevicePtr;
16791        if !crate::tp::fuse_rope_append_on() {
16792            return Ok(None);
16793        }
16794        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16795            return Ok(None);
16796        };
16797        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16798            return Ok(None);
16799        };
16800        let Some(attention) = tp.attention.as_ref() else {
16801            return Ok(None);
16802        };
16803        let geometry = self.step35_geom(il);
16804        let head_dim = geometry.head_dim_k as usize;
16805        if head_dim != 128 {
16806            return Ok(None);
16807        }
16808        let heads = geometry.n_head as usize;
16809        let window = geometry.window.map(|w| w as usize);
16810        let ranks = tp.runtime.devices().len();
16811        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16812            return Ok(None);
16813        };
16814        if distributed.kv_dim_k() != distributed.kv_dim_v() {
16815            return Ok(None);
16816        }
16817        {
16818            let rank0 = distributed.rank(0).ok_or("verify rope pass lost rank 0")?;
16819            if rank0.base_d().is_none()
16820                && distributed.staged_len() + t > distributed.physical_capacity()
16821            {
16822                return Ok(None);
16823            }
16824        }
16825        let mut rope_freqs = Vec::with_capacity(ranks);
16826        for rank in 0..ranks {
16827            let engine = tp
16828                .runtime
16829                .rank_engine(rank)
16830                .ok_or("verify rope pass lost a rank engine")?;
16831            rope_freqs.push(if geometry.rope_factors {
16832                match self
16833                    .step35_aux
16834                    .as_ref()
16835                    .and_then(|aux| aux.rope_freqs(engine))
16836                {
16837                    Some(f) => Some(f),
16838                    None => return Ok(None),
16839                }
16840            } else {
16841                None
16842            });
16843        }
16844        let ladder = |t_kv: usize| -> usize {
16845            if t_kv <= 2048 {
16846                16
16847            } else if t_kv <= 16384 {
16848                64
16849            } else {
16850                128
16851            }
16852        };
16853        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
16854        let mut max_ns = 1usize;
16855        let mut positions = Vec::with_capacity(t);
16856        for r in 0..t {
16857            positions.push((pos0 + r) as i32);
16858            let t_kv = window
16859                .map(|w| (pos0 + r + 1).min(w))
16860                .unwrap_or(pos0 + r + 1);
16861            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
16862        }
16863        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
16864        let mut tab_keys = vec![0u64; ranks];
16865        for rank in 0..ranks {
16866            let engine = tp
16867                .runtime
16868                .rank_engine(rank)
16869                .ok_or("verify rope pass lost a rank engine")?;
16870            let rank_cache = distributed
16871                .rank(rank)
16872                .ok_or("verify rope pass lost a KV cache rank")?;
16873            let _main = engine.gpu.enter_main()?;
16874            let s = engine.stream();
16875            let (kp, _g0) = rank_cache.k().device_ptr(&s);
16876            let (vp, _g1) = rank_cache.v().device_ptr(&s);
16877            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
16878            let bp = match rank_cache.base_d() {
16879                Some(b) => {
16880                    let (p, _g) = b.device_ptr(&s);
16881                    p as u64
16882                }
16883                None => 0u64,
16884            };
16885            tab_keys[rank] = (kp as u64)
16886                .rotate_left(17)
16887                .wrapping_add(bp)
16888                .wrapping_add((il as u64) << 32)
16889                .wrapping_add(t as u64)
16890                .wrapping_add(1 << 63);
16891            for _r in 0..t {
16892                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
16893            }
16894        }
16895        let ws_index = tp
16896            .runtime
16897            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16898        tp.runtime
16899            .decode_v2_rope_fa_rows(
16900                ws_index,
16901                e,
16902                &tp.o,
16903                &session_parts,
16904                &tab_keys,
16905                &positions,
16906                stage_pos,
16907                true,
16908                &attention.q_norm,
16909                &attention.k_norm,
16910                &rope_freqs,
16911                t,
16912                head_dim,
16913                geometry.n_rot as usize,
16914                window.unwrap_or(0),
16915                max_ns,
16916                geometry.attention_scale(),
16917                k_tok_bytes,
16918                v_tok_bytes,
16919                self.cfg.rms_eps,
16920                geometry.rope_base,
16921            )
16922            .map(Some)
16923    }
16924
16925    /// FULL t-row attention pass for the batched walk (rope/append + fa + combine +
16926    /// o_proj join, 3 launches/rank/layer): returns None when the fused-rope class does
16927    /// not hold — the caller falls back to the per-row stash flow. The caller has
16928    /// already passed `step35_batch_fa_rows_precheck`.
16929    #[allow(clippy::too_many_arguments)]
16930    pub(crate) fn step35_batch_rope_fa_pass(
16931        &self,
16932        e: &Engine,
16933        il: usize,
16934        caches: &[&mut Cache],
16935        row_to_cache: impl Fn(usize) -> usize,
16936        positions: &[i32],
16937        t: usize,
16938        stage_pos: bool,
16939    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16940        use cudarc::driver::DevicePtr;
16941        if !crate::tp::fuse_rope_append_on() {
16942            return Ok(None);
16943        }
16944        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16945            return Ok(None);
16946        };
16947        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16948            return Ok(None);
16949        };
16950        let Some(attention) = tp.attention.as_ref() else {
16951            return Ok(None);
16952        };
16953        let geometry = self.step35_geom(il);
16954        let head_dim = geometry.head_dim_k as usize;
16955        if head_dim != 128 {
16956            return Ok(None);
16957        }
16958        let heads = geometry.n_head as usize;
16959        let window = geometry.window.map(|w| w as usize);
16960        let ranks = tp.runtime.devices().len();
16961        // The rows kernels never arm base_d; refuse once a ring could have rebased
16962        // without an armed base (the table would read base=0 after a real rebase).
16963        for r in 0..t {
16964            let cache = &caches[row_to_cache(r)];
16965            let Some(distributed) = cache.tp_kv[il].as_ref() else {
16966                return Ok(None);
16967            };
16968            if distributed.kv_dim_k() != distributed.kv_dim_v() {
16969                return Ok(None);
16970            }
16971            let rank0 = distributed.rank(0).ok_or("rope fa pass lost rank 0")?;
16972            if rank0.base_d().is_none()
16973                && distributed.staged_len() + t > distributed.physical_capacity()
16974            {
16975                return Ok(None);
16976            }
16977        }
16978        let mut rope_freqs = Vec::with_capacity(ranks);
16979        for rank in 0..ranks {
16980            let engine = tp
16981                .runtime
16982                .rank_engine(rank)
16983                .ok_or("rope fa pass lost a rank engine")?;
16984            rope_freqs.push(if geometry.rope_factors {
16985                match self
16986                    .step35_aux
16987                    .as_ref()
16988                    .and_then(|aux| aux.rope_freqs(engine))
16989                {
16990                    Some(f) => Some(f),
16991                    None => return Ok(None),
16992                }
16993            } else {
16994                None
16995            });
16996        }
16997        let ladder = |t_kv: usize| -> usize {
16998            if t_kv <= 2048 {
16999                16
17000            } else if t_kv <= 16384 {
17001                64
17002            } else {
17003                128
17004            }
17005        };
17006        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17007        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17008        let mut tab_keys = vec![0u64; ranks];
17009        for (r, &pos) in positions.iter().enumerate().take(t) {
17010            let cache = &caches[row_to_cache(r)];
17011            let distributed = cache.tp_kv[il]
17012                .as_ref()
17013                .ok_or("rope fa pass lost a distributed KV cache")?;
17014            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17015            let t_kv = window
17016                .map(|w| (pos as usize + 1).min(w))
17017                .unwrap_or(pos as usize + 1);
17018            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17019            for rank in 0..ranks {
17020                let engine = tp
17021                    .runtime
17022                    .rank_engine(rank)
17023                    .ok_or("rope fa pass lost a rank engine")?;
17024                let rank_cache = distributed
17025                    .rank(rank)
17026                    .ok_or("rope fa pass lost a KV cache rank")?;
17027                let _main = engine.gpu.enter_main()?;
17028                let s = engine.stream();
17029                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17030                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17031                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17032                let bp = match rank_cache.base_d() {
17033                    Some(b) => {
17034                        let (p, _g) = b.device_ptr(&s);
17035                        p as u64
17036                    }
17037                    None => 0u64,
17038                };
17039                tab_keys[rank] = tab_keys[rank]
17040                    .rotate_left(9)
17041                    .wrapping_add(kp as u64)
17042                    .wrapping_add(bp)
17043                    .wrapping_add(il as u64);
17044                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17045            }
17046        }
17047        let ws_index = tp
17048            .runtime
17049            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17050        tp.runtime
17051            .decode_v2_rope_fa_rows(
17052                ws_index,
17053                e,
17054                &tp.o,
17055                &session_parts,
17056                &tab_keys,
17057                positions,
17058                stage_pos,
17059                false,
17060                &attention.q_norm,
17061                &attention.k_norm,
17062                &rope_freqs,
17063                t,
17064                head_dim,
17065                geometry.n_rot as usize,
17066                window.unwrap_or(0),
17067                max_ns,
17068                geometry.attention_scale(),
17069                k_tok_bytes,
17070                v_tok_bytes,
17071                self.cfg.rms_eps,
17072                geometry.rope_base,
17073            )
17074            .map(Some)
17075    }
17076
17077    /// Multi-session t-row fa join (batched serving): per-row table entries point at
17078    /// each row's OWN session rings/counters (len_back = 0 — every session appended
17079    /// exactly its one row). Tables stage once per (layer, rank, session-set, t).
17080    #[allow(clippy::too_many_arguments)]
17081    pub(crate) fn step35_batch_fa_rows_join(
17082        &self,
17083        e: &Engine,
17084        il: usize,
17085        caches: &[&mut Cache],
17086        row_to_cache: impl Fn(usize) -> usize,
17087        positions: &[i32],
17088        t: usize,
17089    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17090        use cudarc::driver::DevicePtr;
17091        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17092            return Err("batch fa rows join expects full attention".into());
17093        };
17094        let tp = fa
17095            .step_tp_qkv
17096            .as_ref()
17097            .ok_or("batch fa rows join lost its resident projections")?;
17098        let geometry = self.step35_geom(il);
17099        let heads = geometry.n_head as usize;
17100        let head_dim = geometry.head_dim_k as usize;
17101        let window = geometry.window.map(|w| w as usize);
17102        let ladder = |t_kv: usize| -> usize {
17103            if t_kv <= 2048 {
17104                16
17105            } else if t_kv <= 16384 {
17106                64
17107            } else {
17108                128
17109            }
17110        };
17111        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17112        for (r, &pos) in positions.iter().enumerate() {
17113            let cache = &caches[row_to_cache(r)];
17114            let distributed = cache.tp_kv[il]
17115                .as_ref()
17116                .ok_or("batch fa rows join lost a distributed KV cache")?;
17117            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17118            let t_kv = window
17119                .map(|w| (pos as usize + 1).min(w))
17120                .unwrap_or(pos as usize + 1);
17121            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17122        }
17123        static TABS: std::sync::Mutex<
17124            Option<std::collections::HashMap<(usize, usize, u64, usize), CudaSlice<u64>>>,
17125        > = std::sync::Mutex::new(None);
17126        let ranks = tp.runtime.devices().len();
17127        let mut keys = Vec::with_capacity(ranks);
17128        let mut guard = TABS
17129            .lock()
17130            .map_err(|_| "batch fa rows table lock is poisoned")?;
17131        {
17132            let map = guard.get_or_insert_with(Default::default);
17133            for rank in 0..ranks {
17134                let engine = tp
17135                    .runtime
17136                    .rank_engine(rank)
17137                    .ok_or("batch fa rows join lost a rank engine")?;
17138                let _main = engine.gpu.enter_main()?;
17139                let s = engine.stream();
17140                let mut host = Vec::with_capacity(t * 6);
17141                let mut sig = 0u64;
17142                for r in 0..t {
17143                    let cache = &caches[row_to_cache(r)];
17144                    let distributed = cache.tp_kv[il]
17145                        .as_ref()
17146                        .ok_or("batch fa rows join lost a distributed KV cache")?;
17147                    let rank_cache = distributed
17148                        .rank(rank)
17149                        .ok_or("batch fa rows join lost a KV cache rank")?;
17150                    let (kp, _g0) = rank_cache.k().device_ptr(&s);
17151                    let (vp, _g1) = rank_cache.v().device_ptr(&s);
17152                    let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17153                    let bp = match rank_cache.base_d() {
17154                        Some(b) => {
17155                            let (p, _g) = b.device_ptr(&s);
17156                            p as u64
17157                        }
17158                        None => 0u64,
17159                    };
17160                    sig = sig.rotate_left(7).wrapping_add(kp as u64).wrapping_add(bp);
17161                    host.extend_from_slice(&[kp as u64, vp as u64, lp as u64, bp, 0u64, 0u64]);
17162                }
17163                let key = (il, rank, sig, t);
17164                if !map.contains_key(&key) {
17165                    map.insert(key, engine.stream().clone_htod(&host)?);
17166                }
17167                keys.push(key);
17168            }
17169        }
17170        let map = guard.as_ref().expect("armed above");
17171        let tabs: Vec<&CudaSlice<u64>> = keys
17172            .iter()
17173            .map(|k| map.get(k).expect("inserted above"))
17174            .collect();
17175        let ws_index = tp
17176            .runtime
17177            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17178        tp.runtime.decode_v2_fa_rows_join(
17179            ws_index,
17180            e,
17181            &tp.o,
17182            &tabs,
17183            t,
17184            head_dim,
17185            window.unwrap_or(0),
17186            max_ns,
17187            geometry.attention_scale(),
17188            k_tok_bytes,
17189            v_tok_bytes,
17190        )
17191    }
17192
17193    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
17194    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
17195    /// slab on `e`.
17196    pub(crate) fn step35_verify_spec_fa2_join(
17197        &self,
17198        e: &Engine,
17199        il: usize,
17200        cache: &Cache,
17201        pos0: usize,
17202    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17203        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17204            return Err("spec fa2 join expects full attention".into());
17205        };
17206        let tp = fa
17207            .step_tp_qkv
17208            .as_ref()
17209            .ok_or("spec fa2 join lost its resident projections")?;
17210        let geometry = self.step35_geom(il);
17211        let heads = geometry.n_head as usize;
17212        let head_dim = geometry.head_dim_k as usize;
17213        let window = geometry.window.map(|w| w as usize);
17214        // POST-append view of the second row (kernel T1 = len - lstart with len =
17215        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
17216        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
17217        let distributed = cache.tp_kv[il]
17218            .as_ref()
17219            .ok_or("spec fa2 join lost its distributed KV cache")?;
17220        let ws_index = tp
17221            .runtime
17222            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17223        tp.runtime.decode_v2_spec_fa2_join(
17224            ws_index,
17225            e,
17226            &tp.o,
17227            distributed,
17228            head_dim,
17229            window.unwrap_or(0),
17230            bucket,
17231            geometry.attention_scale(),
17232        )
17233    }
17234
17235    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
17236    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
17237    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
17238    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
17239    /// (caller falls back to the per-column walk).
17240    pub(crate) fn step35_verify_moe_tn(
17241        &self,
17242        e: &Engine,
17243        il: usize,
17244        z_t: &CudaSlice<f32>,
17245        t: usize,
17246    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17247        let layer = &self.layers[il];
17248        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
17249            return Ok(None);
17250        };
17251        let Some(tp) = m.step_tp.as_ref() else {
17252            return Ok(None);
17253        };
17254        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
17255            return Ok(None);
17256        };
17257        if !crate::tp::step_nvfp4_dev_routes_enabled()?
17258            || !crate::tp::step_tp_dev_router_enabled()?
17259            || !crate::tp::nvfp4_bank_v2_on()
17260            || bank.ep2
17261        {
17262            return Ok(None);
17263        }
17264        let cfg = &self.cfg;
17265        let Some(moe) = cfg.moe.as_ref() else {
17266            return Ok(None);
17267        };
17268        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
17269            return Ok(None);
17270        };
17271        let n_embd = cfg.n_embd as usize;
17272        let n_expert = moe.expert_count as usize;
17273        let n_used = moe.expert_used_count as usize;
17274        if t < 2 || t > 32 || z_t.len() < t * n_embd {
17275            return Err("verify moe t-row geometry".into());
17276        }
17277        let trace = std::env::var("MEMRA_TN_TRACE").as_deref() == Ok("1");
17278        if trace {
17279            eprintln!("[tn-trace] il={il} t={t} logits");
17280        }
17281        let logits = Self::moe_router_logits(e, m, z_t, t, cfg)?;
17282        // Persistent selection rows (host-op diet, same shape law as the t=1 SELW),
17283        // sized for the widest walk (t <= 8).
17284        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
17285            std::sync::Mutex::new(None);
17286        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
17287        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
17288            *selw = Some((
17289                e.ctx().ordinal(),
17290                e.htod_i32(&vec![0i32; 32 * n_used])?,
17291                e.htod(&vec![0.0f32; 32 * n_used])?,
17292            ));
17293        }
17294        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
17295        if trace {
17296            eprintln!("[tn-trace] il={il} topk logits_len={}", logits.len());
17297        }
17298        e.moe_router_sigmoid_topk_into(
17299            &logits,
17300            t,
17301            n_expert,
17302            n_used,
17303            m.active_count(),
17304            &m.exp_probs_b_dev,
17305            &m.active_experts_dev,
17306            sf,
17307            route_norm,
17308            sel_d,
17309            w_d,
17310        )?;
17311        if trace {
17312            eprintln!("[tn-trace] il={il} driver");
17313        }
17314        let mut out_t = tp
17315            .runtime
17316            .run_tensor_parallel_routes_nvfp4_device_routed_tn(
17317                bank,
17318                e,
17319                z_t,
17320                sel_d,
17321                w_d,
17322                t,
17323                n_used,
17324                tp.activation_limit,
17325            )?;
17326        if trace {
17327            eprintln!("[tn-trace] il={il} shexp out_t={}", out_t.len());
17328        }
17329        // Shared expert: ONE t-row pass through the per-row-exact twins when the bf16
17330        // dual-silu shape holds (each row's program == the t=1 fused path); otherwise the
17331        // exact t=1 program per column.
17332        if !Self::step35_shexp_rows(e, m, z_t, t, cfg, il as u16, &mut out_t)? {
17333            let mut z_row = e.uninit(n_embd)?;
17334            let mut out_row = e.uninit(n_embd)?;
17335            for c in 0..t {
17336                e.dtod_copy_view(&z_t.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
17337                e.dtod_copy_view(&out_t.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
17338                Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
17339                e.dtod_copy_into(&out_row, &mut out_t, c * n_embd)?;
17340            }
17341        }
17342        Ok(Some(out_t))
17343    }
17344
17345    /// T-ROW shared expert (spec verify / batched serving): dual-silu + down + gate +
17346    /// scaled accumulate over all rows in four launches, each the per-row-exact twin of
17347    /// the t=1 fused path. Returns false (untouched `out_t`) when the shape is ineligible.
17348    fn step35_shexp_rows(
17349        e: &Engine,
17350        m: &MoeWeights,
17351        z_t: &CudaSlice<f32>,
17352        t: usize,
17353        cfg: &ModelConfig,
17354        il: u16,
17355        out_t: &mut CudaSlice<f32>,
17356    ) -> Result<bool, Box<dyn std::error::Error>> {
17357        let n_embd = cfg.n_embd as usize;
17358        let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
17359            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
17360        else {
17361            return Ok(false);
17362        };
17363        if !crate::Engine::bf16_mmv_on() || n_embd % 8 != 0 || cfg.m3.is_some() {
17364            return Ok(false);
17365        }
17366        let (
17367            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
17368            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
17369            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
17370        ) = (gate_shexp, up_shexp, down_shexp)
17371        else {
17372            return Ok(false);
17373        };
17374        let n_ff_sh = gate_shexp.out_features();
17375        let lim = cfg.clamp_shexp_at(il as u32);
17376        // Persistent t-row buffers (widest walk t <= 8).
17377        static WS: std::sync::Mutex<Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>> =
17378            std::sync::Mutex::new(None);
17379        let mut guard = WS.lock().map_err(|_| "shexp rows ws lock is poisoned")?;
17380        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
17381        if guard
17382            .as_ref()
17383            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
17384        {
17385            *guard = Some((
17386                pins.0,
17387                pins.1,
17388                pins.2,
17389                e.uninit(32 * n_ff_sh)?,
17390                e.uninit(32 * n_embd)?,
17391            ));
17392        }
17393        let (_, _, _, act_t, sh_t) = guard.as_mut().expect("armed above");
17394        e.matvec_bf16_dual_silu_rows_into(wg, wu, z_t, act_t, n_embd, n_ff_sh, lim, t)?;
17395        e.matvec_bf16_rows_into(wd, act_t, sh_t, n_ff_sh, n_embd, t)?;
17396        // Head gate: sigmoid_dot_rows is the exact t=1 expression per row; gate-less
17397        // shexp accumulates at weight 1 (the fuse_da identity).
17398        let gate = match &m.gate_inp_shexp {
17399            Some(gate_inp_shexp) => {
17400                e.sigmoid_dot_rows(z_t, gate_inp_shexp.float_data(), n_embd, t)?
17401            }
17402            None => e.htod(&vec![1.0f32; t])?,
17403        };
17404        e.add_scaled_rows(sh_t, &gate, out_t, n_embd, t)?;
17405        Ok(true)
17406    }
17407
17408    fn step35_tp_decode_attn_resident_v2(
17409        &self,
17410        e: &Engine,
17411        fa: &FullAttnLayer,
17412        il: usize,
17413        h: &CudaSlice<f32>,
17414        pos_d: &CudaSlice<i32>,
17415        cache: &mut Cache,
17416    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17417        let tp = fa
17418            .step_tp_qkv
17419            .as_ref()
17420            .ok_or("Step TP decode lost its resident projections")?;
17421        let attention = tp
17422            .attention
17423            .as_ref()
17424            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
17425        if !tp.runtime.native_p2p() {
17426            return Err("rank-local Step attention requires native P2P".into());
17427        }
17428        if crate::Engine::kv_fp8_on() {
17429            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
17430        }
17431
17432        let geometry = self.step35_geom(il);
17433        let window = geometry.window.map(|window| window as usize);
17434        let ranks = tp.runtime.devices().len();
17435        let head_dim = geometry.head_dim_k as usize;
17436        let heads = geometry.n_head as usize;
17437        let kv_heads = geometry.n_head_kv as usize;
17438        if heads % ranks != 0 || kv_heads % ranks != 0 {
17439            return Err(format!(
17440                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
17441            )
17442            .into());
17443        }
17444        let local_heads = heads / ranks;
17445        let local_kv_heads = kv_heads / ranks;
17446        let max_ctx = cache.max_ctx;
17447
17448        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
17449
17450        let base_len = cache.kv[il]
17451            .as_ref()
17452            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
17453            .len;
17454        {
17455            let distributed = cache.tp_kv[il]
17456                .as_ref()
17457                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
17458            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
17459                return Err(format!(
17460                    "Step TP layer {il} cache lengths diverged before decode: \
17461                     local={base_len} distributed={}/{}",
17462                    distributed.committed_len(),
17463                    distributed.staged_len()
17464                )
17465                .into());
17466            }
17467        }
17468        if pos_d.len() != 1 {
17469            return Err(format!(
17470                "rank-local Step decode requires one position, got {}",
17471                pos_d.len()
17472            )
17473            .into());
17474        }
17475
17476        let decode_input = attention
17477            .decode_input
17478            .as_ref()
17479            .ok_or("Step TP decode v2 requires the replicated decode input")?;
17480        let mut decode_input = decode_input
17481            .lock()
17482            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
17483
17484        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
17485        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
17486        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
17487        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
17488        let use_gate_shards = (attention.gate_shards.is_some()
17489            || attention.gate_shards_bf16.is_some())
17490            && crate::tp::step_tp_qkv_fused_enabled()?;
17491        let gate_raw = if use_gate_shards {
17492            None
17493        } else {
17494            let gate_weight = fa
17495                .attn_gate
17496                .as_ref()
17497                .ok_or("step35 layer is missing attn_gate.weight")?;
17498            let gate_raw = e.matmul(gate_weight, h, 1)?;
17499            if gate_raw.len() != heads {
17500                return Err(format!(
17501                    "Step TP layer {il} gate output {} != {heads}",
17502                    gate_raw.len()
17503                )
17504                .into());
17505            }
17506            Some(gate_raw)
17507        };
17508
17509        let ws_index = tp
17510            .runtime
17511            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17512        let mut ws_guard = tp
17513            .runtime
17514            .decode_v2_workspace()
17515            .lock()
17516            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
17517        let ws = ws_guard
17518            .get_mut(ws_index)
17519            .ok_or("Step TP decode v2 workspace missing after ensure")?;
17520
17521        let mut rope_freqs = Vec::with_capacity(ranks);
17522        for rank in 0..ranks {
17523            let engine = tp
17524                .runtime
17525                .rank_engine(rank)
17526                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17527            rope_freqs.push(if geometry.rope_factors {
17528                self.step35_aux
17529                    .as_ref()
17530                    .and_then(|aux| aux.rope_freqs(engine))
17531            } else {
17532                None
17533            });
17534        }
17535        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
17536        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
17537        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
17538        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
17539        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
17540        // the fused rope+append+inc launch on dcw tokens.)
17541        let staged_next = base_len + 1;
17542        let t_kv_eff = window
17543            .map(|window| staged_next.min(window))
17544            .unwrap_or(staged_next);
17545        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
17546            let (write_row, would_rebase) = cache.tp_kv[il]
17547                .as_ref()
17548                .expect("distributed cache checked above")
17549                .peek_append_ring(1)?;
17550            if !would_rebase {
17551                // Arm the base mirrors on first use: base = logical staged - physical row.
17552                let base = (base_len - write_row) as i32;
17553                let distributed = cache.tp_kv[il]
17554                    .as_mut()
17555                    .expect("distributed cache checked above");
17556                for rank in 0..ranks {
17557                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
17558                        format!("Step TP layer {il} has no engine for rank {rank}")
17559                    })?;
17560                    let _main = engine.gpu.enter_main()?;
17561                    let rank_cache = distributed
17562                        .rank_mut(rank)
17563                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17564                    if rank_cache.base_d().is_none() {
17565                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
17566                    }
17567                }
17568            }
17569            !would_rebase
17570        };
17571        let fuse_rope = dcw
17572            && crate::tp::fuse_rope_append_on()
17573            && head_dim == 128
17574            && cache.tp_kv[il]
17575                .as_ref()
17576                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
17577                .unwrap_or(false);
17578
17579        let tcol_col = crate::tp::take_verify_tcol();
17580        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
17581        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
17582        // state must advance per column) but skips the fa+gate launch; post-rope q and
17583        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
17584        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
17585        // normally and the walk consumes the real output — stash flag stays unset).
17586        let fa2_col = crate::tp::take_spec_fa2_defer();
17587        tp.runtime.decode_v2_input_qkv(
17588            ws,
17589            e,
17590            h,
17591            pos_d,
17592            gate_raw.as_ref(),
17593            if !use_gate_shards {
17594                None
17595            } else if let Some(shards) = attention.gate_shards.as_deref() {
17596                Some(crate::tp::StepTpGateShards::F32(shards))
17597            } else {
17598                attention
17599                    .gate_shards_bf16
17600                    .as_deref()
17601                    .map(crate::tp::StepTpGateShards::Bf16)
17602            },
17603            &mut decode_input,
17604            &tp.q,
17605            &tp.k,
17606            &tp.v,
17607            &attention.q_norm,
17608            &attention.k_norm,
17609            head_dim,
17610            geometry.n_rot as usize,
17611            geometry.rope_base,
17612            &rope_freqs,
17613            self.cfg.rms_eps,
17614            fuse_rope,
17615            tcol_col,
17616        )?;
17617
17618        let transaction = cache.tp_kv[il]
17619            .as_mut()
17620            .expect("distributed cache checked above")
17621            .begin_transaction()?;
17622        let append_result = tp.runtime.append_tp_kv_transaction_inner(
17623            cache.tp_kv[il]
17624                .as_mut()
17625                .expect("distributed cache checked above"),
17626            transaction,
17627            &ws.k,
17628            &ws.v_raw,
17629            1,
17630            dcw,
17631        );
17632        if let Err(error) = append_result {
17633            let _ = tp.runtime.rollback_tp_kv_transaction(
17634                cache.tp_kv[il]
17635                    .as_mut()
17636                    .expect("distributed cache checked above"),
17637                transaction,
17638            );
17639            return Err(error);
17640        }
17641
17642        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17643            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
17644            // reborrows the cache mutably per rank.
17645            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
17646                let distributed = cache.tp_kv[il]
17647                    .as_ref()
17648                    .expect("distributed cache checked above");
17649                let staged_len = distributed.staged_len();
17650                let view_start = window
17651                    .map(|window| staged_len.saturating_sub(window))
17652                    .unwrap_or(0);
17653                (
17654                    staged_len,
17655                    distributed.physical_range(view_start, staged_len)?,
17656                    distributed.k_tok_bytes(),
17657                    distributed.v_tok_bytes(),
17658                    distributed.physical_capacity(),
17659                )
17660            };
17661            let view_start = window
17662                .map(|window| staged_len.saturating_sub(window))
17663                .unwrap_or(0);
17664            let t_kv = staged_len - view_start;
17665            for rank in 0..ranks {
17666                let engine = tp
17667                    .runtime
17668                    .rank_engine(rank)
17669                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
17670                let _main = engine.gpu.enter_main()?;
17671                if dcw {
17672                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
17673                    // stream visit. distributed is borrowed shared here; the planes need mut —
17674                    // reborrow through the cache Option (the closure holds cache mutably).
17675                    {
17676                        let distributed_mut = cache.tp_kv[il]
17677                            .as_mut()
17678                            .expect("distributed cache checked above");
17679                        let (kv_dim_k, kv_dim_v) =
17680                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
17681                        let (k_tok_bytes, v_tok_bytes) =
17682                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
17683                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17684                            format!("Step TP layer {il} has no KV cache rank {rank}")
17685                        })?;
17686                        let (k_plane, v_plane, len_d, base_d) =
17687                            rank_cache.planes_and_counters_mut();
17688                        if fuse_rope {
17689                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
17690                            // + last-block len inc in ONE launch. Bit-identical bodies.
17691                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
17692                            let crate::tp::StepTpDecodeV2Ws {
17693                                q_raw,
17694                                k_raw,
17695                                v_raw,
17696                                q,
17697                                k,
17698                                pos,
17699                                pos_stage,
17700                                fuse_ctr,
17701                                ..
17702                            } = &mut *ws;
17703                            // Same-device rank: the staged-copy elision leaves pos[rank]
17704                            // stale — read the e-context pos stage directly (mirrors the
17705                            // rope elision in input_qkv_rank).
17706                            let pos_ref: &CudaSlice<i32> = if same_dev {
17707                                pos_stage
17708                                    .as_ref()
17709                                    .ok_or("step TP decode v2 pos stage not armed")?
17710                            } else {
17711                                &pos[rank]
17712                            };
17713                            engine.qk_norm_rope_append_inc_dcw(
17714                                &q_raw[rank],
17715                                &k_raw[rank],
17716                                &v_raw[rank],
17717                                &attention.q_norm[rank],
17718                                &attention.k_norm[rank],
17719                                &mut q[rank],
17720                                &mut k[rank],
17721                                pos_ref,
17722                                k_plane,
17723                                v_plane,
17724                                len_d,
17725                                base_d,
17726                                &mut fuse_ctr[rank],
17727                                kv_dim_k,
17728                                kv_dim_v,
17729                                k_tok_bytes,
17730                                v_tok_bytes,
17731                                head_dim,
17732                                geometry.n_rot as usize,
17733                                local_heads,
17734                                local_kv_heads,
17735                                self.cfg.rms_eps,
17736                                geometry.rope_base,
17737                                1.0,
17738                                rope_freqs[rank],
17739                            )?;
17740                        } else {
17741                            engine.append_kv_quantized_dcw(
17742                                &ws.k[rank],
17743                                &ws.v_raw[rank],
17744                                k_plane,
17745                                v_plane,
17746                                len_d,
17747                                base_d,
17748                                kv_dim_k,
17749                                kv_dim_v,
17750                                k_tok_bytes,
17751                                v_tok_bytes,
17752                            )?;
17753                        }
17754                        if !fuse_rope {
17755                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
17756                                format!("Step TP layer {il} has no KV cache rank {rank}")
17757                            })?;
17758                            engine.inc_i32(rank_cache.len_d_mut())?;
17759                        }
17760                    }
17761                    let distributed = cache.tp_kv[il]
17762                        .as_ref()
17763                        .expect("distributed cache checked above");
17764                    let rank_cache = distributed
17765                        .rank(rank)
17766                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17767                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
17768                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
17769                    if fa2_col.is_some() {
17770                        // SPEC_FA2 defer: append landed above; the fa for this column
17771                        // runs in the T=2 joined launch after the pair's second append.
17772                        continue;
17773                    }
17774                    {
17775                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
17776                        // the gated output directly (bit-identical; one launch saved).
17777                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
17778                        engine.fa_decode_dcw(
17779                            &q[rank],
17780                            &k_ring,
17781                            &v_ring,
17782                            &mut gated[rank],
17783                            head_dim,
17784                            local_heads,
17785                            local_kv_heads,
17786                            rank_cache.len_d(),
17787                            rank_cache.base_d(),
17788                            window.unwrap_or(0),
17789                            t_kv,
17790                            geometry.attention_scale(),
17791                            k_tok_bytes_c,
17792                            v_tok_bytes_c,
17793                            Some(&gate[rank]),
17794                        )?;
17795                    }
17796                    continue;
17797                }
17798                let distributed = cache.tp_kv[il]
17799                    .as_ref()
17800                    .expect("distributed cache checked above");
17801                let rank_cache = distributed
17802                    .rank(rank)
17803                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
17804                let k_view = engine.view_u8_range(
17805                    rank_cache.k(),
17806                    physical.start * k_tok_bytes_c,
17807                    physical.end * k_tok_bytes_c,
17808                );
17809                let v_view = engine.view_u8_range(
17810                    rank_cache.v(),
17811                    physical.start * v_tok_bytes_c,
17812                    physical.end * v_tok_bytes_c,
17813                );
17814                engine.fa_decode_kvmod(
17815                    &ws.q[rank],
17816                    &k_view,
17817                    &v_view,
17818                    &mut ws.attn_out[rank],
17819                    head_dim,
17820                    local_heads,
17821                    local_kv_heads,
17822                    t_kv,
17823                    geometry.attention_scale(),
17824                    k_tok_bytes_c,
17825                    v_tok_bytes_c,
17826                    false,
17827                )?;
17828                engine.attn_head_gate(
17829                    &ws.attn_out[rank],
17830                    &ws.gate[rank],
17831                    &mut ws.gated[rank],
17832                    None,
17833                    head_dim,
17834                    local_heads,
17835                    1,
17836                )?;
17837            }
17838
17839            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
17840            // column's `gated` rows and skip the per-column finish choreography entirely
17841            // (the batched b4_tcol + join runs after every column). The returned buffer
17842            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
17843            // stashed flag, never this buffer. Ineligible configs fall back to the
17844            // normal finish and the driver consumes the real `mixed` per column.
17845            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
17846                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
17847                // finish all run in the joined pass. Returned buffer is UNWRITTEN
17848                // (oproj-defer precedent — the walk reads the stash flag, never this).
17849                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
17850                crate::tp::set_spec_fa2_stashed();
17851                e.uninit(ws.o_out)?
17852            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
17853                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
17854                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
17855                    crate::tp::set_tcol_oproj_stashed();
17856                    e.uninit(ws.o_out)?
17857                } else {
17858                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17859                }
17860            } else {
17861                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
17862            };
17863
17864            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
17865            // decode_v2_finish ordered behind the root event. Same math and cache state
17866            // transitions as v1.
17867            let local = cache.kv[il]
17868                .as_mut()
17869                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
17870            if local.len != base_len || base_len + 1 > max_ctx {
17871                return Err(format!(
17872                    "Step TP layer {il} local cache changed during decode: \
17873                     len={} base={base_len} max={max_ctx}",
17874                    local.len
17875                )
17876                .into());
17877            }
17878            if crate::tp::no_local_shadow_on() {
17879                // Lengths advance, contents stay stale (graph-door precedent: decode reads
17880                // only the distributed TP caches; local contents feed spec/MTP scratch).
17881                local.len = base_len + 1;
17882                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
17883                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
17884                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
17885                if !crate::tp::len_mirror_lazy_on() {
17886                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
17887                }
17888            } else {
17889                let retain_from = window
17890                    .map(|window| {
17891                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
17892                        let rollback_retain =
17893                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
17894                        staged_retain.min(rollback_retain)
17895                    })
17896                    .unwrap_or(0);
17897                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
17898                e.append_kv_quantized(
17899                    &ws.k_shadow,
17900                    &ws.v_shadow,
17901                    &mut local.k,
17902                    &mut local.v,
17903                    write_row,
17904                    local.kv_dim_k,
17905                    local.kv_dim_v,
17906                    local.k_tok_bytes,
17907                    local.v_tok_bytes,
17908                    false,
17909                )?;
17910                local.len = base_len + 1;
17911                e.set_i32_one(&mut local.len_d, local.len as i32)?;
17912            }
17913            Ok(output)
17914        })();
17915
17916        let output = match staged {
17917            Ok(output) => output,
17918            Err(error) => {
17919                let _ = tp.runtime.rollback_tp_kv_transaction(
17920                    cache.tp_kv[il]
17921                        .as_mut()
17922                        .expect("distributed cache checked above"),
17923                    transaction,
17924                );
17925                if let Some(local) = cache.kv[il].as_mut() {
17926                    local.len = base_len;
17927                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
17928                }
17929                return Err(error);
17930            }
17931        };
17932        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
17933        // the rank counters (same value as the absolute re-set on full accept), so commit
17934        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
17935        // keeps the absolute set (its appends do NOT inc).
17936        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
17937        if lazy_commit {
17938            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
17939                cache.tp_kv[il]
17940                    .as_mut()
17941                    .expect("distributed cache checked above"),
17942                transaction,
17943                1,
17944            ) {
17945                let _ = tp.runtime.rollback_tp_kv_transaction(
17946                    cache.tp_kv[il]
17947                        .as_mut()
17948                        .expect("distributed cache checked above"),
17949                    transaction,
17950                );
17951                let local = cache.kv[il].as_mut().expect("local cache checked above");
17952                local.len = base_len;
17953                e.set_i32_one(&mut local.len_d, base_len as i32)?;
17954                return Err(error);
17955            }
17956        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
17957            cache.tp_kv[il]
17958                .as_mut()
17959                .expect("distributed cache checked above"),
17960            transaction,
17961            1,
17962        ) {
17963            let _ = tp.runtime.rollback_tp_kv_transaction(
17964                cache.tp_kv[il]
17965                    .as_mut()
17966                    .expect("distributed cache checked above"),
17967                transaction,
17968            );
17969            let local = cache.kv[il].as_mut().expect("local cache checked above");
17970            local.len = base_len;
17971            e.set_i32_one(&mut local.len_d, base_len as i32)?;
17972            return Err(error);
17973        }
17974
17975        let committed = cache.tp_kv[il]
17976            .as_ref()
17977            .expect("distributed cache checked above")
17978            .committed_len();
17979        let local_len = cache.kv[il]
17980            .as_ref()
17981            .expect("local cache checked above")
17982            .len;
17983        if committed != local_len {
17984            return Err(format!(
17985                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
17986            )
17987            .into());
17988        }
17989        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
17990        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
17991            eprintln!(
17992                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
17993                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
17994                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
17995                 attention_tensor_parallel=true attention_scope={} \
17996                 input_path=root-device-replicated gate_tensor_parallel=false \
17997                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
17998                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
17999                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
18000                 performance_claim=false (logged once; every decode layer runs this driver)",
18001                tp.layer,
18002                tp.devices,
18003                if window.is_some() {
18004                    "rank-local-swa-ring"
18005                } else {
18006                    "rank-local-global"
18007                },
18008                tp.runtime.transport_label(),
18009                tp.runtime.bulk_p2p(),
18010            );
18011        }
18012        Ok(output)
18013    }
18014
18015    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
18016    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
18017    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
18018    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
18019    /// requiring `attn_gate`).
18020    ///
18021    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
18022    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
18023    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
18024    #[allow(clippy::too_many_arguments)]
18025    pub(crate) fn step35_decode_attn(
18026        &self,
18027        e: &Engine,
18028        fa: &FullAttnLayer,
18029        il: usize,
18030        h: &CudaSlice<f32>,
18031        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
18032        pos_d: &CudaSlice<i32>,
18033        cache: &mut Cache,
18034    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18035        if fa
18036            .step_tp_qkv
18037            .as_ref()
18038            .is_some_and(|tp| tp.attention.is_some())
18039        {
18040            if pre_q.is_some() {
18041                return Err(
18042                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
18043                     pre-quantized decode path"
18044                        .into(),
18045                );
18046            }
18047            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
18048        }
18049
18050        let geometry = self.step35_geom(il);
18051        let hd = geometry.head_dim_k as usize;
18052        let nkv = geometry.n_head_kv as usize;
18053        let nh = geometry.n_head as usize;
18054        let rbase = geometry.rope_base;
18055        let scale = geometry.attention_scale();
18056        let swa = geometry.window.is_some();
18057        let eps = self.cfg.rms_eps;
18058        let win = geometry.window.unwrap_or(0) as usize;
18059        let n_rot = geometry.n_rot as usize;
18060        let n_embd = self.cfg.n_embd as usize;
18061        let gw = fa
18062            .attn_gate
18063            .as_ref()
18064            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
18065
18066        let tp_qkv = if fa.step_tp_qkv.is_some() {
18067            if pre_q.is_some() {
18068                return Err(
18069                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
18070                     pre-quantized decode path"
18071                        .into(),
18072                );
18073            }
18074            self.step35_tp_qkv(e, fa, h, 1)?
18075        } else {
18076            None
18077        };
18078
18079        let (q0, k0, v0, gt) = match tp_qkv {
18080            Some(mut g3) => {
18081                let v = g3.pop().unwrap();
18082                let k = g3.pop().unwrap();
18083                let q = g3.pop().unwrap();
18084                let gt = e.matmul(gw, h, 1)?;
18085                (q, k, v, gt)
18086            }
18087            None => match pre_q {
18088                Some((hq, hdq)) => {
18089                    debug_assert!(
18090                        e.uses_q8_1_fast(gw),
18091                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
18092                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
18093                    );
18094                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
18095                        Some(t3) => t3,
18096                        None => (
18097                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
18098                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
18099                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
18100                        ),
18101                    };
18102                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
18103                    (a, b, c, gt)
18104                }
18105                None => {
18106                    if e.uses_q8_1_fast(&fa.wq)
18107                        && e.uses_q8_1_fast(&fa.wk)
18108                        && e.uses_q8_1_fast(&fa.wv)
18109                        && e.uses_q8_1_fast(gw)
18110                    {
18111                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
18112                        let (a, b, c) =
18113                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
18114                                Some(t3) => t3,
18115                                None => (
18116                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
18117                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
18118                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
18119                                ),
18120                            };
18121                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
18122                        (a, b, c, gt)
18123                    } else {
18124                        (
18125                            e.matmul(&fa.wq, h, 1)?,
18126                            e.matmul(&fa.wk, h, 1)?,
18127                            e.matmul(&fa.wv, h, 1)?,
18128                            e.matmul(gw, h, 1)?,
18129                        )
18130                    }
18131                }
18132            },
18133        };
18134
18135        let mut q = e.uninit(nh * hd)?;
18136        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
18137        let mut k = e.uninit(nkv * hd)?;
18138        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
18139        let ff = if swa {
18140            None
18141        } else {
18142            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
18143        };
18144        #[cfg(debug_assertions)]
18145        if let Some(ff) = ff {
18146            crate::debug_assert_tensor_stream_device(
18147                ff,
18148                &e.stream(),
18149                "step35_decode_attn.rope_freqs",
18150            );
18151        }
18152        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
18153
18154        if std::env::var("MEMRA_NOFA").is_ok() {
18155            return Err(
18156                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
18157                        cache; unset MEMRA_NOFA to use fa_decode"
18158                    .into(),
18159            );
18160        }
18161        let kvl = cache.kv[il].as_mut().unwrap();
18162        let next_len = kvl.len + 1;
18163        let (off, t_kv) = if swa && next_len > win {
18164            (next_len - win, win)
18165        } else {
18166            (0, next_len)
18167        };
18168        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
18169        e.append_kv_quantized(
18170            &k,
18171            &v0,
18172            &mut kvl.k,
18173            &mut kvl.v,
18174            write_row,
18175            kvl.kv_dim_k,
18176            kvl.kv_dim_v,
18177            kvl.k_tok_bytes,
18178            kvl.v_tok_bytes,
18179            crate::Engine::kv_fp8_on(),
18180        )?;
18181        kvl.len = next_len;
18182        let physical = kvl.physical_rows(off, off + t_kv)?;
18183        let k_view = e.view_u8_range(
18184            &kvl.k,
18185            physical.start * kvl.k_tok_bytes,
18186            physical.end * kvl.k_tok_bytes,
18187        );
18188        let v_view = e.view_u8_range(
18189            &kvl.v,
18190            physical.start * kvl.v_tok_bytes,
18191            physical.end * kvl.v_tok_bytes,
18192        );
18193        let mut attn = e.uninit(nh * hd)?;
18194        e.fa_decode_kvmod(
18195            &q,
18196            &k_view,
18197            &v_view,
18198            &mut attn,
18199            hd,
18200            nh,
18201            nkv,
18202            t_kv,
18203            scale,
18204            kvl.k_tok_bytes,
18205            kvl.v_tok_bytes,
18206            crate::Engine::kv_fp8_on(),
18207        )?;
18208
18209        let mut ag = e.uninit(nh * hd)?;
18210        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
18211        self.step35_o(e, fa, &ag, 1)
18212    }
18213}
18214
18215// ===================================================================================== //
18216//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
18217//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
18218//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
18219//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
18220//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
18221//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
18222// ===================================================================================== //
18223impl HybridModel {
18224    pub fn is_gemma4_e4b(&self) -> bool {
18225        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
18226    }
18227
18228    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
18229    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
18230    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
18231    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
18232        let g = self.cfg.gemma4.as_ref().unwrap();
18233        let swa = g.swa_pattern[il];
18234        let hd = if swa {
18235            g.key_length_swa
18236        } else {
18237            g.key_length_global
18238        } as usize;
18239        let Mixer::Full(fa) = &self.layers[il].mixer else {
18240            panic!("e4b layer {il} not full-attn")
18241        };
18242        let nh = fa.wq.out_features() / hd;
18243        let nkv = fa.wk.out_features() / hd;
18244        (
18245            hd,
18246            nkv,
18247            nh,
18248            if swa {
18249                g.rope_base_swa
18250            } else {
18251                g.rope_base_global
18252            },
18253            1.0,
18254            swa,
18255        )
18256    }
18257
18258    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
18259    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
18260        self.layers[il]
18261            .gemma4
18262            .as_ref()
18263            .and_then(|b| b.e4b.as_ref())
18264            .and_then(|e4| e4.kv_share.map(|t| t as usize))
18265    }
18266
18267    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
18268    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
18269    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
18270    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
18271    fn gemma4_e4b_inp_pl(
18272        &self,
18273        e: &Engine,
18274        tokens: &[u32],
18275        x_scaled: &CudaSlice<f32>,
18276        t: usize,
18277    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18278        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
18279        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
18280    }
18281
18282    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
18283    fn gemma4_e4b_inp_pl_dev(
18284        &self,
18285        e: &Engine,
18286        tok_d: &CudaSlice<u32>,
18287        x_scaled: &CudaSlice<f32>,
18288        t: usize,
18289    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18290        let aux = self.gemma4_aux.as_ref().unwrap();
18291        let m = aux.e4b.as_ref().unwrap();
18292        let n_embd = self.cfg.n_embd as usize;
18293        let n_layer = self.layers.len();
18294        let width = m.n_epl * n_layer;
18295        let tbl = m.tok_tbl_gpu.get_or_init(|| {
18296            e.upload_u8(&m.tok_embd_bytes)
18297                .expect("e4b per-layer token table upload")
18298        });
18299        let mut a =
18300            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
18301        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
18302        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
18303        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
18304        let mut pn = e.uninit(t * width)?;
18305        e.rms_norm(
18306            &p,
18307            m.proj_norm.float_data(),
18308            &mut pn,
18309            m.n_epl,
18310            t * n_layer,
18311            self.cfg.rms_eps,
18312        )?;
18313        let mut out = e.uninit(t * width)?;
18314        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
18315        Ok(out)
18316    }
18317
18318    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
18319    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
18320    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
18321    /// already holds this forward's rows — the target runs earlier in the stack).
18322    #[allow(clippy::too_many_arguments)]
18323    fn gemma4_e4b_attn(
18324        &self,
18325        e: &Engine,
18326        il: usize,
18327        hq: &CudaSlice<i8>,
18328        hdq: &CudaSlice<f32>,
18329        pos_d: &CudaSlice<i32>,
18330        t: usize,
18331        cache: &mut Cache,
18332        dc_bucket: Option<usize>,
18333    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18334        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
18335        let eps = self.cfg.rms_eps;
18336        let aux = self.gemma4_aux.as_ref().unwrap();
18337        let ones = aux.ones(e);
18338        #[cfg(debug_assertions)]
18339        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
18340        let Mixer::Full(fa) = &self.layers[il].mixer else {
18341            unreachable!()
18342        };
18343        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
18344        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
18345        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
18346        let h0 = e.zeros(0)?;
18347        let h = &h0;
18348
18349        let ff = if swa {
18350            None
18351        } else {
18352            Some(
18353                aux.rope_freqs(e)
18354                    .expect("e4b global rope needs rope_freqs.weight"),
18355            )
18356        };
18357        #[cfg(debug_assertions)]
18358        if let Some(ff) = ff {
18359            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
18360        }
18361        let share = self.gemma4_e4b_kv_target(il);
18362        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
18363        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
18364        let mut q;
18365        if let Some(_tgt) = share {
18366            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
18367            q = e.uninit(t * nh * hd)?;
18368            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
18369            // empty; q0 stands in for the unused k/v pointers).
18370            let mut kdummy = e.uninit(1)?;
18371            let mut vdummy = e.uninit(1)?;
18372            e.rms_norm_qkv_rope(
18373                &q0,
18374                &q0,
18375                &q0,
18376                fa.q_norm.float_data(),
18377                fa.q_norm.float_data(),
18378                ones,
18379                &mut q,
18380                &mut kdummy,
18381                &mut vdummy,
18382                hd,
18383                self.gemma4_rope_dims(il),
18384                nh * t,
18385                0,
18386                pos_d,
18387                nh,
18388                1,
18389                base,
18390                1.0,
18391                ff,
18392                eps,
18393            )?;
18394        } else {
18395            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
18396            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
18397            // q|k|v rows — the cat norm+rope twin consumes it directly.
18398            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
18399            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
18400            q = e.uninit(t * nh * hd)?;
18401            let mut k = e.uninit(t * nkv * hd)?;
18402            let mut v = e.uninit(t * nkv * hd)?;
18403            if t == 1 && cat.is_some() {
18404                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
18405                e.rms_norm_qkv_rope_cat(
18406                    &qkv0,
18407                    fa.q_norm.float_data(),
18408                    fa.k_norm.float_data(),
18409                    ones,
18410                    &mut q,
18411                    &mut k,
18412                    &mut v,
18413                    hd,
18414                    self.gemma4_rope_dims(il),
18415                    nh,
18416                    nkv,
18417                    pos_d,
18418                    nh,
18419                    nkv,
18420                    base,
18421                    1.0,
18422                    ff,
18423                    eps,
18424                )?;
18425            } else {
18426                let (q0, k0, v0) = match if t == 1 {
18427                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
18428                } else {
18429                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
18430                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
18431                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18432                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
18433                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
18434                    } else {
18435                        None
18436                    }
18437                } {
18438                    Some(triple) => triple,
18439                    None => (
18440                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
18441                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
18442                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
18443                    ), // E4B: real v (K != V)
18444                };
18445                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
18446                // the normed rows; V ones-rms, never roped).
18447                e.rms_norm_qkv_rope(
18448                    &q0,
18449                    &k0,
18450                    &v0,
18451                    fa.q_norm.float_data(),
18452                    fa.k_norm.float_data(),
18453                    ones,
18454                    &mut q,
18455                    &mut k,
18456                    &mut v,
18457                    hd,
18458                    self.gemma4_rope_dims(il),
18459                    nh * t,
18460                    nkv * t,
18461                    pos_d,
18462                    nh,
18463                    nkv,
18464                    base,
18465                    1.0,
18466                    ff,
18467                    eps,
18468                )?;
18469            }
18470            let kvl = cache.kv[il].as_mut().unwrap();
18471            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
18472            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
18473            // degenerate tok-0 stream, 2026-07-12).
18474            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18475            if dc_bucket.is_some() {
18476                // DC arm (graph serving): append at the len_d slot, advance the counter
18477                // in-stream — replay-correct, no host len in the launch args. Host mirrors
18478                // are NOT touched here (the replay loop owns them; a bump at capture-record
18479                // time would double-count the capture iteration).
18480                debug_assert!(t == 1);
18481                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
18482                e.append_kv_quantized_row_dc_inc(
18483                    &k,
18484                    &v,
18485                    &mut kvl.k,
18486                    &mut kvl.v,
18487                    &mut kvl.len_d,
18488                    kvl.kv_dim_k,
18489                    kvl.kv_dim_v,
18490                    kvl.k_tok_bytes,
18491                    kvl.v_tok_bytes,
18492                    cls,
18493                )?;
18494            } else {
18495                e.append_kv_quantized_rows(
18496                    &k,
18497                    &v,
18498                    &mut kvl.k,
18499                    &mut kvl.v,
18500                    kvl.len,
18501                    t,
18502                    kvl.kv_dim_k,
18503                    kvl.kv_dim_v,
18504                    kvl.k_tok_bytes,
18505                    kvl.v_tok_bytes,
18506                    cls,
18507                )?;
18508                kvl.len += t;
18509            }
18510            kv_f32 = Some((k, v));
18511        }
18512        // attention: per-row causal fa over the (own or target) quantized cache. The cache
18513        // already contains this forward's rows in both arms; row i attends [.., base+i].
18514        let kvl_idx = share.unwrap_or(il);
18515        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
18516        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
18517        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
18518        let mut attn = e.uninit(t * nh * hd)?;
18519        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
18520        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
18521        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
18522        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
18523        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
18524        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
18525        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
18526        //     rows (the T=K verify kernel; the target appended this forward's rows already).
18527        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
18528        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
18529        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
18530        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
18531            if let Some((kf, vf)) = &kv_f32 {
18532                if hd == 256 && t <= win {
18533                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18534                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18535                }
18536                if hd == 256 && swa && t > win {
18537                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18538                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18539                }
18540                if hd == 512 && !swa {
18541                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18542                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18543                }
18544            } else if share.is_some() {
18545                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18546                let k_view = e.view_u8(&kvl.k, kvl.k.len());
18547                let v_view = e.view_u8(&kvl.v, kvl.v.len());
18548                if hd == 256 && (!swa || t <= win) {
18549                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
18550                    e.fa_prefill_view(
18551                        &q,
18552                        &k_view,
18553                        &v_view,
18554                        &mut attn,
18555                        hd,
18556                        nh,
18557                        nkv,
18558                        t,
18559                        t,
18560                        scale,
18561                        true,
18562                        kvl.k_tok_bytes,
18563                        kvl.v_tok_bytes,
18564                        g,
18565                    )?;
18566                    return Ok(e.matmul(&fa.wo, &attn, t)?);
18567                }
18568                // remaining shared classes (swa above the window; hd512 globals): dequant
18569                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
18570                let kv_dim = nkv * hd;
18571                let mut kf = e.uninit(t * kv_dim)?;
18572                let mut vf = e.uninit(t * kv_dim)?;
18573                e.fa_dequant_kv_view_f32(
18574                    &k_view,
18575                    &v_view,
18576                    &mut kf,
18577                    &mut vf,
18578                    kv_dim,
18579                    kv_dim,
18580                    t,
18581                    kvl.k_tok_bytes,
18582                    kvl.v_tok_bytes,
18583                    g,
18584                )?;
18585                if hd == 512 {
18586                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
18587                } else {
18588                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
18589                }
18590                return Ok(e.matmul(&fa.wo, &attn, t)?);
18591            }
18592        }
18593        if let Some(bucket) = dc_bucket {
18594            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
18595            // fa_decode_dc over the live counter. len_d already advanced past this token
18596            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
18597            // counter (advanced when the target ran earlier in the stack).
18598            assert!(t == 1);
18599            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
18600            // and under the window every live t_kv sits below it — cap the capture bucket
18601            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
18602            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
18603            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
18604            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
18605                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
18606            } else {
18607                bucket
18608            };
18609            let k_view = e.view_u8(&kvl.k, kvl.k.len());
18610            let v_view = e.view_u8(&kvl.v, kvl.v.len());
18611            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18612            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
18613            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
18614            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
18615            // captured into the dc graph like any other launch. Extending the cascade to
18616            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
18617            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
18618            // MEMRA_WPF=0 rollback seam.
18619            if crate::Engine::wpf_level() >= 1 {
18620                e.prefetch_weight_l2(&fa.wo)?;
18621            }
18622            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
18623            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
18624            if e.uses_q8_1_fast(&fa.wo) {
18625                let mut oq = e.alloc_i8_uninit(nh * hd)?;
18626                let mut od = e.zeros(nh * hd / 32)?;
18627                e.fa_decode_dc_q8(
18628                    &q,
18629                    &k_view,
18630                    &v_view,
18631                    &mut attn,
18632                    hd,
18633                    nh,
18634                    nkv,
18635                    &kvl.len_d,
18636                    bucket,
18637                    scale,
18638                    kvl.k_tok_bytes,
18639                    kvl.v_tok_bytes,
18640                    g,
18641                    Some((&mut oq, &mut od)),
18642                )?;
18643                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
18644            }
18645            e.fa_decode_dc(
18646                &q,
18647                &k_view,
18648                &v_view,
18649                &mut attn,
18650                hd,
18651                nh,
18652                nkv,
18653                &kvl.len_d,
18654                bucket,
18655                scale,
18656                kvl.k_tok_bytes,
18657                kvl.v_tok_bytes,
18658                g,
18659            )?;
18660            return Ok(e.matmul(&fa.wo, &attn, t)?);
18661        }
18662        for i in 0..t {
18663            let avail = base_len + i + 1;
18664            let (off_tok, t_kv) = if swa && avail > win {
18665                (avail - win, win)
18666            } else {
18667                (0, avail)
18668            };
18669            let k_view = e.view_u8_range(
18670                &kvl.k,
18671                off_tok * kvl.k_tok_bytes,
18672                (off_tok + t_kv) * kvl.k_tok_bytes,
18673            );
18674            let v_view = e.view_u8_range(
18675                &kvl.v,
18676                off_tok * kvl.v_tok_bytes,
18677                (off_tok + t_kv) * kvl.v_tok_bytes,
18678            );
18679            let qv = e.view(&q, t * nh * hd);
18680            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
18681            let mut q_one = e.uninit(nh * hd)?;
18682            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
18683            let mut a_one = e.uninit(nh * hd)?;
18684            // read class MUST match the append class (globals are e4m3 under gkv): the
18685            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
18686            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
18687            e.fa_decode_kvmod(
18688                &q_one,
18689                &k_view,
18690                &v_view,
18691                &mut a_one,
18692                hd,
18693                nh,
18694                nkv,
18695                t_kv,
18696                scale,
18697                kvl.k_tok_bytes,
18698                kvl.v_tok_bytes,
18699                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
18700            )?;
18701            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
18702        }
18703        Ok(e.matmul(&fa.wo, &attn, t)?)
18704    }
18705
18706    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
18707    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
18708    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
18709    /// layer; does NOT advance cache.pos (caller owns pos).
18710    fn gemma4_e4b_trunk(
18711        &self,
18712        e: &Engine,
18713        tokens: &[u32],
18714        pos0: usize,
18715        cache: &mut Cache,
18716        head_last: bool,
18717    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18718        let n_embd = self.cfg.n_embd as usize;
18719        let t = tokens.len();
18720        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18721        let pos_d = e.htod_i32(&pos)?;
18722        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
18723        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18724        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
18725        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
18726    }
18727
18728    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
18729    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
18730    /// eager chain by construction: SAME functions, not twins).
18731    fn gemma4_e4b_trunk_core(
18732        &self,
18733        e: &Engine,
18734        x_in: CudaSlice<f32>,
18735        inp_pl: CudaSlice<f32>,
18736        pos_d: &CudaSlice<i32>,
18737        t: usize,
18738        cache: &mut Cache,
18739        dc_bucket: Option<usize>,
18740        cap_logits: bool,
18741        head_last: bool,
18742    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18743        let n_embd = self.cfg.n_embd as usize;
18744        let eps = self.cfg.rms_eps;
18745        let n_layer = self.layers.len();
18746        let mut x = x_in;
18747        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
18748        let n_epl = aux_e4b.n_epl;
18749
18750        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
18751        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
18752        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
18753        // head rides matmul_pre too. First layer's pair comes from a standalone fused
18754        // norm+quant.
18755        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
18756        for il in 0..n_layer {
18757            let layer = &self.layers[il];
18758            let (hq, hdq) = match h_carry.take() {
18759                Some(p) => p,
18760                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
18761            };
18762            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
18763            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
18764            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
18765            let bits = layer.gemma4.as_ref().unwrap();
18766            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
18767            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
18768            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
18769            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
18770            // the fused single-phase reduction is NOT FP-order-identical to the unfused
18771            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
18772            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
18773            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
18774            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
18775            // gate dropped, decode AND verify ride the same fused chain — parity by
18776            // construction, VERIFY-GATE 0.000e0.
18777            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
18778            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
18779                e,
18780                layer,
18781                &o,
18782                &x,
18783                t,
18784                Some(layer.post_attn_norm.float_data()),
18785                fuse_exit,
18786            )?;
18787            let mut resid = e.uninit(t * n_embd)?;
18788            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
18789            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
18790            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
18791            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
18792            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
18793            let g = if fuse_exit {
18794                // sn here = RAW f0 (post_ffw deferred).
18795                let (rq, rd) = e.rms_pre_add_q8_1(
18796                    &sn,
18797                    bits.post_ffw_norm.float_data(),
18798                    &attn_out,
18799                    &mut resid,
18800                    n_embd,
18801                    t,
18802                    self.cfg.rms_eps,
18803                )?;
18804                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
18805            } else {
18806                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
18807                e.matmul(&e4b.inp_gate, &resid, t)?
18808            };
18809            let mut act = e.uninit(t * n_epl)?;
18810            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
18811                let ipv = e.view(&inp_pl, n_epl * n_layer);
18812                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
18813                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
18814                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
18815            } else {
18816                let mut inp_this = e.uninit(t * n_epl)?;
18817                e.copy_rows_strided(
18818                    &inp_pl,
18819                    &mut inp_this,
18820                    n_epl,
18821                    t,
18822                    n_epl * n_layer,
18823                    il * n_epl,
18824                )?;
18825                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
18826                e.matmul(&e4b.proj, &act, t)?
18827            };
18828            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
18829            // ONE launch (glue-fusion lane; last layer emits through output_norm).
18830            let next_norm = if il + 1 < n_layer {
18831                self.layers[il + 1].attn_norm.float_data()
18832            } else {
18833                self.output_norm.float_data()
18834            };
18835            let mut xn = e.uninit(t * n_embd)?;
18836            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
18837                &y,
18838                e4b.post_norm.float_data(),
18839                &resid,
18840                bits.layer_scale,
18841                next_norm,
18842                &mut xn,
18843                n_embd,
18844                t,
18845                eps,
18846            )?;
18847            h_carry = Some(pair);
18848            x = xn;
18849        }
18850        // the head consumes the last layer's fused (output_norm) emit. head_last callers
18851        // (prime, last_only forward) need only the final row's logits — the all-T head is
18852        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
18853        let (oq, odq) = h_carry.take().unwrap();
18854        let h0 = e.zeros(0)?;
18855        let hm = if head_last { 1 } else { t };
18856        let (hq, hd) = if head_last && t > 1 {
18857            let mut q1 = e.uninit_i8(n_embd)?;
18858            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
18859            let nb = n_embd / 32;
18860            let mut d1 = e.uninit(nb)?;
18861            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
18862            (q1, d1)
18863        } else {
18864            (oq, odq)
18865        };
18866        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
18867        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
18868        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
18869        // Logit-returning callers (host logits / spec prime) keep the capped emit.
18870        if cap_logits {
18871            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
18872            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
18873        }
18874        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
18875        Ok((ld, x))
18876    }
18877
18878    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
18879    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
18880    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
18881    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
18882    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
18883    /// covers exactly the layers that appended).
18884    pub fn gemma4_e4b_decode_step_t_am_dev(
18885        &self,
18886        e: &Engine,
18887        tok_d: &CudaSlice<u32>,
18888        t: usize,
18889        pos0: usize,
18890        cache: &mut Cache,
18891    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18892        let n_embd = self.cfg.n_embd as usize;
18893        let eps = self.cfg.rms_eps;
18894        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
18895        let pos_d = e.htod_i32(&pos)?;
18896        let embd_gpu = self
18897            .embd_gpu
18898            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
18899        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
18900        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
18901        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
18902        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
18903        let (ld, xp) =
18904            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
18905        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
18906        // emit is already capped, matching the eager chain bit-for-bit).
18907        let n_vocab = self.output.out_features();
18908        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
18909        for i in 0..t {
18910            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
18911        }
18912        let mut hn = e.uninit(t * n_embd)?;
18913        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18914        cache.pos += t;
18915        Ok((vam, hn))
18916    }
18917
18918    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
18919    /// prime path — mirror of `gemma4_decode_step_t_h`).
18920    pub(crate) fn gemma4_e4b_decode_step_t_h(
18921        &self,
18922        e: &Engine,
18923        tokens: &[u32],
18924        pos0: usize,
18925        cache: &mut Cache,
18926    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18927        let n_embd = self.cfg.n_embd as usize;
18928        let eps = self.cfg.rms_eps;
18929        let t = tokens.len();
18930        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
18931        let mut hn = e.uninit(t * n_embd)?;
18932        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
18933        cache.pos += t;
18934        Ok((e.dtoh(&ld)?, hn))
18935    }
18936
18937    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
18938    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
18939    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
18940    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
18941    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
18942    pub fn gemma4_e4b_decode_step_dcg(
18943        &self,
18944        e: &Engine,
18945        token_d: &mut CudaSlice<u32>,
18946        pos_d: &mut CudaSlice<i32>,
18947        embd_gpu: &CudaSlice<u8>,
18948        embd_qt: i32,
18949        embd_rb: usize,
18950        cache: &mut Cache,
18951        n_vocab: usize,
18952        bucket: usize,
18953    ) -> Result<(), Box<dyn std::error::Error>> {
18954        let n_embd = self.cfg.n_embd as usize;
18955        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18956        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18957        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18958        let (ld, _x) =
18959            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
18960        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
18961        e.inc_seqlen(pos_d)?;
18962        Ok(())
18963    }
18964
18965    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
18966    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
18967    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
18968    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
18969    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
18970    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
18971    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
18972    #[allow(clippy::too_many_arguments)]
18973    pub fn gemma4_e4b_decode_step_dc(
18974        &self,
18975        e: &Engine,
18976        token_d: &CudaSlice<u32>,
18977        pos_d: &mut CudaSlice<i32>,
18978        embd_gpu: &CudaSlice<u8>,
18979        embd_qt: i32,
18980        embd_rb: usize,
18981        cache: &mut Cache,
18982        n_vocab: usize,
18983    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
18984        let n_embd = self.cfg.n_embd as usize;
18985        let eps = self.cfg.rms_eps;
18986        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
18987        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
18988        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
18989        let (ld, _x) =
18990            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
18991        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
18992        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
18993        e.inc_seqlen(pos_d)?;
18994        cache.pos += 1;
18995        let _ = eps;
18996        Ok(tok_out)
18997    }
18998
18999    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
19000    /// pre-output_norm hidden). Advances cache.pos.
19001    pub(crate) fn gemma4_e4b_decode_step_h(
19002        &self,
19003        e: &Engine,
19004        token: u32,
19005        cache: &mut Cache,
19006    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19007        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
19008        let logits = e.dtoh(&ld)?;
19009        cache.pos += 1;
19010        Ok((logits, x))
19011    }
19012
19013    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
19014    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
19015    /// fast; the prefill fa arms come later.
19016    pub(crate) fn gemma4_e4b_prime(
19017        &self,
19018        e: &Engine,
19019        tokens: &[u32],
19020        cache: &mut Cache,
19021    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19022        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
19023        // process-kill as gemma4_prime — refuse per-request.
19024        if cache.pos != 0 {
19025            return Err(
19026                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
19027                        call or decode tokenwise"
19028                    .into(),
19029            );
19030        }
19031        let n_embd = self.cfg.n_embd as usize;
19032        let t = tokens.len();
19033        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
19034        cache.pos += t;
19035        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
19036        let xv = e.view(&x, t * n_embd);
19037        let row = xv.slice((t - 1) * n_embd..t * n_embd);
19038        let mut h_seed = e.uninit(n_embd)?;
19039        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
19040        Ok((last, h_seed, x))
19041    }
19042
19043    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
19044    pub(crate) fn gemma4_e4b_forward(
19045        &self,
19046        e: &Engine,
19047        tokens: &[u32],
19048        last_only: bool,
19049    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19050        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
19051        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
19052        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
19053    }
19054}
19055
19056#[cfg(test)]
19057mod prime_chunk_schedule_tests {
19058    use super::{
19059        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
19060        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
19061        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
19062        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
19063    };
19064
19065    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
19066        ranges.iter().map(|(start, end)| end - start).collect()
19067    }
19068
19069    fn auto_chunk(t: usize) -> usize {
19070        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
19071    }
19072
19073    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
19074    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
19075    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
19076    /// must land every boundary on it without changing coverage.
19077    #[test]
19078    fn auto_prime_ranges_align_to_the_gdn_grid() {
19079        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
19080        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
19081            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
19082            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
19083            for w in ranges.windows(2) {
19084                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
19085            }
19086            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
19087        };
19088
19089        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
19090        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
19091        let t = 9510usize;
19092        let fill = auto_chunk(t);
19093        let fixed = fixed_prime_chunk_ranges(t, fill);
19094        assert!(
19095            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
19096            "broken arm vanished: fixed auto boundaries all landed on-grid"
19097        );
19098        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
19099        assert!(
19100            dynamic[..dynamic.len() - 1]
19101                .iter()
19102                .any(|&(_, e)| e % c != 0),
19103            "broken arm vanished: dynamic auto boundaries all landed on-grid"
19104        );
19105
19106        for ranges in [&fixed, &dynamic] {
19107            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
19108            assert_covers(&aligned, t);
19109            for &(_, e) in &aligned[..aligned.len() - 1] {
19110                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
19111            }
19112            // boundaries only move DOWN, at most c-1 tokens.
19113            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
19114                assert!(a <= b && b - a < c);
19115            }
19116        }
19117
19118        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
19119        // empty range; the schedule survives degenerate short fills.
19120        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
19121        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
19122        assert_covers(&aligned, 200);
19123        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
19124
19125        // No-ops: single range, c=0 (grid off), already-aligned schedules.
19126        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
19127        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
19128        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
19129        assert_eq!(
19130            align_prime_ranges_to_gdn(&on_grid, 300, c),
19131            on_grid.as_slice()
19132        );
19133    }
19134
19135    #[test]
19136    fn active_matrix_prefix_scopes_reused_prime_slabs() {
19137        assert_eq!(
19138            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
19139            29 * 4096
19140        );
19141        assert_eq!(
19142            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
19143            29 * 4096
19144        );
19145        assert_eq!(
19146            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
19147            24 * 4096
19148        );
19149        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
19150        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
19151    }
19152
19153    #[test]
19154    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
19155        assert!(validate_step_prime_batch_modes(false, false).is_ok());
19156
19157        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
19158        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
19159
19160        for grouped in [false, true] {
19161            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
19162            assert!(err.contains("did not clear the live-server performance gate"));
19163            assert!(err.contains("per-session grouped prefill"));
19164        }
19165    }
19166
19167    #[test]
19168    fn step_grouped_path_is_eager_single_token_only() {
19169        assert!(step_grouped_decode_shape(false, 1));
19170        assert!(!step_grouped_decode_shape(true, 1));
19171        assert!(!step_grouped_decode_shape(false, 2));
19172        assert!(!step_grouped_decode_shape(true, 2));
19173    }
19174
19175    #[test]
19176    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
19177        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
19178        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
19179        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
19180        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
19181        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
19182        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
19183
19184        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
19185        assert!(step_grouped_prefill_shape(
19186            true,
19187            true,
19188            crate::cache::PRIME_CHUNK_MAX_TOKENS,
19189        ));
19190        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
19191        assert!(!step_grouped_prefill_shape(
19192            true,
19193            true,
19194            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
19195        ));
19196        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
19197        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
19198    }
19199
19200    #[test]
19201    fn step_tp_prefill_door_is_strict_and_default_off() {
19202        assert!(!parse_step_tp_prefill(None).unwrap());
19203        assert!(!parse_step_tp_prefill(Some("")).unwrap());
19204        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
19205        assert!(parse_step_tp_prefill(Some("1")).unwrap());
19206        assert!(parse_step_tp_prefill(Some("true")).is_err());
19207        assert!(parse_step_tp_prefill(Some("2")).is_err());
19208    }
19209
19210    #[test]
19211    fn step_tp_prefill_requires_a_qualified_even_rank_shape() {
19212        assert!(step_tp_prefill_shape(
19213            true,
19214            PRIME_MIN_T,
19215            4,
19216            true,
19217            true,
19218            false,
19219        ));
19220        assert!(!step_tp_prefill_shape(
19221            false,
19222            PRIME_MIN_T,
19223            4,
19224            true,
19225            true,
19226            false,
19227        ));
19228        assert!(!step_tp_prefill_shape(
19229            true,
19230            PRIME_MIN_T - 1,
19231            4,
19232            true,
19233            true,
19234            false,
19235        ));
19236        // TP2 admits (2026-08-25); odd/1-card placements still refuse.
19237        assert!(step_tp_prefill_shape(
19238            true,
19239            PRIME_MIN_T,
19240            2,
19241            true,
19242            true,
19243            false
19244        ));
19245        assert!(!step_tp_prefill_shape(
19246            true,
19247            PRIME_MIN_T,
19248            1,
19249            true,
19250            true,
19251            false
19252        ));
19253        assert!(!step_tp_prefill_shape(
19254            true,
19255            PRIME_MIN_T,
19256            3,
19257            true,
19258            true,
19259            false
19260        ));
19261        assert!(!step_tp_prefill_shape(
19262            true,
19263            PRIME_MIN_T,
19264            4,
19265            false,
19266            true,
19267            false,
19268        ));
19269        assert!(!step_tp_prefill_shape(
19270            true,
19271            PRIME_MIN_T,
19272            4,
19273            true,
19274            false,
19275            false,
19276        ));
19277        assert!(!step_tp_prefill_shape(
19278            true,
19279            PRIME_MIN_T,
19280            4,
19281            true,
19282            true,
19283            true,
19284        ));
19285    }
19286
19287    #[test]
19288    fn fixed_schedule_retains_measured_geometry() {
19289        assert_eq!(
19290            sizes(&fixed_prime_chunk_ranges(461, 128)),
19291            vec![128, 128, 128, 77]
19292        );
19293        assert_eq!(
19294            sizes(&fixed_prime_chunk_ranges(1833, 230)),
19295            vec![230, 230, 230, 230, 230, 230, 230, 223]
19296        );
19297        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
19298        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
19299        assert_eq!(capped, vec![4096, 4088, 16]);
19300        assert!(capped.iter().all(|&rows| rows <= 4096));
19301        assert_eq!(
19302            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
19303            vec![4100],
19304            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
19305        );
19306    }
19307
19308    #[test]
19309    fn dynamic_schedule_matches_registered_shapes() {
19310        let cases = [
19311            (461, vec![64, 141, 132, 124]),
19312            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
19313            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
19314        ];
19315        for (t, expected) in cases {
19316            let chunk = auto_chunk(t);
19317            let fixed = fixed_prime_chunk_ranges(t, chunk);
19318            assert_eq!(
19319                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
19320                expected
19321            );
19322        }
19323    }
19324
19325    #[test]
19326    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
19327        for t in 256..=8192 {
19328            let chunk = auto_chunk(t);
19329            let fixed = fixed_prime_chunk_ranges(t, chunk);
19330            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
19331            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
19332            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
19333            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
19334            for pair in dynamic.windows(2) {
19335                assert_eq!(pair[0].1, pair[1].0, "T={t}");
19336            }
19337            assert!(
19338                dynamic
19339                    .iter()
19340                    .all(|(start, end)| end - start >= PRIME_MIN_T),
19341                "T={t} sizes={:?}",
19342                sizes(&dynamic)
19343            );
19344            if dynamic.len() >= 3 {
19345                let chunk_sizes = sizes(&dynamic);
19346                assert!(
19347                    chunk_sizes[0] < chunk_sizes[1],
19348                    "T={t} sizes={chunk_sizes:?}"
19349                );
19350                assert!(
19351                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
19352                    "T={t} sizes={chunk_sizes:?}"
19353                );
19354            }
19355        }
19356    }
19357}
19358
19359#[cfg(test)]
19360mod page_prefetch_tests {
19361    use super::{
19362        grouped_worker_prefetch_position, page_prefetch_positions,
19363        page_prefetch_window_from_values, worker_prefetch_positions,
19364    };
19365
19366    #[test]
19367    fn page_prefetch_window_keeps_existing_opt_in_default() {
19368        assert_eq!(page_prefetch_window_from_values(false, None), 0);
19369        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
19370        assert_eq!(page_prefetch_window_from_values(true, None), 1);
19371        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
19372        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
19373        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
19374    }
19375
19376    #[test]
19377    fn rolling_page_prefetch_advises_each_future_expert_once() {
19378        let advised: Vec<_> = (0..7)
19379            .flat_map(|position| page_prefetch_positions(position, 7, 3))
19380            .collect();
19381        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
19382
19383        let one_ahead: Vec<_> = (0..4)
19384            .flat_map(|position| page_prefetch_positions(position, 4, 1))
19385            .collect();
19386        assert_eq!(one_ahead, vec![1, 2, 3]);
19387        assert!(page_prefetch_positions(0, 4, 0).is_empty());
19388    }
19389
19390    #[test]
19391    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
19392        assert_eq!(grouped_worker_prefetch_position(0, None), None);
19393        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
19394            .chain(
19395                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
19396            )
19397            .collect();
19398        assert_eq!(positions, vec![0, 1, 2, 3]);
19399        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
19400    }
19401
19402    #[test]
19403    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
19404        let queued: Vec<_> = (0..8)
19405            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
19406            .collect();
19407        assert_eq!(queued, (0..8).collect::<Vec<_>>());
19408
19409        let one_at_a_time: Vec<_> = (0..4)
19410            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
19411            .collect();
19412        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
19413        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
19414    }
19415}
19416
19417pub struct G4DcSlots {
19418    x: CudaSlice<f32>,
19419    xn: CudaSlice<f32>,
19420    cur: CudaSlice<f32>,
19421    hq: CudaSlice<i8>,
19422    hd_: CudaSlice<f32>,
19423    q0: CudaSlice<f32>,
19424    k0: CudaSlice<f32>,
19425    v0: CudaSlice<f32>,
19426    q: CudaSlice<f32>,
19427    k: CudaSlice<f32>,
19428    v: CudaSlice<f32>,
19429    attn: CudaSlice<f32>,
19430    o: CudaSlice<f32>,
19431    attn_out: CudaSlice<f32>,
19432    zsh: CudaSlice<f32>,
19433    zq: CudaSlice<i8>,
19434    zd: CudaSlice<f32>,
19435    gate: CudaSlice<f32>,
19436    up: CudaSlice<f32>,
19437    act: CudaSlice<f32>,
19438    actq: CudaSlice<i8>,
19439    actd: CudaSlice<f32>,
19440    f0: CudaSlice<f32>,
19441    sn: CudaSlice<f32>,
19442    hn: CudaSlice<f32>,
19443    logits: CudaSlice<f32>,
19444}
19445
19446/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
19447/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
19448/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
19449/// fixed logits stage the head writes.
19450pub struct Step35TokenGraphState {
19451    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
19452    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
19453    pub token_d: cudarc::driver::CudaSlice<u32>,
19454    pub pos_d: cudarc::driver::CudaSlice<i32>,
19455    pub logits_stage: cudarc::driver::CudaSlice<f32>,
19456    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
19457    /// launch, so an alloc made inside one captured child is not referable from another):
19458    /// the running residual, the post-attention pair, the shared-expert row, and the
19459    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
19460    pub x: cudarc::driver::CudaSlice<f32>,
19461    pub x1: cudarc::driver::CudaSlice<f32>,
19462    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
19463    pub sh_stage: cudarc::driver::CudaSlice<f32>,
19464    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
19465    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
19466    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
19467    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
19468    pub router_logits: cudarc::driver::CudaSlice<f32>,
19469    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
19470    pub shexp_up: cudarc::driver::CudaSlice<f32>,
19471    pub shexp_act: cudarc::driver::CudaSlice<f32>,
19472    pub gate_sig: cudarc::driver::CudaSlice<f32>,
19473    pub dense_z: cudarc::driver::CudaSlice<f32>,
19474    pub dense_gate: cudarc::driver::CudaSlice<f32>,
19475    pub dense_up: cudarc::driver::CudaSlice<f32>,
19476    pub dense_act: cudarc::driver::CudaSlice<f32>,
19477    pub hn: cudarc::driver::CudaSlice<f32>,
19478    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
19479    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
19480    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
19481    pub probe_x: cudarc::driver::CudaSlice<f32>,
19482    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
19483    /// the in-graph tail argmax chain; host reads the ring once per chunk.
19484    pub token_hist: cudarc::driver::CudaSlice<u32>,
19485    pub hist_idx: cudarc::driver::CudaSlice<i32>,
19486}
19487
19488impl HybridModel {
19489    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
19490    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
19491    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
19492    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
19493    /// needs a rebuild this token).
19494    ///
19495    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
19496    /// but not their contents under this door (the TP rank caches are fully maintained
19497    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
19498    /// must not run with the door on until the local-dcw twin lands.
19499    pub(crate) fn step35_token_graph_step(
19500        &self,
19501        e: &Engine,
19502        token: u32,
19503        cache: &mut Cache,
19504    ) -> Result<Option<(Vec<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
19505        if !self.uses_sliding_gated_moe_program()
19506            || !crate::tp::step_tp_graph_enabled()?
19507            || !crate::tp::step_tp_dcw_enabled()?
19508            || !crate::tp::step_tp_qkv_fused_enabled()?
19509            || !crate::tp::step_tp_dev_router_enabled()?
19510            || !crate::tp::step_nvfp4_dev_routes_enabled()?
19511        {
19512            return Ok(None);
19513        }
19514        let n_embd = self.cfg.n_embd as usize;
19515        let n_vocab = self.cfg.n_vocab as usize;
19516        let eps = self.cfg.rms_eps;
19517        let n_layers = self.layers.len();
19518        let pos = cache.pos;
19519        let staged_next = pos + 1;
19520        if staged_next < 96 {
19521            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
19522        }
19523
19524        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
19525        // eager fallback for the whole token; the host path also updates base_d there).
19526        for il in 0..n_layers {
19527            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
19528                return Ok(None); // caches not hydrated yet — eager warms them
19529            };
19530            if tp_kv.peek_append_ring(1)?.1 {
19531                return Ok(None);
19532            }
19533        }
19534
19535        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
19536        // their window and share one bucket forever after ctx > window).
19537        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
19538        if !fa_vec {
19539            return Ok(None);
19540        }
19541        let sp = crate::fa_split_keys(staged_next, 8);
19542        let bucket_max = (n_splits * sp).max(staged_next);
19543
19544        let mut state_guard = self
19545            .step35_token_graph
19546            .lock()
19547            .map_err(|_| "step35 token graph lock is poisoned")?;
19548        if state_guard.is_none() {
19549            let _main = e.gpu.enter_main()?;
19550            let n_expert = self
19551                .cfg
19552                .moe
19553                .as_ref()
19554                .map(|m| m.expert_count as usize)
19555                .unwrap_or(0);
19556            let n_ff_sh = self
19557                .layers
19558                .iter()
19559                .find_map(|l| match &l.ffn {
19560                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
19561                    _ => None,
19562                })
19563                .unwrap_or(0);
19564            let n_ff_dense = self
19565                .layers
19566                .iter()
19567                .find_map(|l| match &l.ffn {
19568                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
19569                    _ => None,
19570                })
19571                .unwrap_or(0);
19572            *state_guard = Some(Step35TokenGraphState {
19573                graphs: Vec::new(),
19574                token_d: e.stream().clone_htod(&[0u32])?,
19575                pos_d: e.htod_i32(&[pos as i32])?,
19576                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
19577                x: e.htod(&vec![0.0f32; n_embd])?,
19578                x1: e.htod(&vec![0.0f32; n_embd])?,
19579                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
19580                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
19581                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19582                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
19583                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
19584                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19585                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19586                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
19587                gate_sig: e.htod(&vec![1.0f32; 1])?,
19588                dense_z: e.htod(&vec![0.0f32; n_embd])?,
19589                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19590                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19591                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
19592                hn: e.htod(&vec![0.0f32; n_embd])?,
19593                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
19594                probe_x: e.htod(&vec![0.0f32; n_embd])?,
19595                token_hist: e.stream().clone_htod(&[0u32; 16])?,
19596                hist_idx: e.htod_i32(&[0])?,
19597            });
19598        }
19599        let state = state_guard.as_mut().expect("state armed above");
19600        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
19601        // first use, and an alloc inside a captured section is a mem node (child graphs
19602        // reject those — the tail argmax chain needs them already resident).
19603        {
19604            let _main = e.gpu.enter_main()?;
19605            let Step35TokenGraphState {
19606                logits_stage,
19607                token_d,
19608                ..
19609            } = &mut *state;
19610            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
19611        }
19612
19613        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
19614        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
19615        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
19616        // ceiling at build so the baked pointers never move.
19617        if state.graphs.is_empty() {
19618            // Build the parent at this bucket. Capture executes nothing; correctness is
19619            // pinned at replay by the token-identity gate.
19620            self.step35_token_graph_build(e, cache, state, bucket_max)?;
19621        }
19622        {
19623            let (b, g) = state.graphs.first_mut().expect("graph built above");
19624            if *b != bucket_max {
19625                g.retarget_bucket(bucket_max)?;
19626                *b = bucket_max;
19627            }
19628        }
19629        let graph = state
19630            .graphs
19631            .first()
19632            .map(|(_, g)| g)
19633            .expect("graph built above");
19634
19635        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
19636        let t_fence = tg_timing.then(std::time::Instant::now);
19637        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
19638        // queued on the rank streams, and graph children carry no ordering edge to those
19639        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
19640        // sync is a no-op between consecutive replays.
19641        {
19642            let fa0 = match &self.layers[0].mixer {
19643                Mixer::Full(fa) => fa,
19644                _ => return Err("step35 token graph expects full-attention layers".into()),
19645            };
19646            let tp0 = fa0
19647                .step_tp_qkv
19648                .as_ref()
19649                .ok_or("step35 token graph lost its TP state")?;
19650            for rank in 0..tp0.runtime.devices().len() {
19651                let engine = tp0
19652                    .runtime
19653                    .rank_engine(rank)
19654                    .ok_or("step35 token graph lost a rank engine")?;
19655                let _main = engine.gpu.enter_main()?;
19656                engine.stream().synchronize()?;
19657            }
19658        }
19659
19660        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
19661        {
19662            let _main = e.gpu.enter_main()?;
19663            e.set_u32_one(&mut state.token_d, token)?;
19664            e.set_i32_one(&mut state.pos_d, pos as i32)?;
19665        }
19666        let t_launch = tg_timing.then(std::time::Instant::now);
19667        graph.launch(e)?;
19668        let t_book = tg_timing.then(std::time::Instant::now);
19669        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
19670        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
19671        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
19672        // replay error the counters are already advanced — acceptable: the decode aborts.
19673        for il in 0..n_layers {
19674            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
19675            let transaction = tp_kv.begin_transaction()?;
19676            let fa = match &self.layers[il].mixer {
19677                Mixer::Full(fa) => fa,
19678                _ => return Err("step35 token graph expects full-attention layers".into()),
19679            };
19680            let tp = fa
19681                .step_tp_qkv
19682                .as_ref()
19683                .ok_or("step35 token graph lost its TP state")?;
19684            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
19685            // incs own the counters). Shards unused.
19686            let empty: [CudaSlice<f32>; 0] = [];
19687            tp.runtime.append_tp_kv_transaction_inner(
19688                tp_kv,
19689                transaction,
19690                &empty,
19691                &empty,
19692                1,
19693                true,
19694            )?;
19695            tp.runtime
19696                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
19697            // Local shadow: lengths advance (v1 keeps contents stale under the door).
19698            if let Some(local) = cache.kv[il].as_mut() {
19699                local.len = pos + 1;
19700                let _main = e.gpu.enter_main()?;
19701                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
19702            }
19703        }
19704        cache.pos = pos + 1;
19705        let t_sync = tg_timing.then(std::time::Instant::now);
19706        let (logits, h_seed) = {
19707            let _main = e.gpu.enter_main()?;
19708            e.stream().synchronize()?;
19709            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
19710        };
19711        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
19712            use std::sync::atomic::{AtomicU64, Ordering};
19713            static NS: [AtomicU64; 5] = [
19714                AtomicU64::new(0),
19715                AtomicU64::new(0),
19716                AtomicU64::new(0),
19717                AtomicU64::new(0),
19718                AtomicU64::new(0),
19719            ];
19720            static CALLS: AtomicU64 = AtomicU64::new(0);
19721            let now = std::time::Instant::now();
19722            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
19723            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
19724            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
19725            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
19726            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
19727            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
19728            if calls % 100 == 0 {
19729                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
19730                eprintln!(
19731                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
19732                     syncdtoh_us={:.0} total_us={:.0}",
19733                    avg(0),
19734                    avg(1),
19735                    avg(2),
19736                    avg(3),
19737                    avg(4)
19738                );
19739            }
19740        }
19741        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
19742        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
19743            use std::io::Write;
19744            let (pm, px) = {
19745                let _main = e.gpu.enter_main()?;
19746                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
19747            };
19748            for (path, data) in [
19749                ("/root/tg-probe-mixed.bin", &pm),
19750                ("/root/tg-probe-x.bin", &px),
19751            ] {
19752                let mut fo = std::fs::OpenOptions::new()
19753                    .create(true)
19754                    .append(true)
19755                    .open(path)?;
19756                for v in data {
19757                    fo.write_all(&v.to_le_bytes())?;
19758                }
19759            }
19760        }
19761        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
19762        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
19763        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
19764            let hh = {
19765                let _main = e.gpu.enter_main()?;
19766                e.dtoh(&state.hn)?
19767            };
19768            use std::io::Write;
19769            let mut fo = std::fs::OpenOptions::new()
19770                .create(true)
19771                .append(true)
19772                .open(path)?;
19773            for v in &hh {
19774                fo.write_all(&v.to_le_bytes())?;
19775            }
19776        }
19777        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
19778        // per rank per token; diagnostics only.
19779        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
19780            for il in [0usize, 1, 44] {
19781                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
19782                let host_len = tp_kv.staged_len();
19783                let fa = match &self.layers[il].mixer {
19784                    Mixer::Full(fa) => fa,
19785                    _ => continue,
19786                };
19787                let tp = fa
19788                    .step_tp_qkv
19789                    .as_ref()
19790                    .ok_or("step35 token graph lost its TP state")?;
19791                for rank in 0..tp.runtime.devices().len() {
19792                    let engine = tp
19793                        .runtime
19794                        .rank_engine(rank)
19795                        .ok_or("step35 token graph lost a rank engine")?;
19796                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
19797                    let _main = engine.gpu.enter_main()?;
19798                    engine.stream().synchronize()?;
19799                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
19800                    let base_d = match rank_cache.base_d() {
19801                        Some(b) => engine.dtoh_i32_one(b)?,
19802                        None => -1,
19803                    };
19804                    eprintln!(
19805                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
19806                         len_d={len_d} base_d={base_d}"
19807                    );
19808                }
19809            }
19810        }
19811        Ok(Some((logits, h_seed)))
19812    }
19813
19814    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
19815    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
19816    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
19817    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
19818    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
19819    pub(crate) fn head_split_matvec(
19820        &self,
19821        e: &Engine,
19822        hn: &CudaSlice<f32>,
19823    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
19824        if self.head_split_fill_device(e, hn)?.is_none() {
19825            return Ok(None);
19826        }
19827        let guard = HEAD_SPLIT_WS
19828            .lock()
19829            .map_err(|_| "head split lock is poisoned")?;
19830        let ws = guard.as_ref().expect("filled above");
19831        let _main = e.gpu.enter_main()?;
19832        Ok(Some(e.dtoh(&ws.logits_e)?))
19833    }
19834
19835    /// Compute body of the split head: arms the replica + staging on first use, then fills
19836    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
19837    /// push) and orders e's stream behind it. None = ineligible.
19838    fn head_split_fill_device(
19839        &self,
19840        e: &Engine,
19841        hn: &CudaSlice<f32>,
19842    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
19843        use cudarc::driver::DevicePtr;
19844        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
19845            return Ok(None);
19846        };
19847        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
19848            Mixer::Full(fa) => fa
19849                .step_tp_qkv
19850                .as_ref()
19851                .and_then(|tp| tp.runtime.rank_engine(1)),
19852            _ => None,
19853        }) else {
19854            return Ok(None);
19855        };
19856        let n_embd = self.cfg.n_embd as usize;
19857        let n_vocab = self.cfg.n_vocab as usize;
19858        let half = n_vocab / 2;
19859        let mut guard = HEAD_SPLIT_WS
19860            .lock()
19861            .map_err(|_| "head split lock is poisoned")?;
19862        let pin = {
19863            let _main = e.gpu.enter_main()?;
19864            let stream = e.stream();
19865            let (ptr, _g) = head.device_ptr(&stream);
19866            ptr as u64
19867        };
19868        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
19869            // One-time: upload rank1's row half + persistent staging.
19870            let hi_rows = n_vocab - half;
19871            let (w1, hn1, y1, ev_done) = {
19872                let _r1 = rank1.gpu.enter_main()?;
19873                (
19874                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
19875                    rank1.htod(&vec![0.0f32; n_embd])?,
19876                    rank1.htod(&vec![0.0f32; hi_rows])?,
19877                    rank1.ctx().new_event(None)?,
19878                )
19879            };
19880            {
19881                use cudarc::driver::sys;
19882                let src = pin + (half * n_embd * 2) as u64;
19883                let dst = {
19884                    let _r1 = rank1.gpu.enter_main()?;
19885                    let rstream = rank1.stream();
19886                    let (d, _g) = w1.device_ptr(&rstream);
19887                    d as u64
19888                };
19889                let _r1 = rank1.gpu.enter_main()?;
19890                let r = unsafe {
19891                    sys::cuMemcpyAsync(
19892                        dst as sys::CUdeviceptr,
19893                        src as sys::CUdeviceptr,
19894                        hi_rows * n_embd * 2,
19895                        rank1.stream().cu_stream() as sys::CUstream,
19896                    )
19897                };
19898                if r != sys::CUresult::CUDA_SUCCESS {
19899                    return Err(format!("head split replica upload: {r:?}").into());
19900                }
19901                rank1.stream().synchronize()?;
19902            }
19903            let (logits_e, ev_hn) = {
19904                let _main = e.gpu.enter_main()?;
19905                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
19906            };
19907            let (raw_hn1, raw_y1) = {
19908                let _r1 = rank1.gpu.enter_main()?;
19909                let rstream = rank1.stream();
19910                let (a, _g0) = hn1.device_ptr(&rstream);
19911                let (b, _g1) = y1.device_ptr(&rstream);
19912                (a as u64, b as u64)
19913            };
19914            let raw_logits_hi = {
19915                let _main = e.gpu.enter_main()?;
19916                let stream = e.stream();
19917                let (l, _g) = logits_e.device_ptr(&stream);
19918                l as u64 + (half * 4) as u64
19919            };
19920            *guard = Some(HeadSplit {
19921                pin,
19922                w1,
19923                hn1,
19924                y1,
19925                logits_e,
19926                ev_hn,
19927                ev_done,
19928                raw_hn1,
19929                raw_y1,
19930                raw_logits_hi,
19931            });
19932        }
19933        let ws = guard.as_mut().expect("armed above");
19934        let hi_rows = n_vocab - half;
19935        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
19936        let raw_hn = {
19937            let _main = e.gpu.enter_main()?;
19938            let stream = e.stream();
19939            let (h, _g) = hn.device_ptr(&stream);
19940            ws.ev_hn.record(&stream)?;
19941            h as u64
19942        };
19943        {
19944            let _r1 = rank1.gpu.enter_main()?;
19945            rank1.stream().wait(&ws.ev_hn)?;
19946            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
19947            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
19948            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
19949            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
19950            ws.ev_done.record(&rank1.stream())?;
19951        }
19952        {
19953            let _main = e.gpu.enter_main()?;
19954            let head_lo = head.slice(0..half * n_embd * 2);
19955            let HeadSplit { logits_e, .. } = &mut *ws;
19956            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
19957            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
19958            e.stream().wait(&ws.ev_done)?;
19959            Ok(Some(()))
19960        }
19961    }
19962
19963    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
19964    /// row exactly like the host variant (identical halves, identical concat) and runs the
19965    /// device argmax into `token_d` — NO host readback. Returns false when the split is
19966    /// ineligible (caller falls back to the plain matmul head).
19967    pub(crate) fn head_split_argmax_device(
19968        &self,
19969        e: &Engine,
19970        hn: &CudaSlice<f32>,
19971        token_d: &mut CudaSlice<u32>,
19972    ) -> Result<bool, Box<dyn std::error::Error>> {
19973        if self.head_split_fill_device(e, hn)?.is_none() {
19974            return Ok(false);
19975        }
19976        let n_vocab = self.cfg.n_vocab as usize;
19977        let guard = HEAD_SPLIT_WS
19978            .lock()
19979            .map_err(|_| "head split lock is poisoned")?;
19980        let ws = guard.as_ref().expect("filled above");
19981        let _main = e.gpu.enter_main()?;
19982        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
19983        Ok(true)
19984    }
19985
19986    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
19987    /// token's row).
19988    pub(crate) fn head_split_logits_dtoh(
19989        &self,
19990        e: &Engine,
19991    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19992        let guard = HEAD_SPLIT_WS
19993            .lock()
19994            .map_err(|_| "head split lock is poisoned")?;
19995        let ws = guard.as_ref().ok_or("head split logits not armed")?;
19996        let _main = e.gpu.enter_main()?;
19997        Ok(e.dtoh(&ws.logits_e)?)
19998    }
19999
20000    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
20001    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
20002    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
20003    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
20004    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
20005    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
20006    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
20007    /// own loop re-derive hist[k-1] from the returned row.
20008    pub fn step35_token_graph_chunk(
20009        &self,
20010        e: &Engine,
20011        token: u32,
20012        k_target: usize,
20013        cache: &mut Cache,
20014    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
20015        if !self.uses_sliding_gated_moe_program()
20016            || !crate::tp::step_tp_graph_enabled()?
20017            || !crate::tp::step_tp_dcw_enabled()?
20018            || !crate::tp::step_tp_qkv_fused_enabled()?
20019            || !crate::tp::step_tp_dev_router_enabled()?
20020            || !crate::tp::step_nvfp4_dev_routes_enabled()?
20021        {
20022            return Ok(None);
20023        }
20024        let n_layers = self.layers.len();
20025        let pos = cache.pos;
20026        let staged_next = pos + 1;
20027        if staged_next < 96 {
20028            return Ok(None);
20029        }
20030        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
20031        // exec's n_splits ladder must match eager per depth).
20032        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20033        if !fa_vec {
20034            return Ok(None);
20035        }
20036        let sp = crate::fa_split_keys(staged_next, 8);
20037        let bucket_max = (n_splits * sp).max(staged_next);
20038        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
20039        let mut k = k_target.min(to_boundary).min(16);
20040        if k < 2 {
20041            return Ok(None);
20042        }
20043        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
20044        for il in 0..n_layers {
20045            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20046                return Ok(None);
20047            };
20048            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
20049                k -= 1;
20050            }
20051            if k < 2 {
20052                return Ok(None);
20053            }
20054        }
20055
20056        let mut state_guard = self
20057            .step35_token_graph
20058            .lock()
20059            .map_err(|_| "step35 token graph lock is poisoned")?;
20060        let Some(state) = state_guard.as_mut() else {
20061            return Ok(None); // per-token path arms the state + stages first
20062        };
20063        if state.graphs.is_empty() {
20064            return Ok(None);
20065        }
20066        {
20067            let (b, g) = state.graphs.first_mut().expect("checked above");
20068            if *b != bucket_max {
20069                g.retarget_bucket(bucket_max)?;
20070                *b = bucket_max;
20071            }
20072        }
20073        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
20074
20075        // Rank-stream fence (eager stragglers; see the per-token path).
20076        {
20077            let fa0 = match &self.layers[0].mixer {
20078                Mixer::Full(fa) => fa,
20079                _ => return Err("step35 token graph expects full-attention layers".into()),
20080            };
20081            let tp0 = fa0
20082                .step_tp_qkv
20083                .as_ref()
20084                .ok_or("step35 token graph lost its TP state")?;
20085            for rank in 0..tp0.runtime.devices().len() {
20086                let engine = tp0
20087                    .runtime
20088                    .rank_engine(rank)
20089                    .ok_or("step35 token graph lost a rank engine")?;
20090                let _main = engine.gpu.enter_main()?;
20091                engine.stream().synchronize()?;
20092            }
20093        }
20094
20095        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
20096        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
20097        {
20098            let _main = e.gpu.enter_main()?;
20099            e.set_u32_one(&mut state.token_d, token)?;
20100            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20101            e.set_i32_one(&mut state.hist_idx, 0)?;
20102        }
20103        for _ in 0..k {
20104            graph.launch(e)?;
20105        }
20106        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
20107        for il in 0..n_layers {
20108            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20109            let transaction = tp_kv.begin_transaction()?;
20110            let fa = match &self.layers[il].mixer {
20111                Mixer::Full(fa) => fa,
20112                _ => return Err("step35 token graph expects full-attention layers".into()),
20113            };
20114            let tp = fa
20115                .step_tp_qkv
20116                .as_ref()
20117                .ok_or("step35 token graph lost its TP state")?;
20118            let empty: [CudaSlice<f32>; 0] = [];
20119            tp.runtime.append_tp_kv_transaction_inner(
20120                tp_kv,
20121                transaction,
20122                &empty,
20123                &empty,
20124                k,
20125                true,
20126            )?;
20127            tp.runtime
20128                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
20129            if let Some(local) = cache.kv[il].as_mut() {
20130                local.len = pos + k;
20131                let _main = e.gpu.enter_main()?;
20132                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
20133            }
20134        }
20135        cache.pos = pos + k;
20136        let (hist, logits) = {
20137            let _main = e.gpu.enter_main()?;
20138            e.stream().synchronize()?;
20139            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
20140        };
20141        Ok(Some((hist[..k].to_vec(), logits)))
20142    }
20143}
20144
20145impl HybridModel {
20146    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
20147    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
20148    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
20149    /// of each phase fork in parallel and merge into the following root section.
20150    #[allow(clippy::too_many_arguments)]
20151    fn step35_token_graph_build(
20152        &self,
20153        e: &Engine,
20154        cache: &mut Cache,
20155        state: &mut Step35TokenGraphState,
20156        bucket_max: usize,
20157    ) -> Result<(), Box<dyn std::error::Error>> {
20158        use cudarc::driver::DevicePtr;
20159        let n_embd = self.cfg.n_embd as usize;
20160        let eps = self.cfg.rms_eps;
20161        let n_layers = self.layers.len();
20162        let started = std::time::Instant::now();
20163        if !crate::router_kernel_on() {
20164            return Err(
20165                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
20166            );
20167        }
20168        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
20169            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
20170        }
20171
20172        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
20173        let embd_gpu = self
20174            .embd_gpu_try(e)
20175            .ok_or("step35 token graph could not upload the device embed table")?;
20176        let embd_qtype = match self.embd.ggml_type {
20177            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
20178            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
20179            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
20180        };
20181        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
20182
20183        // Fixed-stage pointers the sections reference.
20184        let (p_mixed, p_kshadow, p_vshadow) = {
20185            let _main = e.gpu.enter_main()?;
20186            let stream = e.stream();
20187            let (a, _g) = state.mixed_stage.device_ptr(&stream);
20188            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
20189            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
20190            (a as u64, b as u64, c as u64)
20191        };
20192
20193        crate::tp::token_graph_build_begin()?;
20194        let mut group_id: u32 = 0;
20195        for il in 0..n_layers {
20196            let layer = &self.layers[il];
20197            let fa = match &layer.mixer {
20198                Mixer::Full(fa) => fa,
20199                _ => return Err("step35 token graph expects full-attention layers".into()),
20200            };
20201            let tp = fa
20202                .step_tp_qkv
20203                .as_ref()
20204                .ok_or("step35 token graph lost its TP state")?;
20205            let attention = tp
20206                .attention
20207                .as_ref()
20208                .ok_or("step35 token graph lost its attention aux")?;
20209            let geometry = self.step35_geom(il);
20210            let window = geometry.window.map(|w| w as usize);
20211            let head_dim = geometry.head_dim_k as usize;
20212            let heads = geometry.n_head as usize;
20213            let kv_heads = geometry.n_head_kv as usize;
20214            let ranks = tp.runtime.devices().len();
20215            let local_heads = heads / ranks;
20216            let local_kv_heads = kv_heads / ranks;
20217            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
20218            let use_gate_shards =
20219                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
20220            if !use_gate_shards {
20221                return Err("step35 token graph requires the fused gate shards".into());
20222            }
20223
20224            let ws_index = tp
20225                .runtime
20226                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
20227            let ws_mutex = tp.runtime.decode_v2_workspace();
20228            let mut ws_guard = ws_mutex
20229                .lock()
20230                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
20231            let ws = ws_guard
20232                .get_mut(ws_index)
20233                .ok_or("step TP decode v2 workspace missing after ensure")?;
20234            tp.runtime
20235                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
20236            let mut rope_freqs = Vec::with_capacity(ranks);
20237            for rank in 0..ranks {
20238                let engine = tp
20239                    .runtime
20240                    .rank_engine(rank)
20241                    .ok_or("step35 token graph lost a rank engine")?;
20242                rope_freqs.push(if geometry.rope_factors {
20243                    self.step35_aux
20244                        .as_ref()
20245                        .and_then(|aux| aux.rope_freqs(engine))
20246                } else {
20247                    None
20248                });
20249            }
20250            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
20251                Some(crate::tp::StepTpGateShards::F32(shards))
20252            } else {
20253                attention
20254                    .gate_shards_bf16
20255                    .as_deref()
20256                    .map(crate::tp::StepTpGateShards::Bf16)
20257            };
20258
20259            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
20260            let decode_input = attention
20261                .decode_input
20262                .as_ref()
20263                .ok_or("step35 token graph requires the replicated decode input")?;
20264            let mut decode_input = decode_input
20265                .lock()
20266                .map_err(|_| "replicated decode input lock is poisoned")?;
20267            // Stage arming happens through the eager stage flow once; require it here.
20268            if ws.h_stage.is_none() {
20269                return Err(
20270                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
20271                );
20272            }
20273            {
20274                let state_x = &mut state.x;
20275                let token_d = &state.token_d;
20276                let pos_d = &state.pos_d;
20277                crate::tp::graph_section(e, None, || {
20278                    let _main = e.gpu.enter_main()?;
20279                    if il == 0 {
20280                        e.embed_gather_device_into(
20281                            embd_gpu,
20282                            token_d,
20283                            state_x,
20284                            n_embd,
20285                            embd_qtype,
20286                            embd_row_bytes,
20287                        )?;
20288                    }
20289                    {
20290                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
20291                        e.rms_norm(
20292                            state_x,
20293                            layer.attn_norm.float_data(),
20294                            h_stage,
20295                            n_embd,
20296                            1,
20297                            eps,
20298                        )?;
20299                    }
20300                    {
20301                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
20302                        let mut dst = pos_stage.slice_mut(0..1);
20303                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
20304                    }
20305                    Ok(())
20306                })?;
20307            }
20308
20309            // ---- R0/R1 (parallel): projections + dcw attention interior ----
20310            group_id += 1;
20311            for rank in 0..ranks {
20312                let engine = tp
20313                    .runtime
20314                    .rank_engine(rank)
20315                    .ok_or("step35 token graph lost a rank engine")?;
20316                {
20317                    // fa partial pool must reach the RUN CEILING before capture — an
20318                    // in-capture grow is a mem node (child graphs reject those), and the
20319                    // retarget path (increment C) widens the baked memsets up to the ceiling
20320                    // without moving the pool pointers. Two ensures cover both sp rungs.
20321                    let ceiling = window
20322                        .map(|w| cache.max_ctx.min(w))
20323                        .unwrap_or(cache.max_ctx);
20324                    let _main = engine.gpu.enter_main()?;
20325                    engine.fa_dcw_pool_ensure(
20326                        head_dim,
20327                        local_heads,
20328                        local_kv_heads,
20329                        ceiling.min(2048),
20330                    )?;
20331                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
20332                    engine.fa_dcw_pool_ensure(
20333                        head_dim,
20334                        local_heads,
20335                        local_kv_heads,
20336                        layer_bucket,
20337                    )?;
20338                }
20339                let runtime = &tp.runtime;
20340                let q_norm = &attention.q_norm;
20341                let k_norm = &attention.k_norm;
20342                let gate_ref = gate_shards_arg.as_ref();
20343                crate::tp::graph_section(engine, Some(group_id), || {
20344                    runtime.decode_v2_input_qkv_rank(
20345                        ws,
20346                        &state.pos_d,
20347                        &mut decode_input,
20348                        &tp.q,
20349                        &tp.k,
20350                        &tp.v,
20351                        q_norm,
20352                        k_norm,
20353                        head_dim,
20354                        geometry.n_rot as usize,
20355                        geometry.rope_base,
20356                        &rope_freqs,
20357                        eps,
20358                        gate_ref,
20359                        true,
20360                        false,
20361                        rank,
20362                        None,
20363                    )?;
20364                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
20365                    // replayed values track the live counters).
20366                    let distributed = cache.tp_kv[il]
20367                        .as_mut()
20368                        .ok_or("step35 token graph lost a TP cache")?;
20369                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
20370                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
20371                    let capacity = distributed.physical_capacity();
20372                    {
20373                        let rank_cache = distributed
20374                            .rank_mut(rank)
20375                            .ok_or("step35 token graph lost a rank cache")?;
20376                        let (k_plane, v_plane, len_d, base_d) =
20377                            rank_cache.planes_and_counters_mut();
20378                        engine.append_kv_quantized_dcw(
20379                            &ws.k[rank],
20380                            &ws.v_raw[rank],
20381                            k_plane,
20382                            v_plane,
20383                            len_d,
20384                            base_d,
20385                            kv_dim_k,
20386                            kv_dim_v,
20387                            ktb,
20388                            vtb,
20389                        )?;
20390                    }
20391                    {
20392                        let rank_cache = distributed
20393                            .rank_mut(rank)
20394                            .ok_or("step35 token graph lost a rank cache")?;
20395                        engine.inc_i32(rank_cache.len_d_mut())?;
20396                    }
20397                    let rank_cache = distributed
20398                        .rank(rank)
20399                        .ok_or("step35 token graph lost a rank cache")?;
20400                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
20401                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
20402                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
20403                    // retarget addresses combine's nsp at arg slot 6, and the fused
20404                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
20405                    // only the eager arm takes FUSION #2d.
20406                    engine.fa_decode_dcw(
20407                        &ws.q[rank],
20408                        &k_ring,
20409                        &v_ring,
20410                        &mut ws.attn_out[rank],
20411                        head_dim,
20412                        local_heads,
20413                        local_kv_heads,
20414                        rank_cache.len_d(),
20415                        rank_cache.base_d(),
20416                        window.unwrap_or(0),
20417                        layer_bucket,
20418                        geometry.attention_scale(),
20419                        ktb,
20420                        vtb,
20421                        None,
20422                    )?;
20423                    engine.attn_head_gate(
20424                        &ws.attn_out[rank],
20425                        &ws.gate[rank],
20426                        &mut ws.gated[rank],
20427                        None,
20428                        head_dim,
20429                        local_heads,
20430                        1,
20431                    )?;
20432                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
20433                    Ok(())
20434                })?;
20435            }
20436
20437            // ---- ROOT: combine + shadows + e-mirrors ----
20438            {
20439                let root = tp
20440                    .runtime
20441                    .rank_engine(0)
20442                    .ok_or("step35 token graph lost the root engine")?;
20443                let runtime = &tp.runtime;
20444                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
20445            }
20446            drop(ws_guard);
20447            drop(decode_input);
20448
20449            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
20450                .ok()
20451                .and_then(|v| v.parse().ok());
20452            if probe_layer == Some(il) {
20453                let Step35TokenGraphState {
20454                    mixed_stage,
20455                    probe_mixed,
20456                    ..
20457                } = &mut *state;
20458                crate::tp::graph_section(e, None, || {
20459                    let _main = e.gpu.enter_main()?;
20460                    let mut dst = probe_mixed.slice_mut(0..n_embd);
20461                    e.stream()
20462                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
20463                    Ok(())
20464                })?;
20465            }
20466
20467            // ---- FFN half ----
20468            match &layer.ffn {
20469                crate::hybrid::Ffn::Dense {
20470                    ffn_gate,
20471                    ffn_up,
20472                    ffn_down,
20473                } => {
20474                    let n_ff = ffn_gate.out_features();
20475                    let lim = self.cfg.clamp_shexp_at(il as u32);
20476                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
20477                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
20478                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
20479                    if lim.is_some() {
20480                        return Err("step35 token graph dense FFN with clamp unsupported".into());
20481                    }
20482                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
20483                        (
20484                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
20485                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
20486                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
20487                        ) => (wg, wu, wd),
20488                        _ => {
20489                            return Err(
20490                                "step35 token graph dense FFN requires bf16-resident weights"
20491                                    .into(),
20492                            );
20493                        }
20494                    };
20495                    crate::tp::graph_section(e, None, || {
20496                        let _main = e.gpu.enter_main()?;
20497                        let Step35TokenGraphState {
20498                            x,
20499                            x1,
20500                            mixed_stage,
20501                            dense_z,
20502                            dense_gate,
20503                            dense_up,
20504                            dense_act,
20505                            sh_stage,
20506                            ..
20507                        } = &mut *state;
20508                        e.add_rms_norm(
20509                            x,
20510                            mixed_stage,
20511                            layer.post_attn_norm.float_data(),
20512                            x1,
20513                            dense_z,
20514                            n_embd,
20515                            1,
20516                            eps,
20517                        )?;
20518                        // TWO SINGLE matvecs, not the dual: eager dense rides two
20519                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
20520                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
20521                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
20522                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
20523                        Self::ffn_act_lim(
20524                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
20525                        )?;
20526                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
20527                        e.add(x1, sh_stage, x, n_embd)?;
20528                        Ok(())
20529                    })?;
20530                }
20531                crate::hybrid::Ffn::Moe(m) => {
20532                    let moe = self
20533                        .cfg
20534                        .moe
20535                        .as_ref()
20536                        .ok_or("step35 token graph needs moe cfg")?;
20537                    let n_expert = moe.expert_count as usize;
20538                    let n_used = moe.expert_used_count as usize;
20539                    let sigmoid = self
20540                        .cfg
20541                        .sigmoid_router()
20542                        .ok_or("step35 token graph needs the sigmoid router")?;
20543                    let step_tp = m
20544                        .step_tp
20545                        .as_ref()
20546                        .ok_or("step35 token graph needs TP experts")?;
20547                    let bank = match &step_tp.experts {
20548                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
20549                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
20550                    };
20551                    let routes_ws_mutex = bank.device_workspace_handle();
20552                    let mut routes_guard = routes_ws_mutex
20553                        .lock()
20554                        .map_err(|_| "routes workspace lock is poisoned")?;
20555                    let routes_ws = routes_guard
20556                        .as_mut()
20557                        .ok_or("step35 token graph requires the routes workspace warmed")?;
20558                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
20559                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
20560                    let p_z = {
20561                        let root = step_tp
20562                            .runtime
20563                            .rank_engine(0)
20564                            .ok_or("routes root engine missing")?;
20565                        let _main = root.gpu.enter_main()?;
20566                        let stream = root.stream();
20567                        let in_stage = routes_ws
20568                            .in_stage_handle()
20569                            .ok_or("routes in stage not armed")?;
20570                        let (a, _g) = in_stage.device_ptr(&stream);
20571                        a as u64
20572                    };
20573                    let local_out = bank.expert_width / ranks;
20574
20575                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
20576                    crate::tp::graph_section(e, None, || {
20577                        let _main = e.gpu.enter_main()?;
20578                        {
20579                            let in_stage = routes_ws
20580                                .in_stage_mut()
20581                                .ok_or("routes in stage not armed")?;
20582                            let Step35TokenGraphState {
20583                                x, x1, mixed_stage, ..
20584                            } = &mut *state;
20585                            e.add_rms_norm(
20586                                x,
20587                                mixed_stage,
20588                                layer.post_attn_norm.float_data(),
20589                                x1,
20590                                in_stage,
20591                                n_embd,
20592                                1,
20593                                eps,
20594                            )?;
20595                        }
20596                        {
20597                            let z_ref = routes_ws
20598                                .in_stage_handle()
20599                                .ok_or("routes in stage not armed")?;
20600                            e.router_gemv_into(
20601                                m.gate_inp.float_data(),
20602                                z_ref,
20603                                &mut state.router_logits,
20604                                n_embd,
20605                                n_expert,
20606                                1,
20607                            )?;
20608                        }
20609                        let (sel_e, w_e) = routes_ws
20610                            .dev_route_e_mut()
20611                            .ok_or("routes staging not armed")?;
20612                        e.moe_router_sigmoid_topk_into(
20613                            &state.router_logits,
20614                            1,
20615                            n_expert,
20616                            n_used,
20617                            m.active_count(),
20618                            &m.exp_probs_b_dev,
20619                            &m.active_experts_dev,
20620                            sigmoid.0,
20621                            sigmoid.1,
20622                            sel_e,
20623                            w_e,
20624                        )?;
20625                        Ok(())
20626                    })?;
20627
20628                    // ---- R0r/R1r (parallel): routes sweeps ----
20629                    group_id += 1;
20630                    for rank in 0..ranks {
20631                        let engine = step_tp
20632                            .runtime
20633                            .rank_engine(rank)
20634                            .ok_or("routes rank engine missing")?;
20635                        let runtime = &step_tp.runtime;
20636                        crate::tp::graph_section(engine, Some(group_id), || {
20637                            runtime.routes_rank_section(
20638                                bank,
20639                                routes_ws,
20640                                p_z,
20641                                local_out,
20642                                n_used,
20643                                step_tp.activation_limit,
20644                                rank,
20645                            )
20646                        })?;
20647                    }
20648
20649                    // ---- ROOTr: combine into the out stage ----
20650                    {
20651                        let root = step_tp
20652                            .runtime
20653                            .rank_engine(0)
20654                            .ok_or("routes root engine missing")?;
20655                        let runtime = &step_tp.runtime;
20656                        crate::tp::graph_section(root, None, || {
20657                            runtime.routes_root_section(bank, routes_ws)
20658                        })?;
20659                    }
20660
20661                    // ---- E3: shexp + add_shared onto the out stage + residual ----
20662                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
20663                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
20664                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
20665                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
20666                        (
20667                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
20668                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
20669                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
20670                        ) => (wg, wu, wd),
20671                        _ => {
20672                            return Err(
20673                                "step35 token graph shexp requires bf16-resident weights".into()
20674                            );
20675                        }
20676                    };
20677                    let n_ff_sh = m
20678                        .gate_shexp
20679                        .as_ref()
20680                        .expect("matched Some above")
20681                        .out_features();
20682                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
20683                    // init, reproducing eager's ones vector without a launch.
20684                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
20685                    crate::tp::graph_section(e, None, || {
20686                        let _main = e.gpu.enter_main()?;
20687                        let (z_ref, out_stage) = routes_ws
20688                            .in_and_out_stages_mut()
20689                            .ok_or("routes stages not armed")?;
20690                        let Step35TokenGraphState {
20691                            x,
20692                            x1,
20693                            sh_stage,
20694                            shexp_gate,
20695                            shexp_up,
20696                            shexp_act,
20697                            gate_sig,
20698                            ..
20699                        } = &mut *state;
20700                        e.matvec_bf16_dual_into(
20701                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
20702                        )?;
20703                        Self::ffn_act_lim(
20704                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
20705                            n_ff_sh,
20706                        )?;
20707                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
20708                        if let Some(gate_w) = gate_inp_shexp {
20709                            e.sigmoid_dot_rows_into(
20710                                z_ref,
20711                                gate_w.float_data(),
20712                                gate_sig,
20713                                n_embd,
20714                                1,
20715                            )?;
20716                        }
20717                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
20718                        e.add(x1, out_stage, x, n_embd)?;
20719                        Ok(())
20720                    })?;
20721                }
20722            }
20723            if probe_layer == Some(il) {
20724                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
20725                crate::tp::graph_section(e, None, || {
20726                    let _main = e.gpu.enter_main()?;
20727                    let mut dst = probe_x.slice_mut(0..n_embd);
20728                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
20729                    Ok(())
20730                })?;
20731            }
20732        }
20733
20734        // ---- Tail: output norm + head into the logits stage ----
20735        let head = match &self.output {
20736            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
20737            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
20738        };
20739        crate::tp::graph_section(e, None, || {
20740            let _main = e.gpu.enter_main()?;
20741            let Step35TokenGraphState {
20742                x,
20743                hn,
20744                logits_stage,
20745                token_d,
20746                pos_d,
20747                token_hist,
20748                hist_idx,
20749                ..
20750            } = &mut *state;
20751            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
20752            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
20753            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
20754            // argmax_gate-validated), the id lands in the history ring, and pos advances on
20755            // device — consecutive launches chain with NO host sync. Single-token mode
20756            // overwrites token_d/pos_d from the host before each launch, so these nodes are
20757            // harmless there.
20758            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
20759            e.u32_hist_append(token_d, token_hist, hist_idx)?;
20760            e.inc_i32(pos_d)?;
20761            Ok(())
20762        })?;
20763
20764        let graph = crate::tp::token_graph_build_finish()?;
20765        state.graphs.push((bucket_max, graph));
20766        eprintln!(
20767            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
20768             build_ms={:.0} performance_claim=false",
20769            started.elapsed().as_secs_f64() * 1e3
20770        );
20771        Ok(())
20772    }
20773}