Skip to main content

memra_engine/
hybrid_forward.rs

1//! Hybrid forward pass (Stage-1, f32, prefill, single sequence). Per layer dispatches to a
2//! linear-attention (Gated DeltaNet) or full-attention mixer, then SwiGLU FFN. Matches
3//! llama.cpp src/models/qwen35.cpp node-for-node.
4
5use crate::Engine;
6use crate::cache::Cache;
7use cudarc::driver::CudaSlice;
8use memra_gguf::config::ModelConfig;
9
10/// Resident trunk transients for the eager prime (piecewise-graph foundation; see
11/// HybridModel::prime_slabs). Every live buffer prefix is fully overwritten before use per prime;
12/// capacity beyond the current token count must never cross a shape-sensitive boundary.
13pub struct PrimeSlabs {
14    pub t_cap: usize,
15    pub h: CudaSlice<f32>,
16    pub x1: CudaSlice<f32>,
17    pub z: CudaSlice<f32>,
18    pub act: CudaSlice<f32>,
19    pub xa: CudaSlice<f32>,
20    pub xb: CudaSlice<f32>,
21    pub h16: CudaSlice<u8>,
22    pub z16: CudaSlice<u8>,
23    /// piecewise boundary slabs (increment 2): GEMM outputs land here so the
24    /// downstream captured segments see fixed addresses.
25    pub gate: CudaSlice<f32>, // t * n_ff_max
26    pub up: CudaSlice<f32>,      // t * n_ff_max
27    pub ffn_out: CudaSlice<f32>, // t * n_embd
28    /// piecewise increment 3: per-layer S-glue segment graphs (down-add + next
29    /// attn-norm, ALL-slab IO, zero in-graph allocations -> keeperless capture is
30    /// clean). Baked at this t_cap; replay only when t == t_cap. seg_glue[il] fires
31    /// between layer il and il+1 (ping-pong parity is deterministic per il).
32    pub seg_glue: Vec<Option<cudarc::driver::CudaGraph>>,
33    /// increment 5 (core-split edition): the mixer out-GEMM writes _into_ `mixed`
34    /// directly (no staging copy — the increment-4 copy route was refuted), making
35    /// S-mid [add + post-norm] all-slab and capturable.
36    pub mixed: CudaSlice<f32>,
37    pub seg_mid: Vec<Option<cudarc::driver::CudaGraph>>,
38    pub seg_t: usize,
39}
40
41// Split prime ranges cannot enter the full-range segment-graph arm, and every slab access
42// is serialized by its device mutex after binding that device's CUDA context on the thread.
43unsafe impl Send for PrimeSlabs {}
44
45/// Shared-expert gate+up at t==1: NVFP4 fused2 (the ornith15/qwen35moe NVFP4 mints keep
46/// gate/up_shexp uniformly NVFP4, so the Q8-only fused2 never fired there and the pair fell
47/// to two mr2 singles + two re-quantizes of the same z — 2 of the 8 unfused launches/layer
48/// the orndecode B=1 census ranked at 17.1%), else the Q8_0 fused2 (the Q8 35B mint), else
49/// two singles. ONE helper for all three shexp dispatch sites — the MEMRA_GDN_MMA
50/// three-read-sites defect is the precedent for not inlining this thrice. Fusion law
51/// everywhere: per (tensor,row) the fused seg body is verbatim, so fused == singles
52/// bit-identically, and the shared (zq, zd) is the same quantize each single recomputes.
53fn shexp_gate_up_t1(
54    e: &Engine,
55    gate_shexp: &crate::model::GpuTensor,
56    up_shexp: &crate::model::GpuTensor,
57    z: &CudaSlice<f32>,
58    zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
59) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
60    let is_nvfp4 = |w: &crate::model::GpuTensor| matches!(w, crate::model::GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4);
61    if is_nvfp4(gate_shexp) && is_nvfp4(up_shexp) {
62        // Reuse the caller's t==1 z-quantize when one exists (the zq8 seam the dev arm
63        // already consumes) — the helper's own quantize is the identical kernel on the
64        // identical input, so this drops one launch per MoE layer without moving a byte.
65        let pair = match zq8 {
66            Some((zq, zd)) => e.matmul_nvfp4_fused2(gate_shexp, up_shexp, zq, zd, 1)?,
67            None => {
68                let (zq, zd) = e.quantize_q8_1(z, 1, gate_shexp.in_features())?;
69                e.matmul_nvfp4_fused2(gate_shexp, up_shexp, &zq, &zd, 1)?
70            }
71        };
72        if let Some(pair) = pair {
73            return Ok(pair);
74        }
75    }
76    match e.matmul_q8_fused2_x(gate_shexp, up_shexp, z)? {
77        Some(pair) => Ok(pair),
78        None => Ok((e.matmul(gate_shexp, z, 1)?, e.matmul(up_shexp, z, 1)?)),
79    }
80}
81
82fn active_matrix_values(
83    available: usize,
84    rows: usize,
85    columns: usize,
86    label: &str,
87) -> Result<usize, String> {
88    let required = rows
89        .checked_mul(columns)
90        .ok_or_else(|| format!("{label} shape overflows: {rows}x{columns}"))?;
91    if available < required {
92        return Err(format!(
93            "{label} has {available} values, fewer than the active {rows}x{columns} ({required})"
94        ));
95    }
96    Ok(required)
97}
98
99fn step_grouped_decode_shape(prefill: bool, tokens: usize) -> bool {
100    !prefill && tokens == 1
101}
102
103fn parse_step_ep_grouped_prefill(value: Option<&str>) -> Result<bool, String> {
104    match value {
105        None | Some("") | Some("0") => Ok(false),
106        Some("1") => Ok(true),
107        Some(value) => Err(format!(
108            "MEMRA_STEP_EP_GROUPED_PREFILL={value:?} is invalid; expected 0 or 1"
109        )),
110    }
111}
112
113fn step_ep_grouped_prefill_enabled() -> Result<bool, String> {
114    parse_step_ep_grouped_prefill(
115        std::env::var("MEMRA_STEP_EP_GROUPED_PREFILL")
116            .ok()
117            .as_deref(),
118    )
119}
120
121fn step_grouped_prefill_shape(enabled: bool, prefill: bool, tokens: usize) -> bool {
122    enabled && prefill && (PRIME_MIN_T..=crate::cache::PRIME_CHUNK_MAX_TOKENS).contains(&tokens)
123}
124
125fn parse_step_tp_prefill(value: Option<&str>) -> Result<bool, String> {
126    match value {
127        None | Some("") | Some("0") => Ok(false),
128        Some("1") => Ok(true),
129        Some(value) => Err(format!(
130            "MEMRA_STEP_TP_PREFILL={value:?} is invalid; expected 0 or 1"
131        )),
132    }
133}
134
135fn step_tp_prefill_enabled() -> Result<bool, String> {
136    parse_step_tp_prefill(std::env::var("MEMRA_STEP_TP_PREFILL").ok().as_deref())
137}
138
139fn validate_step_prime_batch_modes(tp_prefill: bool, grouped_prefill: bool) -> Result<(), String> {
140    if grouped_prefill && !tp_prefill {
141        return Err("MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into());
142    }
143    if tp_prefill {
144        return Err(
145            "Step TP4 cross-request prime batching did not clear the live-server performance \
146             gate; use per-session grouped prefill"
147                .into(),
148        );
149    }
150    Ok(())
151}
152
153fn step_tp_prefill_shape(
154    enabled: bool,
155    tokens: usize,
156    ranks: usize,
157    native_p2p: bool,
158    has_rank_local_attention: bool,
159    fp8_kv: bool,
160) -> bool {
161    // TP2 admitted 2026-08-25 behind the same off-by-default door. The prefill body is
162    // rank-count-generic (every geometry check divides by `ranks`); only this shape ever
163    // named 4. TP4's high-context NO-GO was a TRANSPORT verdict — token-row column
164    // gathers plus remote O blocks issue ~61,440 peer copies per 4K attention layer on
165    // that placement — which is not evidence about a 2-card native-P2P placement that
166    // reduces O rank-locally. TP2 is UNQUALIFIED until its own prefill argmax + TTFT
167    // receipts land; the door stays off by default.
168    enabled
169        && tokens >= PRIME_MIN_T
170        && matches!(ranks, 2 | 4)
171        && native_p2p
172        && has_rank_local_attention
173        && !fp8_kv
174}
175
176fn empty_cache_layers<T>(n: usize) -> Vec<Option<T>> {
177    std::iter::repeat_with(|| None).take(n).collect()
178}
179
180/// Temporarily move a PP-2 cache's layer state into two independently-owned cache shells.
181/// The stage walkers then receive disjoint `&mut Cache` values and can run on separate host
182/// threads without aliasing. GPU buffers are moved, not copied; Drop restores every layer
183/// and publishes the last position completed by both stages.
184struct PrimeCacheStages<'a> {
185    parent: &'a mut Cache,
186    cut: usize,
187    stage0: Cache,
188    stage1: Cache,
189}
190
191impl<'a> PrimeCacheStages<'a> {
192    fn new(parent: &'a mut Cache, cut: usize) -> Self {
193        let n = parent.kv.len();
194        assert_eq!(parent.recur.len(), n, "cache layer vectors disagree");
195        assert!(cut <= n, "PP-2 cache cut {cut} exceeds {n} layers");
196        let mut kv0 = empty_cache_layers(n);
197        let mut kv1 = empty_cache_layers(n);
198        let mut tp_kv0 = empty_cache_layers(n);
199        let mut tp_kv1 = empty_cache_layers(n);
200        let mut recur0 = empty_cache_layers(n);
201        let mut recur1 = empty_cache_layers(n);
202        for i in 0..cut {
203            kv0[i] = parent.kv[i].take();
204            tp_kv0[i] = parent.tp_kv[i].take();
205            recur0[i] = parent.recur[i].take();
206        }
207        for i in cut..n {
208            kv1[i] = parent.kv[i].take();
209            tp_kv1[i] = parent.tp_kv[i].take();
210            recur1[i] = parent.recur[i].take();
211        }
212        let pos = parent.pos;
213        let max_ctx = parent.max_ctx;
214        Self {
215            parent,
216            cut,
217            stage0: Cache {
218                kv: kv0,
219                tp_kv: tp_kv0,
220                recur: recur0,
221                pos,
222                max_ctx,
223                last_logits_dev: None,
224                dflash_taps: None,
225            },
226            stage1: Cache {
227                kv: kv1,
228                tp_kv: tp_kv1,
229                recur: recur1,
230                pos,
231                max_ctx,
232                last_logits_dev: None,
233                dflash_taps: None,
234            },
235        }
236    }
237
238    fn parts(&mut self) -> (&mut Cache, &mut Cache) {
239        (&mut self.stage0, &mut self.stage1)
240    }
241}
242
243impl Drop for PrimeCacheStages<'_> {
244    fn drop(&mut self) {
245        let n = self.parent.kv.len();
246        for i in 0..n {
247            let source = if i < self.cut {
248                &mut self.stage0
249            } else {
250                &mut self.stage1
251            };
252            debug_assert!(self.parent.kv[i].is_none());
253            debug_assert!(self.parent.tp_kv[i].is_none());
254            debug_assert!(self.parent.recur[i].is_none());
255            self.parent.kv[i] = source.kv[i].take();
256            self.parent.tp_kv[i] = source.tp_kv[i].take();
257            self.parent.recur[i] = source.recur[i].take();
258        }
259        self.parent.pos = self.stage0.pos.min(self.stage1.pos);
260    }
261}
262
263/// task #18 (attn side): one sequence's pre-attention outputs (post-rope q/k, v, out-gate).
264pub(crate) struct AttnPre {
265    pub q: cudarc::driver::CudaSlice<f32>,
266    pub k: cudarc::driver::CudaSlice<f32>,
267    pub v: cudarc::driver::CudaSlice<f32>,
268    pub gate: Option<cudarc::driver::CudaSlice<f32>>,
269}
270
271/// task #18: one sequence's GDN prep outputs (the scan inputs).
272pub(crate) struct GdnPrep {
273    pub hk: usize,
274    pub q_l2: cudarc::driver::CudaSlice<f32>,
275    pub k_l2: cudarc::driver::CudaSlice<f32>,
276    pub v_g: cudarc::driver::CudaSlice<f32>,
277    pub beta: cudarc::driver::CudaSlice<f32>,
278    pub g_log: cudarc::driver::CudaSlice<f32>,
279    pub kb16: Option<cudarc::driver::CudaSlice<u8>>,
280    pub qb16: Option<cudarc::driver::CudaSlice<u8>>,
281}
282
283/// Device scratch for the burst verify stream (see `verify_stream_scratch`).
284pub(crate) struct VerifyStreamScratch {
285    pub pos_d: CudaSlice<i32>,
286    pub row_ctrs: Vec<CudaSlice<i32>>,
287}
288use crate::hybrid::{FullAttnLayer, HybridModel, LinearAttnLayer, Mixer, MoeWeights};
289
290struct MoeInputTraceWriter {
291    dir: std::path::PathBuf,
292    index: std::fs::File,
293    payloads: std::collections::HashMap<u16, (std::fs::File, u64)>,
294}
295
296static MOE_INPUT_TRACE_WRITER: std::sync::OnceLock<std::sync::Mutex<Option<MoeInputTraceWriter>>> =
297    std::sync::OnceLock::new();
298
299/// STAGE-2 GROUPED DECODE gate (MEMRA_MOE_GDEC, default ON; `=0` restores the sequential
300/// per-expert launch chain). See `moe_gdec_token`.
301fn gdec_enabled() -> bool {
302    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
303    *E.get_or_init(|| {
304        std::env::var("MEMRA_MOE_GDEC")
305            .map(|v| v != "0")
306            .unwrap_or(true)
307    })
308}
309
310/// SLAB-LOCAL RESIDENT ARM gate (lane/pp-leverb 2026-08-08, MEMRA_MOE_SLAB, default ON;
311/// `=0` restores the SLRU dispatch even when resident slabs exist). Read PER CALL, never
312/// memoized — probes A/B the two provenances in one process (the MEMRA_PRIME_PP pattern).
313/// See `moe_ffn_sequential_zq8`'s slab_local arm: the sigmoid-router archs (step35/M3/Hy3)
314/// are denied every `dev_exps` consumer (pairs/dev route softmax), so before this arm the
315/// fits-VRAM resident slabs were UPLOADED for them but never READ — the SLRU kept staging
316/// the same bytes beside a dead copy (37 GB H2D per pp4096 prime on the Step SKU, anatomy
317/// receipt). The arm reads the SAME bytes through the SAME kernels; only the pointer
318/// PROVENANCE changes (slab base + ex*stride vs SLRU slot address) — the bit-identity class
319/// `moe_ffn_dev`'s resident arm already documents against its SLRU arm.
320fn moe_slab_enabled() -> bool {
321    std::env::var("MEMRA_MOE_SLAB").as_deref() != Ok("0")
322}
323
324/// Expert-grouped dispatch remains opt-in after the local 5090 transfer gate rejected the
325/// default flip. `=0` selects the established path, while any other explicit value enables the
326/// grouped research arm for the current call.
327fn moe_grouped_enabled(_cfg: &ModelConfig, _prefill: bool) -> bool {
328    std::env::var("MEMRA_MOE_GROUPED")
329        .map(|value| value != "0")
330        .unwrap_or(false)
331}
332
333/// Deterministic in-token expert prefetch. `MEMRA_MOE_PREFETCH=1` overlaps memory-source H2D on the
334/// copy stream; selecting the opt-in worker spill backend enables the same known-next hook for disk.
335fn moe_prefetch_enabled() -> bool {
336    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
337    *E.get_or_init(|| {
338        std::env::var("MEMRA_MOE_PREFETCH").as_deref() == Ok("1")
339            || crate::spill_pread::worker_enabled()
340    })
341}
342
343/// Best-effort OS page-cache prefetch distance for mmap-backed expert ranges. Independent of the
344/// H2D copy-stream experiment so storage->RAM and RAM->HBM overlap can be measured separately.
345/// The opt-in default stays one expert to preserve the original experiment; spill rigs can widen
346/// it with `MEMRA_MOE_PAGE_PREFETCH_WINDOW` to cover NVMe latency.
347fn moe_page_prefetch_window() -> usize {
348    static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
349    *W.get_or_init(|| {
350        page_prefetch_window_from_values(
351            std::env::var("MEMRA_MOE_PAGE_PREFETCH").as_deref() == Ok("1"),
352            std::env::var("MEMRA_MOE_PAGE_PREFETCH_WINDOW")
353                .ok()
354                .as_deref(),
355        )
356    })
357}
358
359fn page_prefetch_window_from_values(enabled: bool, raw_window: Option<&str>) -> usize {
360    if !enabled {
361        return 0;
362    }
363    raw_window.and_then(|value| value.parse().ok()).unwrap_or(1)
364}
365
366/// Return only the newly exposed positions in a rolling lookahead window. Position zero seeds the
367/// full window; each later position adds one expert at the far edge. Thus widening the window does
368/// not repeatedly issue `MADV_WILLNEED` for the same range.
369fn page_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
370    if window == 0 || position >= len {
371        return len..len;
372    }
373    let (start, count) = if position == 0 {
374        (1, window)
375    } else {
376        (position.saturating_add(window), 1)
377    };
378    let start = start.min(len);
379    start..start.saturating_add(count).min(len)
380}
381
382/// Grouped worker-I/O schedule: prime the first active expert before the loop, then queue exactly
383/// one known-next expert at each iteration. Returning positions keeps expert ordering authoritative.
384fn grouped_worker_prefetch_position(order_len: usize, current: Option<usize>) -> Option<usize> {
385    let position = current.map_or(0, |position| position.saturating_add(1));
386    (position < order_len).then_some(position)
387}
388
389/// Fill the worker ring with complete experts, retaining one pinned buffer for an unexpected
390/// demand miss. Each expert has gate/up/down extents, so depth 16 admits a rolling five-expert
391/// window. Position zero primes the current expert too: its three independent reads can run in
392/// parallel instead of demand-serializing gate, up, and down before any useful GPU work exists.
393fn worker_prefetch_window() -> usize {
394    static WINDOW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
395    *WINDOW.get_or_init(|| {
396        let automatic = crate::spill_pread::configured_depth().saturating_sub(1) / 3;
397        std::env::var("MEMRA_SPILL_WORKER_EXPERT_WINDOW")
398            .ok()
399            .and_then(|value| value.parse::<usize>().ok())
400            .unwrap_or(automatic.max(1))
401    })
402}
403
404/// Return only positions newly exposed by a rolling worker-I/O window. Unlike mmap page advice,
405/// this includes the current expert when the window is seeded so all three current projections
406/// enter the CPU pool together.
407fn worker_prefetch_positions(position: usize, len: usize, window: usize) -> std::ops::Range<usize> {
408    if window == 0 || position >= len {
409        return len..len;
410    }
411    let (start, count) = if position == 0 {
412        (0, window)
413    } else {
414        (position.saturating_add(window).saturating_sub(1), 1)
415    };
416    let start = start.min(len);
417    start..start.saturating_add(count).min(len)
418}
419
420/// LAUNCH-STRUCTURE STAGE 3 gate (MEMRA_MOE_DEV, default ON; `=0` restores host routing). The
421/// zero-DtoH device-dispatch path for fully-resident layers: router top-k output stays on device,
422/// expert weight pointers come from the per-layer device table. Requires the fused router (the
423/// dev path consumes the device sel/w directly), so MEMRA_FUSED_ROUTER=0 also disables it.
424fn moe_dev_enabled() -> bool {
425    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
426    *E.get_or_init(|| {
427        std::env::var("MEMRA_MOE_DEV")
428            .map(|v| v != "0")
429            .unwrap_or(true)
430            && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0"))
431    })
432}
433
434/// Device sigmoid top-k is the default for Step-3.7 / M3 / Hy3 / GLM-DSA. `MEMRA_SIG_ROUTER=0` restores
435/// the full-logit DtoH plus `moe_route_sigmoid_host` oracle without changing expert dispatch.
436fn sigmoid_router_enabled() -> bool {
437    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
438    *E.get_or_init(|| {
439        std::env::var("MEMRA_SIG_ROUTER")
440            .map(|v| v != "0")
441            .unwrap_or(true)
442    })
443}
444
445/// MoE EXPERT dp4a gate (MEMRA_MOE_Q8, default ON; `=0` restores the Stage-A f32-dequant expert
446/// kernels). Applies when gate/up/down expert qtypes are all in the dp4a body set (IQ3_S/IQ4_XS).
447/// FP-order differs from Stage-A (int dp4a + warp tree) — argmax/run-gen/stream-identity gates
448/// arbitrate; the sequential and fused q8 paths ship as a matched pair (MEMRA_MOE_GATE contract).
449fn moe_q8_enabled() -> bool {
450    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
451    *E.get_or_init(|| {
452        std::env::var("MEMRA_MOE_Q8")
453            .map(|v| v != "0")
454            .unwrap_or(true)
455    })
456}
457
458/// gemma4 fast-arm gate: qtypes with an `expert_dot_g` dp4a body (superset used by the gelu
459/// dev arm; the qwen q8 arms keep their own battery-gated q8_expert_supported policy).
460fn expert_dp4a_supported(qt: i32) -> bool {
461    qt == crate::QT_Q4_0
462        || qt == crate::QT_IQ3_S
463        || qt == crate::QT_IQ4_XS
464        || qt == crate::QT_Q3_K
465        || qt == crate::QT_Q4_K
466        || qt == crate::QT_Q6_K
467}
468
469fn q8_expert_supported(qt: i32) -> bool {
470    // k-quant arms added 2026-07-06 (Q3_K/Q4_K/Q6_K bodies for the UD tail layers). Briefly
471    // default-excluded the same day when they appeared to break 35B real-prompt spec — the
472    // ACTUAL culprit was the MoE router's cuBLASLt n-dependence (d994271); with the router
473    // decode-exact at verify t, the k-quant arms pass the full spec battery (p1/p2/p3 + raw
474    // K=1..8) and are DEFAULT ON again (+9 tok/s: 148.9 -> 157.9). MEMRA_MOE_Q8_KQ=0 excludes.
475    static KQ: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
476    let kq = *KQ.get_or_init(|| {
477        std::env::var("MEMRA_MOE_Q8_KQ")
478            .map(|v| v != "0")
479            .unwrap_or(true)
480    });
481    // NVFP4 experts: DEFAULT ON (2026-07-17). The M3-era "decode-vs-verify MISMATCH 3.4e1"
482    // that had this excluded was the missing per-expert macro-scale fold, fixed in the
483    // dev-kernel epilogues + moe_w_scale_by_expert; the 35B ct-NVFP4 artifact now runs the
484    // q8 arm at parity with the IQ4_XS daily (174-178 tok/s, spec K=1..8 exact). M3/Hy3
485    // never reach the q8 arms regardless (sigmoid-router cfg gates on pairs/dev/gdec).
486    // MEMRA_MOE_Q8_NVFP4=0 restores the f32 arm.
487    let nvfp4_q8 = std::env::var("MEMRA_MOE_Q8_NVFP4")
488        .map(|v| v != "0")
489        .unwrap_or(true);
490    qt == crate::QT_IQ3_S
491        || qt == crate::QT_IQ4_XS
492        || (nvfp4_q8 && qt == crate::QT_NVFP4)
493        || (kq && (qt == crate::QT_Q3_K || qt == crate::QT_Q4_K || qt == crate::QT_Q6_K))
494}
495
496/// The decode-once (_dec) and IQ-MMA expert kernels dequant via IQ-specific extractors —
497/// k-quant tensors must fall to the _em dot path instead.
498fn q8_expert_dec_supported(qt: i32) -> bool {
499    qt == crate::QT_IQ3_S || qt == crate::QT_IQ4_XS || qt == crate::QT_Q4_0
500}
501
502/// Grouped-f16 door (MEMRA_MOE_F16G) per-projection admission: the qtype has a dequant-to-f16
503/// kernel in cu/moe_f16_grouped.cu AND the projection's k dimension tiles its block size.
504/// Round 49 widened coverage to q35's UD mix (gate/up IQ3_S x39 + Q3_K x1 + IQ4_XS x1; down
505/// IQ4_XS x37 + Q6_K x3 + Q4_K x1) — the round-47 IQ4_XS/Q4_0-only table admitted ~1 of 41
506/// q35 layers, which is why that cell measured FLAT.
507fn f16g_proj_ok(qt: i32, in_f: usize) -> bool {
508    match qt {
509        crate::QT_Q4_0 => in_f % 32 == 0,
510        crate::QT_IQ4_XS | crate::QT_IQ3_S | crate::QT_Q3_K | crate::QT_Q4_K | crate::QT_Q6_K => {
511            in_f % 256 == 0
512        }
513        // NVFP4 (block 64) added lane/moebatch-q35moe 2026-08-21: the ornith15 expert bank is
514        // uniform NVFP4, which passed the pairs q8 gate but missed BOTH batched doors
515        // (use_mma's dec set and this table), so 14.7k-token prefill rode the per-pair _em
516        // fallback — 88.6% of the prime wall (prime-anatomy receipt).
517        crate::QT_NVFP4 => in_f % 64 == 0,
518        _ => false,
519    }
520}
521
522/// STAGE 3 prewarm gate (MEMRA_MOE_PREWARM, default ON; `=0` leaves residency organic). One-shot
523/// per layer: force-admit every block while FREE slots cover the whole layer (never evicts).
524fn moe_prewarm_enabled() -> bool {
525    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
526    *E.get_or_init(|| {
527        std::env::var("MEMRA_MOE_PREWARM")
528            .map(|v| v != "0")
529            .unwrap_or(true)
530    })
531}
532
533/// During a discarded fixed-residency profile, admit CPU-routed misses after their current-token
534/// CPU result is complete. The current result and numeric path are unchanged; later warmup tokens
535/// can then vote for and exercise those experts on GPU before the cache is frozen.
536fn cpu_expert_profile_admit_enabled() -> bool {
537    static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
538    *E.get_or_init(|| std::env::var("MEMRA_CPU_EXPERT_FREEZE_PROFILE_ADMIT").as_deref() == Ok("1"))
539}
540
541/// Minimum prompt length for the BATCHED cache prime (`prime_cache`). Below this the tokenwise
542/// decode loop wins anyway (the batched path's GEMM dispatch needs m>=16, and the stateful conv
543/// kernel needs T >= d_conv-1). Callers: generate / generate_spec.
544pub const PRIME_MIN_T: usize = 16;
545
546/// MEMRA_STEP_GEMM_PRIME_SUFFIX: does a CONTINUATION prime (`cache.pos > 0` — a rewound
547/// session's suffix, or a prompt remainder split across scheduler ticks) ride the batched
548/// GEMM prime, like a fresh prompt does?
549///
550/// DEFAULT ON since 2026-08-29, by decision, under the flip bar the OFF-era FLAGS row
551/// wrote down (never byte identity — a prime-decomposition m-dependence that EVERY
552/// measured prime path shares, walk included, bars that gate for all of them):
553///  1. vendor-default sampled rows: the blind, rubric-pre-registered 8-turn quality A/B
554///     (research/step37-sampled-quality-20260828, 72/72 valid rows, engagement receipts
555///     per row) — WARM-GEMM sits inside COLD's own self-spread at t4 and t8 (t8 carried
556///     at n=16; the round-1 walk-over-gemm signal collapsed at p~0.91).
557///  2. the 8-turn cache-on twin: warm TTFT 0.58 s (door) vs 7.15 s (walk) on the real
558///     warm serving shape, zero faults.
559///  3. the batched prime's own standard: acceptance 0.80-0.86 across all arms with the
560///     door arm highest at t8; interleaved arms; zero ILLEGAL/#87/panics in 19 boots.
561/// Precondition shipped first: the SWA-ring checkpoint restore fix (c9a617ca99) — real
562/// session reuse crosses the grow path before any door question matters.
563/// Why it is worth it, measured: the walk continuation costs 5.5978 ms/suffix-token
564/// against this path's 0.99 ms/token (five-point sweep, R^2 0.9976), a 7.97x suffix
565/// slope collapse.
566///
567/// The `seq_end` fix beneath is NOT gated on this door — it is unconditional, because
568/// the chunk-local `seq_end` it replaced is wrong for a fresh prompt of 4096+k tokens
569/// (k in [PRIME_MIN_T, 512)) with no continuation anywhere in sight.
570///
571/// `=0` is the kill switch (continuations back on the walk, fresh primes keep the fast
572/// path); `=1` forces; `MEMRA_STEP_GEMM_PRIME=0` remains the whole-path seam. Read per
573/// call, not cached — probes flip it in process.
574fn step_gemm_prime_suffix_on() -> bool {
575    std::env::var("MEMRA_STEP_GEMM_PRIME_SUFFIX").as_deref() != Ok("0")
576}
577
578/// Widest tick the MoE DEV per-token program serves (lane/orndecode-20260822). PRIME_MIN_T
579/// doubled as the dev-arm's upper bound on the assumption that t==16 only ever meant real
580/// prefill; the exact-16 decode tier broke that assumption — at B=16 the MoE stage crossed
581/// onto the t>=MMA_T grouped/kq GEMM program (m_e ~1.6 rows/expert: 52.6% of the tick at
582/// ~104 us/launch) or the `_em` per-pair fallback (67.7 us), both catastrophically slower
583/// than the dev q8 kernels that serve B<=8 (8.8 us gate_up covering a token's whole expert
584/// set). Decode widths 2..=16 now ride dev; the grouped/pairs prefill programs start at 17.
585/// gate2/gate3 byte batteries at B=12/16 are the qualification (bit-checked vs isolated).
586const MOE_DEV_MAX_T: usize = 16;
587const PRIME_PIPE_MICROBATCHES: usize = 8;
588const PRIME_PIPE_MIN_CHUNK: usize = 128;
589const PRIME_PIPE_EDGE_MIN_CHUNK: usize = 64;
590const PRIME_PIPE_LINEAR_WORK: usize = 8;
591
592fn prime_pp2_auto_geometry(n_layers: usize) -> bool {
593    crate::pp::prime_pp_on()
594        && !crate::pp::pp2_streams_off()
595        && crate::pp::pp_cuts(n_layers).is_some_and(|cuts| cuts.len() == 3)
596}
597
598/// Effective internal prime chunk. An explicit MEMRA_PRIME_CHUNK is authoritative.
599/// Naked PP-2 primes use the measured pipeline geometry: up to eight microchunks, never
600/// below 128 tokens, while the legacy 4096-token cap remains the long-context bound.
601pub fn prime_chunk_tokens(t: usize, n_layers: usize) -> usize {
602    if let Ok(value) = std::env::var("MEMRA_PRIME_CHUNK") {
603        let parsed = value
604            .parse::<usize>()
605            .unwrap_or(crate::cache::PRIME_CHUNK_MAX_TOKENS);
606        return if crate::cache::swa_ring_on() {
607            if parsed == 0 {
608                crate::cache::PRIME_CHUNK_MAX_TOKENS
609            } else {
610                parsed.min(crate::cache::PRIME_CHUNK_MAX_TOKENS)
611            }
612        } else {
613            parsed
614        };
615    }
616    let chunk = crate::cache::PRIME_CHUNK_MAX_TOKENS;
617    if prime_pp2_auto_geometry(n_layers) && t >= 2 * PRIME_PIPE_MIN_CHUNK {
618        chunk.min(
619            t.div_ceil(PRIME_PIPE_MICROBATCHES)
620                .max(PRIME_PIPE_MIN_CHUNK),
621        )
622    } else {
623        chunk
624    }
625}
626
627fn fixed_prime_chunk_ranges(t: usize, chunk: usize) -> Vec<(usize, usize)> {
628    fixed_prime_chunk_ranges_for_ring(t, chunk, crate::cache::swa_ring_on())
629}
630
631fn fixed_prime_chunk_ranges_for_ring(t: usize, chunk: usize, ring_on: bool) -> Vec<(usize, usize)> {
632    if chunk == 0 || t <= chunk {
633        return vec![(0, t)];
634    }
635    let mut ranges = Vec::with_capacity(t.div_ceil(chunk));
636    let mut start = 0usize;
637    while start < t {
638        let mut end = (start + chunk).min(t);
639        if t - end > 0 && t - end < PRIME_MIN_T {
640            if ring_on {
641                let shifted = t - PRIME_MIN_T;
642                end = if shifted > start { shifted } else { t };
643            } else {
644                end = t;
645            }
646        }
647        ranges.push((start, end));
648        start = end;
649    }
650    ranges
651}
652
653fn prime_chunk_work(prefix: usize, total: usize) -> u128 {
654    let prefix = prefix as u128;
655    prefix * (prefix + (PRIME_PIPE_LINEAR_WORK as u128) * (total as u128))
656}
657
658fn dynamic_prime_chunk_ranges(
659    t: usize,
660    fixed_chunk: usize,
661    fixed: &[(usize, usize)],
662) -> Vec<(usize, usize)> {
663    let n = fixed.len();
664    if n < 3 {
665        return fixed.to_vec();
666    }
667
668    let max_first = t - (n - 1) * PRIME_MIN_T;
669    let first = fixed_chunk
670        .div_ceil(2)
671        .max(PRIME_PIPE_EDGE_MIN_CHUNK)
672        .min(max_first);
673    let mut ranges = Vec::with_capacity(n);
674    ranges.push((0, first));
675
676    let first_work = prime_chunk_work(first, t);
677    let work_span = prime_chunk_work(t, t) - first_work;
678    let denominator = (n - 1) as u128;
679    let mut previous = first;
680    for boundary in 1..n - 1 {
681        let target = first_work * denominator + work_span * (boundary as u128);
682        let remaining = n - 1 - boundary;
683        let mut low = previous + PRIME_MIN_T;
684        let mut high = t - remaining * PRIME_MIN_T;
685        while low < high {
686            let mid = low + (high - low) / 2;
687            if prime_chunk_work(mid, t) * denominator >= target {
688                high = mid;
689            } else {
690                low = mid + 1;
691            }
692        }
693        ranges.push((previous, low));
694        previous = low;
695    }
696    ranges.push((previous, t));
697    ranges
698}
699
700/// Internal prime ranges. The naked PP-2 pipeline defaults to a short-fill,
701/// equal-modeled-time schedule; MEMRA_PRIME_CHUNK_SCHED=fixed restores the measured
702/// equal-token ranges. An explicit MEMRA_PRIME_CHUNK always retains fixed semantics.
703///
704/// `gdn_grid`: the model runs the chunked GDN WY scan (`HybridModel::gdn_prime_grid_on`) —
705/// AUTO-scheduled internal boundaries are then snapped down to the WY-chunk grid
706/// (`align_prime_ranges_to_gdn`; the spec-longctx grid law, extended from serve splits to
707/// the PP prime microchunks). Explicit MEMRA_PRIME_CHUNK keeps its operator-authoritative
708/// (fixed, unaligned) semantics — the FLAGS caveat documents that identity contract.
709pub fn prime_chunk_ranges(t: usize, n_layers: usize, gdn_grid: bool) -> Vec<(usize, usize)> {
710    let explicit_chunk = std::env::var_os("MEMRA_PRIME_CHUNK").is_some();
711    let chunk = prime_chunk_tokens(t, n_layers);
712    let fixed = fixed_prime_chunk_ranges(t, chunk);
713    let dynamic = match std::env::var("MEMRA_PRIME_CHUNK_SCHED") {
714        Ok(value) => value == "dynamic",
715        Err(_) => true,
716    };
717    if explicit_chunk {
718        return fixed;
719    }
720    let ranges = if !dynamic || !prime_pp2_auto_geometry(n_layers) {
721        fixed
722    } else {
723        dynamic_prime_chunk_ranges(t, chunk, &fixed)
724    };
725    // MEMRA_PRIME_GRID_ALIGN=0 is the shared rollback seam of the grid law (same env the
726    // worker's serve-boundary alignment honors, read per call so gates can flip it
727    // in-process): the legacy off-grid auto schedule — the toothed cell's broken arm.
728    if gdn_grid && std::env::var("MEMRA_PRIME_GRID_ALIGN").as_deref() != Ok("0") {
729        align_prime_ranges_to_gdn(&ranges, t, Engine::gdn_chunk_size())
730    } else {
731        ranges
732    }
733}
734
735/// Snap AUTO prime-range internal boundaries DOWN to the GDN WY-chunk grid (lane/
736/// hermes-perf-fixes, 2026-08-23 — the missing helper the PP-auto-ranges finding names).
737///
738/// THE LAW THIS EXTENDS (measured, research/multiturn-cache-20260821/
739/// LONGCTX-EXACTNESS-20260821.md; the serve-split half already ships as the worker's
740/// `grid_align_boundary`): under the chunked WY scan a prompt primed as two calls split at
741/// L is bit-identical to the monolithic prime iff `L % gdn_chunk_size() == 0` — an off-grid
742/// call start shifts the fold grid and materializes recurrent state at a point the
743/// monolithic program never computes. The prime loop walks these ranges as separate
744/// `prime_layers` calls, so INTERNAL microchunk boundaries are the same seam: the PP-2
745/// auto geometry (`t.div_ceil(8).max(128)` fills, and every dynamic short-fill boundary)
746/// lands off the 32-token grid for most prompt lengths, which is exactly the
747/// chunk-value bit-identity the GDN lane falsified (FLAGS PRIME_CHUNK/SCHED caveat).
748///
749/// Boundaries only move DOWN (earlier is always semantically safe — same argument as the
750/// worker's alignment); a boundary that collapses onto its predecessor is dropped (ranges
751/// merge). The final range always ends at `t`. Aligning down only GROWS the tail
752/// remainder, so the fixed-schedule tail-merge rule is never re-violated. Cost bound: at
753/// most `c-1` tokens shift per boundary.
754pub fn align_prime_ranges_to_gdn(
755    ranges: &[(usize, usize)],
756    t: usize,
757    c: usize,
758) -> Vec<(usize, usize)> {
759    if c == 0 || ranges.len() < 2 {
760        return ranges.to_vec();
761    }
762    let mut out: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
763    let mut start = 0usize;
764    for (i, &(_, end)) in ranges.iter().enumerate() {
765        let e = if i + 1 == ranges.len() {
766            t
767        } else {
768            end / c * c
769        };
770        if e > start {
771            out.push((start, e));
772            start = e;
773        } // else: boundary collapsed onto its predecessor — merge into the next range
774    }
775    debug_assert_eq!(out.last().map(|&(_, e)| e), Some(t));
776    out
777}
778
779struct HeadSplit {
780    pin: u64,
781    w1: CudaSlice<u8>,
782    hn1: CudaSlice<f32>,
783    y1: CudaSlice<f32>,
784    logits_e: CudaSlice<f32>,
785    ev_hn: cudarc::driver::CudaEvent,
786    ev_done: cudarc::driver::CudaEvent,
787    raw_hn1: u64,
788    raw_y1: u64,
789    raw_logits_hi: u64,
790    /// SAMPLED-TAIL scratch (perturbed row + the filter's threshold/z/max slots + the row
791    /// index). Allocating these per token cost more than the split head saved: the first
792    /// sampled-split measurement came in at 78.25 tok/s against 78.96 for the unsplit head,
793    /// which is five allocations per token, not arithmetic.
794    samp: Option<SampScratch>,
795}
796
797struct SampScratch {
798    pb: CudaSlice<f32>,
799    th: CudaSlice<f32>,
800    z: CudaSlice<f32>,
801    mx: CudaSlice<f32>,
802    rows: CudaSlice<i32>,
803}
804/// HEAD-SPLIT workspace (host + device twins share it).
805static HEAD_SPLIT_WS: std::sync::Mutex<Option<HeadSplit>> = std::sync::Mutex::new(None);
806
807/// DEV1-LOCAL ROUTER replicas (MEMRA_DEV1_ROUTER): per-layer (gate_inp_f32, exp_probs_b,
808/// active_experts) on rank1 + a shared logits scratch. Deterministic kernels on identical
809/// input bits — rank1's local selection is bit-equal to the root's.
810#[allow(clippy::type_complexity)]
811static DEV1_ROUTER_REPS: std::sync::Mutex<
812    Option<(
813        std::collections::HashMap<u16, (CudaSlice<f32>, CudaSlice<f32>, CudaSlice<u8>)>,
814        Option<CudaSlice<f32>>,
815    )>,
816> = std::sync::Mutex::new(None);
817
818/// SHEXP-ON-DEV1 workspace (MEMRA_SHEXP_DEV1): replica weights + scratch on rank1, the
819/// down row lands ROOT-resident over P2P (single store pass), and apply adds it on e
820/// behind ev_done. (pins, wg1, wu1, wd1, act1, sh_root, ev_z, ev_done).
821#[allow(clippy::type_complexity)]
822static SHEXP_D1_REPS: std::sync::Mutex<
823    Option<std::collections::HashMap<u16, (CudaSlice<u8>, CudaSlice<u8>, CudaSlice<u8>)>>,
824> = std::sync::Mutex::new(None);
825#[allow(clippy::type_complexity)]
826static SHEXP_D1_WS: std::sync::Mutex<
827    Option<(
828        (usize, usize),
829        CudaSlice<f32>,
830        CudaSlice<f32>,
831        CudaSlice<f32>,
832        cudarc::driver::CudaEvent,
833        cudarc::driver::CudaEvent,
834    )>,
835> = std::sync::Mutex::new(None);
836
837/// SHEXP OVERLAP workspace (issue writes, apply reads): (device, n_embd, n_ff_sh, act, sh).
838static SHEXP_OV_WS: std::sync::Mutex<
839    Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>,
840> = std::sync::Mutex::new(None);
841
842impl HybridModel {
843    /// Does this model's prime schedule live under the GDN WY-chunk grid law? True when the
844    /// trunk has GDN (linear-attention) layers AND the chunked scan is on — the regime where
845    /// an off-grid prime-call boundary shifts the WY fold grid (see
846    /// `align_prime_ranges_to_gdn`). Attention-only models and the sequential scan
847    /// (`MEMRA_GDN_CHUNKED=0`) are split-invariant, so the grid is a no-op contract there.
848    pub fn gdn_prime_grid_on(&self) -> bool {
849        Engine::gdn_chunked_enabled()
850            && self
851                .layers
852                .iter()
853                .any(|l| matches!(l.mixer, crate::hybrid::Mixer::Linear(_)))
854    }
855
856    /// Can the step TP runtime run the DEVICE-RESIDENT activation path from this serving
857    /// engine? Native P2P (peer copies replace the host staging) AND a shared root context
858    /// (the device buffers must be addressable on both sides — the TP registry builds its
859    /// own Engine per rank, so this is a real seam, not a formality).
860    fn step35_tp_device_resident(e: &Engine, tp: &crate::hybrid::StepTpQkv) -> bool {
861        tp.runtime.native_p2p() && tp.runtime.root_shares_ctx(e)
862    }
863
864    fn step35_tp_qkv(
865        &self,
866        e: &Engine,
867        fa: &FullAttnLayer,
868        h: &CudaSlice<f32>,
869        t: usize,
870    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
871        let Some(tp) = fa.step_tp_qkv.as_ref() else {
872            return Ok(None);
873        };
874        let values = active_matrix_values(
875            h.len(),
876            t,
877            self.cfg.n_embd as usize,
878            "Step TP QKV activation",
879        )?;
880        // DEVICE-RESIDENT NATIVE PATH (lane/hermes-perf-fixes, 2026-08-23 — the host-bounce
881        // finding): the native-P2P arm used to dtoh the FULL hidden state per layer, run
882        // from a host copy, gather q/k/v to host vectors, and htod all three back — a host
883        // round-trip on every execute that the peer transport exists to remove. The
884        // device twins are byte-identical by construction (the same bytes travel dtod
885        // instead of dtoh+htod; kernels, peer copies, and gather order are shared code).
886        // The host arm below remains the transport for !native_p2p (host staging IS that
887        // transport) and for a root context this engine cannot address.
888        if Self::step35_tp_device_resident(e, tp) {
889            // Producer fence: h was written on THIS engine's stream; the TP ranks read it
890            // on theirs (same context, different streams).
891            e.stream().synchronize()?;
892            let q = tp
893                .runtime
894                .bf16_column_parallel_resident_native_device(&tp.q, h, t)?;
895            let k = tp
896                .runtime
897                .bf16_column_parallel_resident_native_device(&tp.k, h, t)?;
898            let v = tp
899                .runtime
900                .bf16_column_parallel_resident_native_device(&tp.v, h, t)?;
901            Self::step35_tp_log_once(tp, "qkv", "device-resident");
902            return Ok(Some(vec![q, k, v]));
903        }
904        let host = e.dtoh_view(&h.slice(0..values))?;
905        let q = if tp.runtime.native_p2p() {
906            tp.runtime
907                .bf16_column_parallel_resident_native(&tp.q, &host, t)?
908        } else {
909            tp.runtime
910                .bf16_column_parallel_resident(&tp.q, &host, t)?
911                .gathered
912        };
913        let k = if tp.runtime.native_p2p() {
914            tp.runtime
915                .bf16_column_parallel_resident_native(&tp.k, &host, t)?
916        } else {
917            tp.runtime
918                .bf16_column_parallel_resident(&tp.k, &host, t)?
919                .gathered
920        };
921        let v = if tp.runtime.native_p2p() {
922            tp.runtime
923                .bf16_column_parallel_resident_native(&tp.v, &host, t)?
924        } else {
925            tp.runtime
926                .bf16_column_parallel_resident(&tp.v, &host, t)?
927                .gathered
928        };
929        Self::step35_tp_log_once(tp, "qkv", "host-canonical");
930        Ok(Some(vec![e.htod(&q)?, e.htod(&k)?, e.htod(&v)?]))
931    }
932
933    /// One transport banner per (projection, transport) — the old per-call eprintln fired
934    /// on EVERY layer of EVERY step, itself a decode-rate cost on the path this lane is
935    /// unbouncing (the sibling grouped-EP path already learned this).
936    fn step35_tp_log_once(tp: &crate::hybrid::StepTpQkv, proj: &str, activation: &'static str) {
937        use std::sync::atomic::{AtomicBool, Ordering};
938        static LOGGED: [AtomicBool; 4] = [
939            AtomicBool::new(false),
940            AtomicBool::new(false),
941            AtomicBool::new(false),
942            AtomicBool::new(false),
943        ];
944        let idx = 2 * usize::from(proj == "o") + usize::from(activation == "device-resident");
945        if LOGGED[idx].swap(true, Ordering::Relaxed) {
946            return;
947        }
948        eprintln!(
949            "[step-tp-{proj}] execute layer={} devices={:?} projections={proj} \
950             tensor_parallel=true attention_local=true kv_local=true transport={} \
951             native_p2p={} bulk_p2p={} activation={activation} \
952             output={} performance_claim=false (logged once per transport)",
953            tp.layer,
954            tp.devices,
955            tp.runtime.transport_label(),
956            tp.runtime.native_p2p(),
957            tp.runtime.bulk_p2p(),
958            if activation == "device-resident" {
959                "root-resident"
960            } else {
961                "root-readback"
962            },
963        );
964    }
965
966    fn step35_tp_o(
967        &self,
968        e: &Engine,
969        fa: &FullAttnLayer,
970        activation: &CudaSlice<f32>,
971        tokens: usize,
972    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
973        let Some(tp) = fa.step_tp_qkv.as_ref() else {
974            return Ok(None);
975        };
976        // DEVICE-RESIDENT NATIVE PATH — the O-projection half of the same finding: no DtoH
977        // of the attention output, no host O staging, root-resident reduction consumed in
978        // place (byte-identical shared core: `step_bf16_row_native_reduce_from_root`).
979        if Self::step35_tp_device_resident(e, tp) {
980            e.stream().synchronize()?; // producer fence, as the QKV half
981            let output = tp
982                .runtime
983                .step_bf16_row_parallel_resident_native_device(&tp.o, activation, tokens)?;
984            Self::step35_tp_log_once(tp, "o", "device-resident");
985            return Ok(Some(output));
986        }
987        let host = e.dtoh(activation)?;
988        let output = if tp.runtime.native_p2p() {
989            tp.runtime
990                .step_bf16_row_parallel_resident_native(&tp.o, &host, tokens)?
991        } else {
992            tp.runtime
993                .step_bf16_row_parallel_resident(&tp.o, &host, tokens)?
994        };
995        Self::step35_tp_log_once(tp, "o", "host-canonical");
996        Ok(Some(e.htod(&output)?))
997    }
998
999    fn step35_o(
1000        &self,
1001        e: &Engine,
1002        fa: &FullAttnLayer,
1003        activation: &CudaSlice<f32>,
1004        tokens: usize,
1005    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1006        match self.step35_tp_o(e, fa, activation, tokens)? {
1007            Some(output) => Ok(output),
1008            None => e.matmul(&fa.wo, activation, tokens),
1009        }
1010    }
1011
1012    /// CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance): `MEMRA_PRIME_TRACE=<path>`
1013    /// appends one JSONL row per (chunk, layer) with a hash of that layer's last-row
1014    /// post-residual hidden. Diagnostic only — never on in a measured or gated run
1015    /// (it forces a dtoh + host hash per layer).
1016    fn prime_trace_path() -> Option<&'static str> {
1017        static P: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1018        P.get_or_init(|| std::env::var("MEMRA_PRIME_TRACE").ok())
1019            .as_deref()
1020    }
1021
1022    /// PRIME ANATOMY (diagnostic): `MEMRA_PRIME_ANATOMY=1` synchronizes the stream around
1023    /// each prime_layers stage and accumulates wall time per stage class, printed after
1024    /// every prime_layers call (cumulative across chunks/reps). The per-stage syncs
1025    /// serialize launch/execute overlap, so the summed total exceeds the naked prime wall —
1026    /// attribution ratios only, never a measured default run. Non-seg serial arm only.
1027    fn prime_anatomy_on() -> bool {
1028        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1029        *E.get_or_init(|| std::env::var("MEMRA_PRIME_ANATOMY").as_deref() == Ok("1"))
1030    }
1031
1032    fn prime_anatomy_slots() -> &'static [std::sync::atomic::AtomicU64; 5] {
1033        static S: [std::sync::atomic::AtomicU64; 5] = [
1034            std::sync::atomic::AtomicU64::new(0), // 0 mixer full-attn
1035            std::sync::atomic::AtomicU64::new(0), // 1 mixer linear-attn (GDN)
1036            std::sync::atomic::AtomicU64::new(0), // 2 ffn MoE (router + experts + shexp)
1037            std::sync::atomic::AtomicU64::new(0), // 3 ffn dense
1038            std::sync::atomic::AtomicU64::new(0), // 4 norms/adds/glue
1039        ];
1040        &S
1041    }
1042
1043    /// Prefill forward over `tokens`; returns logits [T, n_vocab] (host f32).
1044    pub fn forward(
1045        &self,
1046        e: &Engine,
1047        tokens: &[u32],
1048    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1049        if self.is_gemma4_e4b() {
1050            return self.gemma4_e4b_forward(e, tokens, false);
1051        }
1052        if self.uses_gemma_program() {
1053            return self.gemma4_forward(e, tokens, false);
1054        }
1055        let cfg = &self.cfg;
1056        let n_embd = cfg.n_embd as usize;
1057        let t = tokens.len();
1058        let eps = cfg.rms_eps;
1059        let pos: Vec<i32> = (0..t as i32).collect();
1060        let pos_d = e.htod_i32(&pos)?;
1061
1062        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1063
1064        for (il, layer) in self.layers.iter().enumerate() {
1065            // attn_norm
1066            let mut h = e.uninit(t * n_embd)?;
1067            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1068
1069            let mixed = match &layer.mixer {
1070                Mixer::Full(fa) => self.full_attn(e, fa, &h, &pos_d, t, il)?,
1071                Mixer::Linear(la) => self.linear_attn(e, la, &h, t)?,
1072                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1073            };
1074
1075            // residual 1
1076            let mut x1 = e.uninit(t * n_embd)?;
1077            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1078
1079            // pre-FFN norm (post_attention_norm), FFN (Dense or MoE), residual 2
1080            let mut z = e.uninit(t * n_embd)?;
1081            e.rms_norm(
1082                &x1,
1083                layer.post_attn_norm.float_data(),
1084                &mut z,
1085                n_embd,
1086                t,
1087                eps,
1088            )?;
1089            let ffn_out = match &layer.ffn {
1090                crate::hybrid::Ffn::Dense {
1091                    ffn_gate,
1092                    ffn_up,
1093                    ffn_down,
1094                } => {
1095                    let n_ff = ffn_gate.out_features();
1096                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1097                    let up = g2.pop().unwrap();
1098                    let gate = g2.pop().unwrap();
1099                    let mut act = e.uninit(t * n_ff)?;
1100                    // A DENSE FFN reads the SHEXP clamp array: upstream's one `build_ffn` serves
1101                    // both the dense MLP and the shared expert, and its limit is
1102                    // `swiglu_clamp_shexp[il]` (llama-graph.cpp:1751). step35's leading dense
1103                    // blocks 0-2 therefore key off clamp_shexp, not clamp_exp.
1104                    Self::ffn_act_lim(
1105                        e,
1106                        &self.cfg,
1107                        &gate,
1108                        &up,
1109                        1.0,
1110                        1.0,
1111                        self.cfg.clamp_shexp_at(il as u32),
1112                        &mut act,
1113                        t * n_ff,
1114                    )?;
1115                    e.matmul(ffn_down, &act, t)?
1116                }
1117                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
1118            };
1119            let mut x2 = e.uninit(t * n_embd)?;
1120            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1121            x = x2;
1122        }
1123
1124        let mut hn = e.uninit(t * n_embd)?;
1125        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1126        let logits = e.matmul(&self.output, &hn, t)?;
1127        Ok(e.dtoh(&logits)?)
1128    }
1129
1130    /// Prefill that returns ONLY the last token's logits — the common case (greedy/sample needs
1131    /// just the final position to start decode). Runs the trunk over all T, then the lm_head
1132    /// (output.weight, the largest matrix — 248320 rows) on the LAST hidden row ONLY, not all T.
1133    /// On a 512-token prompt this turns a [512,248320] GEMM into [1,248320] — the dominant prefill
1134    /// cost (nsys: ~99ms when done for all T). Bit-identical last-row logits to forward()[last].
1135    pub fn forward_last(
1136        &self,
1137        e: &Engine,
1138        tokens: &[u32],
1139    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1140        if self.uses_gemma_program() {
1141            return self.gemma4_forward(e, tokens, true);
1142        }
1143        let cfg = &self.cfg;
1144        let n_embd = cfg.n_embd as usize;
1145        let t = tokens.len();
1146        let eps = cfg.rms_eps;
1147        let pos: Vec<i32> = (0..t as i32).collect();
1148        let pos_d = e.htod_i32(&pos)?;
1149
1150        let mut x = self.embed(e, tokens)?; // [T, n_embd]
1151        // MEMRA_LAYER_PROBE=1: synchronize + print after every stage — bisects an in-graph
1152        // ILLEGAL_ADDRESS to (layer, stage) at ~1 line of output per layer (M3 bring-up tool).
1153        let probe = std::env::var("MEMRA_LAYER_PROBE").is_ok();
1154        let anat = Self::prime_anatomy_on();
1155        let mut anat_last = if anat {
1156            e.stream().synchronize()?;
1157            Some(std::time::Instant::now())
1158        } else {
1159            None
1160        };
1161        macro_rules! anat_mark {
1162            ($slot:expr) => {
1163                if let Some(ts) = anat_last.as_mut() {
1164                    e.stream().synchronize()?;
1165                    Self::prime_anatomy_slots()[$slot].fetch_add(
1166                        ts.elapsed().as_nanos() as u64,
1167                        std::sync::atomic::Ordering::Relaxed,
1168                    );
1169                    *ts = std::time::Instant::now();
1170                }
1171            };
1172        }
1173        for (il, layer) in self.layers.iter().enumerate() {
1174            let mut h = e.uninit(t * n_embd)?;
1175            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
1176            if probe {
1177                e.stream().synchronize()?;
1178                eprintln!("[probe] L{il} norm ok");
1179            }
1180            anat_mark!(4);
1181            let mixed = match &layer.mixer {
1182                Mixer::Full(fa) => {
1183                    let y = self.full_attn(e, fa, &h, &pos_d, t, il)?;
1184                    anat_mark!(0);
1185                    y
1186                }
1187                Mixer::Linear(la) => {
1188                    let y = self.linear_attn(e, la, &h, t)?;
1189                    anat_mark!(1);
1190                    y
1191                }
1192                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
1193            };
1194            if probe {
1195                e.stream().synchronize()?;
1196                eprintln!("[probe] L{il} mixer ok");
1197            }
1198            let mut x1 = e.uninit(t * n_embd)?;
1199            e.add(&x, &mixed, &mut x1, t * n_embd)?;
1200            let mut z = e.uninit(t * n_embd)?;
1201            e.rms_norm(
1202                &x1,
1203                layer.post_attn_norm.float_data(),
1204                &mut z,
1205                n_embd,
1206                t,
1207                eps,
1208            )?;
1209            anat_mark!(4);
1210            let ffn_out = match &layer.ffn {
1211                crate::hybrid::Ffn::Dense {
1212                    ffn_gate,
1213                    ffn_up,
1214                    ffn_down,
1215                } => {
1216                    let n_ff = ffn_gate.out_features();
1217                    let mut g2 = e.matmul_group(&[ffn_gate, ffn_up], &z, t)?;
1218                    let up = g2.pop().unwrap();
1219                    let gate = g2.pop().unwrap();
1220                    let mut act = e.uninit(t * n_ff)?;
1221                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
1222                    Self::ffn_act_lim(
1223                        e,
1224                        &self.cfg,
1225                        &gate,
1226                        &up,
1227                        1.0,
1228                        1.0,
1229                        self.cfg.clamp_shexp_at(il as u32),
1230                        &mut act,
1231                        t * n_ff,
1232                    )?;
1233                    let y = e.matmul(ffn_down, &act, t)?;
1234                    anat_mark!(3);
1235                    y
1236                }
1237                crate::hybrid::Ffn::Moe(m) => {
1238                    let y = self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?;
1239                    anat_mark!(2);
1240                    y
1241                }
1242            };
1243            if probe {
1244                e.stream().synchronize()?;
1245                eprintln!("[probe] L{il} ffn ok");
1246            }
1247            let mut x2 = e.uninit(t * n_embd)?;
1248            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
1249            x = x2;
1250        }
1251        if anat {
1252            let s = Self::prime_anatomy_slots();
1253            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
1254            eprintln!(
1255                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
1256                 dense={:.1} norms_adds={:.1} (t={t}, forward_last)",
1257                ms(0),
1258                ms(1),
1259                ms(2),
1260                ms(3),
1261                ms(4)
1262            );
1263        }
1264        // norm over all T, then slice the LAST row and run lm_head on that single row.
1265        let mut hn = e.uninit(t * n_embd)?;
1266        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
1267        let last = e.view(&hn, t * n_embd); // [T, n_embd]
1268        let last_row = last.slice((t - 1) * n_embd..t * n_embd); // [1, n_embd]
1269        let mut hlast = e.uninit(n_embd)?;
1270        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
1271        let logits = e.matmul(&self.output, &hlast, 1)?; // [1, n_vocab] — lm_head on ONE row
1272        Ok(e.dtoh(&logits)?)
1273    }
1274
1275    /// BATCHED PROMPT PRIME (the measured #1 e2e gap, e2e-image-1): `forward_last`'s batched
1276    /// prefill body EXTENDED to leave a DECODE-READY cache behind — vs the tokenwise prime's
1277    /// ~102/38 tok/s (9B/27B) decode_step loop, this runs the whole prompt at prefill throughput.
1278    ///   (a) full-attn layers append their T post-RoPE K/V rows into `cache.kv[il]` via the SAME
1279    ///       per-row quantize kernel as the decode append (bit-identical cache bytes per row);
1280    ///   (b) linear layers run STATEFULLY from the cache's current recurrent state (zero at a
1281    ///       fresh prime): carried-ring conv (ssm_conv1d_tm_state) + ONE gdn_scan(state_in,
1282    ///       state_out) whose internal sequential t-loop equals T chained T=1 steps — but with
1283    ///       the NORMAL prefill matmul dispatch (GEMM at m>=16), NOT the decode-exact MMVQ the
1284    ///       spec verify uses (prime is a prefill-regime pass; the run-gen prefill==decode
1285    ///       argmax gate is the accuracy authority, exactly as for forward_last);
1286    ///   (c) `cache.pos`/KV len/len_d advance by T.
1287    /// Returns (last-row logits host, h_seed = last-row PRE-output_norm hidden [n_embd],
1288    /// hiddens = the full pre-output_norm hidden stack [T, n_embd] — generate_spec's prompt_h).
1289    /// FRESH-PROMPT ONLY (cache.pos == 0): the fa_prefill tiles attend within `tokens` alone.
1290    /// forward_last itself stays untouched (kernel-check / run-gen gate on it).
1291    ///
1292    /// `queued_after` (lane/tick-seg, 2026-08-07): the number of prompt tokens of the SAME
1293    /// REQUEST that the caller will prime in LATER calls — 0 when this call is the whole
1294    /// request (every single-shot caller). Serve splits a long prompt across SEVERAL
1295    /// prime_cache calls (one per scheduler tick, plus the prefix-cache LCP split), and the
1296    /// request's absolute end position `seq_end = cache.pos + t + queued_after` steers step35's
1297    /// SWA prefill arm — computing it per CALL made the arm a function of the tick budget
1298    /// (budgets 512/256/64 DIFFER 1.813e0 vs monolithic, greedy diverging at step 6; dark lanes
1299    /// default to 256 AND cap by live SLO headroom, so identical judge requests primed
1300    /// differently under load — research/tick-seg-20260807, receipt in
1301    /// research/step35-chunkfix-20260807 §9). The parameter is what prime_cache structurally
1302    /// lacked: it cannot know from `tokens` and `cache` alone whether more of the request is
1303    /// coming. A SESSION CONTINUATION (a NEW user turn primed onto a live cache) is a NEW
1304    /// request — its arithmetic is keyed to its own extent, so those callers pass 0; only a
1305    /// caller that SPLITS one request across calls passes the remainder.
1306    pub fn prime_cache(
1307        &self,
1308        e: &Engine,
1309        tokens: &[u32],
1310        cache: &mut Cache,
1311        queued_after: usize,
1312    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1313        self.prime_cache_overlaid(e, tokens, cache, queued_after, None)
1314    }
1315
1316    /// `prime_cache` with a vision embedding overlay (lane/vision): image merger outputs
1317    /// replace the `<|image_pad|>` token embeddings at prompt-relative positions before the
1318    /// trunk walk — the mixed-embedding prime. Text-only callers use `prime_cache` (overlay
1319    /// None, byte-identical path). v1 scope: the serial chunk walk only — PP prime arms and
1320    /// gemma4 refuse loudly (the vision serving box is single-GPU).
1321    pub fn prime_cache_overlaid(
1322        &self,
1323        e: &Engine,
1324        tokens: &[u32],
1325        cache: &mut Cache,
1326        queued_after: usize,
1327        overlay: Option<&crate::vision::EmbedOverlay>,
1328    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1329        let n_embd = self.cfg.n_embd as usize;
1330        let t = tokens.len();
1331        // MEMRA_PRIME_TROWS=1: prefill through the same-session t-row walk (per-row t=1
1332        // program = the tokenwise-prime ORACLE class) — replaces the host-canonical
1333        // per-token step-TP prime. Text-only fresh primes; anything else falls through.
1334        // MEMRA_STEP_GEMM_PRIME: prime the prompt through the batched GEMM path, CHUNKED.
1335        // The batch entry supplies both halves of the fast prime — the GEMM trunk at m = chunk
1336        // and the grouped NVFP4 MoE — which is why routing only the MoE through the ordinary
1337        // chunk loop measured 26.9 s against 3.7 s here. Chunking keeps the transients bounded:
1338        // a whole 32k prompt in one call would build a 262144-pair CSR and ~4.3 GB of partials
1339        // per rank, the blow-up the chunked prime exists to prevent.
1340        //
1341        // CONTINUATION (lane/gemm-suffix, 2026-08-28): the entry NO LONGER requires
1342        // `cache.pos == 0`. The batch core has been continuation-capable since 7700e0b6
1343        // (positions carry each sequence's base; the fresh-prompt guard narrowed to B > 1),
1344        // and d99b2ea3 named this outer guard as the remaining blocker in its own message.
1345        // Every multi-turn suffix and every tick remainder was paying the walk's measured
1346        // ~7.2 ms/token against this path's ~1.0 ms/token, which is why session-affinity
1347        // reuse measured a 1.012x wash on a growing conversation.
1348        // ONE DEFECT HAD TO BE FIXED FIRST, and it was LIVE before this lift:
1349        // `step35_prime_batch_layers` passed `ts[s]` — the CHUNK's length — as `seq_end`.
1350        // `seq_end` is the REQUEST's absolute end position and it steers step35's SWA arm
1351        // (`seq_end > win`, win = 512 on step37). A chunk SHORTER than the window at a
1352        // NONZERO base therefore selected the UNWINDOWED FA arm over a view that the `off`
1353        // trim leaves at ~win-1+t rows: it attended OUTSIDE the sliding window. That was
1354        // already reachable with no continuation at all — a fresh prompt of 4096+k for k in
1355        // [PRIME_MIN_T, 512) ends in a trailing chunk of exactly that shape. `seq_end` is now
1356        // threaded from here (request-absolute, `+ queued_after`, computed ONCE before the
1357        // chunk loop, chunk-size-invariant exactly as on the walk), which is what makes the
1358        // suffix arm expressible at all rather than merely reachable.
1359        let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
1360        let seq_end = if legacy_calllocal {
1361            cache.pos + t
1362        } else {
1363            cache.pos + t + queued_after
1364        };
1365        // MEMRA_STEP_GEMM_PRIME_SUFFIX is the SUFFIX-ONLY seam: off (its default in this
1366        // commit) leaves continuations on the walk while fresh primes stay on the fast path;
1367        // MEMRA_STEP_GEMM_PRIME=0 is the whole-path seam. The `seq_end` threading above is
1368        // deliberately NOT behind either door — it is a correctness fix for the fresh path too.
1369        if overlay.is_none()
1370            && (cache.pos == 0 || step_gemm_prime_suffix_on())
1371            && t >= PRIME_MIN_T
1372            && crate::step_gemm_prime_on()
1373            && self.uses_sliding_gated_moe_program()
1374        {
1375            let n_embd = self.cfg.n_embd as usize;
1376            let base = cache.pos;
1377            let width = crate::cache::PRIME_CHUNK_MAX_TOKENS;
1378            let mut hiddens = e.uninit(t * n_embd)?;
1379            let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1380            let mut start = 0usize;
1381            while start < t {
1382                // A trailing chunk below the walk floor folds into the previous one; every chunk
1383                // this entry sees must clear PRIME_MIN_T on its own.
1384                let mut end = (start + width).min(t);
1385                if t - end > 0 && t - end < PRIME_MIN_T {
1386                    end = t;
1387                }
1388                let mut out = self.step35_prime_cache_batch(
1389                    e,
1390                    &[&tokens[start..end]],
1391                    &mut [cache],
1392                    &[seq_end],
1393                )?;
1394                if out.len() != 1 {
1395                    return Err("B=1 batched prime returned a non-singleton".into());
1396                }
1397                let (logits, h_seed, hidden) = out.remove(0);
1398                e.copy_into(
1399                    &mut hiddens,
1400                    start * n_embd,
1401                    &hidden,
1402                    (end - start) * n_embd,
1403                )?;
1404                last = Some((logits, h_seed));
1405                start = end;
1406            }
1407            let (logits, h_seed) = last.expect("prime produced no chunk");
1408            // ENGAGEMENT RECEIPT, both directions. `base` is the discriminator: base=0 is a
1409            // fresh prime (this line existed before the lift), base>0 is a SUFFIX riding the
1410            // GEMM trunk — the arm this lane added. The declining twin below counts the other
1411            // direction, so a log that shows neither line is an instrument fault, not a pass.
1412            eprintln!(
1413                "[gemm-prime] ENGAGED t={t} base={base} seq_end={seq_end} chunks<={width} (GEMM trunk + grouped MoE)"
1414            );
1415            return Ok((logits, h_seed, hiddens));
1416        }
1417        if self.uses_sliding_gated_moe_program() {
1418            eprintln!(
1419                "[gemm-prime] WALK t={t} base={} seq_end={seq_end} (batched prime declined)",
1420                cache.pos
1421            );
1422        }
1423        if overlay.is_none() {
1424            if let Some(out) = self.step35_prime_trows(e, tokens, cache)? {
1425                return Ok(out);
1426            }
1427        }
1428        // SESSION CONTINUATION (2026-07-05): cache.pos > 0 = priming a NEW SUFFIX onto a live
1429        // session cache — every chunk (including the first) takes the continuation arm
1430        // (fa_prefill_view over the quantized past + this chunk). Fresh prime (pos==0) unchanged.
1431        assert!(
1432            t >= PRIME_MIN_T,
1433            "prime_cache needs T >= {PRIME_MIN_T} (caller gates)"
1434        );
1435        assert!(
1436            cache.pos + t <= cache.max_ctx,
1437            "prime_cache: prompt exceeds cache max_ctx"
1438        );
1439
1440        // CHUNKED PRIME (2026-07-05, the long-ctx OOM fix): the monolithic prime allocates
1441        // per-layer transients proportional to T (gate/up/act = T*n_ff*4B EACH — 1.5GB apiece at
1442        // 16k on the 27B), which OOMs a 24GB card around 16k prompt tokens. Chunk the prompt:
1443        // each chunk runs the full layer stack with transients sized to the chunk, appending its
1444        // K/V to the resident quantized cache and carrying the GDN conv-ring + recurrent state
1445        // through `cache.recur` (linear_attn_prime is already stateful — a chunk boundary is
1446        // exactly the state carry it was built for). Full-attn chunks after the first attend to
1447        // the QUANTIZED past KV via fa_prefill_view (the spec-verify pattern) — same numeric
1448        // class as decode reading the cache. Prompts <= one chunk take the ORIGINAL monolithic
1449        // body byte-for-byte (chunk 0 short-circuits to the f32 fa_prefill path).
1450        // MEMRA_PRIME_CHUNK sets the chunk size (tokens); 0 disables chunking (monolithic).
1451        if self.is_gemma4_e4b() || self.uses_gemma_program() {
1452            if self.is_gemma4_e4b() {
1453                if overlay.is_some() {
1454                    return Err(
1455                        "vision embedding overlay is unsupported on gemma4 E4B (PLE prime)".into(),
1456                    );
1457                }
1458                return self.gemma4_e4b_prime(e, tokens, cache);
1459            }
1460            // gemma4 v0: monolithic fresh-prompt prime (chunked/continuation arms later).
1461            // An overlay takes the masked-prefill arm: image rows splice in unscaled
1462            // (gemma4.cpp:182 — embd batches skip the sqrt(n_embd) scale) and the image
1463            // spans become bidirectional attention islands (lane/gemma-vision).
1464            return self.gemma4_prime(e, tokens, cache, overlay);
1465        }
1466        let ranges = prime_chunk_ranges(t, self.layers.len(), self.gdn_prime_grid_on());
1467        // CHUNK-ORDER INVARIANCE (lane/chunk-invariance, 2026-08-05; vLLM #38561 shape).
1468        // MEMRA_PRIME_CHUNK is documented as a memory-transient knob, but it also decides
1469        // the prefill's ARITHMETIC, so two rigs with different values produced different
1470        // greedy text for the same prompt (research/session-affinity-20260805: 97- and
1471        // 149-token prompts). ROOT CAUSE, measured in research/chunk-invariance-20260805
1472        // (VERDICT.md) — and it is NOT what docs originally said:
1473        //   * NOT trunk GEMM m / reduction order. REFUTED: the prefill GEMM is m-INVARIANT
1474        //     (rows [0,32) bit-identical at m=32 vs m=33..80, both quantized wq and the
1475        //     output head), so growing a chunk cannot move an existing row's value.
1476        //   * NOT the GDN scan segmentation. REFUTED: MEMRA_GDN_CHUNKED=0 (sequential scan,
1477        //     no WY segmentation at all) still diverges, so vLLM's mamba-boundary fix does
1478        //     not describe our leak.
1479        //   * IT IS the attention numeric CLASS edge in full_attn_prime_fa_dispatch, which
1480        //     selects on `base_len == 0`: chunk 0 attends over this batch's f32 K/V
1481        //     (fa_prefill) while every later chunk attends over the q8_0/q5_1 quantized KV
1482        //     cache (fa_prefill_view_ws). The chunk size therefore decides WHERE in the
1483        //     prompt that precision edge falls. Signature: per-row maxdiff is exactly 0.0
1484        //     before the first boundary and O(1) right after, first_div_pos == chunk size.
1485        // The grain-free fix (full_attn_prime_fa_dispatch below) removed that class edge at
1486        // the source — every row is in one numeric class, so the chunk size no longer steers
1487        // arithmetic and MEMRA_PRIME_CHUNK is a pure memory/transient knob again. The interim
1488        // MEMRA_PRIME_INVARIANT/MEMRA_PRIME_GRAIN pin-the-boundary door was superseded by that
1489        // fix and KILLED at v0.71 per the flags doctrine (the jsonl + VERDICT.md are the
1490        // record); the chunkinv gate asserts byte-identity across chunk sizes naked.
1491        // The REQUEST's absolute end position, computed ONCE before the loop: every chunk sees the
1492        // same value, whatever the chunk size. step35's SWA arm selects on it so that kernel
1493        // selection — and therefore the logits — cannot depend on MEMRA_PRIME_CHUNK
1494        // (research/step35-chunkfix-20260807; see step35_attn_pre_wo's doc note).
1495        // `+ queued_after` closes the SECOND axis (lane/tick-seg): when serve splits the request
1496        // across calls, the request still ends at the same absolute position, whatever the tick
1497        // budget or LCP split point. MEMRA_PRIME_CALLLOCAL=1 is the ROLLBACK SEAM to the FULL
1498        // pre-fix arithmetic: the per-call value here AND the unaligned FA view offset in
1499        // step35_attn_pre_wo. Both halves are required for tickinv35's canary teeth under the FA
1500        // default. Read per call, not cached (the probe flips it in-process between arms). Never
1501        // on in a measured default run.
1502        // `seq_end` (and its MEMRA_PRIME_CALLLOCAL seam) is computed ONCE above the batched
1503        // entry so both prime arms read the identical request-absolute value.
1504        if ranges.len() == 1 {
1505            return self.prime_chunk(e, tokens, cache, seq_end, 0, overlay);
1506        }
1507        // PIPELINED PP-2 PRIME (lane/cx-pipeline-prime, 2026-08-08): overlap stage 0 of
1508        // chunk N+1 with stage 1 of chunk N. The serial split stays reachable through
1509        // MEMRA_PRIME_PIPE=0 and is the exactness oracle. N>2 keeps the serial walker;
1510        // this lane owns the balanced two-stage schedule only.
1511        if crate::pp::prime_pipe_on() && crate::pp::prime_pp_on() && !crate::pp::pp2_streams_off() {
1512            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()).filter(|f| f.len() == 3) {
1513                if overlay.is_some() {
1514                    return Err(
1515                        "vision embedding overlay + pipelined PP prime unsupported (v1); \
1516                         run the serial prime (single device or MEMRA_PRIME_PIPE=0)"
1517                            .into(),
1518                    );
1519                }
1520                if crate::pp::pp_multi_stream_same_device() {
1521                    return Err(
1522                        "prime chunk pipeline refused with 2 stage streams on one device — \
1523                         that concurrent-stream placement remains quarantined by the deferred \
1524                         pp flake record. Use one device per stage or MEMRA_PRIME_PIPE=0 for \
1525                         the serial split."
1526                            .into(),
1527                    );
1528                }
1529                return self.prime_cache_pp2_pipelined(e, tokens, cache, seq_end, &ranges, &fence);
1530            }
1531        }
1532        let mut hiddens = e.uninit(t * n_embd)?;
1533        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1534        for &(start, end) in &ranges {
1535            // chunked prime writes tap rows at the chunk's absolute offset
1536            if let Some(taps) = cache.dflash_taps.as_mut() {
1537                taps.base = start;
1538            }
1539            let (l, hs, x) =
1540                self.prime_chunk(e, &tokens[start..end], cache, seq_end, start, overlay)?;
1541            e.copy_into(&mut hiddens, start * n_embd, &x, (end - start) * n_embd)?;
1542            last = Some((l, hs));
1543        }
1544        let (logits, h_seed) = last.unwrap();
1545        Ok((logits, h_seed, hiddens))
1546    }
1547
1548    /// PP-2 chunk scheduler: stage 1 of chunk N and stage 0 of chunk N+1 are both queued
1549    /// before N's epilogue D2H drains the last-stage stream. Arithmetic is unchanged:
1550    /// every chunk still runs the same two `prime_layers` ranges, boundary copy, output
1551    /// norm, lm head, and caller hidden-stack copy as the serial split.
1552    fn prime_cache_pp2_pipelined(
1553        &self,
1554        e: &Engine,
1555        tokens: &[u32],
1556        cache: &mut Cache,
1557        seq_end: usize,
1558        ranges: &[(usize, usize)],
1559        fence: &[usize],
1560    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1561        debug_assert_eq!(fence.len(), 3);
1562        debug_assert!(ranges.len() >= 2);
1563        let rt = crate::pp::PpNRt::get(e)?;
1564        assert_eq!(
1565            rt.n_stages(),
1566            2,
1567            "prime pipeline requires exactly two PP stages"
1568        );
1569        let n_embd = self.cfg.n_embd as usize;
1570        let t = tokens.len();
1571        let initial_base = cache.pos;
1572        let caller_stream = e.stream();
1573
1574        // #87 reverse publication before any new stage allocation, then prewarm both
1575        // boundary slots while the stage streams are otherwise empty. Lazy-growing slot B
1576        // after stage 1(N) is queued would synchronize that stream and erase the first
1577        // overlap on a two-chunk prompt.
1578        rt.fence_stages_behind(&caller_stream)?;
1579        let max_payload = ranges.iter().map(|(s, e)| (e - s) * n_embd).max().unwrap();
1580        rt.prepare_overlap_slots(0, max_payload)?;
1581
1582        let mut hiddens = e.uninit(t * n_embd)?;
1583        let mut last: Option<(Vec<f32>, CudaSlice<f32>)> = None;
1584        let mut stage_caches = PrimeCacheStages::new(cache, fence[1]);
1585        let (cache0, cache1) = stage_caches.parts();
1586        let (first_start, first_end) = ranges[0];
1587        let mut slot = self.prime_pp2_stage0_enqueue(
1588            e,
1589            rt,
1590            &tokens[first_start..first_end],
1591            cache0,
1592            seq_end,
1593            fence,
1594            initial_base + first_start,
1595            true,
1596        )?;
1597        cache0.pos = initial_base + first_end;
1598
1599        for (i, &(start, end)) in ranges.iter().enumerate() {
1600            let base = initial_base + start;
1601            debug_assert_eq!(
1602                cache1.pos, base,
1603                "stage 1 must drain chunks in original position order"
1604            );
1605            let (out, next_slot) = if let Some(&(next_start, next_end)) = ranges.get(i + 1) {
1606                let next_base = initial_base + next_start;
1607                debug_assert_eq!(
1608                    cache0.pos, next_base,
1609                    "stage 0 must issue chunks in original position order"
1610                );
1611                let cache0_stage = &mut *cache0;
1612                // Step's MoE router readback synchronizes once per layer. Two CUDA streams
1613                // on one host thread therefore serialize even if the calls are ordered as
1614                // a pipeline. Drive the disjoint stage caches from two scoped host threads:
1615                // stage 1 consumes slot N while stage 0 produces slot N+1.
1616                std::thread::scope(|scope| -> Result<_, Box<dyn std::error::Error>> {
1617                    let stage0 = scope.spawn(move || -> Result<usize, String> {
1618                        let next = self
1619                            .prime_pp2_stage0_enqueue(
1620                                e,
1621                                rt,
1622                                &tokens[next_start..next_end],
1623                                cache0_stage,
1624                                seq_end,
1625                                fence,
1626                                next_base,
1627                                true,
1628                            )
1629                            .map_err(|err| err.to_string())?;
1630                        cache0_stage.pos = initial_base + next_end;
1631                        Ok(next)
1632                    });
1633                    let x = self.prime_pp2_stage1_enqueue(
1634                        e,
1635                        rt,
1636                        slot,
1637                        end - start,
1638                        cache1,
1639                        seq_end,
1640                        fence,
1641                        base,
1642                        true,
1643                    )?;
1644                    let out = {
1645                        rt.bind_stage(1)?;
1646                        let _st1 = rt.enter(1);
1647                        let e1 = rt.engine(1, e);
1648                        self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1649                    };
1650                    let next = stage0
1651                        .join()
1652                        .map_err(|_| "pipeprime stage-0 host walker panicked")?
1653                        .map_err(|err| -> Box<dyn std::error::Error> { err.into() })?;
1654                    Ok((out, Some(next)))
1655                })?
1656            } else {
1657                let x = self.prime_pp2_stage1_enqueue(
1658                    e,
1659                    rt,
1660                    slot,
1661                    end - start,
1662                    cache1,
1663                    seq_end,
1664                    fence,
1665                    base,
1666                    true,
1667                )?;
1668                let out = {
1669                    rt.bind_stage(1)?;
1670                    let _st1 = rt.enter(1);
1671                    let e1 = rt.engine(1, e);
1672                    self.prime_chunk_epilogue(e1, x, end - start, cache1)?
1673                };
1674                (out, None)
1675            };
1676
1677            rt.publish_to(1, &caller_stream)?;
1678            e.copy_into(&mut hiddens, start * n_embd, &out.2, (end - start) * n_embd)?;
1679            last = Some((out.0, out.1));
1680            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1681
1682            if let Some(next) = next_slot {
1683                // The caller copy above reads a stage-1 allocation. Before stage 1 of the
1684                // next chunk can allocate/reuse blocks, mirror #87's body-entry fence.
1685                // Stage 0(N+1) is already queued before this wait is appended, so its
1686                // overlap with stage 1(N) is preserved.
1687                rt.fence_stages_behind(&caller_stream)?;
1688                slot = next;
1689            }
1690        }
1691
1692        debug_assert_eq!(cache0.pos, initial_base + t);
1693        debug_assert_eq!(cache1.pos, initial_base + t);
1694        let (logits, h_seed) = last.unwrap();
1695        Ok((logits, h_seed, hiddens))
1696    }
1697
1698    /// One prime chunk: the full layer stack over `tokens`, continuing from the cache's current
1699    /// state (`cache.pos` = tokens already primed; 0 = fresh). Positions/RoPE are absolute
1700    /// task #21 de-broadcast: the q/k head count PREP must emit so the scan's consumers
1701    /// agree. Compact (num_k) ONLY when the scan will take the chunked+mma route AND
1702    /// num_k == num_v/2 (the engine-side hint mirrors this exact formula); everything
1703    /// else (s128 verify tier, chunked-off, mma-off) keeps the broadcast layout.
1704    fn gdn_hk(e: &Engine, t: usize, num_v: usize, num_k: usize) -> usize {
1705        if Engine::gdn_db_on()
1706            && Engine::gdn_chunked_enabled()
1707            && t >= 16
1708            && e.gdn_mma_enabled(Engine::gdn_chunk_size())
1709            && num_k * 2 == num_v
1710        {
1711            num_k
1712        } else {
1713            num_v
1714        }
1715    }
1716
1717    /// task #17: gate for the fused fp16-operand epilogues (silu_mul/gated_rmsnorm/sig_mul
1718    /// `_f16out` twins). Bit-identical class (the twins emit the cvt kernel's exact halves),
1719    /// but seam-gated (MEMRA_F16OUT=0) so a bit-check can arbitrate, and OFF under verify-exact
1720    /// (matmul_group skips the f16 lane there; the twins must not resurrect it).
1721    fn f16out_on(e: &Engine, t: usize) -> bool {
1722        crate::f16_ffi::pp_f16_enabled()
1723            && t >= 16
1724            && !e.verify_exact_on()
1725            && std::env::var("MEMRA_F16OUT").as_deref() != Ok("0")
1726    }
1727
1728    /// (cache.pos + i). Returns (last-row logits, h_seed, this chunk's hidden stack [T, n_embd]).
1729    /// See HybridModel::prime_slabs — the eager prime's resident trunk transients.
1730    /// PER-DEVICE since lane/pp-leverb (2026-08-08): the map is keyed by the allocating
1731    /// engine's CUDA ordinal — under the prime stage split each stage's range walks through
1732    /// its OWN slabs on its own device (a dev0 slab dereferenced by a dev1 kernel would be
1733    /// a peer read per GEMM operand, the exact class Lever B removes). Single-device rigs
1734    /// see one entry, byte-identical behavior.
1735    pub fn prime_slabs_get(
1736        &self,
1737        e: &Engine,
1738        t: usize,
1739        n_embd: usize,
1740        n_ff_max: usize,
1741    ) -> Result<std::sync::Arc<std::sync::Mutex<PrimeSlabs>>, Box<dyn std::error::Error>> {
1742        let mut slabs = self.prime_slabs.lock().unwrap();
1743        let dev = e.ctx().ordinal();
1744        let need_new = match slabs.get(&dev) {
1745            None => true,
1746            Some(sl) => sl.lock().unwrap().t_cap < t,
1747        };
1748        if need_new {
1749            slabs.insert(
1750                dev,
1751                std::sync::Arc::new(std::sync::Mutex::new(PrimeSlabs {
1752                    t_cap: t,
1753                    h: e.uninit(t * n_embd)?,
1754                    x1: e.uninit(t * n_embd)?,
1755                    z: e.uninit(t * n_embd)?,
1756                    act: e.uninit(t * n_ff_max)?,
1757                    xa: e.uninit(t * n_embd)?,
1758                    xb: e.uninit(t * n_embd)?,
1759                    h16: e.alloc_u8_uninit(t * n_embd * 2)?,
1760                    z16: e.alloc_u8_uninit(t * n_embd * 2)?,
1761                    gate: e.uninit(t * n_ff_max)?,
1762                    up: e.uninit(t * n_ff_max)?,
1763                    ffn_out: e.uninit(t * n_embd)?,
1764                    seg_glue: Vec::new(),
1765                    mixed: e.uninit(t * n_embd)?,
1766                    seg_mid: Vec::new(),
1767                    seg_t: 0,
1768                })),
1769            );
1770        }
1771        Ok(slabs.get(&dev).expect("prime slab inserted").clone())
1772    }
1773
1774    /// `seq_end` = the whole REQUEST's absolute end position (`cache.pos + prompt_len` at
1775    /// `prime_cache` entry), NOT this chunk's end. Chunk-size-invariant by construction; step35's
1776    /// SWA arm selects on it (see `step35_attn_pre_wo`). Every other arch ignores it.
1777    fn prime_chunk(
1778        &self,
1779        e: &Engine,
1780        tokens: &[u32],
1781        cache: &mut Cache,
1782        seq_end: usize,
1783        chunk_off: usize,
1784        overlay: Option<&crate::vision::EmbedOverlay>,
1785    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1786        if crate::pp::pp_host_bounce_active()
1787            && (self.uses_gemma_program() || !crate::pp::prime_pp_on())
1788        {
1789            return Err(
1790                "prime_chunk: refused with MEMRA_PP_HOST_BOUNCE=1 because this configuration \
1791                 has no active prime stage split and would peer-read remote weights; keep \
1792                 MEMRA_PRIME_PP enabled and use a PP-prime-supported model"
1793                    .into(),
1794            );
1795        }
1796        // LEVER B (lane/pp-leverb, 2026-08-08): the ppN door for the chunked prime. With the
1797        // door open + per-stage streams, each chunk walks its layer ranges on the OWNING
1798        // stage's engine/device (the anatomy receipt this kills: dev1 ran ZERO prefill
1799        // kernels; stage-1 trunk weights were peer-read = 22% of the pp4096 wall).
1800        // MEMRA_PRIME_PP=0 = the unsplit rollback (also the prime-split-gate's reference
1801        // arm — prime deliberately keeps NO refuse_unsplit_if_remote, see pp.rs). The
1802        // MEMRA_PP_STREAMS=0 seam keeps the unsplit walk too: in that regime the sharded
1803        // loader is off and there is nothing remote to split for.
1804        if !self.uses_gemma_program() && !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
1805            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
1806                if overlay.is_some() {
1807                    return Err("vision embedding overlay + PP prime unsupported (v1); \
1808                         run single-device or MEMRA_PRIME_PP=0"
1809                        .into());
1810                }
1811                return self.prime_chunk_ppn(e, tokens, cache, seq_end, &fence);
1812            }
1813        }
1814        if crate::pp::pp_host_bounce_active() {
1815            return Err(
1816                "prime_chunk: MEMRA_PP_HOST_BOUNCE=1 found no valid prime stage split; \
1817                 refusing an unsplit remote-weight walk"
1818                    .into(),
1819            );
1820        }
1821        let t = tokens.len();
1822        let base = cache.pos;
1823        debug_assert!(
1824            seq_end >= base + t,
1825            "prime_chunk: seq_end must cover this chunk"
1826        );
1827        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
1828        let pos_d = e.htod_i32(&pos)?;
1829
1830        let mut x_embed = self.embed(e, tokens)?; // [T, n_embd]
1831        if let Some(ov) = overlay {
1832            // Mixed-embedding splice: image rows overwrite the pad-token embeddings that
1833            // fall inside this chunk's prompt-relative window [chunk_off, chunk_off+t).
1834            // Images larger than one prime chunk straddle boundaries, hence the clipping.
1835            let n_embd = self.cfg.n_embd as usize;
1836            for &(pos, row_off, n_rows) in &ov.spans {
1837                let lo = pos.max(chunk_off);
1838                let hi = (pos + n_rows).min(chunk_off + t);
1839                if lo < hi {
1840                    let src_row = row_off + (lo - pos);
1841                    let view = ov
1842                        .rows
1843                        .slice(src_row * n_embd..(src_row + (hi - lo)) * n_embd);
1844                    e.copy_view_into(
1845                        &mut x_embed,
1846                        (lo - chunk_off) * n_embd,
1847                        &view,
1848                        (hi - lo) * n_embd,
1849                    )?;
1850                }
1851            }
1852        }
1853        let x = self.prime_layers(
1854            e,
1855            x_embed,
1856            0,
1857            self.layers.len(),
1858            &pos_d,
1859            t,
1860            base,
1861            cache,
1862            seq_end,
1863        )?;
1864        self.prime_chunk_epilogue(e, x, t, cache)
1865    }
1866
1867    /// PRIME RANGE SUBGRAPH (lane/pp-leverb, 2026-08-08): layers `[lo, hi)` of the chunked
1868    /// prime walk — `prime_chunk`'s trunk loop extracted verbatim to the
1869    /// `decode_layers_eager(lo, hi)` / `verify_layers(lo, hi)` contract: enters with a
1870    /// MATERIALIZED `[T, n_embd]` residual, exits with the range's final residual
1871    /// materialized (cloned out of the slab). At `lo=0, hi=n_layers` — the unsplit call —
1872    /// the launch sequence is byte-identical to the pre-extraction body. Range semantics:
1873    ///   - the cross-layer [down-add + NEXT attn-norm] fusion is range-LOCAL (`il + 1 < hi`):
1874    ///     layer `hi`'s attn_norm belongs to the NEXT stage's device, so the range ends with
1875    ///     the plain add (materialize) and the next stage hoists its own first norm — the
1876    ///     kernel-check-pinned `add_rms_norm == add then rms_norm` identity, the same law
1877    ///     the decode split rests on (`prime-split-gate` arbitrates end-to-end);
1878    ///   - prime slabs are PER-DEVICE (`prime_slabs_get` keys on the engine ordinal), so
1879    ///     each stage walks through its own resident transients;
1880    ///   - the S-glue/S-mid capture path requires the FULL range (its lookahead fuses
1881    ///     `self.layers[il+1]` unconditionally) — `use_seg` gains `lo == 0 && hi == n_layers`.
1882    #[allow(clippy::too_many_arguments)]
1883    fn prime_layers(
1884        &self,
1885        e: &Engine,
1886        x_in: CudaSlice<f32>,
1887        lo: usize,
1888        hi: usize,
1889        pos_d: &CudaSlice<i32>,
1890        t: usize,
1891        base: usize,
1892        cache: &mut Cache,
1893        seq_end: usize,
1894    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1895        let cfg = &self.cfg;
1896        let n_embd = cfg.n_embd as usize;
1897        let eps = cfg.rms_eps;
1898        // task #14: fuse the fp16 GEMM-operand emission into the trunk norms (kills the
1899        // standalone convert launches). Only when the f16 lane serves and T reaches the
1900        // GEMM tier; bit-identical either way.
1901        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
1902        // PRIME SLABS (piecewise foundation): trunk transients in resident buffers —
1903        // ~224 fewer alloc/free calls per prime and FROZEN Lt operand addresses
1904        // (nvjet's alignment-variant selection becomes run-to-run stable). Every slab is
1905        // live prefix is fully overwritten before use; x ping-pongs xa<->xb; the inactive
1906        // capacity tail must stay behind checked views. The hidden-stack return clones the
1907        // final x (the slab cannot leave). Non-slab fallback: MEMRA_PRIME_SLABS=0.
1908        let n_ff_max = self
1909            .layers
1910            .iter()
1911            .map(|l| match &l.ffn {
1912                crate::hybrid::Ffn::Dense { ffn_gate, .. } => ffn_gate.out_features(),
1913                _ => n_embd,
1914            })
1915            .max()
1916            .unwrap_or(n_embd)
1917            .max(n_embd);
1918        let use_slabs = std::env::var("MEMRA_PRIME_SLABS").as_deref() != Ok("0");
1919        let slab = if use_slabs {
1920            Some(self.prime_slabs_get(e, t, n_embd, n_ff_max)?)
1921        } else {
1922            None
1923        };
1924        let mut slab_guard = slab.as_ref().map(|sl| sl.lock().unwrap());
1925        let mut x_own; // fallback storage when slabs are off
1926        type SlabRefs<'a> = (
1927            &'a mut CudaSlice<f32>,
1928            &'a mut CudaSlice<f32>,
1929            &'a mut CudaSlice<f32>,
1930            &'a mut CudaSlice<f32>,
1931            &'a mut CudaSlice<u8>,
1932            &'a mut CudaSlice<u8>,
1933            &'a mut CudaSlice<f32>,
1934            &'a mut CudaSlice<f32>,
1935            &'a mut CudaSlice<f32>,
1936        );
1937        let (mut x_cur, mut x_nxt, sl): (
1938            &mut CudaSlice<f32>,
1939            &mut CudaSlice<f32>,
1940            Option<SlabRefs>,
1941        );
1942        let mut seg: Option<(
1943            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1944            &mut Vec<Option<cudarc::driver::CudaGraph>>,
1945            &mut CudaSlice<f32>,
1946            &mut usize,
1947        )> = None;
1948        let mut x_own2;
1949        match slab_guard.as_mut() {
1950            Some(g) => {
1951                let slabs = &mut **g;
1952                e.copy_into(&mut slabs.xa, 0, &x_in, t * n_embd)?;
1953                let PrimeSlabs {
1954                    xa,
1955                    xb,
1956                    h,
1957                    x1,
1958                    z,
1959                    act,
1960                    h16,
1961                    z16,
1962                    gate,
1963                    up,
1964                    ffn_out,
1965                    seg_glue,
1966                    mixed,
1967                    seg_mid,
1968                    seg_t,
1969                    ..
1970                } = slabs;
1971                x_cur = xa;
1972                x_nxt = xb;
1973                seg = Some((seg_glue, seg_mid, mixed, seg_t));
1974                sl = Some((h, x1, z, act, h16, z16, gate, up, ffn_out));
1975            }
1976            None => {
1977                x_own = x_in;
1978                x_own2 = e.uninit(t * n_embd)?;
1979                x_cur = &mut x_own;
1980                x_nxt = &mut x_own2;
1981                sl = None;
1982            }
1983        }
1984        let mut alloc_h;
1985        let mut alloc_x1;
1986        let mut alloc_z;
1987        let mut alloc_act;
1988        let mut alloc_h16;
1989        let mut alloc_z16;
1990        let mut alloc_gate;
1991        let mut alloc_up;
1992        let mut alloc_fo;
1993        let (h, x1, z, act): (
1994            &mut CudaSlice<f32>,
1995            &mut CudaSlice<f32>,
1996            &mut CudaSlice<f32>,
1997            &mut CudaSlice<f32>,
1998        );
1999        let (h16, z16): (&mut CudaSlice<u8>, &mut CudaSlice<u8>);
2000        let (sl_gate, sl_up, sl_fo): (
2001            &mut CudaSlice<f32>,
2002            &mut CudaSlice<f32>,
2003            &mut CudaSlice<f32>,
2004        );
2005        match sl {
2006            Some((a, b, c, d, e16, f16b, g, u, fo)) => {
2007                h = a;
2008                x1 = b;
2009                z = c;
2010                act = d;
2011                h16 = e16;
2012                z16 = f16b;
2013                sl_gate = g;
2014                sl_up = u;
2015                sl_fo = fo;
2016            }
2017            None => {
2018                alloc_h = e.uninit(t * n_embd)?;
2019                alloc_x1 = e.uninit(t * n_embd)?;
2020                alloc_z = e.uninit(t * n_embd)?;
2021                alloc_act = e.uninit(t * n_ff_max)?;
2022                alloc_h16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2023                alloc_z16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2024                alloc_gate = e.uninit(t * n_ff_max)?;
2025                alloc_up = e.uninit(t * n_ff_max)?;
2026                alloc_fo = e.uninit(t * n_embd)?;
2027                h = &mut alloc_h;
2028                x1 = &mut alloc_x1;
2029                z = &mut alloc_z;
2030                act = &mut alloc_act;
2031                h16 = &mut alloc_h16;
2032                z16 = &mut alloc_z16;
2033                sl_gate = &mut alloc_gate;
2034                sl_up = &mut alloc_up;
2035                sl_fo = &mut alloc_fo;
2036            }
2037        }
2038        // piecewise increment 3: layer-0 norm hoisted; the per-layer tail fuses
2039        // [down-add + NEXT layer's attn-norm] into one captured S-glue segment
2040        // (all-slab IO, zero in-graph allocations). Capture happens lazily on the
2041        // first prime at this t (capture does not execute -> launch right after).
2042        let n_layers = self.layers.len();
2043        // OPT-IN (2026-07-26 interleaved verdict): 2-kernel segments measured NET
2044        // -0.7%-to-neutral under the interleaved A/B protocol — one cuGraphLaunch costs
2045        // about what two kernel submissions do. The earlier "+0.9%" was cross-run clock
2046        // drift (the repo's interleaved-A/B law exists for exactly this). Larger segments
2047        // (S-prep/S-attn, 7-9 kernels) remain the open hypothesis; the core-split
2048        // machinery stays (byte-identical) as their foundation.
2049        // step35 is excluded: the core-split path calls `full_attn_prime_core_inner`, which is
2050        // the GENERIC attn core (uniform n_head, rope_dim_count, no window, no head-wise gate).
2051        // step35 rides its own mixer through the normal per-layer arm below.
2052        let use_seg = f16fuse
2053            && seg.is_some()
2054            && !self.uses_sliding_gated_moe_program()
2055            && lo == 0
2056            && hi == n_layers
2057            && std::env::var("MEMRA_PRIME_SEG").as_deref() == Ok("1");
2058        if let Some((sg, sm, _, st)) = seg.as_mut() {
2059            if **st != t {
2060                sg.clear();
2061                sg.extend((0..n_layers).map(|_| None));
2062                sm.clear();
2063                sm.extend((0..n_layers).map(|_| None));
2064                **st = t;
2065            }
2066        }
2067        {
2068            let layer_lo = &self.layers[lo];
2069            if f16fuse {
2070                e.rms_norm_f16out(
2071                    x_cur,
2072                    layer_lo.attn_norm.float_data(),
2073                    h,
2074                    h16,
2075                    n_embd,
2076                    t,
2077                    eps,
2078                )?;
2079            } else {
2080                e.rms_norm(x_cur, layer_lo.attn_norm.float_data(), h, n_embd, t, eps)?;
2081            }
2082        }
2083        let anat = Self::prime_anatomy_on();
2084        let mut anat_last = if anat {
2085            e.stream().synchronize()?;
2086            Some(std::time::Instant::now())
2087        } else {
2088            None
2089        };
2090        // Closes the region that just ENDED into `slot`, restarting the clock.
2091        macro_rules! anat_mark {
2092            ($slot:expr) => {
2093                if let Some(ts) = anat_last.as_mut() {
2094                    e.stream().synchronize()?;
2095                    Self::prime_anatomy_slots()[$slot].fetch_add(
2096                        ts.elapsed().as_nanos() as u64,
2097                        std::sync::atomic::Ordering::Relaxed,
2098                    );
2099                    *ts = std::time::Instant::now();
2100                }
2101            };
2102        }
2103        for il in lo..hi {
2104            let layer = &self.layers[il];
2105            let hx16 = if f16fuse { Some(&*h16) } else { None };
2106            if use_seg {
2107                // core-split path: projections -> _inner core -> out-GEMM INTO the mixed
2108                // slab (no copies) -> S-mid segment [add + post-norm] as one graph launch.
2109                let (pre, pre16, w_out) = match &layer.mixer {
2110                    Mixer::Full(fa) => {
2111                        let g3 = match hx16 {
2112                            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
2113                            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
2114                        };
2115                        let (pre, pre16) =
2116                            self.full_attn_prime_core_inner(e, fa, g3, &pos_d, t, cache, il)?;
2117                        (pre, pre16, &fa.wo)
2118                    }
2119                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2120                    Mixer::Linear(la) => {
2121                        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2122                        let g4 = match hx16 {
2123                            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
2124                            None => e.matmul_group(&ws, h, t)?,
2125                        };
2126                        let (pre, pre16) =
2127                            self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, None)?;
2128                        (pre, pre16, &la.ssm_out)
2129                    }
2130                };
2131                {
2132                    let (_, sm, mslab, _) = seg.as_mut().unwrap();
2133                    let pre_n = pre.len() / t;
2134                    let xh_pre = match pre16 {
2135                        Some(x) => x,
2136                        None => e.f16_act(&pre, t * pre_n, pre_n)?,
2137                    };
2138                    if !e.try_f16_gemm_pre_into(w_out, &xh_pre, t, mslab)? {
2139                        let y = e.matmul(w_out, &pre, t)?;
2140                        e.copy_into(mslab, 0, &y, t * n_embd)?;
2141                    }
2142                    if sm[il].is_none() {
2143                        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2144                        let w_post = layer.post_attn_norm.float_data();
2145                        e.stream().synchronize()?;
2146                        e.stream()
2147                            .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2148                        let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2149                            e.add(x_cur, mslab, x1, t * n_embd)?;
2150                            e.rms_norm_f16out(x1, w_post, z, z16, n_embd, t, eps)?;
2151                            Ok(())
2152                        })();
2153                        let g = e.stream().end_capture(
2154                            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH);
2155                        r?;
2156                        sm[il] = Some(g?.ok_or("S-mid capture produced no graph")?);
2157                    }
2158                    sm[il].as_ref().unwrap().launch()?;
2159                }
2160            } else {
2161                let mixed = match &layer.mixer {
2162                    Mixer::Full(fa) => {
2163                        let y =
2164                            self.full_attn_prime(e, fa, h, hx16, &pos_d, t, cache, il, seq_end)?;
2165                        anat_mark!(0);
2166                        y
2167                    }
2168                    Mixer::Linear(la) => {
2169                        let y = self.linear_attn_prime(e, la, h, hx16, t, cache, il)?;
2170                        anat_mark!(1);
2171                        y
2172                    }
2173                    Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2174                };
2175                if f16fuse {
2176                    // round 28: residual+norm in ONE kernel (add_rms_norm precedent,
2177                    // bit-identical) — the standalone add pass disappears.
2178                    e.add_rms_norm_f16out(
2179                        x_cur,
2180                        &mixed,
2181                        layer.post_attn_norm.float_data(),
2182                        x1,
2183                        z,
2184                        z16,
2185                        n_embd,
2186                        t,
2187                        eps,
2188                    )?;
2189                } else {
2190                    e.add(x_cur, &mixed, x1, t * n_embd)?;
2191                    e.rms_norm(x1, layer.post_attn_norm.float_data(), z, n_embd, t, eps)?;
2192                }
2193                anat_mark!(4);
2194            }
2195            let zx16 = if f16fuse { Some(&*z16) } else { None };
2196            match &layer.ffn {
2197                crate::hybrid::Ffn::Dense {
2198                    ffn_gate,
2199                    ffn_up,
2200                    ffn_down,
2201                } => {
2202                    let n_ff = ffn_gate.out_features();
2203                    // gate/up INTO boundary slabs (piecewise increment 2); fall back to
2204                    // the allocating group + copy when a mirror is missing.
2205                    let mut into_ok = false;
2206                    if let Some(xh) = zx16 {
2207                        into_ok = e.try_f16_gemm_pre_into(ffn_gate, xh, t, sl_gate)?
2208                            && e.try_f16_gemm_pre_into(ffn_up, xh, t, sl_up)?;
2209                    }
2210                    if !into_ok {
2211                        let mut g2 = match zx16 {
2212                            Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], z, xh, t)?,
2213                            None => e.matmul_group(&[ffn_gate, ffn_up], z, t)?,
2214                        };
2215                        let up_y = g2.pop().unwrap();
2216                        let gate_y = g2.pop().unwrap();
2217                        e.copy_into(sl_gate, 0, &gate_y, t * n_ff)?;
2218                        e.copy_into(sl_up, 0, &up_y, t * n_ff)?;
2219                    }
2220                    // task #17: the silu arm's f16out twin emits the down GEMM's fp16
2221                    // operand in-epilogue; non-silu activations keep the standalone convert.
2222                    // silu_mul_f16out is PLAIN silu(gate)*up — a clamped layer (step35 dense
2223                    // blocks under a live swiglu_clamp_shexp) must take the ffn_act_lim arm.
2224                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
2225                    let act16 = if Self::f16out_on(e, t) && self.cfg.m3.is_none() && d_lim.is_none()
2226                    {
2227                        let mut a16 = e.alloc_u8_uninit(t * n_ff * 2)?;
2228                        e.silu_mul_f16out(sl_gate, sl_up, act, &mut a16, t * n_ff)?;
2229                        Some(a16)
2230                    } else {
2231                        Self::ffn_act_lim(
2232                            e,
2233                            &self.cfg,
2234                            sl_gate,
2235                            sl_up,
2236                            1.0,
2237                            1.0,
2238                            d_lim,
2239                            act,
2240                            t * n_ff,
2241                        )?;
2242                        None
2243                    };
2244                    // down GEMM into the ffn_out slab (f16 arm; fallback copies)
2245                    let xh_act = match act16 {
2246                        Some(x) => x,
2247                        None => e.f16_act(act, t * n_ff, n_ff)?,
2248                    };
2249                    if !e.try_f16_gemm_pre_into(ffn_down, &xh_act, t, sl_fo)? {
2250                        let y = e.matmul(ffn_down, &*act, t)?;
2251                        e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2252                    }
2253                }
2254                crate::hybrid::Ffn::Moe(m) => {
2255                    let y = self.moe_ffn_il_prefill(e, m, z, t, il as u16)?;
2256                    e.copy_into(sl_fo, 0, &y, t * n_embd)?;
2257                    anat_mark!(2);
2258                }
2259            }
2260            if let (crate::hybrid::Ffn::Dense { .. }, true) = (&layer.ffn, anat) {
2261                anat_mark!(3);
2262            }
2263            if use_seg && il + 1 < hi {
2264                // S-glue segment: [add + next attn-norm(+f16out)] — one cuGraphLaunch
2265                let w_next = self.layers[il + 1].attn_norm.float_data();
2266                let (sg, _, _, _) = seg.as_mut().unwrap();
2267                if sg[il].is_none() {
2268                    use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
2269                    e.stream().synchronize()?;
2270                    e.stream()
2271                        .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
2272                    let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2273                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2274                        e.rms_norm_f16out(x_nxt, w_next, h, h16, n_embd, t, eps)?;
2275                        Ok(())
2276                    })();
2277                    let g = e.stream().end_capture(
2278                        CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
2279                    );
2280                    r?;
2281                    sg[il] = Some(g?.ok_or("S-glue capture produced no graph")?);
2282                }
2283                sg[il].as_ref().unwrap().launch()?;
2284            } else {
2285                if il + 1 < hi {
2286                    let w_next = self.layers[il + 1].attn_norm.float_data();
2287                    if f16fuse {
2288                        e.add_rms_norm_f16out(x1, sl_fo, w_next, x_nxt, h, h16, n_embd, t, eps)?;
2289                    } else {
2290                        e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2291                        e.rms_norm(x_nxt, w_next, h, n_embd, t, eps)?;
2292                    }
2293                } else {
2294                    e.add(x1, sl_fo, x_nxt, t * n_embd)?;
2295                }
2296            }
2297            anat_mark!(4);
2298            // CHUNK-INVARIANCE BISECT SEAM (lane/chunk-invariance, 2026-08-05): dump the
2299            // per-layer post-residual hidden for the LAST row of this chunk, keyed by
2300            // absolute position, so two runs at different MEMRA_PRIME_CHUNK can be diffed
2301            // layer-by-layer to find the FIRST diverging layer. Diagnostic only —
2302            // unset (the default) costs one OnceLock read per layer.
2303            if let Some(path) = Self::prime_trace_path() {
2304                let row = (base + t - 1) as usize;
2305                let host = e.dtoh(x_nxt)?;
2306                let last = &host[(t - 1) * n_embd..t * n_embd];
2307                use std::io::Write as _;
2308                let mut f = std::fs::OpenOptions::new()
2309                    .create(true)
2310                    .append(true)
2311                    .open(path)?;
2312                let mut h64: u64 = 0xcbf29ce484222325;
2313                for v in last {
2314                    h64 ^= v.to_bits() as u64;
2315                    h64 = h64.wrapping_mul(0x100000001b3);
2316                }
2317                writeln!(
2318                    f,
2319                    "{{\"pos\":{row},\"layer\":{il},\"t\":{t},\"base\":{base},\
2320                             \"hash\":\"{h64:016x}\",\"v0\":{:.9e},\"v1\":{:.9e},\"v2\":{:.9e}}}",
2321                    last[0], last[1], last[2]
2322                )?;
2323            }
2324            // dflash/dspark tap (no-op when no sink armed): post-layer residual rows for
2325            // drafter conditioning — the qwen twin of the gemma4 tap sites.
2326            self.dflash_tap(e, cache, il, x_nxt, t)?;
2327            std::mem::swap(&mut x_cur, &mut x_nxt);
2328        }
2329        if anat {
2330            let s = Self::prime_anatomy_slots();
2331            let ms = |i: usize| s[i].load(std::sync::atomic::Ordering::Relaxed) as f64 / 1.0e6;
2332            eprintln!(
2333                "[prime-anatomy] cumulative ms: attn_full={:.1} gdn_linear={:.1} moe={:.1} \
2334                 dense={:.1} norms_adds={:.1} (t={t}, layers {lo}..{hi})",
2335                ms(0),
2336                ms(1),
2337                ms(2),
2338                ms(3),
2339                ms(4)
2340            );
2341        }
2342        // hidden-stack return: clone the final x out of the slab
2343        let mut x = e.uninit(t * n_embd)?;
2344        e.copy_into(&mut x, 0, x_cur, t * n_embd)?;
2345        drop(slab_guard);
2346        Ok(x)
2347    }
2348
2349    /// The prime chunk's tail — h_seed + output_norm + one-row lm head + cache.pos advance —
2350    /// shared verbatim by the unsplit walk and the last stage of the ppN walk (`e` = the
2351    /// engine that produced `x`, i.e. the last stage's under the split; output_norm/output
2352    /// were loaded through that engine by the sharded loader, hybrid.rs `e_head`).
2353    fn prime_chunk_epilogue(
2354        &self,
2355        e: &Engine,
2356        x: CudaSlice<f32>,
2357        t: usize,
2358        cache: &mut Cache,
2359    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2360        let n_embd = self.cfg.n_embd as usize;
2361        let eps = self.cfg.rms_eps;
2362        // h_seed = LAST row of x BEFORE output_norm (MTP-PLAN §A default) or AFTER it
2363        // (MEMRA_SPEC_HPOST — reference convention; hn is computed just below either way, so
2364        // the post-norm copy happens after hn exists).
2365        let mut h_seed = e.uninit(n_embd)?;
2366        if !crate::spec::spec_hpost() {
2367            e.copy_view_into(
2368                &mut h_seed,
2369                0,
2370                &x.slice((t - 1) * n_embd..t * n_embd),
2371                n_embd,
2372            )?;
2373        }
2374        // last-row logits, exactly like forward_last (norm all T — per-row op — then lm_head on 1 row).
2375        let mut hn = e.uninit(t * n_embd)?;
2376        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2377        if crate::spec::spec_hpost() {
2378            e.copy_view_into(
2379                &mut h_seed,
2380                0,
2381                &hn.slice((t - 1) * n_embd..t * n_embd),
2382                n_embd,
2383            )?;
2384        }
2385        let last = e.view(&hn, t * n_embd);
2386        let last_row = last.slice((t - 1) * n_embd..t * n_embd);
2387        let mut hlast = e.uninit(n_embd)?;
2388        e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
2389        let logits = e.matmul(&self.output, &hlast, 1)?;
2390        cache.pos += t;
2391        // Hidden stack handed to generate_spec as prompt_h: pre-norm x (default) or the full
2392        // post-norm stack hn (MEMRA_SPEC_HPOST).
2393        Ok((
2394            e.dtoh(&logits)?,
2395            h_seed,
2396            if crate::spec::spec_hpost() { hn } else { x },
2397        ))
2398    }
2399
2400    /// Post-final-norm hidden state of one row of a prime-returned hidden stack — the
2401    /// embedding-pooling read (lane/embed-serve). `hiddens` is `prime_cache`'s third
2402    /// return: the pre-norm stack by default, but ALREADY post-norm under
2403    /// MEMRA_SPEC_HPOST (see `prime_chunk_epilogue`), so the norm is applied only in
2404    /// the default shape. Returns the host f32 row (`n_embd` wide).
2405    pub fn hidden_postnorm_row(
2406        &self,
2407        e: &Engine,
2408        hiddens: &CudaSlice<f32>,
2409        row: usize,
2410    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2411        let n_embd = self.cfg.n_embd as usize;
2412        let mut x1 = e.uninit(n_embd)?;
2413        e.copy_view_into(
2414            &mut x1,
2415            0,
2416            &hiddens.slice(row * n_embd..(row + 1) * n_embd),
2417            n_embd,
2418        )?;
2419        if crate::spec::spec_hpost() {
2420            return Ok(e.dtoh(&x1)?);
2421        }
2422        let mut hn = e.uninit(n_embd)?;
2423        e.rms_norm(
2424            &x1,
2425            self.output_norm.float_data(),
2426            &mut hn,
2427            n_embd,
2428            1,
2429            self.cfg.rms_eps,
2430        )?;
2431        Ok(e.dtoh(&hn)?)
2432    }
2433
2434    /// THE PRIME STAGE SPLIT (lane/pp-leverb, 2026-08-08): one prime chunk as N stage
2435    /// subgraphs, each range on ITS OWN engine/stream/device, with the `[T, n_embd]`
2436    /// boundary handoff at every fence cut — the prime twin of `decode_step_h_ppn` /
2437    /// `decode_step_t_core_ppn`, and the kill for the anatomy's two receipts: stage-1 trunk
2438    /// weights stop being peer-read (22% of the pp4096 wall) and dev1 stops running zero
2439    /// prefill kernels. Structure mirrors the verify split exactly:
2440    ///   stage 0        `rt.enter(0)` → per-stage pos_d + embed (the table lives with
2441    ///                  stage 0) → `prime_layers(fence[0], fence[1])` → `rt.tx`
2442    ///   middle stages  `rt.rx` → per-stage pos_d → range → `rt.tx`
2443    ///   last stage     `rt.rx` → range → the shared epilogue (output_norm + head live
2444    ///                  there via the sharded loader) → `publish_to`
2445    /// Laws inherited (not relearned): `fence_stages_behind` at entry (#87 — a previous
2446    /// round's stage-freed buffers must not be reused under the caller's queued reads);
2447    /// per-stage `pos_d` (allocated/consumed/freed on one stream); per-stage Engines from
2448    /// `PpNRt` (shared-scratch race); EXIT PUBLICATION for the device-resident returns
2449    /// (h_seed + hidden stack live on the last stage — the caller's stream must wait).
2450    /// KV/MoE locality falls out: each range's KV appends run on the owning stage
2451    /// (`pp::new_cache` placed the buffers there), each stage engine owns its own SLRU
2452    /// pool (per-Engine `moe_cache`, sized on ITS device — the SGLang #33666 law), and the
2453    /// slab-local MoE arm's `DevExps.dev` gate now matches on stage-1 layers too.
2454    /// EXACTNESS: the split adds zero deviation by construction (same kernels, same bytes,
2455    /// boundary = straight f32 copy); `prime-split-gate` (ppsplit) arbitrates bit-for-bit
2456    /// and its liveness counter is bumped here — the gate goes green with this function.
2457    fn prime_chunk_ppn(
2458        &self,
2459        e: &Engine,
2460        tokens: &[u32],
2461        cache: &mut Cache,
2462        seq_end: usize,
2463        fence: &[usize],
2464    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2465        let rt = crate::pp::PpNRt::get(e)?;
2466        let n_st = fence.len() - 1;
2467        assert_eq!(
2468            rt.n_stages(),
2469            n_st,
2470            "PpNRt stage count {} != fence stages {n_st}",
2471            rt.n_stages()
2472        );
2473        let n_embd = self.cfg.n_embd as usize;
2474        let t = tokens.len();
2475        let base = cache.pos;
2476        debug_assert!(
2477            seq_end >= base + t,
2478            "prime_chunk_ppn: seq_end must cover this chunk"
2479        );
2480        let payload = t * n_embd;
2481        // The caller's ambient stream, captured BEFORE any enter() pushes a stage stream
2482        // (inside a stage scope e.stream() IS the stage stream and the exit wait would
2483        // self-order into a no-op) — the decode_step_t_core_ppn pattern.
2484        let caller_stream = e.stream();
2485        rt.fence_stages_behind(&caller_stream)?;
2486
2487        if n_st == 2 {
2488            let slot =
2489                self.prime_pp2_stage0_enqueue(e, rt, tokens, cache, seq_end, fence, base, false)?;
2490            let x =
2491                self.prime_pp2_stage1_enqueue(e, rt, slot, t, cache, seq_end, fence, base, false)?;
2492            let out = {
2493                rt.bind_stage(1)?;
2494                let _st1 = rt.enter(1);
2495                let e1 = rt.engine(1, e);
2496                self.prime_chunk_epilogue(e1, x, t, cache)?
2497            };
2498            rt.publish_to(1, &caller_stream)?;
2499            crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2500            return Ok(out);
2501        }
2502
2503        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2504
2505        // ---- STAGE 0: embed + layers [0, fence[1]) + boundary-0 TX ----
2506        let mut slot = {
2507            let _st0 = rt.enter(0);
2508            let e0 = rt.engine(0, e);
2509            let pos_d = e0.htod_i32(&pos)?;
2510            let x = self.embed(e0, tokens)?;
2511            let x =
2512                self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2513            rt.tx(0, &x, payload)?
2514            // x + pos_d drop here: freed stream-ordered on stage-0's stream after use.
2515        };
2516
2517        // ---- MIDDLE STAGES: RX boundary s-1 -> range -> TX boundary s ----
2518        for s in 1..n_st - 1 {
2519            let _st = rt.enter(s);
2520            let es = rt.engine(s, e);
2521            let pos_d = es.htod_i32(&pos)?;
2522            let x = rt.rx(s - 1, slot, payload)?;
2523            let x = self.prime_layers(
2524                es,
2525                x,
2526                fence[s],
2527                fence[s + 1],
2528                &pos_d,
2529                t,
2530                base,
2531                cache,
2532                seq_end,
2533            )?;
2534            slot = rt.tx(s, &x, payload)?;
2535        }
2536
2537        // ---- LAST STAGE: RX + final range + the shared epilogue ----
2538        let _stl = rt.enter(n_st - 1);
2539        let el = rt.engine(n_st - 1, e);
2540        let pos_d = el.htod_i32(&pos)?;
2541        let x = rt.rx(n_st - 2, slot, payload)?;
2542        let x = self.prime_layers(
2543            el,
2544            x,
2545            fence[n_st - 1],
2546            fence[n_st],
2547            &pos_d,
2548            t,
2549            base,
2550            cache,
2551            seq_end,
2552        )?;
2553        let out = self.prime_chunk_epilogue(el, x, t, cache)?;
2554        // EXIT PUBLICATION: h_seed + the hidden stack are device-resident on the last
2555        // stage's stream; the caller resumes on its own stream (chunk-loop copy_into /
2556        // generate_spec's prompt_h consumer). The logits dtoh above already drained the
2557        // stage stream host-side, but the law is stated in events, not in a dtoh side
2558        // effect a later deferred form would remove.
2559        rt.publish_to(n_st - 1, &caller_stream)?;
2560        crate::pp::PRIME_SPLIT_CHUNKS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2561        Ok(out)
2562    }
2563
2564    fn prime_pp2_stage0_enqueue(
2565        &self,
2566        e: &Engine,
2567        rt: &crate::pp::PpNRt,
2568        tokens: &[u32],
2569        cache: &mut Cache,
2570        seq_end: usize,
2571        fence: &[usize],
2572        base: usize,
2573        pipelined: bool,
2574    ) -> Result<usize, Box<dyn std::error::Error>> {
2575        let t = tokens.len();
2576        let n_embd = self.cfg.n_embd as usize;
2577        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2578        rt.bind_stage(0)?;
2579        let _st0 = rt.enter(0);
2580        let e0 = rt.engine(0, e);
2581        let pos_d = e0.htod_i32(&pos)?;
2582        let x = self.embed(e0, tokens)?;
2583        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2584        let x = self.prime_layers(e0, x, fence[0], fence[1], &pos_d, t, base, cache, seq_end)?;
2585        if pipelined {
2586            rt.tx_pipelined(0, &x, t * n_embd)
2587        } else {
2588            rt.tx(0, &x, t * n_embd)
2589        }
2590    }
2591
2592    fn prime_pp2_stage1_enqueue(
2593        &self,
2594        e: &Engine,
2595        rt: &crate::pp::PpNRt,
2596        slot: usize,
2597        t: usize,
2598        cache: &mut Cache,
2599        seq_end: usize,
2600        fence: &[usize],
2601        base: usize,
2602        pipelined: bool,
2603    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2604        let n_embd = self.cfg.n_embd as usize;
2605        let pos: Vec<i32> = (base as i32..(base + t) as i32).collect();
2606        rt.bind_stage(1)?;
2607        let _st1 = rt.enter(1);
2608        let e1 = rt.engine(1, e);
2609        let pos_d = e1.htod_i32(&pos)?;
2610        let x = rt.rx(0, slot, t * n_embd)?;
2611        let _overlap = pipelined.then(crate::pp::enter_prime_pipe_stage);
2612        self.prime_layers(e1, x, fence[1], fence[2], &pos_d, t, base, cache, seq_end)
2613    }
2614
2615    /// CAPTURE-SAFE prime trunk (task #14 increment 1, ARCHITECTURE-H100.md design v2):
2616    /// prime_chunk's layer stack with every capture hazard hoisted — `x` is the
2617    /// PRE-EMBEDDED input in a STABLE graph-input buffer (embed + its 8MB htod stay
2618    /// eager, one launch), `pos_d` is a baked device param (fresh prime = 0..T, constant
2619    /// per bucket), logits/h_seed/hidden stay DEVICE-resident (no dtoh), and cache.pos is
2620    /// NOT advanced (host state — the replay wrapper owns it). Body mirrors prime_chunk
2621    /// (the prime-graph-gate pins them together). KNOWN smoke-scope gap: append host-len
2622    /// bookkeeping still runs on the host per call — the real replay path moves the write
2623    /// slot to the len_d device counter (increment 3).
2624    /// GRAPH-OUTPUT CONTRACT: results are COPIED into caller-provided stable buffers
2625    /// (`logits_out` [n_vocab], `h_seed_out` [n_embd]) — every internal allocation drops
2626    /// INSIDE the capture region (alloc+free node pairs). Retaining an in-capture
2627    /// allocation across end_capture makes instantiate throw INVALID_VALUE (smoke finding
2628    /// 2026-07-26), and under AUTO_FREE_ON_LAUNCH its address wouldn't survive a launch
2629    /// anyway — the decode GraphSession's pre-allocated-output pattern is the law here.
2630    pub fn prime_chunk_captured(
2631        &self,
2632        e: &Engine,
2633        x_in: &CudaSlice<f32>,
2634        pos_d: &CudaSlice<i32>,
2635        t: usize,
2636        cache: &mut Cache,
2637        len_d: &CudaSlice<i32>,
2638        logits_out: &mut CudaSlice<f32>,
2639        h_seed_out: &mut CudaSlice<f32>,
2640    ) -> Result<(), Box<dyn std::error::Error>> {
2641        let cfg = &self.cfg;
2642        let n_embd = cfg.n_embd as usize;
2643        let eps = cfg.rms_eps;
2644        let f16fuse = crate::f16_ffi::pp_f16_enabled() && t >= 16;
2645        let mut x = e.uninit(t * n_embd)?;
2646        e.copy_into(&mut x, 0, x_in, t * n_embd)?;
2647        for (il, layer) in self.layers.iter().enumerate() {
2648            let mut h = e.uninit(t * n_embd)?;
2649            let mut hx16: Option<CudaSlice<u8>> = None;
2650            if f16fuse {
2651                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2652                e.rms_norm_f16out(
2653                    &x,
2654                    layer.attn_norm.float_data(),
2655                    &mut h,
2656                    &mut b16,
2657                    n_embd,
2658                    t,
2659                    eps,
2660                )?;
2661                hx16 = Some(b16);
2662            } else {
2663                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
2664            }
2665            let mixed = match &layer.mixer {
2666                // The captured prime is ONE unchunked bucket over a FRESH cache (pos == 0), so the
2667                // request ends at t. If bucketed capture ever composes with chunking, seq_end must
2668                // come from the caller (see step35_attn_pre_wo's doc note).
2669                Mixer::Full(fa) => {
2670                    self.full_attn_prime(e, fa, &h, hx16.as_ref(), pos_d, t, cache, il, t)?
2671                }
2672                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
2673                Mixer::Linear(la) => {
2674                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
2675                    let g4 = match hx16.as_ref() {
2676                        Some(xh) => e.matmul_group_xh(&ws, &h, xh, t)?,
2677                        None => e.matmul_group(&ws, &h, t)?,
2678                    };
2679                    self.linear_attn_prime_core_pad(e, la, g4, t, cache, il, Some(len_d))?
2680                }
2681            };
2682            let mut x1 = e.uninit(t * n_embd)?;
2683            e.add(&x, &mixed, &mut x1, t * n_embd)?;
2684            let mut z = e.uninit(t * n_embd)?;
2685            let mut zx16: Option<CudaSlice<u8>> = None;
2686            if f16fuse {
2687                let mut b16 = e.alloc_u8_uninit(t * n_embd * 2)?;
2688                e.rms_norm_f16out(
2689                    &x1,
2690                    layer.post_attn_norm.float_data(),
2691                    &mut z,
2692                    &mut b16,
2693                    n_embd,
2694                    t,
2695                    eps,
2696                )?;
2697                zx16 = Some(b16);
2698            } else {
2699                e.rms_norm(
2700                    &x1,
2701                    layer.post_attn_norm.float_data(),
2702                    &mut z,
2703                    n_embd,
2704                    t,
2705                    eps,
2706                )?;
2707            }
2708            let ffn_out = match &layer.ffn {
2709                crate::hybrid::Ffn::Dense {
2710                    ffn_gate,
2711                    ffn_up,
2712                    ffn_down,
2713                } => {
2714                    let n_ff = ffn_gate.out_features();
2715                    let mut g2 = match &zx16 {
2716                        Some(xh) => e.matmul_group_xh(&[ffn_gate, ffn_up], &z, xh, t)?,
2717                        None => e.matmul_group(&[ffn_gate, ffn_up], &z, t)?,
2718                    };
2719                    let up = g2.pop().unwrap();
2720                    let gate = g2.pop().unwrap();
2721                    let mut act = e.uninit(t * n_ff)?;
2722                    // dense FFN keys off the SHEXP clamp array — see forward()'s note.
2723                    Self::ffn_act_lim(
2724                        e,
2725                        &self.cfg,
2726                        &gate,
2727                        &up,
2728                        1.0,
2729                        1.0,
2730                        self.cfg.clamp_shexp_at(il as u32),
2731                        &mut act,
2732                        t * n_ff,
2733                    )?;
2734                    e.matmul(ffn_down, &act, t)?
2735                }
2736                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il_prefill(e, m, &z, t, il as u16)?,
2737            };
2738            let mut x2 = e.uninit(t * n_embd)?;
2739            e.add(&x1, &ffn_out, &mut x2, t * n_embd)?;
2740            x = x2;
2741        }
2742        // device-indexed TRUE last row (pads sit past it in a bucketed graph)
2743        if !crate::spec::spec_hpost() {
2744            e.row_gather_dev(&x, h_seed_out, len_d, n_embd)?;
2745        }
2746        let mut hn = e.uninit(t * n_embd)?;
2747        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
2748        if crate::spec::spec_hpost() {
2749            e.row_gather_dev(&hn, h_seed_out, len_d, n_embd)?;
2750        }
2751        let mut hlast = e.uninit(n_embd)?;
2752        e.row_gather_dev(&hn, &mut hlast, len_d, n_embd)?;
2753        let logits = e.matmul(&self.output, &hlast, 1)?;
2754        let nv = logits.len();
2755        e.copy_into(logits_out, 0, &logits, nv)?;
2756        Ok(())
2757    }
2758
2759    fn step35_prime_batch_on() -> bool {
2760        std::env::var("MEMRA_STEP35_PRIME_BATCH").as_deref() != Ok("0")
2761    }
2762
2763    /// Step35 cross-request prime range: weight-streaming work runs once at `m=sum(T)`;
2764    /// sequence-scoped attention/KV work stays on each request's own cache and positions.
2765    #[allow(clippy::too_many_arguments)]
2766    /// `seq_ends[s]`: sequence s's REQUEST-absolute end position — NOT `ts[s]`. It is the
2767    /// only thing step35's SWA arm keys on, so a chunk-local value here decides the attention
2768    /// kernel from the chunk size (and, below the 512-row window at a nonzero base, drops the
2769    /// window mask entirely). See the batched entry's note in `prime_cache_overlaid`.
2770    #[allow(clippy::too_many_arguments)]
2771    fn step35_prime_batch_layers(
2772        &self,
2773        e: &Engine,
2774        mut x: CudaSlice<f32>,
2775        lo: usize,
2776        hi: usize,
2777        ts: &[usize],
2778        offs: &[usize],
2779        seq_ends: &[usize],
2780        pos_ds: &[CudaSlice<i32>],
2781        caches: &mut [&mut Cache],
2782    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2783        let cfg = &self.cfg;
2784        let n_embd = cfg.n_embd as usize;
2785        let eps = cfg.rms_eps;
2786        let b = ts.len();
2787        let total: usize = ts.iter().sum();
2788        let f16fuse = crate::f16_ffi::pp_f16_enabled() && total >= 16;
2789
2790        let split = |e: &Engine,
2791                     y: &CudaSlice<f32>,
2792                     dim: usize|
2793         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2794            let mut out = Vec::with_capacity(b);
2795            for s in 0..b {
2796                let mut ys = e.uninit(ts[s] * dim)?;
2797                e.copy_view_into(
2798                    &mut ys,
2799                    0,
2800                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
2801                    ts[s] * dim,
2802                )?;
2803                out.push(ys);
2804            }
2805            Ok(out)
2806        };
2807
2808        // MEMRA_PRIME_PROF=1: per-phase wall inside the prime, sync-bounded (absolute time
2809        // inflates; the SPLIT is the signal). Two inspection passes failed to find where a
2810        // 3.8 s/4096-token chunk goes against a ~0.55 s compute budget, and nsys cannot capture
2811        // through the server's worker, so the walk measures itself.
2812        let prof = std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1");
2813        let mut ph = [0f64; 4]; // 0 norm+qkv, 1 attn, 2 o_proj+norm, 3 moe
2814        let mut mark = |e: &Engine, acc: usize, t0: &mut std::time::Instant, ph: &mut [f64; 4]| {
2815            if prof {
2816                let _ = e.stream().synchronize();
2817                ph[acc] += t0.elapsed().as_secs_f64() * 1e3;
2818                *t0 = std::time::Instant::now();
2819            }
2820        };
2821        let mut pt = std::time::Instant::now();
2822        for il in lo..hi {
2823            let layer = &self.layers[il];
2824            let Mixer::Full(fa) = &layer.mixer else {
2825                return Err(format!("step35 layer {il} is not full-attn — corrupt config").into());
2826            };
2827
2828            let mut h = e.uninit(total * n_embd)?;
2829            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2830            if f16fuse {
2831                e.rms_norm_f16out(
2832                    &x,
2833                    layer.attn_norm.float_data(),
2834                    &mut h,
2835                    &mut hx16,
2836                    n_embd,
2837                    total,
2838                    eps,
2839                )?;
2840            } else {
2841                e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, total, eps)?;
2842            }
2843
2844            // Q/K/V + gate projections and wo are batched weight streams. The step35 core
2845            // remains per sequence, so its partial RoPE, SWA view, KV append, and gate
2846            // application stay verbatim.
2847            let gate_w = fa
2848                .attn_gate
2849                .as_ref()
2850                .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
2851            let mut g4 = if f16fuse {
2852                e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, &hx16, total)?
2853            } else {
2854                e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv, gate_w], &h, total)?
2855            };
2856            let gate = g4.pop().unwrap();
2857            let mut parts: Vec<Vec<CudaSlice<f32>>> =
2858                (0..b).map(|_| Vec::with_capacity(3)).collect();
2859            for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g4) {
2860                for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
2861                    parts[s].push(ys);
2862                }
2863            }
2864            let gates = split(e, &gate, gate_w.out_features())?;
2865            let geometry = self.step35_geom(il);
2866            let hd = geometry.head_dim_k as usize;
2867            let nh = geometry.n_head as usize;
2868            let mut ag_cat = e.uninit(total * nh * hd)?;
2869            for (s, (g3s, gate)) in parts.into_iter().zip(gates).enumerate() {
2870                mark(e, 0, &mut pt, &mut ph);
2871                let ag = self.step35_attn_pre_wo(
2872                    e,
2873                    fa,
2874                    g3s,
2875                    None,
2876                    Some(&gate),
2877                    &pos_ds[s],
2878                    ts[s],
2879                    Some(&mut *caches[s]),
2880                    il,
2881                    seq_ends[s],
2882                )?;
2883                e.copy_into(&mut ag_cat, offs[s] * nh * hd, &ag, ts[s] * nh * hd)?;
2884            }
2885            mark(e, 1, &mut pt, &mut ph);
2886            let mixed = e.matmul(&fa.wo, &ag_cat, total)?;
2887
2888            let mut x1 = e.uninit(total * n_embd)?;
2889            let mut z = e.uninit(total * n_embd)?;
2890            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
2891            if f16fuse {
2892                e.add_rms_norm_f16out(
2893                    &x,
2894                    &mixed,
2895                    layer.post_attn_norm.float_data(),
2896                    &mut x1,
2897                    &mut z,
2898                    &mut zx16,
2899                    n_embd,
2900                    total,
2901                    eps,
2902                )?;
2903            } else {
2904                e.add(&x, &mixed, &mut x1, total * n_embd)?;
2905                e.rms_norm(
2906                    &x1,
2907                    layer.post_attn_norm.float_data(),
2908                    &mut z,
2909                    n_embd,
2910                    total,
2911                    eps,
2912                )?;
2913            }
2914
2915            mark(e, 2, &mut pt, &mut ph);
2916            let ffn_out = match &layer.ffn {
2917                crate::hybrid::Ffn::Dense {
2918                    ffn_gate,
2919                    ffn_up,
2920                    ffn_down,
2921                } => {
2922                    let n_ff = ffn_gate.out_features();
2923                    let mut g2 = if f16fuse {
2924                        e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?
2925                    } else {
2926                        e.matmul_group(&[ffn_gate, ffn_up], &z, total)?
2927                    };
2928                    let up = g2.pop().unwrap();
2929                    let gate = g2.pop().unwrap();
2930                    let mut act = e.uninit(total * n_ff)?;
2931                    let d_lim = cfg.clamp_shexp_at(il as u32);
2932                    if Self::f16out_on(e, total) && cfg.m3.is_none() && d_lim.is_none() {
2933                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
2934                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
2935                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
2936                            Some(y) => y,
2937                            None => e.matmul(ffn_down, &act, total)?,
2938                        }
2939                    } else {
2940                        Self::ffn_act_lim(
2941                            e,
2942                            cfg,
2943                            &gate,
2944                            &up,
2945                            1.0,
2946                            1.0,
2947                            d_lim,
2948                            &mut act,
2949                            total * n_ff,
2950                        )?;
2951                        e.matmul(ffn_down, &act, total)?
2952                    }
2953                }
2954                crate::hybrid::Ffn::Moe(m) => self.moe_ffn_il(e, m, &z, total, il as u16)?,
2955            };
2956            let mut x2 = e.uninit(total * n_embd)?;
2957            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
2958            x = x2;
2959            mark(e, 3, &mut pt, &mut ph);
2960        }
2961        if prof {
2962            eprintln!(
2963                "[prime-prof] t={total} layers={} norm+qkv={:.0}ms attn={:.0}ms o_proj={:.0}ms moe={:.0}ms",
2964                hi - lo,
2965                ph[0],
2966                ph[1],
2967                ph[2],
2968                ph[3]
2969            );
2970        }
2971        Ok(x)
2972    }
2973
2974    fn step35_prime_batch_epilogue(
2975        &self,
2976        e: &Engine,
2977        x: CudaSlice<f32>,
2978        ts: &[usize],
2979        offs: &[usize],
2980        caches: &mut [&mut Cache],
2981    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
2982        let n_embd = self.cfg.n_embd as usize;
2983        let total: usize = ts.iter().sum();
2984        let mut hn = e.uninit(total * n_embd)?;
2985        e.rms_norm(
2986            &x,
2987            self.output_norm.float_data(),
2988            &mut hn,
2989            n_embd,
2990            total,
2991            self.cfg.rms_eps,
2992        )?;
2993
2994        let hidden_src = if crate::spec::spec_hpost() { &hn } else { &x };
2995        let mut out = Vec::with_capacity(ts.len());
2996        for s in 0..ts.len() {
2997            let mut hidden = e.uninit(ts[s] * n_embd)?;
2998            e.copy_view_into(
2999                &mut hidden,
3000                0,
3001                &hidden_src.slice(offs[s] * n_embd..(offs[s] + ts[s]) * n_embd),
3002                ts[s] * n_embd,
3003            )?;
3004            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3005            let mut h_seed = e.uninit(n_embd)?;
3006            e.copy_view_into(
3007                &mut h_seed,
3008                0,
3009                &hidden_src.slice(last0..last0 + n_embd),
3010                n_embd,
3011            )?;
3012            // Exactness-first: the serial reference runs the output head at m=1.
3013            let mut hlast = e.uninit(n_embd)?;
3014            e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3015            let logits = e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?;
3016            caches[s].pos += ts[s];
3017            out.push((logits, h_seed, hidden));
3018        }
3019        Ok(out)
3020    }
3021
3022    /// `seq_ends[s]` = sequence s's REQUEST-absolute end position (`cache.pos + prompt_len
3023    /// + queued_after`, computed once before any chunk loop). Only step35's SWA arm reads it,
3024    /// and it must NOT be this chunk's own length: see the note on the batched entry in
3025    /// `prime_cache_overlaid` for the window the chunk-local value opened.
3026    fn step35_prime_cache_batch(
3027        &self,
3028        e: &Engine,
3029        prompts: &[&[u32]],
3030        caches: &mut [&mut Cache],
3031        seq_ends: &[usize],
3032    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3033        assert_eq!(
3034            seq_ends.len(),
3035            prompts.len(),
3036            "step35 batched prime: one seq_end per sequence"
3037        );
3038        validate_step_prime_batch_modes(
3039            step_tp_prefill_enabled()?,
3040            step_ep_grouped_prefill_enabled()?,
3041        )?;
3042        if crate::pp::pp_host_bounce_active()
3043            && (!crate::pp::prime_pp_on() || crate::pp::pp_cuts(self.layers.len()).is_none())
3044        {
3045            return Err(
3046                "step35_prime_cache_batch: MEMRA_PP_HOST_BOUNCE=1 requires a valid prime \
3047                 stage split; refusing an unsplit remote-weight walk"
3048                    .into(),
3049            );
3050        }
3051        if !Self::step35_prime_batch_on() {
3052            return Err("step35 batched prime is disabled (MEMRA_STEP35_PRIME_BATCH=0)".into());
3053        }
3054        // Continuation chunks are admitted (positions above carry each sequence's base). The
3055        // remaining restriction is genuine: a CROSS-REQUEST batch mixing sequences at different
3056        // positions still needs per-request queued_after to place its KV, so B > 1 keeps the
3057        // fresh-prompt rule.
3058        if prompts.len() > 1 && caches.iter().any(|c| c.pos != 0) {
3059            return Err(
3060                "step35 batched prime supports continuation only at B=1; a cross-request batch \
3061                 at mixed positions requires per-request queued_after"
3062                    .into(),
3063            );
3064        }
3065
3066        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3067        for &t in &ts {
3068            assert!(
3069                t >= PRIME_MIN_T,
3070                "step35 batched prime needs T >= {PRIME_MIN_T}"
3071            );
3072        }
3073        for (s, c) in caches.iter().enumerate() {
3074            // POS-INCLUSIVE, like the walk's assert: a continuation chunk's rows land at
3075            // c.pos.., so the fresh-only `ts[s] <= max_ctx` form under-checked it.
3076            assert!(
3077                c.pos + ts[s] <= c.max_ctx,
3078                "step35 batched prime exceeds cache max_ctx"
3079            );
3080            assert!(
3081                seq_ends[s] >= c.pos + ts[s],
3082                "step35 batched prime: seq_end must cover this chunk"
3083            );
3084        }
3085        // MEMRA_STEP35_PRIME_BATCH_TSEND=1: CANARY SEAM restoring the pre-fix chunk-local
3086        // `seq_end` (this chunk's own length, which `ts[s]` used to supply here). It is suffix-
3087        // and chunk-VARIANT by construction, so the suffix byte-identity gate MUST break under
3088        // it. That is how the defect is DEMONSTRATED rather than argued: one binary, one seam,
3089        // the legacy arm fails cold-vs-rewound identity and the default arm passes. Read per
3090        // call; never on in a measured default run.
3091        let legacy_tsend = std::env::var("MEMRA_STEP35_PRIME_BATCH_TSEND").as_deref() == Ok("1");
3092        let seq_ends_eff: Vec<usize> = if legacy_tsend {
3093            ts.clone()
3094        } else {
3095            seq_ends.to_vec()
3096        };
3097        let offs: Vec<usize> = ts
3098            .iter()
3099            .scan(0usize, |a, &t| {
3100                let o = *a;
3101                *a += t;
3102                Some(o)
3103            })
3104            .collect();
3105        let total: usize = ts.iter().sum();
3106        let payload = total * self.cfg.n_embd as usize;
3107        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3108        // Positions start at each sequence's CURRENT cache position, not 0, so this entry can
3109        // prime a continuation chunk. The attention core already supports it: step35_attn_pre_wo
3110        // with Some(cache) is PRIME mode — it appends this chunk's post-rope K / raw V and
3111        // attends THROUGH the cache view — so only the hardcoded 0..t and the guard below ever
3112        // restricted it to fresh prompts.
3113        let positions: Vec<Vec<i32>> = ts
3114            .iter()
3115            .zip(caches.iter())
3116            .map(|(&t, c)| {
3117                let base = c.pos as i32;
3118                (0..t as i32).map(|i| base + i).collect()
3119            })
3120            .collect();
3121        let upload_positions =
3122            |e: &Engine| -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
3123                positions
3124                    .iter()
3125                    .map(|p| e.htod_i32(p))
3126                    .collect::<Result<_, _>>()
3127            };
3128
3129        static ONCE: std::sync::Once = std::sync::Once::new();
3130        ONCE.call_once(|| {
3131            eprintln!(
3132                "[step35-prime-batch] first concat prime: B={} tokens={total}",
3133                prompts.len()
3134            );
3135        });
3136
3137        let out = if !crate::pp::pp2_streams_off() && crate::pp::prime_pp_on() {
3138            if let Some(fence) = crate::pp::pp_cuts(self.layers.len()) {
3139                let rt = crate::pp::PpNRt::get(e)?;
3140                let n_st = fence.len() - 1;
3141                assert_eq!(
3142                    rt.n_stages(),
3143                    n_st,
3144                    "step35 prime batch stage count mismatch"
3145                );
3146                let caller_stream = e.stream();
3147                rt.fence_stages_behind(&caller_stream)?;
3148
3149                let mut slot = {
3150                    let _st0 = rt.enter(0);
3151                    let e0 = rt.engine(0, e);
3152                    let pos_ds = upload_positions(e0)?;
3153                    let x = self.embed(e0, &cat_tokens)?;
3154                    let x = self.step35_prime_batch_layers(
3155                        e0,
3156                        x,
3157                        fence[0],
3158                        fence[1],
3159                        &ts,
3160                        &offs,
3161                        &seq_ends_eff,
3162                        &pos_ds,
3163                        caches,
3164                    )?;
3165                    rt.tx(0, &x, payload)?
3166                };
3167                for s in 1..n_st - 1 {
3168                    let _st = rt.enter(s);
3169                    let es = rt.engine(s, e);
3170                    let pos_ds = upload_positions(es)?;
3171                    let x = rt.rx(s - 1, slot, payload)?;
3172                    let x = self.step35_prime_batch_layers(
3173                        es,
3174                        x,
3175                        fence[s],
3176                        fence[s + 1],
3177                        &ts,
3178                        &offs,
3179                        &seq_ends_eff,
3180                        &pos_ds,
3181                        caches,
3182                    )?;
3183                    slot = rt.tx(s, &x, payload)?;
3184                }
3185
3186                let _stl = rt.enter(n_st - 1);
3187                let el = rt.engine(n_st - 1, e);
3188                let pos_ds = upload_positions(el)?;
3189                let x = rt.rx(n_st - 2, slot, payload)?;
3190                let x = self.step35_prime_batch_layers(
3191                    el,
3192                    x,
3193                    fence[n_st - 1],
3194                    fence[n_st],
3195                    &ts,
3196                    &offs,
3197                    &seq_ends_eff,
3198                    &pos_ds,
3199                    caches,
3200                )?;
3201                let out = self.step35_prime_batch_epilogue(el, x, &ts, &offs, caches)?;
3202                rt.publish_to(n_st - 1, &caller_stream)?;
3203                crate::pp::STEP35_PRIME_BATCH_SPLITS
3204                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3205                out
3206            } else {
3207                let pos_ds = upload_positions(e)?;
3208                let x = self.embed(e, &cat_tokens)?;
3209                let x = self.step35_prime_batch_layers(
3210                    e,
3211                    x,
3212                    0,
3213                    self.layers.len(),
3214                    &ts,
3215                    &offs,
3216                    &seq_ends_eff,
3217                    &pos_ds,
3218                    caches,
3219                )?;
3220                self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3221            }
3222        } else {
3223            let pos_ds = upload_positions(e)?;
3224            let x = self.embed(e, &cat_tokens)?;
3225            let x = self.step35_prime_batch_layers(
3226                e,
3227                x,
3228                0,
3229                self.layers.len(),
3230                &ts,
3231                &offs,
3232                &seq_ends_eff,
3233                &pos_ds,
3234                caches,
3235            )?;
3236            self.step35_prime_batch_epilogue(e, x, &ts, &offs, caches)?
3237        };
3238        crate::pp::STEP35_PRIME_BATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3239        Ok(out)
3240    }
3241
3242    /// Cross-request BATCHED fresh prime (task #13, design in ARCHITECTURE-H100.md): the
3243    /// trunk's token-parallel ops (embed, norms, adds, ffn, projection GROUPS) run once on
3244    /// the CONCATENATION of B sequences — GEMMs at m = sum_T, the continuous-batching win
3245    /// the serving-lane bench attributed the remaining vLLM gap to. The stateful mixer
3246    /// CORES (QK-norm/RoPE/FA/append, conv/GDN scans) run per sequence on split projection
3247    /// buffers (D2D row copies; the mixers' own out-projections stay per-seq this
3248    /// increment). CONTINUATION primes (increment (b), 2026-07-30): cache.pos > 0 seqs
3249    /// batch the projections/FFN/lm_head exactly like fresh; the mixer cores take the
3250    /// per-seq CONTINUATION arms (Full: core_inner with carried pos_d + fa_prefill_view
3251    /// over the quantized past; Linear: the stateful pad_view twin — the same state
3252    /// carry the chunked single-seq prime rides). The fresh-only favl/gdn-vl fast paths
3253    /// stay byte-identical (gated on !carried). gemma4 models have no continuation
3254    /// prime (v0 monolithic fresh) — carried gemma4 batches return Err (caller falls
3255    /// back to single-chunk serving).
3256    /// NUMERIC CONFIG: a concat GEMM tiles K differently than per-seq GEMMs — same class
3257    /// as every prefill GEMM change; prime_batch_gate arbitrates (argmax + stream battery).
3258    pub fn prime_cache_batch(
3259        &self,
3260        e: &Engine,
3261        prompts: &[&[u32]],
3262        caches: &mut [&mut Cache],
3263    ) -> Result<Vec<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
3264        if crate::pp::pp_cuts(self.layers.len()).is_some()
3265            && !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::Pipeline)
3266        {
3267            return Err("pipeline rewrite is not qualified for batched prime".into());
3268        }
3269        if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::CarriedPrime) {
3270            if !self.rewrite_allowed(memra_gguf::execution_manifest::RewriteSurface::DecodeEager) {
3271                return Err("neither batched-prime nor eager rewrite is qualified".into());
3272            }
3273            if prompts.len() != caches.len() {
3274                return Err("prime fallback prompt/cache shape mismatch".into());
3275            }
3276            static ONCE: std::sync::Once = std::sync::Once::new();
3277            ONCE.call_once(|| {
3278                eprintln!(
3279                    "[rewrite] carried-prime.v1 unqualified; using individual native eager primes"
3280                );
3281            });
3282            return prompts
3283                .iter()
3284                .copied()
3285                .zip(caches.iter_mut())
3286                .map(|(prompt, cache)| self.prime_cache(e, prompt, cache, 0))
3287                .collect();
3288        }
3289        let cfg = &self.cfg;
3290        let n_embd = cfg.n_embd as usize;
3291        let eps = cfg.rms_eps;
3292        let b = prompts.len();
3293        assert!(b >= 1 && b == caches.len());
3294        let pos0s: Vec<usize> = caches.iter().map(|c| c.pos).collect();
3295        let carried = pos0s.iter().any(|&p| p > 0);
3296        // gemma4: refuse UNCONDITIONALLY (2026-08-07, lane/gemma4-serve-gaps). The old guard
3297        // covered only `carried` — two concurrent FRESH gemma4 prompts batched into the
3298        // generic concat attn core below (uniform geometry, no per-layer swa window, no
3299        // softcapped head): compiles, runs, wrong logits. Same silent-wrong class as the
3300        // step35 refusal beneath. The per-sequence `gemma4_prime` is the supported prefill.
3301        if self.uses_gemma_program() {
3302            return Err(
3303                "prime_cache_batch: gemma4 has no batched prime core (per-layer \
3304                        swa/global geometry, softcapped head) — use gemma4_prime per sequence"
3305                    .into(),
3306            );
3307        }
3308        // Step35 has a dedicated concat walk: the generic core below cannot express its
3309        // per-layer geometry/SWA/head gate, and under PP the dedicated path is stage-scoped.
3310        if self.uses_sliding_gated_moe_program() {
3311            // The cross-request driver hands whole requests (no chunk loop of its own), so each
3312            // sequence's request-absolute end IS its base plus its prompt length — the value
3313            // `ts[s]` happened to equal for the fresh B>=1 batches this caller admits, which is
3314            // why this arm is bit-for-bit unchanged by the seq_end threading.
3315            let seq_ends: Vec<usize> = caches
3316                .iter()
3317                .zip(prompts.iter())
3318                .map(|(c, p)| c.pos + p.len())
3319                .collect();
3320            return self.step35_prime_cache_batch(e, prompts, caches, &seq_ends);
3321        }
3322        let ts: Vec<usize> = prompts.iter().map(|p| p.len()).collect();
3323        for &t in &ts {
3324            assert!(
3325                t >= PRIME_MIN_T,
3326                "prime_cache_batch needs T >= {PRIME_MIN_T}"
3327            );
3328        }
3329        for (s, c) in caches.iter().enumerate() {
3330            assert!(
3331                c.pos + ts[s] <= c.max_ctx,
3332                "prime_cache_batch: prompt exceeds cache max_ctx"
3333            );
3334        }
3335        let total: usize = ts.iter().sum();
3336        let offs: Vec<usize> = ts
3337            .iter()
3338            .scan(0usize, |a, &t| {
3339                let o = *a;
3340                *a += t;
3341                Some(o)
3342            })
3343            .collect();
3344        // per-seq positions (fresh: 0..T_s; continuation: pos0..pos0+T_s)
3345        let pos_ds: Vec<CudaSlice<i32>> = ts
3346            .iter()
3347            .zip(&pos0s)
3348            .map(|(&t, &p0)| e.htod_i32(&(p0 as i32..(p0 + t) as i32).collect::<Vec<_>>()))
3349            .collect::<Result<_, _>>()?;
3350        // split a concat [total, dim] buffer into per-seq copies
3351        let split = |e: &Engine,
3352                     y: &CudaSlice<f32>,
3353                     dim: usize|
3354         -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3355            let mut out = Vec::with_capacity(b);
3356            for s in 0..b {
3357                let mut ys = e.uninit(ts[s] * dim)?;
3358                e.copy_view_into(
3359                    &mut ys,
3360                    0,
3361                    &y.slice(offs[s] * dim..(offs[s] + ts[s]) * dim),
3362                    ts[s] * dim,
3363                )?;
3364                out.push(ys);
3365            }
3366            Ok(out)
3367        };
3368
3369        let cat_tokens: Vec<u32> = prompts.iter().flat_map(|p| p.iter().copied()).collect();
3370        let mut x = self.embed(e, &cat_tokens)?; // [total, n_embd]
3371        for (il, layer) in self.layers.iter().enumerate() {
3372            let mut h = e.uninit(total * n_embd)?;
3373            let mut hx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3374            e.rms_norm_f16out(
3375                &x,
3376                layer.attn_norm.float_data(),
3377                &mut h,
3378                &mut hx16,
3379                n_embd,
3380                total,
3381                eps,
3382            )?;
3383            // mixer: projection GROUP on the concat (m = total), stateful core per seq
3384            let mut mixed = e.uninit(total * n_embd)?;
3385            match &layer.mixer {
3386                Mixer::Full(fa) => {
3387                    let g3 = e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], &h, &hx16, total)?;
3388                    // task #18 (attn side): the WHOLE attn core is varlen for fresh gated
3389                    // batches — split/QK-norm/RoPE/append (attn_pre_vl8, view inputs: the
3390                    // q/k/v split copies vanish) + ONE varlen FA. Per-block math identical
3391                    // everywhere (bit-gateable); MEMRA_FA_VL=0 or a non-bf16kv config falls
3392                    // back to the per-seq dispatch.
3393                    let geometry = self.cfg.full_attention_geometry_at(il as u32);
3394                    let (n_head, n_head_kv, head_dim) = (
3395                        geometry.n_head as usize,
3396                        geometry.n_head_kv as usize,
3397                        geometry.head_dim_k as usize,
3398                    );
3399                    let fa_scale = geometry.attention_scale();
3400                    let use_favl = !carried
3401                        && (2..=8).contains(&b)
3402                        && (head_dim == 256 || head_dim == 128)
3403                        && geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ
3404                        && std::env::var("MEMRA_NOFA").is_err()
3405                        && std::env::var("MEMRA_FA_FLOOR").is_err()
3406                        && std::env::var("MEMRA_FA_PP_W2").as_deref() != Ok("1")
3407                        && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0")
3408                        && std::env::var("MEMRA_FA_VL").as_deref() != Ok("0");
3409                    if use_favl {
3410                        let (qf_w, kf_w, vf_w) = (
3411                            fa.wq.out_features(),
3412                            fa.wk.out_features(),
3413                            fa.wv.out_features(),
3414                        );
3415                        // Same bounds contract as `Engine::q_gate_split`, applied to the varlen
3416                        // twin's PER-TOKEN stride. `attn_pre_vl8` takes raw device pointers so it
3417                        // cannot check its own extents; `qf_w` is the wq out-features that set
3418                        // them, and `q_gate_split_vl` reads 2*head_dim per head out of it.
3419                        memra_gguf::config::check_fused_q_gate_extent(qf_w, head_dim, n_head, 1)?;
3420                        struct APre {
3421                            q: CudaSlice<f32>,
3422                            gate: Option<CudaSlice<f32>>,
3423                            qn: CudaSlice<f32>,
3424                            kn: CudaSlice<f32>,
3425                        }
3426                        let mut aps = Vec::with_capacity(b);
3427                        for &t in ts.iter().take(b) {
3428                            aps.push(APre {
3429                                q: e.uninit(t * n_head * head_dim)?,
3430                                gate: Some(e.uninit(t * n_head * head_dim)?),
3431                                qn: e.uninit(t * n_head * head_dim)?,
3432                                kn: e.uninit(t * n_head_kv * head_dim)?,
3433                            });
3434                        }
3435                        let (kv_dim_k, kv_dim_v, ktb, vtb) = {
3436                            let kvl = caches[0].kv[il].as_ref().unwrap();
3437                            (kvl.kv_dim_k, kvl.kv_dim_v, kvl.k_tok_bytes, kvl.v_tok_bytes)
3438                        };
3439                        let pargs: Vec<crate::AttnPreVl> = (0..b)
3440                            .map(|s| {
3441                                let (o, t) = (offs[s], ts[s]);
3442                                let kvl = caches[s].kv[il].as_ref().unwrap();
3443                                assert!(
3444                                    kvl.len == 0 && kvl.len + t <= caches[s].max_ctx,
3445                                    "prime_cache_batch attn vl: fresh + capacity"
3446                                );
3447                                crate::AttnPreVl {
3448                                    qf: e.addr_f32v(&g3[0].slice(o * qf_w..(o + t) * qf_w)),
3449                                    kf: e.addr_f32v(&g3[1].slice(o * kf_w..(o + t) * kf_w)),
3450                                    vf: e.addr_f32v(&g3[2].slice(o * vf_w..(o + t) * vf_w)),
3451                                    q: e.addr_f32(&aps[s].q),
3452                                    gate: e.addr_f32(aps[s].gate.as_ref().unwrap()),
3453                                    qn: e.addr_f32(&aps[s].qn),
3454                                    kn: e.addr_f32(&aps[s].kn),
3455                                    kc: e.addr_u8(&kvl.k),
3456                                    vc: e.addr_u8(&kvl.v),
3457                                    t: t as i32,
3458                                    pad: 0,
3459                                }
3460                            })
3461                            .collect();
3462                        e.attn_pre_vl8(
3463                            &pargs,
3464                            fa.q_norm.float_data(),
3465                            fa.k_norm.float_data(),
3466                            head_dim,
3467                            geometry.n_rot as usize,
3468                            n_head,
3469                            n_head_kv,
3470                            self.cfg.rms_eps,
3471                            geometry.rope_base,
3472                            1.0,
3473                            kv_dim_k,
3474                            kv_dim_v,
3475                            ktb,
3476                            vtb,
3477                        )?;
3478                        for s in 0..b {
3479                            let kvl = caches[s].kv[il].as_mut().unwrap();
3480                            kvl.len += ts[s];
3481                            let new_len = kvl.len as i32;
3482                            e.set_i32_one(&mut kvl.len_d, new_len)?;
3483                        }
3484                        let mut attns = Vec::with_capacity(b);
3485                        let mut mirrors = Vec::with_capacity(b);
3486                        for &t in ts.iter().take(b) {
3487                            attns.push(e.uninit(t * n_head * head_dim)?);
3488                            let n = t * n_head_kv * head_dim;
3489                            mirrors.push((e.alloc_u8_uninit(n * 2)?, e.alloc_u8_uninit(n * 2)?));
3490                        }
3491                        // FA3 batched twin (round 31): TMA-swizzled wgmma vl when the
3492                        // promoted single-seq config is on; else the mma favl.
3493                        let fa3_on = match std::env::var("MEMRA_FA3").as_deref() {
3494                            Ok("0") => false,
3495                            // Same refusal as the single-seq twin (lib.rs fa_prefill): the
3496                            // batched bf16 stage reaches func("f32_to_bf16_bulk"), absent on a
3497                            // portable build.
3498                            Ok("1") => {
3499                                crate::refuse_portable_force(
3500                                    "MEMRA_FA3=1",
3501                                    "the sm_90a fa3/bf16 kernels",
3502                                );
3503                                true
3504                            }
3505                            _ => cfg!(memra_hopper_mma),
3506                        };
3507                        if fa3_on {
3508                            let mut q16s = Vec::with_capacity(b);
3509                            let mut v16s = Vec::with_capacity(b);
3510                            for s in 0..b {
3511                                let t = ts[s];
3512                                let mut q16 = e.alloc_u8_uninit(t * n_head * head_dim * 2)?;
3513                                e.f32_to_bf16_into(&aps[s].qn, &mut q16, t * n_head * head_dim)?;
3514                                let mut k16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3515                                e.f32_to_bf16_into(&aps[s].kn, &mut k16, t * n_head_kv * head_dim)?;
3516                                let mut v16 = e.alloc_u8_uninit(t * n_head_kv * head_dim * 2)?;
3517                                e.f32_to_bf16_v(
3518                                    &g3[2].slice(offs[s] * vf_w..(offs[s] + t) * vf_w),
3519                                    &mut v16,
3520                                    t * n_head_kv * head_dim,
3521                                )?;
3522                                q16s.push(q16);
3523                                v16s.push((k16, v16));
3524                            }
3525                            let mut qp = [core::ptr::null::<core::ffi::c_void>(); 8];
3526                            let mut kp = qp;
3527                            let mut vp = qp;
3528                            let mut op = [core::ptr::null_mut::<f32>(); 8];
3529                            let mut tsv = [0i32; 8];
3530                            for s in 0..b {
3531                                qp[s] = e.addr_u8(&q16s[s]) as *const core::ffi::c_void;
3532                                kp[s] = e.addr_u8(&v16s[s].0) as *const core::ffi::c_void;
3533                                vp[s] = e.addr_u8(&v16s[s].1) as *const core::ffi::c_void;
3534                                op[s] = e.addr_f32(&attns[s]) as *mut f32;
3535                                tsv[s] = ts[s] as i32;
3536                            }
3537                            let rc = unsafe {
3538                                crate::fa3_vl_raw(
3539                                    qp.as_ptr(),
3540                                    kp.as_ptr(),
3541                                    vp.as_ptr(),
3542                                    op.as_ptr(),
3543                                    tsv.as_ptr(),
3544                                    b as i32,
3545                                    n_head as i32,
3546                                    n_head_kv as i32,
3547                                    head_dim as i32,
3548                                    fa_scale,
3549                                    e.stream().cu_stream() as *mut core::ffi::c_void,
3550                                )
3551                            };
3552                            if rc != 0 {
3553                                return Err(format!("memra_fa3_vl rc={rc}").into());
3554                            }
3555                        } else {
3556                            let fargs: Vec<crate::FaSeqVl> = (0..b)
3557                                .map(|s| crate::FaSeqVl {
3558                                    q: e.addr_f32(&aps[s].qn),
3559                                    k16: e.addr_u8(&mirrors[s].0),
3560                                    v16: e.addr_u8(&mirrors[s].1),
3561                                    o: e.addr_f32(&attns[s]),
3562                                    kf: e.addr_f32(&aps[s].kn),
3563                                    vf: e.addr_f32v(
3564                                        &g3[2].slice(offs[s] * vf_w..(offs[s] + ts[s]) * vf_w),
3565                                    ),
3566                                    t: ts[s] as i32,
3567                                    pad: 0,
3568                                })
3569                                .collect();
3570                            e.fa_prefill_vl8(&fargs, head_dim, n_head, n_head_kv, fa_scale)?;
3571                        }
3572                        for (s, attn) in attns.into_iter().enumerate() {
3573                            let (attn_g, ag16) = self.full_attn_prime_post_fa(
3574                                e,
3575                                attn,
3576                                &aps[s].gate,
3577                                ts[s],
3578                                n_head,
3579                                head_dim,
3580                            )?;
3581                            let mut done = false;
3582                            if let Some(xh) = &ag16 {
3583                                done = e.try_f16_gemm_pre_into_off(
3584                                    &fa.wo,
3585                                    xh,
3586                                    ts[s],
3587                                    &mut mixed,
3588                                    offs[s] * n_embd,
3589                                )?;
3590                            }
3591                            if !done {
3592                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3593                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3594                            }
3595                        }
3596                    } else {
3597                        let mut parts: Vec<Vec<CudaSlice<f32>>> =
3598                            (0..b).map(|_| Vec::new()).collect();
3599                        for (w, y) in [&fa.wq, &fa.wk, &fa.wv].iter().zip(g3) {
3600                            for (s, ys) in split(e, &y, w.out_features())?.into_iter().enumerate() {
3601                                parts[s].push(ys);
3602                            }
3603                        }
3604                        for (s, g3s) in parts.into_iter().enumerate() {
3605                            // task #16 gather removal: wo writes into `mixed` at offs[s] directly.
3606                            let (attn_g, ag16) = self.full_attn_prime_core_inner(
3607                                e, fa, g3s, &pos_ds[s], ts[s], caches[s], il,
3608                            )?;
3609                            let mut done = false;
3610                            if let Some(xh) = &ag16 {
3611                                done = e.try_f16_gemm_pre_into_off(
3612                                    &fa.wo,
3613                                    xh,
3614                                    ts[s],
3615                                    &mut mixed,
3616                                    offs[s] * n_embd,
3617                                )?;
3618                            }
3619                            if !done {
3620                                let m = e.matmul(&fa.wo, &attn_g, ts[s])?;
3621                                e.copy_into(&mut mixed, offs[s] * n_embd, &m, ts[s] * n_embd)?;
3622                            }
3623                        }
3624                    }
3625                }
3626                Mixer::Mla(_) => crate::hybrid::mla_forward_unimplemented(),
3627                Mixer::Linear(la) => {
3628                    // task #16: NO split copies (cores read row-offset views of the concat
3629                    // outputs; out-GEMMs write into `mixed` at offs[s]). task #18: the core
3630                    // itself is BATCHED — per-seq prep/K1-K3, then ONE varlen K4 + ONE
3631                    // varlen K5 launch for all sequences.
3632                    let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
3633                    let g4 = e.matmul_group_xh(&ws, &h, &hx16, total)?;
3634                    let outs =
3635                        self.linear_attn_prime_core_batch(e, la, &g4, &offs, &ts, caches, il)?;
3636                    for (s, (gn, gn16)) in outs.into_iter().enumerate() {
3637                        let (o, t) = (offs[s], ts[s]);
3638                        let mut done = false;
3639                        if let Some(xh) = &gn16 {
3640                            done = e.try_f16_gemm_pre_into_off(
3641                                &la.ssm_out,
3642                                xh,
3643                                t,
3644                                &mut mixed,
3645                                o * n_embd,
3646                            )?;
3647                        }
3648                        if !done {
3649                            let m = e.matmul(&la.ssm_out, &gn, t)?;
3650                            e.copy_into(&mut mixed, o * n_embd, &m, t * n_embd)?;
3651                        }
3652                    }
3653                }
3654            }
3655            let mut x1 = e.uninit(total * n_embd)?;
3656            let mut z = e.uninit(total * n_embd)?;
3657            let mut zx16 = e.alloc_u8_uninit(total * n_embd * 2)?;
3658            e.add_rms_norm_f16out(
3659                &x,
3660                &mixed,
3661                layer.post_attn_norm.float_data(),
3662                &mut x1,
3663                &mut z,
3664                &mut zx16,
3665                n_embd,
3666                total,
3667                eps,
3668            )?;
3669            let ffn_out = match &layer.ffn {
3670                crate::hybrid::Ffn::Dense {
3671                    ffn_gate,
3672                    ffn_up,
3673                    ffn_down,
3674                } => {
3675                    let n_ff = ffn_gate.out_features();
3676                    let mut g2 = e.matmul_group_xh(&[ffn_gate, ffn_up], &z, &zx16, total)?;
3677                    let up = g2.pop().unwrap();
3678                    let gate = g2.pop().unwrap();
3679                    let mut act = e.uninit(total * n_ff)?;
3680                    // task #17 (batch trunk): silu twin emits the down GEMM's fp16 operand
3681                    // in-epilogue (nsys round-26: this trunk still paid 32 cvt passes).
3682                    // A clamped layer must skip the plain-SiLU twin (see prime_chunk's note).
3683                    let d_lim = self.cfg.clamp_shexp_at(il as u32);
3684                    if Self::f16out_on(e, total) && self.cfg.m3.is_none() && d_lim.is_none() {
3685                        let mut a16 = e.alloc_u8_uninit(total * n_ff * 2)?;
3686                        e.silu_mul_f16out(&gate, &up, &mut act, &mut a16, total * n_ff)?;
3687                        match e.try_f16_gemm_pre(ffn_down, &a16, total)? {
3688                            Some(y) => y,
3689                            None => e.matmul(ffn_down, &act, total)?,
3690                        }
3691                    } else {
3692                        Self::ffn_act_lim(
3693                            e,
3694                            &self.cfg,
3695                            &gate,
3696                            &up,
3697                            1.0,
3698                            1.0,
3699                            d_lim,
3700                            &mut act,
3701                            total * n_ff,
3702                        )?;
3703                        e.matmul(ffn_down, &act, total)?
3704                    }
3705                }
3706                crate::hybrid::Ffn::Moe(m) => {
3707                    self.moe_ffn_il_prefill(e, m, &z, total, il as u16)?
3708                }
3709            };
3710            let mut x2 = e.uninit(total * n_embd)?;
3711            e.add(&x1, &ffn_out, &mut x2, total * n_embd)?;
3712            x = x2;
3713        }
3714        // epilogue per seq (identical math to prime_chunk: norm all rows, lm_head on last row)
3715        let mut hn = e.uninit(total * n_embd)?;
3716        e.rms_norm(
3717            &x,
3718            self.output_norm.float_data(),
3719            &mut hn,
3720            n_embd,
3721            total,
3722            eps,
3723        )?;
3724        // batched lm_head (nsys round-26: B sequential m=1 matvecs re-read the 600MB
3725        // lm_head weight B times — 2.2ms at B=6): gather the B last rows and run ONE
3726        // m=B GEMM (f16 lane; falls back to the per-seq matvec when no mirror). The
3727        // f16-vs-mmvq logits delta is the prefill GEMM numeric class — prime_batch_gate's
3728        // argmax battery arbitrates, same as every other prefill GEMM change.
3729        let mut hcat = e.uninit(b * n_embd)?;
3730        for s in 0..b {
3731            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3732            e.copy_view_into(
3733                &mut hcat,
3734                s * n_embd,
3735                &hn.slice(last0..last0 + n_embd),
3736                n_embd,
3737            )?;
3738        }
3739        let logits_cat = if b >= 2 {
3740            e.try_f16_gemm(&self.output, &hcat, b)?
3741        } else {
3742            None
3743        };
3744        let logits_host: Option<Vec<f32>> = match &logits_cat {
3745            Some(lc) => Some(e.dtoh(lc)?),
3746            None => None,
3747        };
3748        let n_vocab = self.output.out_features();
3749        let mut hidden_all = if crate::spec::spec_hpost() {
3750            split(e, &hn, n_embd)?
3751        } else {
3752            split(e, &x, n_embd)?
3753        };
3754        let mut out = Vec::with_capacity(b);
3755        for s in 0..b {
3756            let last0 = (offs[s] + ts[s] - 1) * n_embd;
3757            let mut h_seed = e.uninit(n_embd)?;
3758            if !crate::spec::spec_hpost() {
3759                e.copy_view_into(&mut h_seed, 0, &x.slice(last0..last0 + n_embd), n_embd)?;
3760            } else {
3761                e.copy_view_into(&mut h_seed, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3762            }
3763            let logits = match &logits_host {
3764                Some(lh) => lh[s * n_vocab..(s + 1) * n_vocab].to_vec(),
3765                None => {
3766                    let mut hlast = e.uninit(n_embd)?;
3767                    e.copy_view_into(&mut hlast, 0, &hn.slice(last0..last0 + n_embd), n_embd)?;
3768                    e.dtoh(&e.matmul(&self.output, &hlast, 1)?)?
3769                }
3770            };
3771            caches[s].pos += ts[s];
3772            out.push((logits, h_seed, hidden_all.remove(0)));
3773        }
3774        Ok(out)
3775    }
3776
3777    /// `full_attn` (batched prefill mixer) + the cache side-effect: append the T post-RoPE K/V
3778    /// rows into the resident quantized KV cache (q8_0 K / q5_1 V) and advance len/len_d. Row
3779    /// bytes are BIT-IDENTICAL to the decode append (same per-warp quant kernel per row; the
3780    /// batched `append_kv_quantized_rows` runs that exact warp math on a (block, token) grid).
3781    /// The attention itself is unchanged prefill math (fa_prefill over the f32 K/V).
3782    ///
3783    /// `seq_end` = the ABSOLUTE end position of the WHOLE prime request (`cache.pos + prompt_len`
3784    /// measured BEFORE the chunk loop starts), i.e. a chunk-size-invariant property of the request.
3785    /// Only step35's SWA arm reads it (`step35_attn_pre_wo`'s doc note explains why keying on the
3786    /// chunk's own `t_kv` made the output depend on MEMRA_PRIME_CHUNK); every other arch ignores it.
3787    #[allow(clippy::too_many_arguments)]
3788    fn full_attn_prime(
3789        &self,
3790        e: &Engine,
3791        fa: &FullAttnLayer,
3792        h: &CudaSlice<f32>,
3793        hx: Option<&CudaSlice<u8>>,
3794        pos_d: &CudaSlice<i32>,
3795        t: usize,
3796        cache: &mut Cache,
3797        il: usize,
3798        seq_end: usize,
3799    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3800        if self.uses_sliding_gated_moe_program() {
3801            return self.step35_attn_prime(e, fa, h, hx, pos_d, t, cache, il, seq_end);
3802        }
3803        // PROJ/CORE SPLIT (task #13, 2026-07-26): the q/k/v group projection is hoisted so
3804        // the cross-request batch driver can run it at m = sum_T over concatenated tokens;
3805        // this single-seq path composes proj+core identically (byte-for-byte the old body).
3806        // task #14: `hx` = the norm-fused fp16 twin of `h` (skips the convert launch).
3807        let g3 = match hx {
3808            Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
3809            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
3810        };
3811        self.full_attn_prime_core(e, fa, g3, pos_d, t, cache, il)
3812    }
3813
3814    /// Everything after the q/k/v projections (split/QK-norm/RoPE/FA/gate/append + wo).
3815    /// `g3` = matmul_group([wq, wk, wv]) output rows for THIS sequence.
3816    /// Composes inner + wo (== the pre-split core; every existing caller unchanged).
3817    fn full_attn_prime_core(
3818        &self,
3819        e: &Engine,
3820        fa: &FullAttnLayer,
3821        g3: Vec<CudaSlice<f32>>,
3822        pos_d: &CudaSlice<i32>,
3823        t: usize,
3824        cache: &mut Cache,
3825        il: usize,
3826    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3827        let (attn_g, ag16) = self.full_attn_prime_core_inner(e, fa, g3, pos_d, t, cache, il)?;
3828        if let Some(xh) = &ag16 {
3829            if let Some(y) = e.try_f16_gemm_pre(&fa.wo, xh, t)? {
3830                return Ok(y);
3831            }
3832        }
3833        Ok(e.matmul(&fa.wo, &attn_g, t)?)
3834    }
3835
3836    fn full_attn_prime_core_inner(
3837        &self,
3838        e: &Engine,
3839        fa: &FullAttnLayer,
3840        g3: Vec<CudaSlice<f32>>,
3841        pos_d: &CudaSlice<i32>,
3842        t: usize,
3843        cache: &mut Cache,
3844        il: usize,
3845    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
3846        let cfg = &self.cfg;
3847        let geometry = cfg.full_attention_geometry_at(il as u32);
3848        let n_head = geometry.n_head as usize;
3849        let n_head_kv = geometry.n_head_kv as usize;
3850        let head_dim = geometry.head_dim_k as usize;
3851        let scale = geometry.attention_scale();
3852        let (pre, base_len) = self.full_attn_prime_pre_fa(e, fa, g3, pos_d, t, cache, il)?;
3853        let AttnPre { q, k, v, gate } = pre;
3854        let mut attn = e.uninit(t * n_head * head_dim)?;
3855        self.full_attn_prime_fa_dispatch(
3856            e, &q, &k, &v, &mut attn, base_len, t, cache, il, head_dim, n_head, n_head_kv, scale,
3857        )?;
3858        self.full_attn_prime_post_fa(e, attn, &gate, t, n_head, head_dim)
3859    }
3860
3861    /// task #18 (attn side): projections tail through KV append — everything before the
3862    /// attention kernel. Returns the post-rope q/k, v, optional out-gate, and the KV rows
3863    /// present BEFORE this chunk's append (base_len; 0 == fresh).
3864    #[allow(clippy::type_complexity)]
3865    fn full_attn_prime_pre_fa(
3866        &self,
3867        e: &Engine,
3868        fa: &FullAttnLayer,
3869        mut g3: Vec<CudaSlice<f32>>,
3870        pos_d: &CudaSlice<i32>,
3871        t: usize,
3872        cache: &mut Cache,
3873        il: usize,
3874    ) -> Result<(AttnPre, usize), Box<dyn std::error::Error>> {
3875        let cfg = &self.cfg;
3876        let geometry = cfg.full_attention_geometry_at(il as u32);
3877        let n_head = geometry.n_head as usize;
3878        let n_head_kv = geometry.n_head_kv as usize;
3879        let head_dim = geometry.head_dim_k as usize;
3880        let eps = cfg.rms_eps;
3881
3882        // qwen35 fuses [q|gate] per head in wq (2*head_dim stride); M3/Hy3 have NO output gate
3883        // (attention_output_gate=false) — wq out = n_head*head_dim exactly, and q_gate_split
3884        // would read 2x out of bounds. `gated` keys both the split and the sigmoid epilogue.
3885        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
3886        let v = g3.pop().unwrap();
3887        let mut k = g3.pop().unwrap();
3888        let qf = g3.pop().unwrap();
3889        let (mut q, gate) = if gated {
3890            let mut q = e.uninit(t * n_head * head_dim)?;
3891            let mut gate = e.uninit(t * n_head * head_dim)?;
3892            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
3893            (q, Some(gate))
3894        } else {
3895            (qf, None)
3896        };
3897
3898        let mut qn = e.uninit(t * n_head * head_dim)?;
3899        e.rms_norm(
3900            &q,
3901            fa.q_norm.float_data(),
3902            &mut qn,
3903            head_dim,
3904            n_head * t,
3905            eps,
3906        )?;
3907        q = qn;
3908        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
3909        e.rms_norm(
3910            &k,
3911            fa.k_norm.float_data(),
3912            &mut kn,
3913            head_dim,
3914            n_head_kv * t,
3915            eps,
3916        )?;
3917        k = kn;
3918        let rope_dims = geometry.n_rot as usize;
3919        e.rope_neox(
3920            &mut q,
3921            pos_d,
3922            head_dim,
3923            rope_dims,
3924            n_head,
3925            t,
3926            geometry.rope_base,
3927            1.0,
3928        )?;
3929        e.rope_neox(
3930            &mut k,
3931            pos_d,
3932            head_dim,
3933            rope_dims,
3934            n_head_kv,
3935            t,
3936            geometry.rope_base,
3937            1.0,
3938        )?;
3939
3940        // CACHE SIDE-EFFECT: append the T post-rope K/V token rows (token-major [T, kv_dim] ==
3941        // the cache row layout) quantized into cache.kv[il], then advance len + device len_d.
3942        {
3943            let kvl = cache.kv[il].as_mut().unwrap();
3944            assert!(kvl.len + t <= cache.max_ctx, "prime_cache: KV overflow");
3945            e.append_kv_quantized_rows(
3946                &k,
3947                &v,
3948                &mut kvl.k,
3949                &mut kvl.v,
3950                kvl.len,
3951                t,
3952                kvl.kv_dim_k,
3953                kvl.kv_dim_v,
3954                kvl.k_tok_bytes,
3955                kvl.v_tok_bytes,
3956                crate::Engine::kv_fp8_on(),
3957            )?;
3958            kvl.len += t;
3959            let new_len = kvl.len as i32;
3960            e.set_i32_one(&mut kvl.len_d, new_len)?;
3961        }
3962
3963        let base_len = {
3964            let kvl = cache.kv[il].as_ref().unwrap();
3965            kvl.len - t // KV rows present BEFORE this chunk's append above
3966        };
3967        Ok((AttnPre { q, k, v, gate }, base_len))
3968    }
3969
3970    /// batched prefill attention. FRESH prime (no past KV): unchanged forward_last math over
3971    /// the f32 K/V of this batch. CONTINUATION chunk (past KV present): the chunk's queries
3972    /// must attend to [0 .. base+t) — run fa_prefill_view over the resident QUANTIZED cache
3973    /// (the spec-verify pattern; kernel's causal mask offsets by T_kv-T). Numerically this
3974    /// reads q8_0/q5_1-dequantized K/V for the past AND the current chunk — the same class as
3975    /// decode reading the cache; the run-gen/first-16 battery is the accuracy authority.
3976    #[allow(clippy::too_many_arguments)]
3977    fn full_attn_prime_fa_dispatch(
3978        &self,
3979        e: &Engine,
3980        q: &CudaSlice<f32>,
3981        k: &CudaSlice<f32>,
3982        v: &CudaSlice<f32>,
3983        attn: &mut CudaSlice<f32>,
3984        base_len: usize,
3985        t: usize,
3986        cache: &mut Cache,
3987        il: usize,
3988        head_dim: usize,
3989        n_head: usize,
3990        n_head_kv: usize,
3991        scale: f32,
3992    ) -> Result<(), Box<dyn std::error::Error>> {
3993        // GRAIN-FREE CHUNK-INVARIANCE FIX (lane/chunkinv-flip, 2026-08-05): the base_len == 0
3994        // f32 special case (fa_prefill over this batch's f32 K/V) is DROPPED. pre_fa appends
3995        // the chunk's quantized rows into cache.kv[il] BEFORE this dispatch, so chunk 0 can
3996        // attend through the quantized cache exactly like every later chunk (quantize-then-
3997        // attend). One numeric class for every row => the chunk size cannot decide where a
3998        // precision edge falls, so chunked prefill is reduction-order-stable with NO grain
3999        // knob (the stronger fix VERDICT.md filed; supersedes the MEMRA_PRIME_INVARIANT door's
4000        // pin-the-boundary approach).
4001        // MEMRA_PRIME_F32CHUNK0=1 is the ROLLBACK SEAM to the old arithmetic (chunk 0 attends
4002        // f32 K/V) — flags-doctrine rollback door AND the chunkinv gate's canary injection:
4003        // with the fix unconditional, only re-introducing the class edge can prove the gate
4004        // still detects the mechanism. Never on in a measured default run.
4005        if base_len == 0 && std::env::var("MEMRA_PRIME_F32CHUNK0").as_deref() == Ok("1") {
4006            if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4007                e.sdpa_naive(
4008                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4009                )?;
4010            } else {
4011                e.fa_prefill(
4012                    q, k, v, attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4013                )?;
4014            }
4015            return Ok(());
4016        }
4017        let kvl = cache.kv[il].as_ref().unwrap();
4018        let t_kv = base_len + t;
4019        let k_view = e.view_u8(&kvl.k, t_kv * kvl.k_tok_bytes);
4020        let v_view = e.view_u8(&kvl.v, t_kv * kvl.v_tok_bytes);
4021        // fa_prefill_q/_qw twins are stamped for head_dim 256 (qwen35) and 128 (M3) only;
4022        // other dims (and MEMRA_NOFA) take the naive quantized-view SDPA — SAME cache bytes,
4023        // same numeric class, so the uniform contract holds on the fallback too.
4024        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4025            e.sdpa_naive_quantized_view(
4026                q,
4027                &k_view,
4028                &v_view,
4029                attn,
4030                head_dim,
4031                n_head,
4032                n_head_kv,
4033                t,
4034                t_kv,
4035                scale,
4036                true,
4037                kvl.k_tok_bytes,
4038                kvl.v_tok_bytes,
4039            )?;
4040            return Ok(());
4041        }
4042        // ARC B (2026-07-05): dequant-once workspace, DEFAULT ON. fa_prefill_q's inline
4043        // dequant re-reads+re-dequants the whole quantized KV stream from every one of the
4044        // T/64 x n_head CTAs (64x+ redundant at chunk=4096; 30.5% of the 32k prime wall).
4045        // fa_prefill_view_ws dequants K/V ONCE into a resident bf16 workspace then runs the
4046        // bit-identical bf16 twin (fa_prefill_qw) — same staged values, same FP order, token-
4047        // identical output (gate: MEMRA_PRIME_CHUNK=4096 ws-on vs ws-off vs monolithic).
4048        // MEMRA_PRIME_DEQW=0 reverts to the inline-dequant kernel.
4049        let deqw = std::env::var("MEMRA_PRIME_DEQW")
4050            .map(|v| v != "0")
4051            .unwrap_or(true);
4052        if deqw {
4053            e.fa_prefill_view_ws(
4054                q,
4055                &k_view,
4056                &v_view,
4057                attn,
4058                head_dim,
4059                n_head,
4060                n_head_kv,
4061                t,
4062                t_kv,
4063                scale,
4064                true,
4065                kvl.k_tok_bytes,
4066                kvl.v_tok_bytes,
4067                crate::Engine::kv_fp8_on(),
4068            )?;
4069        } else {
4070            e.fa_prefill_view(
4071                q,
4072                &k_view,
4073                &v_view,
4074                attn,
4075                head_dim,
4076                n_head,
4077                n_head_kv,
4078                t,
4079                t_kv,
4080                scale,
4081                true,
4082                kvl.k_tok_bytes,
4083                kvl.v_tok_bytes,
4084                crate::Engine::kv_fp8_on(),
4085            )?;
4086        }
4087        Ok(())
4088    }
4089
4090    /// task #17: sig_mul_f16out fuses [sigmoid + mul + f16 convert] into one launch
4091    /// (bit-identical composition) and hands wo its fp16 operand directly.
4092    fn full_attn_prime_post_fa(
4093        &self,
4094        e: &Engine,
4095        attn: CudaSlice<f32>,
4096        gate: &Option<CudaSlice<f32>>,
4097        t: usize,
4098        n_head: usize,
4099        head_dim: usize,
4100    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4101        let (attn_g, ag16) = match gate {
4102            Some(gate) => {
4103                let n = t * n_head * head_dim;
4104                let mut ag = e.uninit(n)?;
4105                if Self::f16out_on(e, t) {
4106                    let mut a16 = e.alloc_u8_uninit(n * 2)?;
4107                    e.sig_mul_f16out(&attn, gate, &mut ag, &mut a16, n)?;
4108                    (ag, Some(a16))
4109                } else {
4110                    let mut gsig = e.uninit(n)?;
4111                    e.sigmoid(gate, &mut gsig, n)?;
4112                    e.mul(&attn, &gsig, &mut ag, n)?;
4113                    (ag, None)
4114                }
4115            }
4116            None => (attn, None),
4117        };
4118        Ok((attn_g, ag16))
4119    }
4120
4121    /// STATEFUL batched linear-attention prime: `linear_attn`'s prefill-dispatch pass (normal
4122    /// `e.matmul` — GEMM at m>=16 — plus the prefill repack/L2/glog kernels) but with the state
4123    /// carried THROUGH the cache like the spec verify does: carried-ring conv
4124    /// (ssm_conv1d_tm_state writes the final ring back) + ONE gdn_scan from cache.recur[il]'s
4125    /// current state (zero at a fresh prime) whose final state ping-pongs back into the cache.
4126    /// Wiring mirrors `linear_attn_verify_t` (spec.rs); dispatch mirrors `linear_attn` (prefill).
4127    fn linear_attn_prime(
4128        &self,
4129        e: &Engine,
4130        la: &LinearAttnLayer,
4131        h: &CudaSlice<f32>,
4132        hx: Option<&CudaSlice<u8>>,
4133        t: usize,
4134        cache: &mut Cache,
4135        il: usize,
4136    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4137        // PROJ/CORE SPLIT (task #13): see full_attn_prime — same hoist for the GDN 4-tuple.
4138        let ws = [&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha];
4139        let g4 = match hx {
4140            Some(xh) => e.matmul_group_xh(&ws, h, xh, t)?,
4141            None => e.matmul_group(&ws, h, t)?,
4142        };
4143        self.linear_attn_prime_core(e, la, g4, t, cache, il)
4144    }
4145
4146    /// Everything after the GDN 4-tuple projections (conv/chunk stack/gated norm + ssm_out).
4147    fn linear_attn_prime_core(
4148        &self,
4149        e: &Engine,
4150        la: &LinearAttnLayer,
4151        mut g4: Vec<CudaSlice<f32>>,
4152        t: usize,
4153        cache: &mut Cache,
4154        il: usize,
4155    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4156        self.linear_attn_prime_core_pad(e, la, g4.drain(..).collect(), t, cache, il, None)
4157    }
4158
4159    /// task #14: `pad_len` = device true length for PADDED prime graphs. Pads become
4160    /// identity GDN steps (gdn_pad_mask zeroes beta/g_log past the true length) and the
4161    /// conv ring writes back from the true tail. None = classic path, byte-identical.
4162    #[allow(clippy::too_many_arguments)]
4163    fn linear_attn_prime_core_pad_inner(
4164        &self,
4165        e: &Engine,
4166        la: &LinearAttnLayer,
4167        mut g4: Vec<CudaSlice<f32>>,
4168        t: usize,
4169        cache: &mut Cache,
4170        il: usize,
4171        pad_len: Option<&CudaSlice<i32>>,
4172    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4173        // shim over the view twin (task #16): full-range views of the owned buffers.
4174        let geometry = la.geometry;
4175        let d_state = geometry.key_head_dim as usize;
4176        let num_k = geometry.key_heads as usize;
4177        let num_v = geometry.value_heads as usize;
4178        let key_dim = d_state * num_k;
4179        let value_dim = geometry.value_head_dim as usize * num_v;
4180        let conv_dim = key_dim * 2 + value_dim;
4181        let alpha = g4.pop().unwrap(); // [T, num_v]
4182        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4183        let z = g4.pop().unwrap(); // [T, value_dim]
4184        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4185        self.linear_attn_prime_core_pad_view(
4186            e,
4187            la,
4188            &qkv_mixed.slice(0..t * conv_dim),
4189            &z.slice(0..t * value_dim),
4190            &beta_raw.slice(0..t * num_v),
4191            &alpha.slice(0..t * num_v),
4192            t,
4193            cache,
4194            il,
4195            pad_len,
4196        )
4197    }
4198
4199    /// task #18: the GDN prep stage (conv + repack + l2 x2 + sigmoid + glog + pad-mask) —
4200    /// shared verbatim by the per-seq scan path and the varlen batched path.
4201    #[allow(clippy::too_many_arguments)]
4202    fn linear_attn_gdn_prep(
4203        &self,
4204        e: &Engine,
4205        la: &LinearAttnLayer,
4206        qkv_mixed: &cudarc::driver::CudaView<f32>,
4207        beta_raw: &cudarc::driver::CudaView<f32>,
4208        alpha: &cudarc::driver::CudaView<f32>,
4209        t: usize,
4210        cache: &mut Cache,
4211        il: usize,
4212        pad_len: Option<&CudaSlice<i32>>,
4213    ) -> Result<GdnPrep, Box<dyn std::error::Error>> {
4214        let cfg = &self.cfg;
4215        let geometry = la.geometry;
4216        let d_state = geometry.key_head_dim as usize;
4217        let num_k = geometry.key_heads as usize;
4218        let num_v = geometry.value_heads as usize;
4219        let d_conv = geometry.conv_kernel as usize;
4220        let key_dim = d_state * num_k; // 2048
4221        let value_dim = geometry.value_head_dim as usize * num_v;
4222        let conv_dim = key_dim * 2 + value_dim; // 8192
4223        let eps = cfg.rms_eps;
4224        debug_assert!(
4225            t >= d_conv - 1,
4226            "stateful conv needs T >= pad (PRIME_MIN_T gates)"
4227        );
4228
4229        // conv with CARRIED ring state + ring roll (state read + final-window write-back).
4230        // task #18 conv-fuse (default ON): conv + SiLU + repack in one pass — the 67MB
4231        // conv_out intermediate and its transposed re-read disappear (values bit-identical).
4232        // MEMRA_CONV_FUSE=0 reverts to the two-kernel chain.
4233        let rl = cache.recur[il].as_mut().unwrap();
4234        let hk = Self::gdn_hk(e, t, num_v, num_k);
4235        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
4236        let hk = if conv_fuse { hk } else { num_v }; // de-broadcast rides the fused conv
4237        let mut q_g = e.uninit(d_state * hk * t)?;
4238        let mut k_g = e.uninit(d_state * hk * t)?;
4239        let mut v_g = e.uninit(d_state * num_v * t)?;
4240        if conv_fuse {
4241            e.ssm_conv1d_gdn_state_pad(
4242                qkv_mixed,
4243                &mut rl.conv_state,
4244                la.ssm_conv1d.float_data(),
4245                &mut q_g,
4246                &mut k_g,
4247                &mut v_g,
4248                conv_dim,
4249                t,
4250                d_conv,
4251                d_state,
4252                num_v,
4253                num_k,
4254                key_dim,
4255                hk,
4256                pad_len,
4257            )?;
4258        } else {
4259            let mut conv_out = e.uninit(conv_dim * t)?; // [conv_dim, T] channel-major, SiLU
4260            e.ssm_conv1d_tm_state_pad_v(
4261                qkv_mixed,
4262                &mut rl.conv_state,
4263                la.ssm_conv1d.float_data(),
4264                &mut conv_out,
4265                conv_dim,
4266                t,
4267                d_conv,
4268                pad_len,
4269            )?;
4270            e.qkv_to_gdn_repack(
4271                &conv_out, &mut q_g, &mut k_g, &mut v_g, d_state, num_v, num_k, key_dim, t,
4272            )?;
4273        }
4274        let mut q_l2 = e.uninit(d_state * hk * t)?;
4275        // mirror-fold (round 35): q's bf16 twin (wgmma K45/K2 A-operand) in-epilogue too.
4276        // Emitted only where a consumer exists (the wgmma config) — on other arches the
4277        // alloc + epilogue stores would be pure waste.
4278        let qb16 = if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(32) {
4279            let mut qb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4280            e.l2_norm_pp(&q_g, &mut q_l2, Some(&mut qb), d_state, hk * t, eps)?;
4281            Some(qb)
4282        } else {
4283            e.l2_norm_pp(&q_g, &mut q_l2, None, d_state, hk * t, eps)?;
4284            None
4285        };
4286        let mut k_l2 = e.uninit(d_state * hk * t)?;
4287        // mirror-fold: emit k's bf16 twin (the chunked-scan kb16 mirror) in-epilogue
4288        let kb16 = if Engine::l2_v2_on(d_state) {
4289            let mut kb = e.alloc_u8_uninit(d_state * hk * t * 2)?;
4290            e.l2_norm_pp(&k_g, &mut k_l2, Some(&mut kb), d_state, hk * t, eps)?;
4291            Some(kb)
4292        } else {
4293            e.l2_norm_pp(&k_g, &mut k_l2, None, d_state, hk * t, eps)?;
4294            None
4295        };
4296        let mut beta = e.uninit(t * num_v)?;
4297        e.sigmoid_v(beta_raw, &mut beta, t * num_v)?;
4298        let mut g_log = e.uninit(t * num_v)?;
4299        e.gdn_glog_v(
4300            alpha,
4301            la.ssm_dt.float_data(),
4302            la.ssm_a.float_data(),
4303            &mut g_log,
4304            num_v,
4305            t,
4306        )?;
4307        if let Some(len_d) = pad_len {
4308            e.gdn_pad_mask(&mut beta, &mut g_log, len_d, num_v, t)?;
4309        }
4310        Ok(GdnPrep {
4311            hk,
4312            q_l2,
4313            k_l2,
4314            v_g,
4315            beta,
4316            g_log,
4317            kb16,
4318            qb16,
4319        })
4320    }
4321
4322    /// task #18: batched GDN mixer core — per-seq prep + K1-K3, then ONE varlen K4 and
4323    /// ONE varlen K5 launch for all B sequences (full-machine occupancy vs B underfilled
4324    /// per-seq trains). Per-block math identical to the per-seq path (bit-gateable);
4325    /// MEMRA_GDN_VL=0 or a non-mma/non-C32 config falls back to the per-seq loop.
4326    #[allow(clippy::too_many_arguments)]
4327    fn linear_attn_prime_core_batch(
4328        &self,
4329        e: &Engine,
4330        la: &LinearAttnLayer,
4331        g4: &[CudaSlice<f32>],
4332        offs: &[usize],
4333        ts: &[usize],
4334        caches: &mut [&mut Cache],
4335        il: usize,
4336    ) -> Result<Vec<(CudaSlice<f32>, Option<CudaSlice<u8>>)>, Box<dyn std::error::Error>> {
4337        let geometry = la.geometry;
4338        let d_state = geometry.key_head_dim as usize;
4339        let num_k = geometry.key_heads as usize;
4340        let num_v = geometry.value_heads as usize;
4341        let d_conv = geometry.conv_kernel as usize;
4342        let key_dim = d_state * num_k;
4343        let value_dim = geometry.value_head_dim as usize * num_v;
4344        let conv_dim = key_dim * 2 + value_dim;
4345        let eps = self.cfg.rms_eps;
4346        let scale = 1.0 / (d_state as f32).sqrt();
4347        let b = ts.len();
4348        let c = Engine::gdn_chunk_size();
4349        // CONTINUATION batches (increment (b)) take the per-seq stateful pad_view twin
4350        // below — the varlen K4/K5 chain is fresh-only (zero initial state assumed).
4351        let carried = caches.iter().any(|c| c.pos > 0);
4352        let use_vl = !carried
4353            && (2..=8).contains(&b)
4354            && Engine::gdn_chunked_enabled()
4355            && ts.iter().all(|&t| t >= 16)
4356            && e.gdn_mma_enabled(c)
4357            && std::env::var("MEMRA_GDN_VL").as_deref() != Ok("0");
4358        if !use_vl {
4359            return (0..b)
4360                .map(|s| {
4361                    let (o, t) = (offs[s], ts[s]);
4362                    self.linear_attn_prime_core_pad_view(
4363                        e,
4364                        la,
4365                        &g4[0].slice(o * conv_dim..(o + t) * conv_dim),
4366                        &g4[1].slice(o * value_dim..(o + t) * value_dim),
4367                        &g4[2].slice(o * num_v..(o + t) * num_v),
4368                        &g4[3].slice(o * num_v..(o + t) * num_v),
4369                        t,
4370                        caches[s],
4371                        il,
4372                        None,
4373                    )
4374                })
4375                .collect();
4376        }
4377        // increment 3: the ENTIRE core is varlen — allocs only per seq, then 13 launches
4378        // total for the whole batch: [conv, ring, repack, l2(q+k), gate-prep] +
4379        // [k-mirror, K1, K2, K3, w-mirror, K4, K5] + [gated-norm tail].
4380        struct SeqBufs {
4381            conv_out: CudaSlice<f32>,
4382            q_g: CudaSlice<f32>,
4383            k_g: CudaSlice<f32>,
4384            v_g: CudaSlice<f32>,
4385            q_l2: CudaSlice<f32>,
4386            k_l2: CudaSlice<f32>,
4387            beta: CudaSlice<f32>,
4388            g_log: CudaSlice<f32>,
4389            gn: CudaSlice<f32>,
4390            gn16: CudaSlice<u8>,
4391        }
4392        let f16o = Self::f16out_on(e, 16);
4393        let hk = Self::gdn_hk(e, 16, num_v, num_k); // vl path is always chunked+mma
4394        let mut sb = Vec::with_capacity(b);
4395        let mut pres = Vec::with_capacity(b);
4396        for &t in ts.iter().take(b) {
4397            sb.push(SeqBufs {
4398                conv_out: e.uninit(conv_dim * t)?,
4399                q_g: e.uninit(d_state * hk * t)?,
4400                k_g: e.uninit(d_state * hk * t)?,
4401                v_g: e.uninit(d_state * num_v * t)?,
4402                q_l2: e.uninit(d_state * hk * t)?,
4403                k_l2: e.uninit(d_state * hk * t)?,
4404                beta: e.uninit(t * num_v)?,
4405                g_log: e.uninit(t * num_v)?,
4406                gn: e.uninit(d_state * num_v * t)?,
4407                gn16: e.alloc_u8_uninit(d_state * num_v * t * 2)?,
4408            });
4409            pres.push(e.gdn_chunk_alloc(num_v, t, c, hk)?);
4410        }
4411        let prep_args: Vec<crate::GdnPrepVl> = (0..b)
4412            .map(|s| {
4413                let (o, t) = (offs[s], ts[s]);
4414                let rl = caches[s].recur[il].as_ref().unwrap();
4415                crate::GdnPrepVl {
4416                    qkv: e.addr_f32v(&g4[0].slice(o * conv_dim..(o + t) * conv_dim)),
4417                    conv_state: e.addr_f32(&rl.conv_state),
4418                    conv_out: e.addr_f32(&sb[s].conv_out),
4419                    q_g: e.addr_f32(&sb[s].q_g),
4420                    k_g: e.addr_f32(&sb[s].k_g),
4421                    v_g: e.addr_f32(&sb[s].v_g),
4422                    q_l2: e.addr_f32(&sb[s].q_l2),
4423                    k_l2: e.addr_f32(&sb[s].k_l2),
4424                    beta_raw: e.addr_f32v(&g4[2].slice(o * num_v..(o + t) * num_v)),
4425                    alpha: e.addr_f32v(&g4[3].slice(o * num_v..(o + t) * num_v)),
4426                    beta: e.addr_f32(&sb[s].beta),
4427                    g_log: e.addr_f32(&sb[s].g_log),
4428                    o: e.addr_f32(&pres[s].o),
4429                    z: e.addr_f32v(&g4[1].slice(o * value_dim..(o + t) * value_dim)),
4430                    gn: e.addr_f32(&sb[s].gn),
4431                    gn16: e.addr_u8(&sb[s].gn16),
4432                    kb16: if Engine::l2_v2_on(d_state) {
4433                        e.addr_u8(&pres[s].kb16)
4434                    } else {
4435                        0
4436                    },
4437                    qb16: if Engine::l2_v2_on(d_state) && e.gdn_wgmma_on(c) {
4438                        e.addr_u8(&pres[s].qb16)
4439                    } else {
4440                        0
4441                    },
4442                    t: t as i32,
4443                    pad: 0,
4444                }
4445            })
4446            .collect();
4447        let args: Vec<crate::GdnSeqVl> = (0..b)
4448            .map(|s| {
4449                let rl = caches[s].recur[il].as_ref().unwrap();
4450                crate::GdnSeqVl {
4451                    kb16: e.addr_u8(&pres[s].kb16),
4452                    gcum: e.addr_f32(&pres[s].gcum),
4453                    beta: e.addr_f32(&sb[s].beta),
4454                    u: e.addr_f32(&pres[s].u),
4455                    wb16: e.addr_u8(&pres[s].wb16),
4456                    y: e.addr_u8(&pres[s].y16),
4457                    ssnap: e.addr_u8(&pres[s].ssnap16),
4458                    state_in: e.addr_f32(&rl.ssm_state),
4459                    state_out: e.addr_f32(&rl.ssm_state_alt),
4460                    q: e.addr_f32(&sb[s].q_l2),
4461                    p: e.addr_f32(&pres[s].p),
4462                    o: e.addr_f32(&pres[s].o),
4463                    k: e.addr_f32(&sb[s].k_l2),
4464                    v: e.addr_f32(&sb[s].v_g),
4465                    g: e.addr_f32(&sb[s].g_log),
4466                    a: e.addr_f32(&pres[s].a),
4467                    w: e.addr_f32(&pres[s].w),
4468                    t: ts[s] as i32,
4469                    nc: pres[s].nc as i32,
4470                }
4471            })
4472            .collect();
4473        e.gdn_prep_vl8(
4474            &prep_args,
4475            la.ssm_conv1d.float_data(),
4476            la.ssm_dt.float_data(),
4477            la.ssm_a.float_data(),
4478            conv_dim,
4479            d_conv,
4480            d_state,
4481            num_v,
4482            num_k,
4483            key_dim,
4484            hk,
4485            eps,
4486        )?;
4487        // mirror-fold (round 27): l2 v2 emits kb16 in-epilogue; K3's store emits wb16 —
4488        // both standalone mirror launches vanish on the default config.
4489        if !Engine::l2_v2_on(d_state) {
4490            e.gdn_mirror_vl8(&args, num_v, 0, hk)?;
4491        }
4492        // task #22: wgmma-fused vl twins — qb16 mirrors + GdnWVl8 side-struct.
4493        let wq8: Option<crate::GdnWVl8> = if e.gdn_wgmma_on(c) {
4494            // qb16 emitted by the vl l2 mirror-fold when l2-v2 serves; bulk cvt otherwise
4495            if !Engine::l2_v2_on(d_state) {
4496                for s in 0..b {
4497                    e.f32_to_bf16_into(&sb[s].q_l2, &mut pres[s].qb16, d_state * hk * ts[s])?;
4498                }
4499            }
4500            let mut wa = [crate::GdnWVl::default(); 8];
4501            for s in 0..b {
4502                wa[s] = crate::GdnWVl {
4503                    qb16: e.addr_u8(&pres[s].qb16),
4504                    pb16: e.addr_u8(&pres[s].pb16),
4505                };
4506            }
4507            Some(crate::GdnWVl8(wa))
4508        } else {
4509            None
4510        };
4511        e.gdn_chunk_k123_vl8(&args, num_v, hk, wq8.as_ref())?;
4512        e.gdn_chunk_vl8(&args, num_v, scale, hk, wq8.as_ref())?;
4513        if f16o {
4514            e.gdn_tail_vl8(&prep_args, la.ssm_norm.float_data(), d_state, num_v, eps)?;
4515        }
4516        // per-seq state swap (+ non-f16out tail fallback)
4517        let mut out = Vec::with_capacity(b);
4518        for (s, bufs) in sb.into_iter().enumerate() {
4519            let rl = caches[s].recur[il].as_mut().unwrap();
4520            std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4521            let (o, t) = (offs[s], ts[s]);
4522            let SeqBufs { mut gn, gn16, .. } = bufs;
4523            if f16o {
4524                out.push((gn, Some(gn16)));
4525            } else {
4526                let z_v = g4[1].slice(o * value_dim..(o + t) * value_dim);
4527                e.gated_rmsnorm_zv(
4528                    &pres[s].o,
4529                    la.ssm_norm.float_data(),
4530                    &z_v,
4531                    &mut gn,
4532                    d_state,
4533                    num_v * t,
4534                    eps,
4535                )?;
4536                out.push((gn, None));
4537            }
4538        }
4539        Ok(out)
4540    }
4541
4542    /// task #16: view-consuming GDN prime core — the batched prime hands row-offset
4543    /// views of the CONCAT projection outputs directly (no per-seq split copies).
4544    /// Same kernels, same values, byte-identical to the Vec shim above.
4545    #[allow(clippy::too_many_arguments)]
4546    fn linear_attn_prime_core_pad_view(
4547        &self,
4548        e: &Engine,
4549        la: &LinearAttnLayer,
4550        qkv_mixed: &cudarc::driver::CudaView<f32>,
4551        z: &cudarc::driver::CudaView<f32>,
4552        beta_raw: &cudarc::driver::CudaView<f32>,
4553        alpha: &cudarc::driver::CudaView<f32>,
4554        t: usize,
4555        cache: &mut Cache,
4556        il: usize,
4557        pad_len: Option<&CudaSlice<i32>>,
4558    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<u8>>), Box<dyn std::error::Error>> {
4559        let cfg = &self.cfg;
4560        let geometry = la.geometry;
4561        let d_state = geometry.key_head_dim as usize;
4562        let num_v = geometry.value_heads as usize;
4563        let eps = cfg.rms_eps;
4564        let scale = 1.0 / (d_state as f32).sqrt();
4565
4566        let prep =
4567            self.linear_attn_gdn_prep(e, la, qkv_mixed, beta_raw, alpha, t, cache, il, pad_len)?;
4568
4569        // ONE gdn_scan over T from the cache's CURRENT state (zero at fresh prime); the final
4570        // state lands in the spare buffer and ping-pongs back (stable resident pointers, the
4571        // decode-determinism discipline from linear_attn_decode_inner). A4: `gdn_scan_prefill`
4572        // dispatches the chunked WY form under MEMRA_GDN_CHUNKED (prefill-only seam; decode +
4573        // verify keep the sequential kernel).
4574        let mut o = e.uninit(d_state * num_v * t)?;
4575        let rl = cache.recur[il].as_mut().unwrap();
4576        {
4577            let crate::cache::RecurLayer {
4578                ssm_state,
4579                ssm_state_alt,
4580                ..
4581            } = rl;
4582            e.gdn_scan_prefill(
4583                &prep.q_l2,
4584                &prep.k_l2,
4585                &prep.v_g,
4586                &prep.g_log,
4587                &prep.beta,
4588                prep.kb16.as_ref(),
4589                prep.qb16.as_ref(),
4590                ssm_state,
4591                ssm_state_alt,
4592                &mut o,
4593                num_v,
4594                t,
4595                scale,
4596                prep.hk,
4597            )?;
4598        }
4599        std::mem::swap(&mut rl.ssm_state, &mut rl.ssm_state_alt);
4600
4601        // gated RMSNorm + out projection (prefill dispatch). task #17: the f16out twin also
4602        // emits the ssm_out GEMM's fp16 operand in-epilogue (kills the standalone convert).
4603        let mut gn = e.uninit(d_state * num_v * t)?;
4604        let gn16 = if Self::f16out_on(e, t) {
4605            let mut g16 = e.alloc_u8_uninit(d_state * num_v * t * 2)?;
4606            e.gated_rmsnorm_f16out_zv(
4607                &o,
4608                la.ssm_norm.float_data(),
4609                z,
4610                &mut gn,
4611                &mut g16,
4612                d_state,
4613                num_v * t,
4614                eps,
4615            )?;
4616            Some(g16)
4617        } else {
4618            e.gated_rmsnorm_zv(
4619                &o,
4620                la.ssm_norm.float_data(),
4621                z,
4622                &mut gn,
4623                d_state,
4624                num_v * t,
4625                eps,
4626            )?;
4627            None
4628        };
4629        Ok((gn, gn16))
4630    }
4631
4632    /// Task #15 core-split wrapper: composes inner + ssm_out (== the old core).
4633    #[allow(clippy::too_many_arguments)]
4634    fn linear_attn_prime_core_pad(
4635        &self,
4636        e: &Engine,
4637        la: &LinearAttnLayer,
4638        g4: Vec<CudaSlice<f32>>,
4639        t: usize,
4640        cache: &mut Cache,
4641        il: usize,
4642        pad_len: Option<&CudaSlice<i32>>,
4643    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4644        let (gn, gn16) = self.linear_attn_prime_core_pad_inner(e, la, g4, t, cache, il, pad_len)?;
4645        if let Some(xh) = &gn16 {
4646            if let Some(y) = e.try_f16_gemm_pre(&la.ssm_out, xh, t)? {
4647                return Ok(y);
4648            }
4649        }
4650        Ok(e.matmul(&la.ssm_out, &gn, t)?)
4651    }
4652
4653    /// Full-attention mixer with QK-norm, partial RoPE, sigmoid output gate (qwen35 :257-336).
4654    ///
4655    /// `il` = layer index: step35 needs it (per-layer n_head / rope width / window / gate) and
4656    /// routes to its own mixer. Every other arch ignores it (uniform geometry).
4657    pub fn full_attn(
4658        &self,
4659        e: &Engine,
4660        fa: &FullAttnLayer,
4661        h: &CudaSlice<f32>,
4662        pos_d: &CudaSlice<i32>,
4663        t: usize,
4664        il: usize,
4665    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4666        if self.uses_sliding_gated_moe_program() {
4667            return self.step35_attn(e, fa, h, pos_d, t, il);
4668        }
4669        let cfg = &self.cfg;
4670        let _n_embd = cfg.n_embd as usize;
4671        let geometry = cfg.full_attention_geometry_at(il as u32);
4672        let n_head = geometry.n_head as usize;
4673        let n_head_kv = geometry.n_head_kv as usize;
4674        let head_dim = geometry.head_dim_k as usize;
4675        let eps = cfg.rms_eps;
4676        let scale = geometry.attention_scale();
4677
4678        // qwen35: wq output = head_dim*2*n_head (fused [q|gate] per head). M3/Hy3: NO output
4679        // gate — wq out = n_head*head_dim, no split (see prime-path note).
4680        let gated = geometry.attention_gate == memra_gguf::config::AttentionGateKind::FusedQ;
4681        // grouped: one f16 activation convert feeds q/k/v (matmul_group)
4682        let mut g3 = e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?;
4683        let v = g3.pop().unwrap();
4684        let mut k = g3.pop().unwrap();
4685        let qf = g3.pop().unwrap();
4686        let (mut q, gate) = if gated {
4687            let mut q = e.uninit(t * n_head * head_dim)?;
4688            let mut gate = e.uninit(t * n_head * head_dim)?;
4689            e.q_gate_split(&qf, &mut q, &mut gate, head_dim, n_head, t)?;
4690            (q, Some(gate))
4691        } else {
4692            (qf, None)
4693        };
4694
4695        // QK-norm (per head_dim row), then partial RoPE.
4696        let mut qn = e.uninit(t * n_head * head_dim)?;
4697        e.rms_norm(
4698            &q,
4699            fa.q_norm.float_data(),
4700            &mut qn,
4701            head_dim,
4702            n_head * t,
4703            eps,
4704        )?;
4705        q = qn;
4706        let mut kn = e.uninit(t * n_head_kv * head_dim)?;
4707        e.rms_norm(
4708            &k,
4709            fa.k_norm.float_data(),
4710            &mut kn,
4711            head_dim,
4712            n_head_kv * t,
4713            eps,
4714        )?;
4715        k = kn;
4716        let rope_dims = geometry.n_rot as usize;
4717        e.rope_neox(
4718            &mut q,
4719            pos_d,
4720            head_dim,
4721            rope_dims,
4722            n_head,
4723            t,
4724            geometry.rope_base,
4725            1.0,
4726        )?;
4727        e.rope_neox(
4728            &mut k,
4729            pos_d,
4730            head_dim,
4731            rope_dims,
4732            n_head_kv,
4733            t,
4734            geometry.rope_base,
4735            1.0,
4736        )?;
4737
4738        // SDPA
4739        let mut attn = e.uninit(t * n_head * head_dim)?;
4740        // hand-written FlashAttention prefill (head_dim 256/128 stamped twins). MEMRA_NOFA
4741        // falls back to naive sdpa.
4742        if std::env::var("MEMRA_NOFA").is_ok() || !(head_dim == 256 || head_dim == 128) {
4743            // head_dim gate: see prime-path note (fa_prefill is stamped at 256 and 128 only).
4744            e.sdpa_naive(
4745                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4746            )?;
4747        } else {
4748            e.fa_prefill(
4749                &q, &k, &v, &mut attn, head_dim, n_head, n_head_kv, t, t, scale, true,
4750            )?;
4751        }
4752
4753        // output gate: attn * sigmoid(gate) — qwen35 only (M3 has no gate).
4754        let attn_g = match &gate {
4755            Some(gate) => {
4756                let mut gsig = e.uninit(t * n_head * head_dim)?;
4757                e.sigmoid(gate, &mut gsig, t * n_head * head_dim)?;
4758                let mut ag = e.uninit(t * n_head * head_dim)?;
4759                e.mul(&attn, &gsig, &mut ag, t * n_head * head_dim)?;
4760                ag
4761            }
4762            None => attn,
4763        };
4764
4765        // o projection
4766        let o = e.matmul(&fa.wo, &attn_g, t)?;
4767        Ok(o)
4768    }
4769
4770    /// Linear-attention (Gated DeltaNet) mixer (qwen35 :338-470).
4771    pub fn linear_attn(
4772        &self,
4773        e: &Engine,
4774        la: &LinearAttnLayer,
4775        h: &CudaSlice<f32>,
4776        t: usize,
4777    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4778        let cfg = &self.cfg;
4779        let _n_embd = cfg.n_embd as usize;
4780        let geometry = la.geometry;
4781        let d_state = geometry.key_head_dim as usize;
4782        let num_k = geometry.key_heads as usize;
4783        let num_v = geometry.value_heads as usize;
4784        let d_conv = geometry.conv_kernel as usize;
4785        let head_k = d_state;
4786        let head_v = geometry.value_head_dim as usize;
4787        let key_dim = head_k * num_k; // 2048
4788        let value_dim = head_v * num_v; // 4096
4789        let conv_dim = key_dim * 2 + value_dim; // 8192
4790        let eps = cfg.rms_eps;
4791        let scale = 1.0 / (d_state as f32).sqrt();
4792
4793        // projections
4794        // grouped: one f16 activation convert feeds all four projections (matmul_group)
4795        let mut g4 = e.matmul_group(
4796            &[&la.wqkv, &la.wqkv_gate, &la.ssm_beta, &la.ssm_alpha],
4797            h,
4798            t,
4799        )?;
4800        let alpha = g4.pop().unwrap(); // [T, num_v]
4801        let beta_raw = g4.pop().unwrap(); // [T, num_v]
4802        let z = g4.pop().unwrap(); // [T, value_dim]
4803        let qkv_mixed = g4.pop().unwrap(); // [T, conv_dim] token-major
4804
4805        // conv + GDN repack, FUSED (2026-07-03): ssm_conv1d_gdn reads qkv_mixed [T, conv_dim]
4806        // token-major DIRECTLY (causal window rows t-pad..t, rows<0 = zero prefill state), applies
4807        // the 8-tap conv + SiLU, and scatters straight into the GDN [d_state, num_v, T] q/k/v
4808        // layout with the modulo head-repeat. Replaces transpose + zeros + conv_left_pad +
4809        // ssm_conv1d + qkv_to_gdn_repack (5 launches, conv_in/conv_out scratch + a 16MB@T=512
4810        // round-trip). BIT-IDENTICAL accumulation and scatter mapping.
4811        let _ = (head_k, head_v);
4812        let mut q_g = e.uninit(d_state * num_v * t)?;
4813        let mut k_g = e.uninit(d_state * num_v * t)?;
4814        let mut v_g = e.uninit(d_state * num_v * t)?;
4815        e.ssm_conv1d_gdn(
4816            &qkv_mixed,
4817            la.ssm_conv1d.float_data(),
4818            &mut q_g,
4819            &mut k_g,
4820            &mut v_g,
4821            conv_dim,
4822            t,
4823            d_conv,
4824            d_state,
4825            num_v,
4826            num_k,
4827            key_dim,
4828        )?;
4829        // L2-norm q,k per (head_dim) row — rows are contiguous d_state in q_g.
4830        let mut q_l2 = e.uninit(d_state * num_v * t)?;
4831        e.l2_norm(&q_g, &mut q_l2, d_state, num_v * t, eps)?;
4832        let mut k_l2 = e.uninit(d_state * num_v * t)?;
4833        e.l2_norm(&k_g, &mut k_l2, d_state, num_v * t, eps)?;
4834        let v_gd = v_g;
4835
4836        // beta = sigmoid(beta_raw) ; g_log = a * softplus(alpha + dt). Both need [num_v, T] layout
4837        // (g[t*num_v + h]). beta_raw/alpha are [T, num_v] token-major == that layout already.
4838        let mut beta = e.uninit(t * num_v)?;
4839        e.sigmoid(&beta_raw, &mut beta, t * num_v)?;
4840        // gdn_glog expects alpha [H,T] with alpha[t*H+h] and dt_bias/a [H] — matches token-major [T,num_v].
4841        let mut g_log = e.uninit(t * num_v)?;
4842        e.gdn_glog(
4843            &alpha,
4844            la.ssm_dt.float_data(),
4845            la.ssm_a.float_data(),
4846            &mut g_log,
4847            num_v,
4848            t,
4849        )?;
4850
4851        // GDN scan (A4: gdn_scan_prefill dispatches chunked WY under MEMRA_GDN_CHUNKED)
4852        let state_in = e.zeros(d_state * d_state * num_v)?; // zero state (prefill)
4853        let mut state_out = e.zeros(d_state * d_state * num_v)?;
4854        let mut o = e.uninit(d_state * num_v * t)?;
4855        e.gdn_scan_prefill(
4856            &q_l2,
4857            &k_l2,
4858            &v_gd,
4859            &g_log,
4860            &beta,
4861            None,
4862            None,
4863            &state_in,
4864            &mut state_out,
4865            &mut o,
4866            num_v,
4867            t,
4868            scale,
4869            num_v,
4870        )?;
4871
4872        // gated RMSNorm: dst = RMSNorm(o, ssm_norm[head_v]) * silu(z). o is [d_state, num_v, T];
4873        // rows of head_v=d_state, nrows = num_v*T. z must match row layout: z is [T, value_dim] token-major
4874        // = [T, num_v*head_v]; per (t, vh) the head_v slice is contiguous -> rows align as (t*num_v+vh).
4875        // o rows are (t*num_v+vh) too. Good.
4876        let mut gn = e.uninit(d_state * num_v * t)?;
4877        e.gated_rmsnorm(
4878            &o,
4879            la.ssm_norm.float_data(),
4880            &z,
4881            &mut gn,
4882            d_state,
4883            num_v * t,
4884            eps,
4885        )?;
4886
4887        // ssm_out projection: gn is [d_state, num_v, T] = [value_dim, T] viewed token-major as [T, value_dim]?
4888        // gn layout: (t*num_v+vh)*d_state + i  == token t, then (vh,i) = channel vh*d_state+i. That's
4889        // token-major [T, value_dim]. linear wants [T, in=value_dim]. Good.
4890        let out = e.matmul(&la.ssm_out, &gn, t)?;
4891        Ok(out)
4892    }
4893}
4894
4895impl HybridModel {
4896    /// MoE FFN (EDGE-1). z: [T, n_embd] (already post-attention-normed). Returns moe_out [T, n_embd].
4897    /// Node-for-node vs llama.cpp build_moe_ffn + qwen35moe::build_layer_ffn.
4898    ///
4899    /// `il` is the trunk layer index — the residency-cache key prefix (a gate-expert of layer 3 is a
4900    /// different 860160-byte block than the same expert of layer 7).
4901    ///
4902    /// Routing: host softmax+sort (default) OR the fused router kernel (MEMRA_FUSED_ROUTER).
4903    /// Dispatch: stage-every-token into 3 scratch slots (default) OR the SLRU residency cache
4904    /// (MEMRA_MOE_CACHE). The cache-HIT weight path is bit-identical to stage-every-token (§B.3).
4905    /// Convenience wrapper used by the hybrid trunk/MTP loops: pulls dims + max-block from `self`.
4906    pub fn moe_ffn_il(
4907        &self,
4908        e: &Engine,
4909        m: &MoeWeights,
4910        z: &CudaSlice<f32>,
4911        t: usize,
4912        il: u16,
4913    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4914        Self::moe_ffn_inner(
4915            e,
4916            m,
4917            z,
4918            None,
4919            t,
4920            &self.cfg,
4921            il,
4922            self.max_moe_block(),
4923            false,
4924            None,
4925            self.uses_sliding_gated_moe_program(),
4926        )
4927    }
4928
4929    /// Prefill twin: Step35 promotes expert-grouped dispatch by default while decode/spec callers
4930    /// keep `moe_ffn_il` and therefore retain their existing dispatch class.
4931    pub fn moe_ffn_il_prefill(
4932        &self,
4933        e: &Engine,
4934        m: &MoeWeights,
4935        z: &CudaSlice<f32>,
4936        t: usize,
4937        il: u16,
4938    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4939        Self::moe_ffn_inner(
4940            e,
4941            m,
4942            z,
4943            None,
4944            t,
4945            &self.cfg,
4946            il,
4947            self.max_moe_block(),
4948            true,
4949            Some(&self.step_grouped_prefill),
4950            self.uses_sliding_gated_moe_program(),
4951        )
4952    }
4953
4954    /// Decode-path twin with a PRE-QUANTIZED z (from add_rms_norm_zq8): threads (zq, zd) into the
4955    /// t=1 dev arm so the per-layer standalone quantize_q8_1 launch folds away. Identical bytes
4956    /// (the fused kernel reproduces quantize_q8_1 exactly); every other path ignores the pair.
4957    pub fn moe_ffn_il_zq8(
4958        &self,
4959        e: &Engine,
4960        m: &MoeWeights,
4961        z: &CudaSlice<f32>,
4962        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
4963        t: usize,
4964        il: u16,
4965    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4966        Self::moe_ffn_inner(
4967            e,
4968            m,
4969            z,
4970            zq8,
4971            t,
4972            &self.cfg,
4973            il,
4974            self.max_moe_block(),
4975            false,
4976            None,
4977            self.uses_sliding_gated_moe_program(),
4978        )
4979    }
4980
4981    /// MoE FFN (EDGE-1), source-/model-agnostic. z: [T, n_embd] (already post-attention-normed).
4982    /// Returns moe_out [T, n_embd]. Node-for-node vs llama.cpp build_moe_ffn. Shared by the hybrid
4983    /// (qwen35moe, shared expert present) and the dense-attention MoE (OLMoE, no shared expert) paths;
4984    /// `cfg.moe` supplies the dims and the optional shexp fields decide whether step 3 runs.
4985    ///
4986    /// `il` is the layer index — the residency-cache key prefix. `max_block` is the global max expert
4987    /// stride (fixed cache-slot size); pass `self.max_moe_block()`.
4988    pub(crate) fn moe_ffn(
4989        e: &Engine,
4990        m: &MoeWeights,
4991        z: &CudaSlice<f32>,
4992        t: usize,
4993        cfg: &ModelConfig,
4994        il: u16,
4995        max_block: usize,
4996    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4997        Self::moe_ffn_inner(e, m, z, None, t, cfg, il, max_block, false, None, false)
4998    }
4999
5000    #[allow(clippy::too_many_arguments)]
5001    pub(crate) fn moe_ffn_inner(
5002        e: &Engine,
5003        m: &MoeWeights,
5004        z: &CudaSlice<f32>,
5005        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
5006        t: usize,
5007        cfg: &ModelConfig,
5008        il: u16,
5009        max_block: usize,
5010        prefill: bool,
5011        grouped_prefill: Option<&std::sync::Mutex<crate::hybrid::StepEpGroupedPrefill>>,
5012        sliding_gated_moe: bool,
5013    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5014        let worker_io = crate::spill_pread::worker_enabled();
5015        let epoch_lfu = std::env::var_os("MEMRA_MOE_LFU_DECAY").is_some();
5016        if Engine::moe_cache_enabled() && (worker_io || epoch_lfu) {
5017            e.with_moe_cache(max_block, |cache, _| {
5018                cache.begin_forward_epoch(il, t);
5019                if worker_io {
5020                    cache.begin_worker_scope();
5021                }
5022                Ok(())
5023            })?;
5024        }
5025        if m.step_ep.is_some() || m.step_tp.is_some() {
5026            let moe = cfg
5027                .moe
5028                .as_ref()
5029                .ok_or("Step distributed execution requires MoE model metadata")?;
5030            let n_embd = cfg.n_embd as usize;
5031            let n_expert = moe.expert_count as usize;
5032            let n_used = moe.expert_used_count as usize;
5033            let sigmoid = cfg
5034                .sigmoid_router()
5035                .ok_or("Step distributed execution requires the Step sigmoid router")?;
5036            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5037            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5038            let grouped_prefill_requested = prefill && step_ep_grouped_prefill_enabled()?;
5039            if grouped_prefill_requested && !step_tp_prefill_enabled()? {
5040                return Err(
5041                    "MEMRA_STEP_EP_GROUPED_PREFILL=1 requires MEMRA_STEP_TP_PREFILL=1".into(),
5042                );
5043            }
5044            if grouped_prefill_requested && !step_grouped_prefill_shape(true, prefill, t) {
5045                return Err(format!(
5046                    "Step grouped prefill tokens {t} are outside the qualified {}..={} range",
5047                    PRIME_MIN_T,
5048                    crate::cache::PRIME_CHUNK_MAX_TOKENS,
5049                )
5050                .into());
5051            }
5052            let grouped_decode_shape = step_grouped_decode_shape(prefill, t);
5053            let grouped_prefill_shape =
5054                step_grouped_prefill_shape(grouped_prefill_requested, prefill, t);
5055            if let Some(ep) = m.step_ep.as_ref().filter(|ep| {
5056                ep.grouped_decode.is_some() && (grouped_decode_shape || grouped_prefill_shape)
5057            }) {
5058                let (selected, route_weights) =
5059                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sigmoid)?;
5060                crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5061                Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5062                Self::trace_moe_input(e, il, t, n_embd, z)?;
5063                let selected = selected
5064                    .iter()
5065                    .map(|&expert| expert as usize)
5066                    .collect::<Vec<_>>();
5067
5068                // The narrow route readback above orders the owning-stage producer. The grouped
5069                // runtime then copies the resident root activation into its persistent rank inputs.
5070                e.stream().synchronize()?;
5071                let execute = |state: &mut crate::hybrid::StepEpGroupedDecode| {
5072                    state.projection.set_activation_limit(ep.activation_limit)?;
5073                    ep.runtime
5074                        .refresh_step_grouped_expert_parallel_gate_from_root_device(
5075                            ep.experts.e4m3()?,
5076                            &mut state.projection,
5077                            z,
5078                            t,
5079                            &selected,
5080                        )?;
5081                    ep.runtime.refresh_step_grouped_expert_parallel_combine(
5082                        &state.projection,
5083                        &mut state.combine,
5084                        &route_weights,
5085                    )?;
5086                    ep.runtime.execute_step_grouped_expert_parallel_gate(
5087                        ep.experts.e4m3()?,
5088                        &mut state.projection,
5089                    )?;
5090                    ep.runtime.execute_step_grouped_expert_parallel_combine(
5091                        &state.projection,
5092                        &mut state.combine,
5093                    )?;
5094                    let mut output = ep.runtime.copy_step_grouped_expert_parallel_combine_root(
5095                        &state.projection,
5096                        &state.combine,
5097                        e,
5098                    )?;
5099                    Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5100                    if prefill {
5101                        // A shared plan may be reused by the next layer on a different runtime
5102                        // stream. Complete the owning-stage copy before its source is overwritten.
5103                        e.stream().synchronize()?;
5104                    }
5105                    eprintln!(
5106                        "[step-tp-ep-grouped] execute layer={il} tokens={t} devices={:?} \
5107                         attention_layout=tensor-parallel expert_layout=expert-parallel \
5108                         expert_transport={} native_p2p=true route_control=host-narrow \
5109                         input=root-device projection_workspaces=persistent \
5110                         combine=root-device output=owning-stage-device \
5111                         prefill={prefill} batched_decode=false capacity={} \
5112                         performance_claim=false",
5113                        ep.devices,
5114                        ep.runtime.transport_label(),
5115                        state.projection.max_tokens(),
5116                    );
5117                    Ok::<_, Box<dyn std::error::Error>>(output)
5118                };
5119
5120                if grouped_prefill_shape {
5121                    let grouped_prefill = grouped_prefill
5122                        .ok_or("Step grouped prefill has no model-scoped executor")?;
5123                    let mut shared = grouped_prefill
5124                        .lock()
5125                        .map_err(|_| "Step grouped prefill state lock is poisoned")?;
5126                    let needs_prepare = shared.state.as_ref().is_none_or(|state| {
5127                        state.devices != ep.devices
5128                            || state.grouped.projection.max_tokens() < t
5129                            || state.grouped.projection.input_width() != n_embd
5130                            || state.grouped.projection.expert_width()
5131                                != moe.expert_ff_length as usize
5132                    });
5133                    if needs_prepare {
5134                        let seed_input = vec![0.0f32; n_embd];
5135                        let seed_selected = &selected[..n_used];
5136                        let seed_weights = &route_weights[..n_used];
5137                        let projection = ep
5138                            .runtime
5139                            .prepare_step_grouped_expert_parallel_gate_with_capacity(
5140                                ep.experts.e4m3()?,
5141                                &seed_input,
5142                                1,
5143                                seed_selected,
5144                                ep.activation_limit,
5145                                t,
5146                            )?;
5147                        let combine = ep.runtime.prepare_step_grouped_expert_parallel_combine(
5148                            &projection,
5149                            seed_weights,
5150                        )?;
5151                        shared.state = Some(crate::hybrid::StepEpGroupedPrefillState {
5152                            devices: ep.devices.clone(),
5153                            grouped: crate::hybrid::StepEpGroupedDecode {
5154                                projection,
5155                                combine,
5156                            },
5157                        });
5158                        eprintln!(
5159                            "[step-tp-ep-grouped-prefill] prepare capacity={t} devices={:?} \
5160                             shared_across_layers=true performance_claim=false",
5161                            ep.devices,
5162                        );
5163                    }
5164                    return execute(
5165                        &mut shared
5166                            .state
5167                            .as_mut()
5168                            .expect("Step grouped prefill state prepared above")
5169                            .grouped,
5170                    );
5171                }
5172
5173                let mut grouped = ep
5174                    .grouped_decode
5175                    .as_ref()
5176                    .expect("grouped decode presence checked above")
5177                    .lock()
5178                    .map_err(|_| "Step grouped decode state lock is poisoned")?;
5179                return execute(&mut grouped);
5180            }
5181            if grouped_prefill_shape {
5182                return Err(
5183                    "Step grouped prefill requires native-P2P expert-owner device arithmetic"
5184                        .into(),
5185                );
5186            }
5187            // MEMRA_STEP_TP_DEV_ROUTER=1 (t=1): route on device and feed the device-routed
5188            // expert program — the per-layer host logits readback (the last per-layer host
5189            // sync) disappears. Selection tie-breaking may differ from the host router:
5190            // numeric-class door, run-gen argmax gate + boot battery.
5191            // STEP TP2 GEMM PRIME (2026-08-27, TTFT lane): a prime chunk's routed MoE goes
5192            // through ONE grouped f16 GEMM per projection over the resident NVFP4 banks —
5193            // the per-token device routes below cost 240 s at m=4092 (measured), the grouped
5194            // lane's sizing rows run 170-270 TFLOP/s. Router selections come from the same
5195            // sigmoid host oracle the EP arm uses; shexp rides the canonical grouped add.
5196            // t>=16 alone keys the branch: the batch prime reaches here through moe_ffn_il,
5197            // whose `prefill` is FALSE (only the _prefill twin sets it), and no other step37
5198            // route runs t>=16 — verify walks t<=8, decode t=1. Requiring `prefill` made the
5199            // first gate arm skip this branch entirely and wake the generic f16g arm instead
5200            // (48 s + kq_gemm_sk rc=1001, 2026-08-27).
5201            if t >= 16 && crate::step_gemm_prime_on() {
5202                if let Some(tp) = &m.step_tp {
5203                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5204                        // MEMRA_PRIME_PROF=1 sub-split of the moe bucket. The phase timer put
5205                        // 1788 ms of a 3093 ms chunk here, but forcing the 32-row tile form (4x
5206                        // more weight dequant) moved it only 5% — so the grouped GEMM is not
5207                        // obviously what dominates. The router below is a HOST oracle: sigmoid +
5208                        // top-8 over 288 experts for every one of 4096 tokens, per layer, which
5209                        // is a D2H copy and a full pipeline drain 42 times per chunk. Attribute
5210                        // it before optimizing the kernel it sits in front of.
5211                        let mprof =
5212                            std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1") && t >= 16;
5213                        let mut mt = std::time::Instant::now();
5214                        let (selected, route_weights) = Self::moe_route_sigmoid_cfg(
5215                            e, &logits, t, n_expert, n_used, m, sigmoid,
5216                        )?;
5217                        let sel_i32: Vec<i32> = selected.iter().map(|&x| x as i32).collect();
5218                        let d_router = if mprof {
5219                            let _ = e.stream().synchronize();
5220                            let v = mt.elapsed().as_secs_f64() * 1e3;
5221                            mt = std::time::Instant::now();
5222                            v
5223                        } else {
5224                            0.0
5225                        };
5226                        // MEMRA_MOE_DETERM=1: run the WHOLE grouped routine twice on identical
5227                        // inputs and diff. The standalone harness cleared the grouped GEMM kernels
5228                        // (8 invocations, both lanes, maxdiff 0.0 over 20.9M elements) but it does
5229                        // not model the cross-device join/scatter or the o_proj-style reduction,
5230                        // and the loader refuses both topologies (TP1, same-device) that would
5231                        // isolate those by env. This tests the un-excluded region directly, in
5232                        // the place it actually runs.
5233                        //
5234                        // The prime is nondeterministic: same prompt, one forward, temperature=0,
5235                        // max_tokens=1, and the first token varies across reps. That blocks
5236                        // MEMRA_PP_BF16's correctness receipt and invalidates every byte-identity
5237                        // gate taken through the server. This probe also yields the jitter
5238                        // MAGNITUDE, which any tolerance band needs.
5239                        let mdet = std::env::var("MEMRA_MOE_DETERM").as_deref() == Ok("1")
5240                            && t >= 16
5241                            && il < 4;
5242                        if mdet {
5243                            let a = tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5244                                bank,
5245                                e,
5246                                z,
5247                                t,
5248                                &sel_i32,
5249                                &route_weights,
5250                                n_used,
5251                                tp.activation_limit,
5252                            )?;
5253                            let b = tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5254                                bank,
5255                                e,
5256                                z,
5257                                t,
5258                                &sel_i32,
5259                                &route_weights,
5260                                n_used,
5261                                tp.activation_limit,
5262                            )?;
5263                            let (ha, hb) = (e.dtoh(&a)?, e.dtoh(&b)?);
5264                            let mut md = 0.0f32;
5265                            let mut ndiff = 0usize;
5266                            for (x, y) in ha.iter().zip(hb.iter()) {
5267                                let d = (x - y).abs();
5268                                if d > 0.0 {
5269                                    ndiff += 1;
5270                                }
5271                                if d > md {
5272                                    md = d;
5273                                }
5274                            }
5275                            eprintln!(
5276                                "[moe-determ] il={il} t={t} maxdiff={md:.3e} \
5277                                 differing={ndiff}/{} -> {}",
5278                                ha.len(),
5279                                if ndiff == 0 {
5280                                    "IDENTICAL"
5281                                } else {
5282                                    "NONDETERMINISTIC"
5283                                }
5284                            );
5285                        }
5286                        let mut output =
5287                            tp.runtime.run_tensor_parallel_routes_nvfp4_prime_grouped(
5288                                bank,
5289                                e,
5290                                z,
5291                                t,
5292                                &sel_i32,
5293                                &route_weights,
5294                                n_used,
5295                                tp.activation_limit,
5296                            )?;
5297                        let d_gemm = if mprof {
5298                            let _ = e.stream().synchronize();
5299                            let v = mt.elapsed().as_secs_f64() * 1e3;
5300                            mt = std::time::Instant::now();
5301                            v
5302                        } else {
5303                            0.0
5304                        };
5305                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5306                        if mprof {
5307                            let _ = e.stream().synchronize();
5308                            let d_shared = mt.elapsed().as_secs_f64() * 1e3;
5309                            // Per LAYER, not accumulated: the four trunk phases already carry the
5310                            // per-chunk totals, and one line per layer is what shows whether the
5311                            // cost is flat across layers or concentrated in a few.
5312                            eprintln!(
5313                                "[moe-prof] il={il} t={t} router={d_router:.1}ms \
5314                                 gemm={d_gemm:.1}ms shared={d_shared:.1}ms"
5315                            );
5316                        }
5317                        return Ok(output);
5318                    }
5319                }
5320            }
5321            if t == 1
5322                && crate::tp::step_nvfp4_dev_routes_enabled()?
5323                && crate::tp::step_tp_dev_router_enabled()?
5324            {
5325                if let Some(tp) = &m.step_tp {
5326                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5327                        let (sf, route_norm) = sigmoid;
5328                        // MEMRA_ROUTES_PRESTAGE=1: issue the rank input pull + quantize NOW,
5329                        // before the router — the rank streams overlap the gemv+topk.
5330                        // MEMRA_DEV1_ROUTER=1 rides the prestage hook: rank1 routes LOCALLY
5331                        // from its own z copy (replicated deterministic router — identical
5332                        // bits in, identical sel/w out) and starts its sweep without
5333                        // waiting the root's sel broadcast.
5334                        static D1_ROUTER: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5335                        let d1_router = *D1_ROUTER.get_or_init(|| {
5336                            std::env::var("MEMRA_DEV1_ROUTER").as_deref() == Ok("1")
5337                        });
5338                        if d1_router {
5339                            let (sf_h, rn_h) = sigmoid;
5340                            let n_ex = m.gate_exps.n_expert;
5341                            let act_ct = m.active_count();
5342                            let _ = tp.runtime.nvfp4_routes_prestage_with(
5343                                bank,
5344                                e,
5345                                z,
5346                                |rank1, in1, sel1, w1| {
5347                                    let mut guard = DEV1_ROUTER_REPS
5348                                        .lock()
5349                                        .map_err(|_| "dev1 router replica lock")?;
5350                                    let (reps, scratch) =
5351                                        guard.get_or_insert_with(|| (Default::default(), None));
5352                                    if !reps.contains_key(&il) {
5353                                        use cudarc::driver::DevicePtr;
5354                                        let (g1, p1, a1) = (
5355                                            rank1.htod(&vec![0.0f32; n_ex * n_embd])?,
5356                                            rank1.htod(&vec![0.0f32; n_ex])?,
5357                                            rank1.alloc_u8_uninit(n_ex)?,
5358                                        );
5359                                        for (src, dst_len, dst) in [
5360                                            (
5361                                                {
5362                                                    let s = e.stream();
5363                                                    let (p, _g) =
5364                                                        m.gate_inp.float_data().device_ptr(&s);
5365                                                    p as u64
5366                                                },
5367                                                n_ex * n_embd * 4,
5368                                                {
5369                                                    let s = rank1.stream();
5370                                                    let (p, _g) = g1.device_ptr(&s);
5371                                                    p as u64
5372                                                },
5373                                            ),
5374                                            (
5375                                                {
5376                                                    let s = e.stream();
5377                                                    let (p, _g) = m.exp_probs_b_dev.device_ptr(&s);
5378                                                    p as u64
5379                                                },
5380                                                n_ex * 4,
5381                                                {
5382                                                    let s = rank1.stream();
5383                                                    let (p, _g) = p1.device_ptr(&s);
5384                                                    p as u64
5385                                                },
5386                                            ),
5387                                            (
5388                                                {
5389                                                    let s = e.stream();
5390                                                    let (p, _g) =
5391                                                        m.active_experts_dev.device_ptr(&s);
5392                                                    p as u64
5393                                                },
5394                                                n_ex,
5395                                                {
5396                                                    let s = rank1.stream();
5397                                                    let (p, _g) = a1.device_ptr(&s);
5398                                                    p as u64
5399                                                },
5400                                            ),
5401                                        ] {
5402                                            crate::tp::raw_copy_bytes(dst, src, dst_len, rank1)?;
5403                                        }
5404                                        rank1.stream().synchronize()?;
5405                                        reps.insert(il, (g1, p1, a1));
5406                                    }
5407                                    if scratch.is_none() {
5408                                        *scratch = Some(rank1.htod(&vec![0.0f32; n_ex])?);
5409                                    }
5410                                    let (g1, p1, a1) = reps.get(&il).expect("armed above");
5411                                    let logits1 = scratch.as_mut().expect("armed above");
5412                                    rank1.router_gemv_into(g1, in1, logits1, n_embd, n_ex, 1)?;
5413                                    rank1.moe_router_sigmoid_topk_into(
5414                                        logits1, 1, n_ex, n_used, act_ct, p1, a1, sf_h, rn_h, sel1,
5415                                        w1,
5416                                    )?;
5417                                    Ok(true)
5418                                },
5419                            )?;
5420                        } else {
5421                            let _ = tp.runtime.nvfp4_routes_prestage(bank, e, z)?;
5422                        }
5423                        // Persistent selection buffers: the allocating topk built two fresh
5424                        // slices per layer; sel/w land in process-static rows instead
5425                        // (host-op diet — same kernel, same bytes).
5426                        static SELW: std::sync::Mutex<
5427                            Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>,
5428                        > = std::sync::Mutex::new(None);
5429                        let mut selw = SELW.lock().map_err(|_| "selw lock poisoned")?;
5430                        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
5431                            *selw = Some((
5432                                e.ctx().ordinal(),
5433                                e.htod_i32(&vec![0i32; n_used])?,
5434                                e.htod(&vec![0.0f32; n_used])?,
5435                            ));
5436                        }
5437                        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
5438                        e.moe_router_sigmoid_topk_into(
5439                            &logits,
5440                            t,
5441                            n_expert,
5442                            n_used,
5443                            m.active_count(),
5444                            &m.exp_probs_b_dev,
5445                            &m.active_experts_dev,
5446                            sf,
5447                            route_norm,
5448                            sel_d,
5449                            w_d,
5450                        )?;
5451                        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
5452                        // MEMRA_SHEXP_OVERLAP=1: issue the shared expert from the routes
5453                        // PREJOIN hook so it executes while the peer rank drains its sweep
5454                        // (fills dev0's join wait); apply adds the identical values after.
5455                        static SHEXP_OV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5456                        let shexp_ov = *SHEXP_OV.get_or_init(|| {
5457                            std::env::var("MEMRA_SHEXP_OVERLAP").as_deref() == Ok("1")
5458                        });
5459                        // MEMRA_SHEXP_DEV1=1 (supersedes the dev0 overlap): the shared
5460                        // expert runs on rank1 — the idle device — same kernels, same
5461                        // split program, down row root-resident: bit-identical.
5462                        static SHEXP_D1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5463                        let shexp_d1 = *SHEXP_D1.get_or_init(|| {
5464                            std::env::var("MEMRA_SHEXP_DEV1").as_deref() == Ok("1")
5465                        }) && tp.runtime.rank_engine(1).is_some();
5466                        // MOE TAIL FUSION M1 (MEMRA_TAIL_ADD3=0 reverts): pre-arm the
5467                        // overlap ws + ones row and hand their RAW pointers to the routed
5468                        // run — the join add folds the shexp apply into one launch.
5469                        static TAIL3: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5470                        let tail3 = *TAIL3
5471                            .get_or_init(|| std::env::var("MEMRA_TAIL_ADD3").as_deref() != Ok("0"));
5472                        let mut ov_issued = false;
5473                        let mut d1_issued = false;
5474                        let mut tail_folded = false;
5475                        let mut output = if shexp_d1 {
5476                            let rank1 = tp.runtime.rank_engine(1).expect("checked above");
5477                            tp.runtime
5478                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
5479                                    bank,
5480                                    e,
5481                                    z,
5482                                    &sel_d,
5483                                    &w_d,
5484                                    n_used,
5485                                    tp.activation_limit,
5486                                    || {
5487                                        d1_issued = Self::shexp_dev1_issue(
5488                                            e, rank1, m, z, cfg, il, n_embd,
5489                                        )?;
5490                                        Ok(())
5491                                    },
5492                                )?
5493                        } else if shexp_ov {
5494                            // Raw sh/ones pointers for the fused tail (persistent statics;
5495                            // pointers stable, no lock held across the routed call). The
5496                            // sh CONTENT is written by the prejoin-issued kernels earlier
5497                            // on e's stream — stream order covers the fused add.
5498                            let post_add = if tail3 {
5499                                Self::shexp_overlap_tail_ptrs(e, m, cfg, n_embd)?
5500                            } else {
5501                                None
5502                            };
5503                            let used_post = post_add.is_some();
5504                            let out = tp
5505                                .runtime
5506                                .run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
5507                                    bank,
5508                                    e,
5509                                    z,
5510                                    &sel_d,
5511                                    &w_d,
5512                                    n_used,
5513                                    tp.activation_limit,
5514                                    || {
5515                                        ov_issued =
5516                                            Self::shexp_overlap_issue(e, m, z, cfg, il, n_embd)?;
5517                                        Ok(())
5518                                    },
5519                                    post_add,
5520                                )?;
5521                            // ov_issued false with post_add armed = an early-return arm
5522                            // (the GRAPH door) skipped the prejoin AND ignored post_add —
5523                            // fall through to the normal shexp add (battery v22 receipt:
5524                            // the strict error here failed every graph-door boot).
5525                            if used_post && ov_issued {
5526                                tail_folded = true; // apply folded into the join add
5527                            }
5528                            out
5529                        } else {
5530                            tp.runtime.run_tensor_parallel_routes_nvfp4_device_routed(
5531                                bank,
5532                                e,
5533                                z,
5534                                &sel_d,
5535                                &w_d,
5536                                n_used,
5537                                tp.activation_limit,
5538                            )?
5539                        };
5540                        if output.len() != t * n_embd {
5541                            return Err(format!(
5542                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5543                                output.len()
5544                            )
5545                            .into());
5546                        }
5547                        if tail_folded {
5548                            // shexp already folded into the join add (MOE TAIL FUSION M1)
5549                        } else if d1_issued {
5550                            Self::shexp_dev1_apply(e, &mut output, n_embd)?;
5551                        } else if ov_issued {
5552                            Self::shexp_overlap_apply(e, &mut output, n_embd)?;
5553                        } else {
5554                            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5555                        }
5556                        static DR_LOGGED: std::sync::atomic::AtomicU64 =
5557                            std::sync::atomic::AtomicU64::new(0);
5558                        let layer_bit = 1u64 << (il as u64 % 64);
5559                        if DR_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5560                            & layer_bit
5561                            == 0
5562                        {
5563                            eprintln!(
5564                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5565                                 expert_transport={} native_p2p={} router=device \
5566                                 activation=host-canonical accumulation=host-canonical \
5567                                 output=e-device io=device performance_claim=false \
5568                                 (logged once per layer)",
5569                                tp.devices,
5570                                tp.runtime.transport_label(),
5571                                tp.runtime.native_p2p(),
5572                            );
5573                        }
5574                        return Ok(output);
5575                    }
5576                }
5577            }
5578            // MEMRA_STEP_TP_TIMING=1: cumulative cost of the host routing seam (the dtoh here
5579            // drains every e-stream op queued since the layer's FFN entry, so this bills the
5580            // router matmul + glue too — the decode-bucket ffn residue decomposes here).
5581            static ROUTE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5582            static ROUTE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5583            let route_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
5584            let route_started = route_timing.then(std::time::Instant::now);
5585            let (selected, route_weights, input) = Self::moe_route_sigmoid_with_input(
5586                e,
5587                &logits,
5588                z,
5589                t,
5590                n_embd,
5591                n_expert,
5592                n_used,
5593                m.exp_probs_b.as_deref(),
5594                sigmoid,
5595                m.active_experts.as_deref(),
5596            )?;
5597            if let Some(started) = route_started {
5598                use std::sync::atomic::Ordering;
5599                let ns = ROUTE_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
5600                    + started.elapsed().as_nanos() as u64;
5601                let calls = ROUTE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
5602                if calls % 430 == 0 {
5603                    eprintln!(
5604                        "[moe-route-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
5605                        ns as f64 / 1.0e6,
5606                        ns as f64 / calls as f64 / 1.0e3,
5607                    );
5608                }
5609            }
5610            crate::moesd::record_host_routes(il, n_expert, n_used, &selected)?;
5611            Self::trace_moe_routes(il, t, &selected, &route_weights)?;
5612            Self::trace_moe_input(e, il, t, n_embd, z)?;
5613            let selected = selected
5614                .iter()
5615                .map(|&expert| expert as usize)
5616                .collect::<Vec<_>>();
5617            // Device-IO routes (t=1): the layer input goes to the ranks as a device row and the
5618            // combined output comes back as an e-context row — no host round-trip, no host
5619            // stream sync. Program bytes identical to the host-IO twin (dtoh/htod and dtod
5620            // both preserve f32 bits), gated by greedy token identity.
5621            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5622                if let Some(tp) = &m.step_tp {
5623                    if let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts {
5624                        let mut output = tp.runtime.run_tensor_parallel_routes_nvfp4_device_io(
5625                            bank,
5626                            e,
5627                            z,
5628                            &selected,
5629                            &route_weights,
5630                            n_used,
5631                            tp.activation_limit,
5632                        )?;
5633                        if output.len() != t * n_embd {
5634                            return Err(format!(
5635                                "Step tp routed output has {} values, expected {t}x{n_embd}",
5636                                output.len()
5637                            )
5638                            .into());
5639                        }
5640                        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5641                        static IO_LOGGED: std::sync::atomic::AtomicU64 =
5642                            std::sync::atomic::AtomicU64::new(0);
5643                        let layer_bit = 1u64 << (il as u64 % 64);
5644                        if IO_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed)
5645                            & layer_bit
5646                            == 0
5647                        {
5648                            eprintln!(
5649                                "[step-tp] execute layer={il} tokens={t} devices={:?} \
5650                                 expert_transport={} native_p2p={} activation=host-canonical \
5651                                 accumulation=host-canonical output=e-device io=device \
5652                                 performance_claim=false (logged once per layer)",
5653                                tp.devices,
5654                                tp.runtime.transport_label(),
5655                                tp.runtime.native_p2p(),
5656                            );
5657                        }
5658                        return Ok(output);
5659                    }
5660                }
5661            }
5662            let (routed, mode, devices, transport, native_p2p) = if let Some(tp) = &m.step_tp {
5663                (
5664                    match &tp.experts {
5665                        crate::hybrid::StepTpExpertBank::E4m3(bank) => {
5666                            tp.runtime.run_tensor_parallel_routes(
5667                                bank,
5668                                &input,
5669                                t,
5670                                &selected,
5671                                &route_weights,
5672                                n_used,
5673                            )?
5674                        }
5675                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => {
5676                            if t == 1 && crate::tp::step_nvfp4_dev_routes_enabled()? {
5677                                tp.runtime.run_tensor_parallel_routes_nvfp4_device(
5678                                    bank,
5679                                    &input,
5680                                    &selected,
5681                                    &route_weights,
5682                                    n_used,
5683                                    tp.activation_limit,
5684                                )?
5685                            } else {
5686                                tp.runtime.run_tensor_parallel_routes_nvfp4(
5687                                    bank,
5688                                    &input,
5689                                    t,
5690                                    &selected,
5691                                    &route_weights,
5692                                    n_used,
5693                                    tp.activation_limit,
5694                                )?
5695                            }
5696                        }
5697                    },
5698                    "tp",
5699                    &tp.devices,
5700                    tp.runtime.transport_label(),
5701                    tp.runtime.native_p2p(),
5702                )
5703            } else {
5704                let ep = m
5705                    .step_ep
5706                    .as_ref()
5707                    .ok_or("Step distributed runtime has no EP or TP state")?;
5708                (
5709                    match &ep.experts {
5710                        crate::hybrid::StepEpExpertBank::E4m3(bank) => {
5711                            ep.runtime.run_routed_experts(
5712                                bank,
5713                                &input,
5714                                t,
5715                                &selected,
5716                                &route_weights,
5717                                n_used,
5718                                ep.activation_limit,
5719                            )?
5720                        }
5721                        crate::hybrid::StepEpExpertBank::Nvfp4(bank) => {
5722                            ep.runtime.run_routed_experts_nvfp4(
5723                                bank,
5724                                &input,
5725                                t,
5726                                &selected,
5727                                &route_weights,
5728                                n_used,
5729                                ep.activation_limit,
5730                            )?
5731                        }
5732                    },
5733                    if ep.configured_by_tp { "tp-ep" } else { "ep" },
5734                    &ep.devices,
5735                    ep.runtime.transport_label(),
5736                    ep.runtime.native_p2p(),
5737                )
5738            };
5739            if routed.len() != t * n_embd {
5740                return Err(format!(
5741                    "Step {mode} routed output has {} values, expected {t}x{n_embd}",
5742                    routed.len()
5743                )
5744                .into());
5745            }
5746            let mut output = e.htod(&routed)?;
5747            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut output)?;
5748            // Once per layer per process: the topology contract line is a boot receipt, not a
5749            // per-token trace — 4520 of these per 64-token run measured as real decode wall.
5750            static STEP_LOGGED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
5751            let layer_bit = 1u64 << (il as u64 % 64);
5752            if STEP_LOGGED.fetch_or(layer_bit, std::sync::atomic::Ordering::Relaxed) & layer_bit
5753                == 0
5754            {
5755                eprintln!(
5756                    "[step-{mode}] execute layer={il} tokens={t} devices={devices:?} \
5757                     expert_transport={transport} native_p2p={native_p2p} \
5758                     activation={} accumulation={} output={} \
5759                     performance_claim=false (logged once per layer)",
5760                    if let Some(ep) = &m.step_ep {
5761                        ep.runtime.expert_activation_label()
5762                    } else {
5763                        "host-canonical"
5764                    },
5765                    if let Some(ep) = &m.step_ep {
5766                        ep.runtime.expert_accumulation_label()
5767                    } else {
5768                        "host-canonical"
5769                    },
5770                    if let Some(ep) = &m.step_ep {
5771                        ep.runtime.expert_output_label()
5772                    } else {
5773                        "host-accumulated"
5774                    },
5775                );
5776                if let Some(ep) = &m.step_ep {
5777                    if let Some(limit) = ep.activation_limit {
5778                        eprintln!(
5779                            "[step-ep-clamp] execute layer={il} tokens={t} routed_clamp={limit} \
5780                             formula=min-silu-times-clamped-up performance_claim=false"
5781                        );
5782                    }
5783                }
5784            }
5785            return Ok(output);
5786        }
5787        if Self::sigmoid_resident_dev_eligible(e, m, cfg, sliding_gated_moe) {
5788            let moe = cfg.moe.as_ref().unwrap();
5789            let n_expert = moe.expert_count as usize;
5790            let n_used = moe.expert_used_count as usize;
5791            let sigmoid = cfg.sigmoid_router().unwrap();
5792            let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
5793            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sigmoid)?;
5794            return Self::moe_ffn_sigmoid_dev(e, m, z, zq8, &logits, t, cfg, il, sigmoid);
5795        }
5796        // Expert-grouped dispatch for prefill (T>1). MEMRA_MOE_GROUPED explicitly opts the
5797        // current caller into this research arm; the naked default stays on the established path.
5798        if t > 1 && moe_grouped_enabled(cfg, prefill) {
5799            let grouped_out = Self::moe_ffn_grouped(e, m, z, t, cfg, il, max_block)?;
5800            // MEMRA_MOE_GATE: byte-identity comparison vs sequential path.
5801            // The grouped q8 path uses the same row-wise quantize/dot/activation/FMA programs as
5802            // sequential dispatch; mixed or q8-disabled layouts fall back to the matching f32
5803            // path. A mismatch is therefore a correctness failure, not an accepted numeric class.
5804            if std::env::var("MEMRA_MOE_GATE").is_ok() {
5805                let seq_out = Self::moe_ffn_sequential(e, m, z, t, cfg, il, max_block)?;
5806                let g_host = e.dtoh(&grouped_out)?;
5807                let s_host = e.dtoh(&seq_out)?;
5808                let g_bytes: &[u8] = unsafe {
5809                    std::slice::from_raw_parts(g_host.as_ptr() as *const u8, g_host.len() * 4)
5810                };
5811                let s_bytes: &[u8] = unsafe {
5812                    std::slice::from_raw_parts(s_host.as_ptr() as *const u8, s_host.len() * 4)
5813                };
5814                if g_bytes == s_bytes {
5815                    println!("moe-gate il={il} t={t} BYTE-IDENTICAL");
5816                } else {
5817                    let diffs = g_host
5818                        .iter()
5819                        .zip(s_host.iter())
5820                        .enumerate()
5821                        .filter(|(_, (a, b))| a != b)
5822                        .count();
5823                    let maxdiff = g_host
5824                        .iter()
5825                        .zip(s_host.iter())
5826                        .map(|(a, b)| (a - b).abs())
5827                        .fold(0.0f32, f32::max);
5828                    panic!(
5829                        "moe-gate il={il} t={t} MISMATCH: {diffs}/{} elems differ, maxdiff={maxdiff:.6e}",
5830                        g_host.len()
5831                    );
5832                }
5833            }
5834            return Ok(grouped_out);
5835        }
5836        Self::moe_ffn_sequential_zq8(e, m, z, zq8, t, cfg, il, max_block)
5837    }
5838
5839    fn sigmoid_resident_dev_eligible(
5840        e: &Engine,
5841        m: &MoeWeights,
5842        cfg: &ModelConfig,
5843        sliding_gated_moe: bool,
5844    ) -> bool {
5845        let Some(moe) = cfg.moe.as_ref() else {
5846            return false;
5847        };
5848        // Cached once per process: this predicate runs per MoE layer per decode step, and five
5849        // uncached env reads were ~300+ environ scans/step on the exact path this arm de-hosts.
5850        static OBSERVATION_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5851        let observation_mode = *OBSERVATION_MODE.get_or_init(|| {
5852            std::env::var("MEMRA_MOE_STATS").is_ok()
5853                || std::env::var("MEMRA_MOE_TRACE").is_ok()
5854                || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
5855                || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok()
5856                || std::env::var("MEMRA_MOE_GATE").is_ok()
5857        });
5858        let resident_layout_supported = m.dev_exps.as_ref().is_some_and(|dev| {
5859            if dev.dev != e.ctx().ordinal() {
5860                return false;
5861            }
5862            let q8 = moe_q8_enabled()
5863                && q8_expert_supported(m.gate_exps.qtype)
5864                && q8_expert_supported(m.up_exps.qtype)
5865                && q8_expert_supported(m.down_exps.qtype);
5866            let fp8 = dev.fp8_blk.is_some()
5867                && m.gate_exps.qtype == crate::QT_F8_E4M3_BLK
5868                && m.up_exps.qtype == crate::QT_F8_E4M3_BLK
5869                && m.down_exps.qtype == crate::QT_F8_E4M3_BLK;
5870            q8 || fp8
5871        });
5872        sliding_gated_moe
5873            && sigmoid_router_enabled()
5874            && moe_dev_enabled()
5875            && moe_slab_enabled()
5876            && !observation_mode
5877            && moe.expert_used_count <= 8
5878            && m.has_uniform_expert_layout()
5879            && m.gate_exps.macros.is_none()
5880            && m.up_exps.macros.is_none()
5881            && m.down_exps.macros.is_none()
5882            && !m.has_macros
5883            && resident_layout_supported
5884    }
5885
5886    /// Sequential (per-token) MoE FFN -- the original path. Factored out for the gate comparison.
5887    pub(crate) fn moe_ffn_sequential(
5888        e: &Engine,
5889        m: &MoeWeights,
5890        z: &CudaSlice<f32>,
5891        t: usize,
5892        cfg: &ModelConfig,
5893        il: u16,
5894        max_block: usize,
5895    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5896        Self::moe_ffn_sequential_zq8(e, m, z, None, t, cfg, il, max_block)
5897    }
5898
5899    /// One router-logit selector shared by sequential and grouped dispatch. Real prefill must use
5900    /// the row-wise GEMV when exact routing is enabled: cuBLASLt's reduction changes with `m`,
5901    /// which makes expert selection depend on the caller's chunk or concat-batch shape.
5902    fn moe_router_logits(
5903        e: &Engine,
5904        m: &MoeWeights,
5905        z: &CudaSlice<f32>,
5906        t: usize,
5907        cfg: &ModelConfig,
5908    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5909        if t < PRIME_MIN_T {
5910            // Decode and speculative verify use one fixed per-row reduction program.
5911            if crate::router_kernel_on() {
5912                e.router_gemv(
5913                    m.gate_inp.float_data(),
5914                    z,
5915                    cfg.n_embd as usize,
5916                    m.gate_exps.n_expert,
5917                    t,
5918                )
5919            } else {
5920                e.matmul_decode_exact(&m.gate_inp, z, t)
5921            }
5922        } else if crate::router_prefill_exact_on() && crate::router_kernel_on() {
5923            e.router_gemv(
5924                m.gate_inp.float_data(),
5925                z,
5926                cfg.n_embd as usize,
5927                m.gate_exps.n_expert,
5928                t,
5929            )
5930        } else {
5931            e.matmul(&m.gate_inp, z, t)
5932        }
5933    }
5934
5935    /// Append the host-visible router selection for one layer/forward when calibration tracing is
5936    /// enabled. Both sequential and expert-grouped prefill must call this after routing so the
5937    /// trace is independent of the dispatch optimization selected for the forward.
5938    fn trace_moe_routes(
5939        il: u16,
5940        t: usize,
5941        sel_all: &[u32],
5942        weights: &[f32],
5943    ) -> Result<(), Box<dyn std::error::Error>> {
5944        use std::io::Write as _;
5945        if let Ok(path) = std::env::var("MEMRA_MOE_TRACE") {
5946            let mut f = std::fs::OpenOptions::new()
5947                .create(true)
5948                .append(true)
5949                .open(path)?;
5950            let ids: Vec<String> = sel_all.iter().map(|s| s.to_string()).collect();
5951            writeln!(f, "{} {} {}", il, t, ids.join(","))?;
5952        }
5953        if let Ok(path) = std::env::var("MEMRA_MOE_WEIGHT_TRACE") {
5954            let mut f = std::fs::OpenOptions::new()
5955                .create(true)
5956                .append(true)
5957                .open(path)?;
5958            let pairs: Vec<String> = sel_all
5959                .iter()
5960                .zip(weights)
5961                .map(|(expert, weight)| format!("{expert}:{weight:.9}"))
5962                .collect();
5963            writeln!(f, "{} {} {}", il, t, pairs.join(","))?;
5964        }
5965        Ok(())
5966    }
5967
5968    #[allow(clippy::too_many_arguments)]
5969    fn trace_sigmoid_router_logits(
5970        e: &Engine,
5971        il: u16,
5972        t: usize,
5973        n_expert: usize,
5974        n_used: usize,
5975        logits: &CudaSlice<f32>,
5976        m: &MoeWeights,
5977        (scaling_factor, route_norm): (f32, bool),
5978    ) -> Result<(), Box<dyn std::error::Error>> {
5979        if !crate::sigrouter_contract::served_logit_trace_enabled() || t != 1 {
5980            return Ok(());
5981        }
5982        let logits = e.dtoh(logits)?;
5983        let active: Vec<u8> = m
5984            .active_experts
5985            .as_ref()
5986            .map(|mask| mask.iter().map(|&enabled| u8::from(enabled)).collect())
5987            .unwrap_or_else(|| vec![1; n_expert]);
5988        let bias = m.exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
5989        crate::sigrouter_contract::capture_served_logits(
5990            il as u32,
5991            t,
5992            n_expert,
5993            n_used,
5994            scaling_factor,
5995            route_norm,
5996            &active,
5997            &bias,
5998            &logits,
5999        )?;
6000        Ok(())
6001    }
6002
6003    /// Append the f32 input to one MoE layer for offline, layerwise calibration. This diagnostic
6004    /// intentionally performs a DtoH copy and is therefore disabled unless an explicit fresh trace
6005    /// directory is supplied. Each layer owns one payload file; index.jsonl records byte offsets so
6006    /// a validator can prove request/layer coverage before the trace is used for pruning or healing.
6007    fn trace_moe_input(
6008        e: &Engine,
6009        il: u16,
6010        t: usize,
6011        n_embd: usize,
6012        z: &CudaSlice<f32>,
6013    ) -> Result<(), Box<dyn std::error::Error>> {
6014        use std::io::Write as _;
6015        let Ok(dir) = std::env::var("MEMRA_MOE_INPUT_TRACE_DIR") else {
6016            return Ok(());
6017        };
6018        let values = active_matrix_values(z.len(), t, n_embd, "MoE input trace activation")?;
6019        let host = e.dtoh_view(&z.slice(0..values))?;
6020        let bytes = unsafe {
6021            std::slice::from_raw_parts(
6022                host.as_ptr().cast::<u8>(),
6023                host.len() * std::mem::size_of::<f32>(),
6024            )
6025        };
6026        let state = MOE_INPUT_TRACE_WRITER.get_or_init(|| std::sync::Mutex::new(None));
6027        let mut state = state
6028            .lock()
6029            .map_err(|_| "MoE input trace writer lock is poisoned")?;
6030        if state.is_none() {
6031            let dir = std::path::PathBuf::from(&dir);
6032            std::fs::create_dir_all(&dir)?;
6033            let index = std::fs::OpenOptions::new()
6034                .create(true)
6035                .append(true)
6036                .open(dir.join("index.jsonl"))?;
6037            *state = Some(MoeInputTraceWriter {
6038                dir,
6039                index,
6040                payloads: std::collections::HashMap::new(),
6041            });
6042        }
6043        let writer = state.as_mut().unwrap();
6044        if writer.dir != std::path::Path::new(&dir) {
6045            return Err("MEMRA_MOE_INPUT_TRACE_DIR changed after capture started".into());
6046        }
6047        let file_name = format!("layer-{il:03}.f32");
6048        if !writer.payloads.contains_key(&il) {
6049            let payload = std::fs::OpenOptions::new()
6050                .create(true)
6051                .append(true)
6052                .open(writer.dir.join(&file_name))?;
6053            let offset = payload.metadata()?.len();
6054            writer.payloads.insert(il, (payload, offset));
6055        }
6056        let (payload, offset) = writer.payloads.get_mut(&il).unwrap();
6057        let row_offset = *offset;
6058        payload.write_all(bytes)?;
6059        *offset += bytes.len() as u64;
6060        writeln!(
6061            writer.index,
6062            "{{\"format\":\"memra-moe-input-trace-v1\",\"layer\":{il},\"tokens\":{t},\
6063             \"hidden_size\":{n_embd},\"file\":\"{file_name}\",\"offset\":{row_offset},\
6064             \"payload_bytes\":{}}}",
6065            bytes.len()
6066        )?;
6067        Ok(())
6068    }
6069
6070    #[allow(clippy::too_many_arguments)]
6071    pub(crate) fn moe_ffn_sequential_zq8(
6072        e: &Engine,
6073        m: &MoeWeights,
6074        z: &CudaSlice<f32>,
6075        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
6076        t: usize,
6077        cfg: &ModelConfig,
6078        il: u16,
6079        max_block: usize,
6080    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6081        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
6082        let moe = cfg.moe.as_ref().unwrap();
6083        let n_embd = cfg.n_embd as usize; // 2048 (gate/up in_f, down out_f)
6084        let n_expert = moe.expert_count as usize; // 256
6085        let n_used = moe.expert_used_count as usize; // 8
6086        let n_ff_exp = moe.expert_ff_length as usize; // 512 (gate/up out_f, down in_f)
6087
6088        // verify the HostExps dims match cfg (catches a wrong-file / transpose mixup)
6089        debug_assert_eq!(m.gate_exps.in_f, n_embd);
6090        debug_assert_eq!(m.gate_exps.out_f, n_ff_exp);
6091        debug_assert_eq!(m.down_exps.in_f, n_ff_exp); // down is TRANSPOSED: in=512
6092        debug_assert_eq!(m.down_exps.out_f, n_embd); //                     out=2048
6093        debug_assert_eq!(m.gate_exps.n_expert, n_expert);
6094
6095        // step35 PER-LAYER SwiGLU clamp (None on every other arch and on every unclamped layer).
6096        // Routed experts and the shared expert read SEPARATE arrays — never share one value.
6097        let lim_exp = cfg.clamp_exp_at(il as u32);
6098        let lim_shexp = cfg.clamp_shexp_at(il as u32);
6099        let use_cache = Engine::moe_cache_enabled();
6100        let uniform_experts = m.has_uniform_expert_layout();
6101        let moe_q8 = uniform_experts
6102            && moe_q8_enabled()
6103            && q8_expert_supported(m.gate_exps.qtype)
6104            && q8_expert_supported(m.up_exps.qtype)
6105            && q8_expert_supported(m.down_exps.qtype);
6106        // Experimental secondary backend: complete experts already resident in the SLRU stay on
6107        // CUDA; any expert missing one or more projections runs from the original host GGUF bytes
6108        // through llama.cpp CPU quant dots. Small-t only keeps decode and speculative verification
6109        // in the same numeric/dispatch class; real prefill remains on the established GPU path and
6110        // seeds the residency cache. An explicit library path is the build/runtime gate, so naked
6111        // commands and CI have no llama.cpp or OpenMP dependency.
6112        let cpu_expert_requested = crate::cpu_experts::configured();
6113        if cpu_expert_requested && (cfg.hy3.is_none() || cfg.m3.is_some()) {
6114            return Err(std::io::Error::other(
6115                "MEMRA_CPU_EXPERT_LIB is experimental and currently gated to Hy3",
6116            )
6117            .into());
6118        }
6119        let cpu_hybrid = cpu_expert_requested && t < PRIME_MIN_T && m.dev_exps.is_none();
6120        // A dynamic SLRU changes which complete experts run on CUDA versus llama.cpp CPU dots.
6121        // Those backends are each deterministic but are different numeric configurations, so a
6122        // later prefill eviction can change greedy output. Freeze after the first real prefill;
6123        // decode/spec reads the fixed resident set, while later prefill misses use transient GPU
6124        // staging below and cannot change backend assignment.
6125        let freeze_cpu_residency = cpu_expert_requested
6126            && std::env::var("MEMRA_CPU_EXPERT_FREEZE_CACHE").as_deref() == Ok("1");
6127        let caller_warms_before_freeze = std::env::var("MEMRA_CPU_EXPERT_FREEZE_WARMUP_TOKENS")
6128            .ok()
6129            .and_then(|value| value.parse::<usize>().ok())
6130            .is_some_and(|tokens| tokens > 0);
6131        if cpu_hybrid && freeze_cpu_residency && !caller_warms_before_freeze {
6132            e.freeze_moe_cache();
6133        }
6134        let cache_frozen = use_cache && e.moe_cache_frozen();
6135        let cache_dispatch = use_cache && (!cache_frozen || cpu_hybrid);
6136
6137        // 1. ROUTER: one selector is shared with expert-grouped prefill so changing dispatch
6138        // cannot change logits, selected expert ids, or routing weights.
6139        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
6140        if let Some(sig) = cfg.sigmoid_router() {
6141            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
6142        }
6143
6144        // LAUNCH-STRUCTURE STAGE 3 (2026-07-05, MEMRA_MOE_DEV default ON, =0 rollback): ZERO-DtoH
6145        // device-dispatch when this layer's expert blocks are ALL cache-resident. The fused
6146        // router's sel/w stay ON DEVICE; the expert weight pointers come from the per-layer
6147        // device table of fixed slot addresses; gate/up/silu + down/fma run as the same TWO
6148        // launches per token as gdec. Removes the per-layer router DtoH + stream sync — the
6149        // per-token host stall that dominated the 35B decode wall after stages 1+2.
6150        // BIT-IDENTITY: the router kernel is selection-exact vs the host oracle (kernel-check
6151        // tie gate) and the _dev matvec twins reproduce the gdec kernels' exact FP chains; the
6152        // only difference is where sel/w/pointers are READ from (device instead of params).
6153        // Residency: one-shot PREWARM force-admits the layer while free slots cover it
6154        // (MEMRA_MOE_PREWARM=0 -> organic residency, dev path fires when the SLRU fills).
6155        // Any non-resident layer falls through to host routing + the gdec/sequential path.
6156        // FITS-VRAM RESIDENT EXPERTS (2026-07-06): the layer's expert slabs are device-resident
6157        // (load-time decision) — fire the zero-DtoH dev path unconditionally with the prebuilt
6158        // pointer row. No cache, no dispatch, no residency check: the llama full-offload regime
6159        // (it measured 169.55 vs the cache path's 28.5 on the local 35B — the residency-gate
6160        // all-or-nothing fallback was the 6x). BIT-IDENTITY: same _dev kernels, same math; only
6161        // the pointer table's provenance differs (slab base+stride vs SLRU slot addresses).
6162        // MoE PREFILL PAIR-BATCH (2026-07-06, the 16x pp hole): t>1 on resident experts — ONE
6163        // launch per proj covers ALL (token,expert) pairs (grid.y=pair, warp-per-row), replacing
6164        // the per-expert loop (256 experts x 3-4 launches x tiny m_e). Scatter is slot-ordered
6165        // per token (the sequential-axpy bit-identity class). Requires q8-supported qtypes +
6166        // resident slabs. MEMRA_MOE_PAIRS=0 rollback.
6167        // t >= PRIME_MIN_T only (2026-07-06 exactness fix, verify-probe proof): spec VERIFY
6168        // batches (t = 2..K+2) previously rode these pairs kernels while T=1 decode rode the
6169        // dev_q8 loop — different FP chains, verify-T2 logit maxdiff 2.6e-1 vs eager -> greedy
6170        // flips at tight margins -> 35B real-prompt spec self-consistency FAIL (the 27B
6171        // "verify must be kernel-DISPATCH-identical to decode" lesson, MoE edition). Small-t
6172        // now rides the dev loop below (same kernels per token as decode); pairs serves real
6173        // prefill (t >= 16, where spec never verifies).
6174        // sigmoid-router archs must NOT enter the pairs/dev arms: those dispatch arms still route
6175        // via the fused SOFTMAX device router (moe_router_topk) — silently wrong experts (the
6176        // M3 gate-MISMATCH 74602-vs-92 lesson, 2026-07-07). Sigmoid routing has its own device
6177        // top-k kernel; MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle instead.
6178        // Per-expert macro-scales (compressed-tensors NVFP4 ST class, e.g. unsloth 35B-A3B):
6179        // the device-dispatch kernels (pairs/dev) do NOT fold them — those checkpoints must
6180        // ride the macro-aware sequential/staged paths below or every expert output is off by
6181        // its global scale (~3e4x, measured garbage 2026-07-16). GGUF experts: macros None.
6182        let no_exp_macros = m.gate_exps.macros.is_none()
6183            && m.up_exps.macros.is_none()
6184            && m.down_exps.macros.is_none();
6185        // A clamped-SwiGLU layer (step35 43/44) also cannot ride pairs: moe_pairs_silu_mul's
6186        // epilogue is plain silu(gate)*up with no clamp form, and moe_ffn_pairs takes no `il`
6187        // so it cannot even see the per-layer limit.
6188        if cfg.sigmoid_router().is_none()
6189            && cfg.m3.is_none()
6190            && cfg.hy3.is_none()
6191            && !cfg.swiglu_clamped_at(il as u32)
6192            && no_exp_macros
6193            // > MOE_DEV_MAX_T, not >= PRIME_MIN_T: t==16 is a decode width under the
6194            // exact-16 tier and rides the dev per-token program (see MOE_DEV_MAX_T);
6195            // pairs serves real prefill from 17 up.
6196            && t > MOE_DEV_MAX_T
6197            && m.dev_exps.is_some()
6198            && moe_q8_enabled()
6199            && q8_expert_supported(m.gate_exps.qtype)
6200            && q8_expert_supported(m.up_exps.qtype)
6201            && q8_expert_supported(m.down_exps.qtype)
6202            && std::env::var("MEMRA_MOE_PAIRS")
6203                .map(|v| v != "0")
6204                .unwrap_or(true)
6205            && std::env::var("MEMRA_MOE_STATS").is_err()
6206        {
6207            return Self::moe_ffn_pairs(e, m, z, &logits, t, cfg);
6208        }
6209
6210        // t < PRIME_MIN_T: moe_ffn_dev loops tokens serially (1 launch-pair per token) — the
6211        // decode path AND the spec-verify path (dispatch parity = exactness; see pairs gate
6212        // above). Serial launches are fine at t<=10 (K+2); real prefill never lands here.
6213        // moe_ffn_dev routes via the FUSED SOFTMAX device router (moe_router_topk). Sigmoid
6214        // routing has a separate device top-k kernel, but this dev arm cannot consume its sel/w
6215        // contract, so sigmoid-router arches must NOT enter it: with MOE_CACHE=1 M3 silently
6216        // routed softmax = wrong experts (gate MISMATCH 74602 vs 92, caught 2026-07-07).
6217        // MEMRA_SIG_ROUTER=0 selects the host sigmoid oracle for the supported sigmoid path.
6218        // macro-carrying experts are handled inside the dev path now (epilogue fold + w-scale);
6219        // pairs/gdec/csr keep their macro gates until their kernels grow the fold.
6220        // step35 (2026-08-06) is the third sigmoid-router arch and the deny above was written as
6221        // two arch names, not as the mechanism — so `dev_ok` let it into moe_ffn_dev's
6222        // moe_router_topk (softmax, no exp_probs_b bias, no expert_weights_scale) = silently
6223        // wrong experts, the same failure the pairs gate at :2254 already blocks by predicate.
6224        // Keyed off sigmoid_router() so arch #4 is denied by construction.
6225        // The clamped-SwiGLU layers must also fall through: every moe_ffn_dev kernel's fused
6226        // epilogue is plain silu(gate)*up (moe_gate_up_silu8_dev_*), which has no clamp form.
6227        let dev_ok = uniform_experts
6228            && cfg.sigmoid_router().is_none()
6229            && cfg.m3.is_none()
6230            && cfg.hy3.is_none()
6231            && !cfg.swiglu_clamped_at(il as u32);
6232        // Observation modes must route through the host-visible selection below. Otherwise a fully
6233        // resident layer returns through device dispatch before its trace/stats row is recorded,
6234        // silently biasing calibration toward only non-resident layers on large-VRAM machines.
6235        let observe_routes = std::env::var("MEMRA_MOE_STATS").is_ok()
6236            || std::env::var("MEMRA_MOE_TRACE").is_ok()
6237            || std::env::var("MEMRA_MOE_WEIGHT_TRACE").is_ok()
6238            || std::env::var("MEMRA_MOE_INPUT_TRACE_DIR").is_ok();
6239        if dev_ok
6240            && t <= MOE_DEV_MAX_T
6241            && m.dev_exps.is_some()
6242            && n_used <= 8
6243            && moe_dev_enabled()
6244            && !observe_routes
6245        {
6246            return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
6247        }
6248        if dev_ok && use_cache && n_used <= 8 && moe_dev_enabled() && !observe_routes {
6249            let row_ok = e.with_moe_cache(max_block, |c, eng| {
6250                if moe_prewarm_enabled() {
6251                    c.prewarm_layer(il, m, eng)?;
6252                }
6253                Ok(c.layer_dev_row(il, n_expert, eng)?.is_some())
6254            })?;
6255            if row_ok {
6256                return Self::moe_ffn_dev(e, m, z, zq8, &logits, t, cfg, il, max_block);
6257            }
6258        }
6259
6260        // Per-token (sel[8], w[8]) — sigmoid host oracle (M3/Hy3), else fused-router/softmax.
6261        let (sel_all, w_all, routed_cpu_input) = if let Some(sig) = cfg.sigmoid_router() {
6262            if cpu_hybrid {
6263                let (sel, w, input) = Self::moe_route_sigmoid_with_input(
6264                    e,
6265                    &logits,
6266                    z,
6267                    t,
6268                    n_embd,
6269                    n_expert,
6270                    n_used,
6271                    m.exp_probs_b.as_deref(),
6272                    sig,
6273                    m.active_experts.as_deref(),
6274                )?;
6275                (sel, w, Some(input))
6276            } else {
6277                let (sel, w) =
6278                    Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?;
6279                (sel, w, None)
6280            }
6281        } else {
6282            let (sel, w) =
6283                Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?;
6284            (sel, w, None)
6285        };
6286        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
6287
6288        // MEMRA_MOE_TRACE=<path>: append one line per (layer, step) with the selected expert ids —
6289        // offline analysis derives the decode working set + step-to-step reuse (the go/no-go
6290        // measurement for resident-expert tiering; see rig5090.jsonl 2026-07-07 pinned-tier row).
6291        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
6292        Self::trace_moe_input(e, il, t, n_embd, z)?;
6293
6294        // Worker reads ordinarily overlap NVMe with the current expert but leave their H2D copies
6295        // demand-serialized on the compute stream. Hy3 decode already has a safe owner-thread
6296        // boundary here: sigmoid routing read `logits` back to the host, which synchronized all
6297        // earlier-layer compute. Submit the complete selected set first, then reserve only victims
6298        // outside that set and queue its H2D copies on the copy stream. Dispatch inserts an event
6299        // wait for each pending block, so later copies can overlap the earlier expert kernels while
6300        // the selected weights and numerical kernels remain unchanged. Restrict this experiment to
6301        // T=1; batched forwards can have token-local consumers still in flight between selections.
6302        // The CPU backend reads mmap-backed model bytes directly. Positioned-read worker buffers
6303        // are CUDA write-combined staging allocations and are intentionally not CPU-compute inputs;
6304        // do not spend NVMe bandwidth filling them for a layer whose misses go to CPU.
6305        let worker_disk_prefetch =
6306            cache_dispatch && crate::spill_pread::worker_enabled() && !cpu_hybrid;
6307        let promote_worker_h2d =
6308            t == 1 && worker_disk_prefetch && crate::spill_pread::copy_h2d_enabled();
6309        if promote_worker_h2d {
6310            let mut selected_blocks = Vec::with_capacity(n_used * 3);
6311            for &ex in sel_all.iter().take(n_used) {
6312                let ex = ex as u16;
6313                selected_blocks.extend([
6314                    BlockId::new(il, PROJ_GATE, ex),
6315                    BlockId::new(il, PROJ_UP, ex),
6316                    BlockId::new(il, PROJ_DOWN, ex),
6317                ]);
6318            }
6319            for &ex in sel_all.iter().take(n_used) {
6320                Self::moe_prefetch_disk_expert(e, il, ex as usize, m, max_block, &selected_blocks)?;
6321            }
6322            e.with_moe_cache(max_block, |cache, eng| {
6323                cache.promote_worker_reads_at_safe_boundary(
6324                    &selected_blocks,
6325                    &selected_blocks,
6326                    eng,
6327                )?;
6328                Ok(())
6329            })?;
6330        }
6331
6332        // MEMRA_MOE_STATS: per-layer routing stats for the A2 (expert-grouped prefill) baseline —
6333        // per-token expert-id entropy, active-expert coverage, tokens-per-expert group sizes.
6334        if t > 1 && std::env::var("MEMRA_MOE_STATS").is_ok() {
6335            let mut cnt = vec![0u32; n_expert];
6336            for &s in sel_all.iter() {
6337                cnt[s as usize] += 1;
6338            }
6339            let total = sel_all.len() as f64;
6340            let mut h = 0.0f64;
6341            let mut active = 0usize;
6342            for &c in &cnt {
6343                if c > 0 {
6344                    active += 1;
6345                    let p = c as f64 / total;
6346                    h -= p * p.log2();
6347                }
6348            }
6349            let maxc = cnt.iter().copied().max().unwrap_or(0);
6350            println!(
6351                "moe-stats il={} t={} assignments={} active={}/{} entropy={:.3}b (max {:.3}b) mean_tok_per_active={:.2} max_tok_per_expert={}",
6352                il,
6353                t,
6354                sel_all.len(),
6355                active,
6356                n_expert,
6357                h,
6358                (n_expert as f64).log2(),
6359                total / active.max(1) as f64,
6360                maxc
6361            );
6362        }
6363
6364        // LAUNCH-STRUCTURE STAGE 2 (2026-07-05): moe_out memset elision on the gdec path.
6365        // moe_down8_fma_f32 FULLY overwrites its token row (dst[o] = the in-kernel FMA chain that
6366        // starts at 0.0f — numerically the axpy-into-zeroed-row chain), so when the grouped-decode
6367        // path fires the upfront `e.zeros(t*n_embd)` memset is pure launch churn. Allocate uninit
6368        // when gdec CAN fire (any t — decode t=1 AND the spec verify t=K+1 route here per token)
6369        // and lazily zero ONLY the row of a token that falls through to the sequential axpy loop.
6370        // BIT-IDENTITY: unchanged — every row is either fully overwritten (gdec) or
6371        // zeroed-then-accumulated exactly as before (fallback).
6372        // `!swiglu_clamped_at`: the grouped-decode kernels' fused epilogue is plain
6373        // silu(gate)*up (see the m3 note at the call sites below) — a clamped layer must fall
6374        // through to the sequential loop's ffn_act_lim. Hoisted into the fire predicate so the
6375        // uninit/memset invariant at :2418 stays consistent with what actually dispatches.
6376        let gdec_may_fire = uniform_experts
6377            && use_cache
6378            && n_used <= 8
6379            && gdec_enabled()
6380            && !cfg.swiglu_clamped_at(il as u32);
6381        // SLAB-LOCAL RESIDENT ARM (lane/pp-leverb 2026-08-08): the fits-VRAM resident slabs
6382        // (`dev_exps`, sized per PP device by cx-503b) were consumed ONLY by the pairs/dev
6383        // arms — every one of which DENIES sigmoid-router archs (step35/M3/Hy3). So on those
6384        // archs the slabs were uploaded but never read, and every expert went through the
6385        // SLRU (37 GB H2D staging per pp4096 prime on the Step SKU; and post-cx-503b the
6386        // dead slabs additionally STARVE the SLRU, which sizes itself on free-VRAM-after-
6387        // residents). This arm reads the slabs through the SAME kernels the SLRU arm runs —
6388        // gdec's fused pair when eligible, the per-expert qmatvec twins otherwise — with the
6389        // slab base + ex*stride as the pointer provenance (the exact bit-identity class
6390        // `moe_ffn_dev` documents for its resident-vs-SLRU arms; bytes are the same HostExps
6391        // bytes either way). LOCALITY GATE: `d.dev == e.ctx().ordinal()` — a slab on another
6392        // device must NOT be dereferenced (m=1 peer reads are the measured 34-150x class,
6393        // strictly worse than staging); under PP-2 without the prime walker this admits
6394        // stage-0 layers on dev0 and leaves stage-1 layers on the SLRU, which is the honest
6395        // interim shape. MEMRA_MOE_SLAB=0 = rollback/A-B seam (read per call).
6396        let slab_local = m
6397            .dev_exps
6398            .as_ref()
6399            .filter(|d| !d.gu_il && moe_slab_enabled() && d.dev == e.ctx().ordinal());
6400        let slab_bases = slab_local.map(|d| {
6401            use cudarc::driver::DevicePtr;
6402            let s = e.stream();
6403            let (pg, _g0) = d.gate.device_ptr(&s);
6404            let (pu, _g1) = d.up.device_ptr(&s);
6405            let (pd, _g2) = d.down.device_ptr(&s);
6406            (pg as u64, pu as u64, pd as u64)
6407        });
6408        // Fused-pair eligibility mirrors the gdec call sites exactly (plain-SiLU epilogue,
6409        // no macros, m3 clamp excluded, q8-supported qtypes, <=8 experts) — INCLUDING
6410        // `gdec_enabled()`: the pair IS the gdec kernel pair, so MEMRA_MOE_GDEC=0 must
6411        // disable it here too. That seam is also the exactness localizer: with GDEC=0 both
6412        // provenances run the SAME per-expert qmatvec kernels (slab base+stride vs SLRU
6413        // slot) and must be BIT-IDENTICAL — the true provenance-only pair — while GDEC=1
6414        // compares the fused-pair class against the SLRU's hit/miss MIX (gdec for
6415        // all-resident tokens, staged loop for misses), which is a dispatch-class
6416        // comparison, not a provenance one.
6417        let slab_fused_may_fire = slab_bases.is_some()
6418            && n_used <= 8
6419            && gdec_enabled()
6420            && !cfg.swiglu_clamped_at(il as u32)
6421            && cfg.m3.is_none()
6422            && no_exp_macros
6423            && moe_q8;
6424        // moe_out memset elision: BOTH full-row-overwrite arms (gdec + slab fused) allocate
6425        // uninit; a token that falls through to any accumulating loop zeroes its own row.
6426        let mut moe_out = if gdec_may_fire || slab_fused_may_fire {
6427            e.uninit(t * n_embd)?
6428        } else {
6429            e.zeros(t * n_embd)?
6430        };
6431        // The router readback above already established a host boundary. Copy each small-t hidden
6432        // row once so CPU miss experts can start while the owner thread queues resident GPU work.
6433        let cpu_input = if cpu_hybrid {
6434            Some(routed_cpu_input.ok_or("CPU expert routing did not return the MoE input")?)
6435        } else {
6436            None
6437        };
6438
6439        // GPU scratch: one slot per proj, big enough for ONE expert (default stage-every-token path).
6440        // STAGE 2: LAZY — allocated only if the no-cache staging path actually runs (under
6441        // MEMRA_MOE_CACHE they were 3 dead ~1MB alloc_zeros + memset + free per layer per token,
6442        // measured ~123 memsets/token of the decode wall).
6443        let g_len = m.gate_exps.max_expert_bytes(); // 860160 for the uniform 35B gate
6444        let u_len = m.up_exps.max_expert_bytes(); // 860160 for the uniform 35B up
6445        let d_len = m.down_exps.max_expert_bytes(); // 1114112 for the uniform 35B down
6446        let mut scratch_g: Option<CudaSlice<u8>> = None;
6447        let mut scratch_u: Option<CudaSlice<u8>> = None;
6448        let mut scratch_d: Option<CudaSlice<u8>> = None;
6449        // `max_block` (the GLOBAL max expert stride across all layers) is passed in — the cache slots
6450        // are FIXED-ADDRESS and must fit any layer's block (UD/dynamic GGUFs vary quant per layer).
6451
6452        // EDGE-1 §C.2/C.3: the optional pipeline queues the next selected expert's MISS blocks on
6453        // the copy stream before launching the current expert's compute. Pending slots stay invisible
6454        // to cache hits until dispatch inserts the completion-event wait; current gate/up/down ids are
6455        // protected from eviction. This changes scheduling only, never the GGUF bytes or GEMM path.
6456        let page_window = moe_page_prefetch_window();
6457
6458        // 2. PER TOKEN: routed-expert loop. The ONE dispatch change vs Stage-1: a resident slot
6459        //    (cache HIT, no H2D) OR a staged slot (MISS) feeds the SAME unchanged qmatvec_view.
6460        for tok in 0..t {
6461            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
6462            let w = &w_all[tok * n_used..(tok + 1) * n_used];
6463            let zt = z.slice(tok * n_embd..(tok + 1) * n_embd); // CudaView<f32>
6464            let mut tok_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
6465
6466            // STAGE-2 GROUPED DECODE (2026-07-04, MEMRA_MOE_GDEC default ON, =0 rollback): fold
6467            // this token's whole routed-expert FFN (8x gate/up/silu + 8x down/axpy = 40 launches)
6468            // into TWO launches via expert-pointer indirection over the fixed-address cache slots.
6469            // Fires only when ALL 3*n_used blocks are ALREADY cache-resident (pure-HIT: zero
6470            // memcpy, zero admission, so no slot can move under the collected pointers) — any
6471            // miss falls through to the sequential loop below, which admits as before. In steady
6472            // state on a fully-resident rig every token-layer takes the grouped path.
6473            // BIT-IDENTITY: each in-kernel dot reproduces qmatvec_f32's exact reduction; SiLU is
6474            // silu_mul_f32's exact expression; the down accumulation is a slot-ordered
6475            // __fmaf_rn chain == the sequential axpy_f32 chain (MEMRA_MOE_GDEC_GATE compares).
6476            // cfg.m3: the grouped kernels' fused epilogues are plain SiLU — M3's swigluoai must
6477            // NOT take them until the kernels grow the clamped variant. NVFP4 experts carry
6478            // per-expert macro-scales the fused kernels don't fold — those fall through too.
6479            let no_macros = m.gate_exps.macros.is_none()
6480                && m.up_exps.macros.is_none()
6481                && m.down_exps.macros.is_none();
6482            // SLAB-LOCAL FUSED PAIR (lane/pp-leverb 2026-08-08): the gdec launch pair
6483            // (moe_gate_up_silu8_q8 + moe_down8_fma_q8 — the EXACT kernels, same FP chains)
6484            // with pointers computed from the resident slab base + ex*stride instead of
6485            // collected SLRU slot addresses. No cache lock, no residency predicate — the
6486            // slab holds every expert by construction, so this arm never falls through
6487            // (gdec's P(all 24 resident) ≈ 0.37 coin-flip and the miss path's ~49-launch
6488            // staging both die). Bit-identity class: pointer provenance only, the same
6489            // slab-vs-SLRU equivalence moe_ffn_dev documents. Ordered BEFORE gdec: when a
6490            // slab exists it is strictly better (no lock, no miss).
6491            if slab_fused_may_fire {
6492                let (pg, pu, pd) = slab_bases.unwrap();
6493                let mut gp = [0u64; 8];
6494                let mut up = [0u64; 8];
6495                let mut dp = [0u64; 8];
6496                for (j, &ex) in sel.iter().enumerate() {
6497                    let ex = ex as usize;
6498                    gp[j] = pg + (ex * m.gate_exps.expert_stride) as u64;
6499                    up[j] = pu + (ex * m.up_exps.expert_stride) as u64;
6500                    dp[j] = pd + (ex * m.down_exps.expert_stride) as u64;
6501                }
6502                let mut wv = [0f32; 8];
6503                wv[..n_used].copy_from_slice(w);
6504                if tok_q8.is_none() {
6505                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6506                }
6507                let (zq, zd) = tok_q8.as_ref().unwrap();
6508                let act = e.moe_gate_up_silu8_q8(
6509                    crate::WPtr8(gp),
6510                    crate::WPtr8(up),
6511                    zq,
6512                    zd,
6513                    n_embd,
6514                    n_ff_exp,
6515                    n_used,
6516                    m.gate_exps.qtype,
6517                    m.up_exps.qtype,
6518                    m.gate_exps.row_bytes,
6519                    m.up_exps.row_bytes,
6520                )?;
6521                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
6522                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6523                e.moe_down8_fma_q8(
6524                    crate::WPtr8(dp),
6525                    crate::F32x8(wv),
6526                    &aq2,
6527                    &ad2,
6528                    &mut dst,
6529                    n_ff_exp,
6530                    n_embd,
6531                    n_used,
6532                    m.down_exps.qtype,
6533                    m.down_exps.row_bytes,
6534                )?;
6535                continue;
6536            }
6537            if gdec_may_fire && moe_q8 && cfg.m3.is_none() && no_macros {
6538                if tok_q8.is_none() {
6539                    tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6540                }
6541                let (zq, zd) = tok_q8.as_ref().unwrap();
6542                if Self::moe_gdec_token_q8(
6543                    e,
6544                    m,
6545                    il,
6546                    max_block,
6547                    zq,
6548                    zd,
6549                    sel,
6550                    w,
6551                    &mut moe_out,
6552                    tok,
6553                    n_embd,
6554                    n_ff_exp,
6555                    n_used,
6556                )? {
6557                    continue;
6558                }
6559            } else if gdec_may_fire
6560                && cfg.m3.is_none()
6561                && no_macros
6562                && Self::moe_gdec_token(
6563                    e,
6564                    m,
6565                    il,
6566                    max_block,
6567                    &zt,
6568                    sel,
6569                    w,
6570                    &mut moe_out,
6571                    tok,
6572                    n_embd,
6573                    n_ff_exp,
6574                    n_used,
6575                )?
6576            {
6577                continue;
6578            }
6579
6580            // STAGE 2 memset-elision invariant: moe_out was allocated UNINIT when gdec or the
6581            // slab pair could fire. This token fell through to a sequential axpy loop, which
6582            // ACCUMULATES — zero its row first (row-sized memset; other rows are owned by the
6583            // full-overwrite arms). slab_fused_may_fire never actually reaches here (its arm
6584            // has no fallible predicate), included for the allocation invariant's symmetry.
6585            if gdec_may_fire || slab_fused_may_fire {
6586                let mut row = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6587                e.memset_zeros_view(&mut row)?;
6588            }
6589
6590            // Fiddler-style split at whole-expert granularity. Partial residency is deliberately a
6591            // CPU assignment: transferring the missing projection would reintroduce the PCIe/NVMe
6592            // stall this path exists to remove, while mixing projections would require another
6593            // activation round-trip. Weight addresses remain valid until this worker is joined at
6594            // the bottom of the token scope.
6595            let mut cpu_mask = vec![false; sel.len()];
6596            let cpu_worker = if let Some(host_input) = cpu_input.as_ref() {
6597                let gpu_resident = if use_cache {
6598                    e.with_moe_cache(max_block, |cache, _| {
6599                        Ok(sel
6600                            .iter()
6601                            .map(|&expert| {
6602                                let expert = expert as u16;
6603                                [PROJ_GATE, PROJ_UP, PROJ_DOWN]
6604                                    .into_iter()
6605                                    .filter(|&projection| {
6606                                        cache
6607                                            .resident(BlockId::new(il, projection, expert))
6608                                            .is_some()
6609                                    })
6610                                    .count()
6611                            })
6612                            .collect::<Vec<_>>())
6613                    })?
6614                } else {
6615                    vec![0; sel.len()]
6616                };
6617                let mut cpu_selected = Vec::new();
6618                for (index, (&expert, &route_weight)) in sel.iter().zip(w).enumerate() {
6619                    if gpu_resident[index] != 3 {
6620                        cpu_mask[index] = true;
6621                        crate::cpu_experts::record_incomplete_gpu_residency(gpu_resident[index]);
6622                        let expert = expert as usize;
6623                        cpu_selected.push((expert, route_weight));
6624                    }
6625                }
6626                if crate::cpu_experts::predictor_enabled() {
6627                    // Fire-and-forget lookahead: the predictor worker scores layers il+1..
6628                    // from this layer's MoE input and prefetches predicted-and-missing
6629                    // experts into the companion RAM cache. Never blocks this thread.
6630                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6631                    crate::cpu_experts::predictor_submit(il, row);
6632                }
6633                if cpu_selected.is_empty() {
6634                    None
6635                } else {
6636                    let row = &host_input[tok * n_embd..(tok + 1) * n_embd];
6637                    let job = crate::cpu_experts::prepare_job(m, il, &cpu_selected, row)
6638                        .map_err(std::io::Error::other)?;
6639                    Some(crate::cpu_experts::submit(job).map_err(std::io::Error::other)?)
6640                }
6641            } else {
6642                None
6643            };
6644
6645            let worker_window = worker_disk_prefetch
6646                .then(worker_prefetch_window)
6647                .unwrap_or(0);
6648            for (j, &ex) in sel.iter().enumerate() {
6649                if cpu_mask[j] {
6650                    continue;
6651                }
6652                let ex = ex as usize;
6653                // PER-EXPERT SLAB READ (lane/pp-leverb 2026-08-08): the layers the fused
6654                // pair above excludes — step35's CLAMPED layers 43/44 (ffn_act_lim has no
6655                // fused form) and macro-carrying artifacts — still have their bytes in the
6656                // local resident slab. Same kernels as the SLRU arms (`qmatvec_expert_q8` /
6657                // `qmatvec_view`), same ffn_act_lim/macro folds; provenance = slab base +
6658                // ex*stride. No dispatch lock, no admission, no prefetch — nothing to miss.
6659                if let Some(d) = slab_local {
6660                    let gl = m.gate_exps.expert_layout(ex);
6661                    let ul = m.up_exps.expert_layout(ex);
6662                    let dl = m.down_exps.expert_layout(ex);
6663                    let (g0, u0, d0) = (
6664                        ex * m.gate_exps.expert_stride,
6665                        ex * m.up_exps.expert_stride,
6666                        ex * m.down_exps.expert_stride,
6667                    );
6668                    let (gate, up) = if moe_q8 {
6669                        if tok_q8.is_none() {
6670                            tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6671                        }
6672                        let (zq, zd) = tok_q8.as_ref().unwrap();
6673                        (
6674                            e.qmatvec_expert_q8(
6675                                &d.gate,
6676                                g0..g0 + gl.len,
6677                                zq,
6678                                zd,
6679                                1,
6680                                m.gate_exps.in_f,
6681                                m.gate_exps.out_f,
6682                                gl.qtype,
6683                                gl.row_bytes,
6684                            )?,
6685                            e.qmatvec_expert_q8(
6686                                &d.up,
6687                                u0..u0 + ul.len,
6688                                zq,
6689                                zd,
6690                                1,
6691                                m.up_exps.in_f,
6692                                m.up_exps.out_f,
6693                                ul.qtype,
6694                                ul.row_bytes,
6695                            )?,
6696                        )
6697                    } else {
6698                        (
6699                            e.qmatvec_view(
6700                                &d.gate,
6701                                g0..g0 + gl.len,
6702                                &zt,
6703                                1,
6704                                m.gate_exps.in_f,
6705                                m.gate_exps.out_f,
6706                                gl.qtype,
6707                                gl.row_bytes,
6708                            )?,
6709                            e.qmatvec_view(
6710                                &d.up,
6711                                u0..u0 + ul.len,
6712                                &zt,
6713                                1,
6714                                m.up_exps.in_f,
6715                                m.up_exps.out_f,
6716                                ul.qtype,
6717                                ul.row_bytes,
6718                            )?,
6719                        )
6720                    };
6721                    let mut act = e.uninit(n_ff_exp)?;
6722                    Self::ffn_act_lim(
6723                        e,
6724                        cfg,
6725                        &gate,
6726                        &up,
6727                        m.gate_exps.macro_scale(ex),
6728                        m.up_exps.macro_scale(ex),
6729                        lim_exp,
6730                        &mut act,
6731                        n_ff_exp,
6732                    )?;
6733                    let y = if moe_q8 {
6734                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6735                        e.qmatvec_expert_q8(
6736                            &d.down,
6737                            d0..d0 + dl.len,
6738                            &aq2,
6739                            &ad2,
6740                            1,
6741                            m.down_exps.in_f,
6742                            m.down_exps.out_f,
6743                            dl.qtype,
6744                            dl.row_bytes,
6745                        )?
6746                    } else {
6747                        let actv = act.slice(0..n_ff_exp);
6748                        e.qmatvec_view(
6749                            &d.down,
6750                            d0..d0 + dl.len,
6751                            &actv,
6752                            1,
6753                            m.down_exps.in_f,
6754                            m.down_exps.out_f,
6755                            dl.qtype,
6756                            dl.row_bytes,
6757                        )?
6758                    };
6759                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6760                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6761                    continue;
6762                }
6763                for next in page_prefetch_positions(j, sel.len(), page_window) {
6764                    Self::moe_prefetch_host_expert(sel[next] as usize, m);
6765                }
6766                let keep = [
6767                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_GATE, ex as u16),
6768                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_UP, ex as u16),
6769                    crate::moe_cache::BlockId::new(il, crate::moe_cache::PROJ_DOWN, ex as u16),
6770                ];
6771                if worker_disk_prefetch && worker_window > 0 {
6772                    for next in worker_prefetch_positions(j, sel.len(), worker_window) {
6773                        Self::moe_prefetch_disk_expert(
6774                            e,
6775                            il,
6776                            sel[next] as usize,
6777                            m,
6778                            max_block,
6779                            &keep,
6780                        )?;
6781                    }
6782                } else if cache_dispatch
6783                    && !cpu_hybrid
6784                    && moe_prefetch_enabled()
6785                    && j + 1 < sel.len()
6786                {
6787                    let next = sel[j + 1] as usize;
6788                    Self::moe_prefetch_expert(e, il, next, m, max_block, &keep)?;
6789                }
6790                let [gate_q8, up_q8, down_q8] = [moe_q8; 3];
6791                if cache_dispatch && (gate_q8 || up_q8 || down_q8) {
6792                    // dp4a EXPERT PATH (MEMRA_MOE_Q8): quantize z-row once per token. Mixed expert
6793                    // layouts stay on the metadata-aware f32 path.
6794                    if (gate_q8 || up_q8) && tok_q8.is_none() {
6795                        tok_q8 = Some(e.quantize_q8_1_view(&zt, 1, n_embd)?);
6796                    }
6797                    let gate = if gate_q8 {
6798                        let (zq, zd) = tok_q8.as_ref().unwrap();
6799                        Self::moe_cached_gemm_q8(e, il, PROJ_GATE, ex, m, max_block, zq, zd)?
6800                    } else {
6801                        Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?
6802                    };
6803                    let up = if up_q8 {
6804                        let (zq, zd) = tok_q8.as_ref().unwrap();
6805                        Self::moe_cached_gemm_q8(e, il, PROJ_UP, ex, m, max_block, zq, zd)?
6806                    } else {
6807                        Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?
6808                    };
6809                    let mut act = e.uninit(n_ff_exp)?;
6810                    Self::ffn_act_lim(
6811                        e,
6812                        cfg,
6813                        &gate,
6814                        &up,
6815                        m.gate_exps.macro_scale(ex),
6816                        m.up_exps.macro_scale(ex),
6817                        lim_exp,
6818                        &mut act,
6819                        n_ff_exp,
6820                    )?;
6821                    let y = if down_q8 {
6822                        let (aq2, ad2) = e.quantize_q8_1(&act, 1, n_ff_exp)?;
6823                        Self::moe_cached_gemm_q8(e, il, PROJ_DOWN, ex, m, max_block, &aq2, &ad2)?
6824                    } else {
6825                        let actv = act.slice(0..n_ff_exp);
6826                        Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?
6827                    };
6828                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6829                    // down-proj macro folds into the accumulate weight (1.0 for non-macro archs).
6830                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6831                } else if cache_dispatch {
6832                    // SLRU residency cache: per-projection, dispatch the block (HIT => resident slot,
6833                    // MISS => staged slot) then run the SAME unchanged qmatvec_view from that slot.
6834                    // The bytes the kernel reads are byte-for-byte the same GGUF block (§B.3); the
6835                    // only difference between HIT and MISS is whether the memcpy_htod ran.
6836                    let gate = Self::moe_cached_gemm(e, il, PROJ_GATE, ex, m, max_block, &zt)?;
6837                    let up = Self::moe_cached_gemm(e, il, PROJ_UP, ex, m, max_block, &zt)?;
6838                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6839                    Self::ffn_act_lim(
6840                        e,
6841                        cfg,
6842                        &gate,
6843                        &up,
6844                        m.gate_exps.macro_scale(ex),
6845                        m.up_exps.macro_scale(ex),
6846                        lim_exp,
6847                        &mut act,
6848                        n_ff_exp,
6849                    )?;
6850                    let actv = act.slice(0..n_ff_exp);
6851                    let y = Self::moe_cached_gemm(e, il, PROJ_DOWN, ex, m, max_block, &actv)?;
6852                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6853                    // down-proj macro folds into the accumulate weight (post-matmul linear scale).
6854                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6855                } else if cache_frozen {
6856                    // A later prompt prime must not change the CPU/GPU assignment frozen after the
6857                    // first prime. Reuse every fixed resident projection directly and stage only a
6858                    // true miss through the ordinary scratch slot. This preserves the established
6859                    // f32-dequant numeric path while avoiding a full-bank reread on every prime.
6860                    let gate = Self::moe_frozen_gemm(
6861                        e,
6862                        il,
6863                        PROJ_GATE,
6864                        ex,
6865                        m,
6866                        max_block,
6867                        &zt,
6868                        &mut scratch_g,
6869                        g_len,
6870                    )?;
6871                    let up = Self::moe_frozen_gemm(
6872                        e,
6873                        il,
6874                        PROJ_UP,
6875                        ex,
6876                        m,
6877                        max_block,
6878                        &zt,
6879                        &mut scratch_u,
6880                        u_len,
6881                    )?;
6882                    let mut act = e.uninit(n_ff_exp)?;
6883                    Self::ffn_act_lim(
6884                        e,
6885                        cfg,
6886                        &gate,
6887                        &up,
6888                        m.gate_exps.macro_scale(ex),
6889                        m.up_exps.macro_scale(ex),
6890                        lim_exp,
6891                        &mut act,
6892                        n_ff_exp,
6893                    )?;
6894                    let actv = act.slice(0..n_ff_exp);
6895                    let y = Self::moe_frozen_gemm(
6896                        e,
6897                        il,
6898                        PROJ_DOWN,
6899                        ex,
6900                        m,
6901                        max_block,
6902                        &actv,
6903                        &mut scratch_d,
6904                        d_len,
6905                    )?;
6906                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6907                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6908                } else {
6909                    // Stage-1: stage gate/up/down for expert `ex` into the scratch slots, then GEMM.
6910                    // Lazy scratch: first no-cache expert allocates the 3 slots (uninit — stage_expert
6911                    // fully overwrites the byte range the GEMM reads).
6912                    if scratch_g.is_none() {
6913                        scratch_g = Some(e.alloc_u8_uninit(g_len)?);
6914                        scratch_u = Some(e.alloc_u8_uninit(u_len)?);
6915                        scratch_d = Some(e.alloc_u8_uninit(d_len)?);
6916                    }
6917                    let (sg, su, sd) = (
6918                        scratch_g.as_mut().unwrap(),
6919                        scratch_u.as_mut().unwrap(),
6920                        scratch_d.as_mut().unwrap(),
6921                    );
6922                    let gl = m.gate_exps.expert_layout(ex);
6923                    let ul = m.up_exps.expert_layout(ex);
6924                    let dl = m.down_exps.expert_layout(ex);
6925                    e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
6926                    let gate = e.qmatvec_view(
6927                        sg,
6928                        0..gl.len,
6929                        &zt,
6930                        1,
6931                        m.gate_exps.in_f,
6932                        m.gate_exps.out_f,
6933                        gl.qtype,
6934                        gl.row_bytes,
6935                    )?;
6936
6937                    e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
6938                    let up = e.qmatvec_view(
6939                        su,
6940                        0..ul.len,
6941                        &zt,
6942                        1,
6943                        m.up_exps.in_f,
6944                        m.up_exps.out_f,
6945                        ul.qtype,
6946                        ul.row_bytes,
6947                    )?;
6948
6949                    let mut act = e.uninit(n_ff_exp)?; // activation fully overwrites
6950                    Self::ffn_act_lim(
6951                        e,
6952                        cfg,
6953                        &gate,
6954                        &up,
6955                        m.gate_exps.macro_scale(ex),
6956                        m.up_exps.macro_scale(ex),
6957                        lim_exp,
6958                        &mut act,
6959                        n_ff_exp,
6960                    )?;
6961
6962                    e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
6963                    let actv = act.slice(0..n_ff_exp);
6964                    let y = e.qmatvec_view(
6965                        sd,
6966                        0..dl.len,
6967                        &actv,
6968                        1,
6969                        m.down_exps.in_f,
6970                        m.down_exps.out_f,
6971                        dl.qtype,
6972                        dl.row_bytes,
6973                    )?;
6974
6975                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6976                    e.axpy_into(&y, w[j] * m.down_exps.macro_scale(ex), &mut dst, n_embd)?;
6977                }
6978            }
6979            if let Some(worker) = cpu_worker {
6980                let cpu_output = worker.wait().map_err(std::io::Error::other)?;
6981                let cpu_output = e.htod(&cpu_output)?;
6982                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
6983                e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
6984            }
6985            if cpu_hybrid && !cache_frozen && cpu_expert_profile_admit_enabled() {
6986                for (j, &ex) in sel.iter().enumerate() {
6987                    if cpu_mask[j] {
6988                        Self::moe_profile_admit_expert(e, il, ex as usize, m, max_block)?;
6989                    }
6990                }
6991            }
6992        }
6993
6994        // 3. SHARED EXPERT (ALWAYS-ON, no routing) on the SAME z — qwen35moe only. OLMoE and most
6995        //    vanilla MoE have NO shared expert (the shexp tensors are absent / `None`); skip it then.
6996        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
6997        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
6998        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
6999            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
7000        {
7001            let n_ff_sh = gate_shexp.out_features(); // 512
7002            // Q8 TRUNK-FUSION (decode t=1): gate_shexp+up_shexp are Q8_0 same-shape on the 35B —
7003            // ONE fused2 launch (also folds the two per-matmul re-quantizes of z into one).
7004            // Bit-identical per (tensor,row); falls back to the two matmul calls when ineligible.
7005            // Small-t (spec verify 2..15) rides matmul_decode_exact so shexp FP chains match the
7006            // t==1 decode chain per column (cuBLASLt n-dependence + dp4a-vs-mmvq class); real
7007            // prefill keeps the batched matmul. Activation routes through ffn_act (SiLU for
7008            // softmax archs, clamped swigluoai for M3 — identical to silu_mul when cfg.m3 is None).
7009            let verify_t = t > 1 && t < PRIME_MIN_T;
7010            let (sg_gate, sg_up) = if t == 1 {
7011                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
7012            } else if verify_t {
7013                (
7014                    e.matmul_decode_exact(gate_shexp, z, t)?,
7015                    e.matmul_decode_exact(up_shexp, z, t)?,
7016                )
7017            } else {
7018                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?) // [T, 512] each
7019            };
7020            let mut sa = e.uninit(t * n_ff_sh)?; // activation fully overwrites
7021            Self::ffn_act_lim(
7022                e,
7023                cfg,
7024                &sg_gate,
7025                &sg_up,
7026                1.0,
7027                1.0,
7028                lim_shexp,
7029                &mut sa,
7030                t * n_ff_sh,
7031            )?;
7032            let sh = if verify_t {
7033                e.matmul_decode_exact(down_shexp, &sa, t)?
7034            } else {
7035                e.matmul(down_shexp, &sa, t)?
7036            }; // [T, n_embd]
7037
7038            // shexp gate: qwen35moe sigmoid-gates via ffn_gate_inp_shexp (1-D ne=[n_embd] ->
7039            // out_f=1); M3 has no gate tensor -> weight 1.0. Decode + verify ride the fused
7040            // sigmoid-dot kernel (one fold order for both chains; kills the per-layer
7041            // cuBLASLt m=1 splitK GEMM — 40x/step, ~10% of the H100 q35 decode step).
7042            // SERVE ISOLATION (lane/concat-prime-exact, 2026-08-02): PREFILL rides it too.
7043            // This out_f=1 cuBLASLt GEMV is the SECOND m-dependent op in the trunk (probed:
7044            // rows [0,19) move by 1.07e-4 between m=74 and m=75 while sigmoid_dot_rows is
7045            // BIT-IDENTICAL — allw-shexpgate-o35b.log). The gate multiplies the shared
7046            // expert's contribution into every token's residual, so under cross-request
7047            // concat prefill a session's hidden state depended on its co-arrivals' token
7048            // count. MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched cuBLASLt linear.
7049            let g = match &m.gate_inp_shexp {
7050                Some(gate_inp_shexp) => {
7051                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
7052                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
7053                    } else {
7054                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
7055                        let mut g = e.uninit(t)?; // sigmoid fully overwrites
7056                        e.sigmoid(&gs, &mut g, t)?;
7057                        g
7058                    }
7059                }
7060                None => e.htod(&vec![1.0f32; t])?,
7061            };
7062            // moe_out[r, :] += sh[r, :] * g[r]   (per-token scalar gate; g=1 ungated)
7063            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
7064        }
7065
7066        Ok(moe_out)
7067    }
7068
7069    /// Stage-1 (no-cache) per-DECODE-TOKEN H2D bytes: every routed block re-staged every layer every
7070    /// token = sum over MoE layers of n_used * (gate+up+down expert_stride). The §D.4 PCIe baseline.
7071    pub fn stage1_h2d_per_token(&self) -> u64 {
7072        use crate::hybrid::Ffn;
7073        let n_used = self
7074            .cfg
7075            .moe
7076            .as_ref()
7077            .map(|m| m.expert_used_count as u64)
7078            .unwrap_or(0);
7079        let mut bytes = 0u64;
7080        for l in self.layers.iter() {
7081            if let Ffn::Moe(m) = &l.ffn {
7082                bytes += n_used
7083                    * (m.gate_exps.max_expert_bytes()
7084                        + m.up_exps.max_expert_bytes()
7085                        + m.down_exps.max_expert_bytes()) as u64;
7086            }
7087        }
7088        bytes
7089    }
7090
7091    /// Largest expert block (bytes) across ALL MoE layers + the MTP head — the fixed cache slot size.
7092    /// UD/dynamic GGUFs quant different layers differently, so `expert_stride` varies per layer; the
7093    /// residency cache slots are fixed-address and must fit any block, so size to this global max.
7094    pub(crate) fn max_moe_block(&self) -> usize {
7095        use crate::hybrid::Ffn;
7096        let mut mx = 0usize;
7097        let mut scan = |ffn: &Ffn| {
7098            if let Ffn::Moe(m) = ffn {
7099                mx = mx
7100                    .max(m.gate_exps.max_expert_bytes())
7101                    .max(m.up_exps.max_expert_bytes())
7102                    .max(m.down_exps.max_expert_bytes());
7103            }
7104        };
7105        for l in self.layers.iter() {
7106            scan(&l.ffn);
7107        }
7108        if let Some(mtp) = self.mtp.as_ref() {
7109            scan(&mtp.ffn);
7110        }
7111        mx
7112    }
7113
7114    /// Exact retained projection lengths for cache class sizing. Pruned ids keep router positions
7115    /// but have no bytes and therefore consume no residency slot.
7116    pub(crate) fn moe_cache_block_sizes(&self) -> Vec<usize> {
7117        use crate::hybrid::Ffn;
7118        let mut sizes = Vec::new();
7119        let mut scan = |ffn: &Ffn| {
7120            let Ffn::Moe(m) = ffn else { return };
7121            for ex in 0..m.gate_exps.n_expert {
7122                if m.active_experts.as_ref().is_some_and(|active| !active[ex]) {
7123                    continue;
7124                }
7125                for exps in [&m.gate_exps, &m.up_exps, &m.down_exps] {
7126                    let len = exps.expert_layout(ex).len;
7127                    if len > 0 {
7128                        sizes.push(len);
7129                    }
7130                }
7131            }
7132        };
7133        for layer in &self.layers {
7134            scan(&layer.ffn);
7135        }
7136        if let Some(mtp) = &self.mtp {
7137            scan(&mtp.ffn);
7138        }
7139        sizes
7140    }
7141
7142    /// Persist the frozen residency set so a later process can restage it directly and skip
7143    /// the profiling warmup. Plain text: a versioned header binding slot geometry, then one
7144    /// `layer proj ex` triple per line. A mismatched or stale profile is rejected at load
7145    /// (header check) or degrades to fewer restaged blocks (per-id checks); either way the
7146    /// post-freeze argmax gate still validates the serving assignment.
7147    pub fn save_cpu_expert_residency_profile(
7148        &self,
7149        e: &Engine,
7150        path: &std::path::Path,
7151    ) -> Result<(), Box<dyn std::error::Error>> {
7152        let Some(ids) = e.export_moe_residency() else {
7153            return Err("no MoE residency cache to persist".into());
7154        };
7155        let mut body = format!(
7156            "memra-freeze-profile v1 max_block={} blocks={}\n",
7157            self.max_moe_block(),
7158            ids.len()
7159        );
7160        for (layer, proj, ex) in &ids {
7161            body.push_str(&format!("{layer} {proj} {ex}\n"));
7162        }
7163        let tmp = path.with_extension("tmp");
7164        std::fs::write(&tmp, body)?;
7165        std::fs::rename(&tmp, path)?;
7166        println!(
7167            "[moe-cache] freeze profile saved: {} blocks -> {}",
7168            ids.len(),
7169            path.display()
7170        );
7171        Ok(())
7172    }
7173
7174    /// Restage a saved freeze profile and freeze immediately, skipping the profiling warmup.
7175    /// Returns false (leaving the cache untouched for a normal warmup) when the profile is
7176    /// missing or its header does not match this model's slot geometry.
7177    pub fn restore_cpu_expert_residency_profile(
7178        &self,
7179        e: &Engine,
7180        path: &std::path::Path,
7181    ) -> Result<bool, Box<dyn std::error::Error>> {
7182        use crate::hybrid::Ffn;
7183        use crate::moe_cache::BlockId;
7184        let Ok(content) = std::fs::read_to_string(path) else {
7185            return Ok(false);
7186        };
7187        let mut lines = content.lines();
7188        let Some(header) = lines.next() else {
7189            return Ok(false);
7190        };
7191        let expected = format!("memra-freeze-profile v1 max_block={}", self.max_moe_block());
7192        if !header.starts_with(&expected) {
7193            println!(
7194                "[moe-cache] freeze profile ignored (geometry mismatch): {}",
7195                path.display()
7196            );
7197            return Ok(false);
7198        }
7199        let mut by_layer: std::collections::HashMap<u16, Vec<BlockId>> =
7200            std::collections::HashMap::new();
7201        for line in lines {
7202            let mut fields = line.split_whitespace();
7203            let (Some(layer), Some(proj), Some(ex)) = (fields.next(), fields.next(), fields.next())
7204            else {
7205                continue;
7206            };
7207            let (Ok(layer), Ok(proj), Ok(ex)) =
7208                (layer.parse::<u16>(), proj.parse::<u8>(), ex.parse::<u16>())
7209            else {
7210                continue;
7211            };
7212            by_layer
7213                .entry(layer)
7214                .or_default()
7215                .push(BlockId::new(layer, proj, ex));
7216        }
7217        let requested: usize = by_layer.values().map(Vec::len).sum();
7218        if requested == 0 {
7219            return Ok(false);
7220        }
7221        let max_block = self.max_moe_block();
7222        let mut restaged = 0usize;
7223        let mut stage_layer =
7224            |layer_index: u16, ffn: &Ffn| -> Result<(), Box<dyn std::error::Error>> {
7225                let Ffn::Moe(m) = ffn else { return Ok(()) };
7226                let Some(ids) = by_layer.get(&layer_index) else {
7227                    return Ok(());
7228                };
7229                e.with_moe_cache(max_block, |cache, eng| {
7230                    for id in ids {
7231                        if cache.restage_block(*id, m, eng)? {
7232                            restaged += 1;
7233                        }
7234                    }
7235                    Ok(())
7236                })
7237            };
7238        for (index, layer) in self.layers.iter().enumerate() {
7239            stage_layer(index as u16, &layer.ffn)?;
7240        }
7241        if let Some(mtp) = self.mtp.as_ref() {
7242            stage_layer(u16::MAX, &mtp.ffn)?;
7243        }
7244        e.freeze_moe_cache();
7245        println!(
7246            "[moe-cache] freeze profile restored: {restaged}/{requested} blocks restaged from {}",
7247            path.display()
7248        );
7249        Ok(true)
7250    }
7251
7252    /// Freeze the heterogeneous CPU/GPU split after the caller's discarded profile warmup.
7253    pub fn freeze_cpu_expert_residency(
7254        &self,
7255        e: &Engine,
7256    ) -> Result<(), Box<dyn std::error::Error>> {
7257        e.freeze_moe_cache();
7258        Ok(())
7259    }
7260
7261    /// FFN activation dispatch: swigluoai (clamped, alpha/limit) when cfg.m3 says so, else the
7262    /// standard SiLU*up. One seam so every FFN site (dense, routed expert, shared expert) follows
7263    /// the model's activation exactly.
7264    ///
7265    /// NO-`il` FORM: cannot apply step35's PER-LAYER SwiGLU clamp. Only call it from a site whose
7266    /// layer provably has no live limit (dense-FFN layers, MTP blocks) — `ffn_act_lim` is the
7267    /// form for anything that can land on a clamped layer.
7268    pub fn ffn_act(
7269        e: &Engine,
7270        cfg: &ModelConfig,
7271        gate: &CudaSlice<f32>,
7272        up: &CudaSlice<f32>,
7273        act: &mut CudaSlice<f32>,
7274        n: usize,
7275    ) -> Result<(), Box<dyn std::error::Error>> {
7276        Self::ffn_act_scaled(e, cfg, gate, up, 1.0, 1.0, act, n)
7277    }
7278
7279    /// ffn_act with per-tensor post-matmul macro-scales folded in (gs/us == 1.0 -> identical
7280    /// float ops to ffn_act; used by the ModelOpt NVFP4 expert path where each expert tensor
7281    /// carries a `weight_scale_2`). Same no-`il` contract as `ffn_act`.
7282    #[allow(clippy::too_many_arguments)]
7283    pub(crate) fn ffn_act_scaled(
7284        e: &Engine,
7285        cfg: &ModelConfig,
7286        gate: &CudaSlice<f32>,
7287        up: &CudaSlice<f32>,
7288        gs: f32,
7289        us: f32,
7290        act: &mut CudaSlice<f32>,
7291        n: usize,
7292    ) -> Result<(), Box<dyn std::error::Error>> {
7293        Self::ffn_act_lim(e, cfg, gate, up, gs, us, None, act, n)
7294    }
7295
7296    /// ffn_act_scaled + step35's PER-LAYER clamped SwiGLU. `limit`:
7297    ///   * `None`   -> the unclamped dispatch (every arch except step35's layers 43-44).
7298    ///   * `Some(l)`-> `min(silu(gate*gs), l) * clamp(up*us, +-l)` (llama-graph.cpp:2146/1751,
7299    ///                 non-DEEPSEEK4 branch). Callers source it from `cfg.clamp_exp_at(il)`
7300    ///                 (routed experts) or `cfg.clamp_shexp_at(il)` (shared expert) — the two
7301    ///                 arrays are SEPARATE and a layer can have one without the other.
7302    /// The `> 1e-6` eps gate lives in `clamp_exp_at`/`clamp_shexp_at`, so a `Some` here is
7303    /// already known live.
7304    #[allow(clippy::too_many_arguments)]
7305    pub(crate) fn ffn_act_lim(
7306        e: &Engine,
7307        cfg: &ModelConfig,
7308        gate: &CudaSlice<f32>,
7309        up: &CudaSlice<f32>,
7310        gs: f32,
7311        us: f32,
7312        limit: Option<f32>,
7313        act: &mut CudaSlice<f32>,
7314        n: usize,
7315    ) -> Result<(), Box<dyn std::error::Error>> {
7316        if let Some(m3) = cfg.m3.as_ref() {
7317            debug_assert!(
7318                limit.is_none(),
7319                "m3 swigluoai and step35 clamp are different archs"
7320            );
7321            return e.swigluoai_mul_scaled(
7322                gate,
7323                up,
7324                gs,
7325                us,
7326                m3.swiglu_alpha,
7327                m3.swiglu_limit,
7328                act,
7329                n,
7330            );
7331        }
7332        if let Some(l) = limit {
7333            return e.swiglu_clamped_mul_scaled(gate, up, gs, us, l, act, n);
7334        }
7335        if gs == 1.0 && us == 1.0 {
7336            return e.silu_mul(gate, up, act, n);
7337        }
7338        e.silu_mul_scaled(gate, up, gs, us, act, n)
7339    }
7340
7341    /// Routing for the whole batch: returns (sel [T*n_used] expert ids, w [T*n_used] renorm weights),
7342    /// token-major. Default = the Stage-1 host path (dtoh logits, softmax-256, stable DESC top-k,
7343    /// renorm). MEMRA_FUSED_ROUTER = the device kernel (§A) which reproduces the same numerics; we
7344    /// still dtoh the tiny [T,n_used] sel/w buffers (64 B/token vs 1 KB/token) — the host loop
7345    /// indexes HostExps.bytes on the CPU to choose the DMA source (§A.2 output staging).
7346    fn moe_route(
7347        e: &Engine,
7348        logits: &CudaSlice<f32>,
7349        t: usize,
7350        n_expert: usize,
7351        n_used: usize,
7352    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7353        Self::moe_route_cfg(e, logits, t, n_expert, n_used, None)
7354    }
7355
7356    /// DeepSeek-V3-class sigmoid routing (Step-3.7, MiniMax-M3, Hy3, GLM-DSA). Reference:
7357    /// M3/Hy3 modeling code — scores = sigmoid(logits); selection over scores + expert bias
7358    /// (M3 `e_score_correction_bias` / Hy3 `expert_bias`, both surfaced as `exp_probs_b`);
7359    /// weights = un-biased scores of the selected experts, sum-normalized when `route_norm`,
7360    /// x scaling factor (M3 routed_scaling_factor 2.0 / Hy3 router_scaling_factor 2.826).
7361    /// Default uses one device kernel plus the same pinned sel/w readback contract as the fused
7362    /// softmax router. `MEMRA_SIG_ROUTER=0` restores the full-logit DtoH host oracle.
7363    #[allow(clippy::too_many_arguments)]
7364    fn moe_route_sigmoid_cfg(
7365        e: &Engine,
7366        logits: &CudaSlice<f32>,
7367        t: usize,
7368        n_expert: usize,
7369        n_used: usize,
7370        m: &MoeWeights,
7371        (sf, route_norm): (f32, bool),
7372    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7373        if sigmoid_router_enabled() {
7374            return e.moe_router_sigmoid_topk_host(
7375                logits,
7376                t,
7377                n_expert,
7378                n_used,
7379                m.active_count(),
7380                &m.exp_probs_b_dev,
7381                &m.active_experts_dev,
7382                sf,
7383                route_norm,
7384            );
7385        }
7386        let lg = e.dtoh(logits)?;
7387        Self::moe_route_sigmoid_host(
7388            &lg,
7389            t,
7390            n_expert,
7391            n_used,
7392            m.exp_probs_b.as_deref(),
7393            sf,
7394            route_norm,
7395            m.active_experts.as_deref(),
7396        )
7397    }
7398
7399    /// Softmax router for qwen35moe/OLMoE. An active-mask overlay retains the host oracle because
7400    /// the existing softmax device kernel has no mask input.
7401    fn moe_route_cfg(
7402        e: &Engine,
7403        logits: &CudaSlice<f32>,
7404        t: usize,
7405        n_expert: usize,
7406        n_used: usize,
7407        active: Option<&[bool]>,
7408    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7409        // LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router DEFAULT ON (MEMRA_FUSED_ROUTER=0
7410        // rollback) via the single-sync pinned readback — softmax arch only.
7411        if active.is_none() && !matches!(std::env::var("MEMRA_FUSED_ROUTER").as_deref(), Ok("0")) {
7412            return e.moe_router_topk_host(logits, t, n_expert, n_used);
7413        }
7414        // Host oracle (the §D bit-identity reference).
7415        let lg = e.dtoh(logits)?; // [T*n_expert] host
7416        let mut sel = vec![0u32; t * n_used];
7417        let mut w_out = vec![0f32; t * n_used];
7418        for tok in 0..t {
7419            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7420            // softmax over ALL n_expert (stable: subtract max)
7421            let maxl = row
7422                .iter()
7423                .enumerate()
7424                .filter(|(i, _)| active.is_none_or(|mask| mask[*i]))
7425                .map(|(_, &x)| x)
7426                .fold(f32::NEG_INFINITY, f32::max);
7427            let mut probs = vec![0f32; n_expert];
7428            let mut den = 0f32;
7429            for i in 0..n_expert {
7430                if active.is_some_and(|mask| !mask[i]) {
7431                    continue;
7432                }
7433                let x = (row[i] - maxl).exp();
7434                probs[i] = x;
7435                den += x;
7436            }
7437            for p in probs.iter_mut() {
7438                *p /= den;
7439            }
7440            // stable DESC sort: prob DESC, ascending-index tiebreak.
7441            let mut idx: Vec<usize> = (0..n_expert)
7442                .filter(|&i| active.is_none_or(|mask| mask[i]))
7443                .collect();
7444            idx.sort_by(|&a, &b| probs[b].total_cmp(&probs[a]).then(a.cmp(&b)));
7445            let sl = &idx[..n_used];
7446            let mut wv: Vec<f32> = sl.iter().map(|&i| probs[i]).collect();
7447            let mut ws: f32 = wv.iter().sum();
7448            ws = ws.max(6.103515625e-5_f32); // F16 smallest normal, clamp BEFORE divide
7449            for x in wv.iter_mut() {
7450                *x /= ws;
7451            }
7452            for j in 0..n_used {
7453                sel[tok * n_used + j] = sl[j] as u32;
7454                w_out[tok * n_used + j] = wv[j];
7455            }
7456        }
7457        Ok((sel, w_out))
7458    }
7459
7460    #[allow(clippy::too_many_arguments)]
7461    fn moe_route_sigmoid_with_input(
7462        e: &Engine,
7463        logits: &CudaSlice<f32>,
7464        input: &CudaSlice<f32>,
7465        t: usize,
7466        in_features: usize,
7467        n_expert: usize,
7468        n_used: usize,
7469        bias: Option<&[f32]>,
7470        (sf, route_norm): (f32, bool),
7471        active: Option<&[bool]>,
7472    ) -> Result<(Vec<u32>, Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
7473        let logit_values =
7474            active_matrix_values(logits.len(), t, n_expert, "sigmoid router logits")?;
7475        let input_values =
7476            active_matrix_values(input.len(), t, in_features, "sigmoid router input")?;
7477        let (lg, input) = e.dtoh_pair_views(
7478            &logits.slice(0..logit_values),
7479            &input.slice(0..input_values),
7480        )?;
7481        let (sel, w) =
7482            Self::moe_route_sigmoid_host(&lg, t, n_expert, n_used, bias, sf, route_norm, active)?;
7483        Ok((sel, w, input))
7484    }
7485
7486    /// Start the prediction-guided prefetch worker (MEMRA_MOE_PREFETCH=depth). Call after
7487    /// residency freeze: the worker filters against a static snapshot of the frozen HBM set.
7488    /// Builds a fully-owned per-layer table (host router copies via one-time DtoH, bias,
7489    /// active mask, prebuilt projection descriptors) so no model reference escapes.
7490    pub fn start_moe_prefetch_predictor(
7491        &self,
7492        e: &Engine,
7493        cfg: &ModelConfig,
7494    ) -> Result<(), Box<dyn std::error::Error>> {
7495        use crate::hybrid::Ffn;
7496        let Some(sig) = cfg.sigmoid_router() else {
7497            return Err("prefetch predictor requires a sigmoid-router arch".into());
7498        };
7499        let resident: std::collections::HashSet<(u16, u8, u16)> = e
7500            .export_moe_residency()
7501            .ok_or("prefetch predictor needs the frozen MoE residency cache")?
7502            .into_iter()
7503            .collect();
7504        let mut layers = Vec::new();
7505        for (index, layer) in self.layers.iter().enumerate() {
7506            let Ffn::Moe(m) = &layer.ffn else { continue };
7507            let crate::model::GpuTensor::Float { data, .. } = &m.gate_inp else {
7508                continue;
7509            };
7510            let router = e.dtoh(data)?;
7511            let n_expert = m.gate_exps.n_expert;
7512            let n_embd = m.gate_exps.in_f;
7513            if router.len() != n_embd * n_expert {
7514                continue;
7515            }
7516            let build = |exps: &crate::model::HostExps| {
7517                (0..n_expert)
7518                    .map(|expert| crate::cpu_experts::predictor_projection(exps, expert))
7519                    .collect::<Vec<_>>()
7520            };
7521            layers.push((
7522                index as u16,
7523                crate::cpu_experts::PredictLayerInit {
7524                    router,
7525                    bias: m.exp_probs_b.clone(),
7526                    active: m.active_experts.clone(),
7527                    n_embd,
7528                    n_used: cfg
7529                        .moe
7530                        .as_ref()
7531                        .map(|moe| moe.expert_used_count as usize)
7532                        .ok_or("prefetch predictor requires MoE config")?,
7533                    sig,
7534                    weights_n_expert: n_expert,
7535                    gate: build(&m.gate_exps),
7536                    up: build(&m.up_exps),
7537                    down: build(&m.down_exps),
7538                },
7539            ));
7540        }
7541        crate::cpu_experts::start_prefetch_predictor(layers, resident).map_err(|error| error.into())
7542    }
7543
7544    /// Sigmoid-routing oracle shared by the prefetch predictor and `kernel-check`: identical
7545    /// selection math to the rollback runtime, applied to host-computed logits.
7546    #[allow(clippy::too_many_arguments)]
7547    pub fn moe_route_sigmoid_host_public(
7548        logits: &[f32],
7549        t: usize,
7550        n_expert: usize,
7551        n_used: usize,
7552        bias: Option<&[f32]>,
7553        sf: f32,
7554        route_norm: bool,
7555        active: Option<&[bool]>,
7556    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7557        Self::moe_route_sigmoid_host(logits, t, n_expert, n_used, bias, sf, route_norm, active)
7558    }
7559
7560    #[allow(clippy::too_many_arguments)]
7561    fn moe_route_sigmoid_host(
7562        lg: &[f32],
7563        t: usize,
7564        n_expert: usize,
7565        n_used: usize,
7566        bias: Option<&[f32]>,
7567        sf: f32,
7568        route_norm: bool,
7569        active: Option<&[bool]>,
7570    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
7571        let active_count = active
7572            .map(|mask| mask.iter().filter(|&&enabled| enabled).count())
7573            .unwrap_or(n_expert);
7574        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
7575        if lg.len() != t * n_expert {
7576            return Err(format!(
7577                "sigmoid router logits length mismatch: got {}, expected {}",
7578                lg.len(),
7579                t * n_expert,
7580            )
7581            .into());
7582        }
7583        let mut sel = vec![0u32; t * n_used];
7584        let mut w_out = vec![0f32; t * n_used];
7585        for tok in 0..t {
7586            let row = &lg[tok * n_expert..(tok + 1) * n_expert];
7587            let scores: Vec<f32> = row.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect();
7588            // selection score = sigmoid + bias; weight = plain sigmoid.
7589            let selsc: Vec<f32> = match bias {
7590                Some(b) => scores.iter().zip(b).map(|(s, bb)| s + bb).collect(),
7591                None => scores.clone(),
7592            };
7593            let mut idx: Vec<usize> = (0..n_expert)
7594                .filter(|&i| active.is_none_or(|mask| mask[i]))
7595                .collect();
7596            idx.sort_by(|&a, &b| selsc[b].total_cmp(&selsc[a]).then(a.cmp(&b)));
7597            let sl = &idx[..n_used];
7598            let mut wv: Vec<f32> = sl.iter().map(|&i| scores[i]).collect();
7599            if route_norm {
7600                let ws: f32 = wv.iter().sum::<f32>().max(1e-20);
7601                for x in wv.iter_mut() {
7602                    *x = *x / ws * sf;
7603                }
7604            } else {
7605                for x in wv.iter_mut() {
7606                    *x *= sf;
7607                }
7608            }
7609            for j in 0..n_used {
7610                sel[tok * n_used + j] = sl[j] as u32;
7611                w_out[tok * n_used + j] = wv[j];
7612            }
7613        }
7614        Ok((sel, w_out))
7615    }
7616
7617    /// Step-3.7 resident-slab dispatch: sigmoid top-k outputs remain on device and feed the
7618    /// existing slot-ordered q8 expert kernels directly. Mixed layouts, spill, remote slabs,
7619    /// macro-scaled experts, and observation modes are denied by the caller.
7620    #[allow(clippy::too_many_arguments)]
7621    fn moe_ffn_sigmoid_dev(
7622        e: &Engine,
7623        m: &MoeWeights,
7624        z: &CudaSlice<f32>,
7625        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
7626        logits: &CudaSlice<f32>,
7627        t: usize,
7628        cfg: &ModelConfig,
7629        il: u16,
7630        (scaling_factor, route_norm): (f32, bool),
7631    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7632        let moe = cfg.moe.as_ref().unwrap();
7633        let n_embd = cfg.n_embd as usize;
7634        let n_expert = moe.expert_count as usize;
7635        let n_used = moe.expert_used_count as usize;
7636        let n_ff_exp = moe.expert_ff_length as usize;
7637        let dev = m.dev_exps.as_ref().unwrap();
7638        debug_assert_eq!(dev.dev, e.ctx().ordinal());
7639        debug_assert!(m.has_uniform_expert_layout());
7640        debug_assert!(!m.has_macros);
7641
7642        let (sel_d, w_d) = e.moe_router_sigmoid_topk(
7643            logits,
7644            t,
7645            n_expert,
7646            n_used,
7647            m.active_count(),
7648            &m.exp_probs_b_dev,
7649            &m.active_experts_dev,
7650            scaling_factor,
7651            route_norm,
7652        )?;
7653        crate::moesd::record_device_routes(e, il, n_expert, n_used, &sel_d)?;
7654        if let Some(fp8) = dev.fp8_blk.as_ref() {
7655            debug_assert_eq!(m.gate_exps.qtype, crate::QT_F8_E4M3_BLK);
7656            debug_assert_eq!(m.up_exps.qtype, crate::QT_F8_E4M3_BLK);
7657            debug_assert_eq!(m.down_exps.qtype, crate::QT_F8_E4M3_BLK);
7658            debug_assert_eq!(fp8.gate.rows, m.gate_exps.out_f.div_ceil(128));
7659            debug_assert_eq!(fp8.up.rows, m.up_exps.out_f.div_ceil(128));
7660            debug_assert_eq!(fp8.down.rows, m.down_exps.out_f.div_ceil(128));
7661
7662            // Official Step-3.7 FP8 uses dynamic per-token/per-128 E4M3
7663            // activations with block-128 E4M3 weights. This deliberately
7664            // simple resident reference is the correctness oracle for later
7665            // grouped and TP/EP execution. MEMRA_ST_E4M3=0 chooses the
7666            // load-time Q8 diagnostic representation, so one process never
7667            // crosses between numerical programs.
7668            let selected = e.dtoh_i32(&sel_d)?;
7669            let route_weights = e.dtoh(&w_d)?;
7670            let mut moe_out = e.zeros(t * n_embd)?;
7671            for tok in 0..t {
7672                let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
7673                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
7674                for j in 0..n_used {
7675                    let pair = tok * n_used + j;
7676                    let expert = selected[pair] as usize;
7677                    let gate = Self::moe_resident_fp8_e4m3(
7678                        e,
7679                        &m.gate_exps,
7680                        &dev.gate,
7681                        &fp8.gate,
7682                        expert,
7683                        &zt,
7684                        1,
7685                    )?;
7686                    let up = Self::moe_resident_fp8_e4m3(
7687                        e, &m.up_exps, &dev.up, &fp8.up, expert, &zt, 1,
7688                    )?;
7689                    let mut act = e.uninit(n_ff_exp)?;
7690                    Self::ffn_act_lim(
7691                        e,
7692                        cfg,
7693                        &gate,
7694                        &up,
7695                        1.0,
7696                        1.0,
7697                        cfg.clamp_exp_at(il as u32),
7698                        &mut act,
7699                        n_ff_exp,
7700                    )?;
7701                    let act = act.slice(0..n_ff_exp);
7702                    let down = Self::moe_resident_fp8_e4m3(
7703                        e,
7704                        &m.down_exps,
7705                        &dev.down,
7706                        &fp8.down,
7707                        expert,
7708                        &act,
7709                        1,
7710                    )?;
7711                    e.axpy_into(&down, route_weights[pair], &mut dst, n_embd)?;
7712                }
7713            }
7714            if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7715                eprintln!(
7716                    "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} \
7717                     native=fp8blk-w8a8-e4m3-reference clamp={}",
7718                    cfg.clamp_exp_at(il as u32).is_some(),
7719                );
7720            }
7721            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7722            return Ok(moe_out);
7723        }
7724        let (gate_row_bytes, up_row_bytes) = if dev.gu_il {
7725            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7726            (combined, combined)
7727        } else {
7728            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7729        };
7730        let (zq, zd) = match (t, zq8) {
7731            (1, Some((q, d))) => (q.clone(), d.clone()),
7732            _ => e.quantize_q8_1(z, t, n_embd)?,
7733        };
7734        let n_pairs = t * n_used;
7735        let mut moe_out = if cfg.clamp_exp_at(il as u32).is_some() {
7736            // The final Step layers retain the established separate gate/up -> clamp -> down
7737            // arithmetic. Pair rows are derived from token position; selected expert ids and
7738            // routing weights remain the device router's buffers throughout.
7739            let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
7740            let pair_tok_d = e.htod_i32(&pair_tok)?;
7741            let gate = e.moe_pairs_matvec_q8(
7742                &dev.ptr_row,
7743                0,
7744                &pair_tok_d,
7745                &sel_d,
7746                &zq,
7747                &zd,
7748                n_embd,
7749                n_ff_exp,
7750                n_expert,
7751                n_pairs,
7752                m.gate_exps.qtype,
7753                gate_row_bytes,
7754            )?;
7755            let up = e.moe_pairs_matvec_q8(
7756                &dev.ptr_row,
7757                1,
7758                &pair_tok_d,
7759                &sel_d,
7760                &zq,
7761                &zd,
7762                n_embd,
7763                n_ff_exp,
7764                n_expert,
7765                n_pairs,
7766                m.up_exps.qtype,
7767                up_row_bytes,
7768            )?;
7769            let mut act = e.uninit(n_pairs * n_ff_exp)?;
7770            Self::ffn_act_lim(
7771                e,
7772                cfg,
7773                &gate,
7774                &up,
7775                1.0,
7776                1.0,
7777                cfg.clamp_exp_at(il as u32),
7778                &mut act,
7779                n_pairs * n_ff_exp,
7780            )?;
7781            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7782            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
7783            let pair_self_d = e.htod_i32(&pair_self)?;
7784            let down = e.moe_pairs_matvec_q8(
7785                &dev.ptr_row,
7786                2,
7787                &pair_self_d,
7788                &sel_d,
7789                &aq2,
7790                &ad2,
7791                n_ff_exp,
7792                n_embd,
7793                n_expert,
7794                n_pairs,
7795                m.down_exps.qtype,
7796                m.down_exps.row_bytes,
7797            )?;
7798            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7799            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7800            let tok_off_d = e.htod_i32(&tok_off)?;
7801            let tok_ids_d = e.htod_i32(&tok_ids)?;
7802            let mut output = e.uninit(t * n_embd)?;
7803            e.moe_pairs_scatter(&down, &w_d, &tok_off_d, &tok_ids_d, &mut output, t, n_embd)?;
7804            output
7805        } else {
7806            let act = e.moe_gate_up_silu8_dev_q8_rows(
7807                &dev.ptr_row,
7808                &sel_d,
7809                &zq,
7810                &zd,
7811                t,
7812                n_embd,
7813                n_ff_exp,
7814                n_used,
7815                n_expert,
7816                m.gate_exps.qtype,
7817                m.up_exps.qtype,
7818                gate_row_bytes,
7819                up_row_bytes,
7820                &m.dev_macros,
7821            )?;
7822            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
7823            let mut output = e.uninit(t * n_embd)?;
7824            e.moe_down8_fma_dev_q8_rows_g(
7825                &dev.ptr_row,
7826                &sel_d,
7827                &w_d,
7828                &aq2,
7829                &ad2,
7830                &mut output,
7831                t,
7832                n_ff_exp,
7833                n_embd,
7834                n_used,
7835                n_expert,
7836                m.down_exps.qtype,
7837                m.down_exps.row_bytes,
7838            )?;
7839            output
7840        };
7841
7842        if std::env::var("MEMRA_SIG_ROUTER_DISPATCH_TRACE").as_deref() == Ok("1") {
7843            eprintln!(
7844                "[sigrouter-dev] layer={il} tokens={t} experts={n_expert} used={n_used} clamp={} gu_il={}",
7845                cfg.clamp_exp_at(il as u32).is_some(),
7846                dev.gu_il,
7847            );
7848        }
7849        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
7850        Ok(moe_out)
7851    }
7852
7853    #[allow(clippy::too_many_arguments)]
7854    fn moe_resident_fp8_e4m3(
7855        e: &Engine,
7856        exps: &crate::model::HostExps,
7857        bytes: &CudaSlice<u8>,
7858        scales: &crate::hybrid::DevExpertFp8ProjectionScales,
7859        expert: usize,
7860        x: &cudarc::driver::CudaView<f32>,
7861        m: usize,
7862    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7863        let layout = exps.expert_layout(expert);
7864        debug_assert_eq!(layout.qtype, crate::QT_F8_E4M3_BLK);
7865        debug_assert_eq!(scales.rows * scales.cols, scales.expert_stride);
7866        let byte_start = expert * exps.expert_stride;
7867        let scale_start = expert * scales.expert_stride;
7868        let weight = bytes.slice(byte_start..byte_start + layout.len);
7869        let scale = scales
7870            .scales
7871            .slice(scale_start..scale_start + scales.expert_stride);
7872        e.qmatvec_mmq_fp8_blk_view(&weight, &scale, x, m, exps.in_f, exps.out_f)
7873    }
7874
7875    /// LAUNCH-STRUCTURE STAGE 3: the ZERO-DtoH fully-resident MoE FFN. Caller guarantees the
7876    /// layer's device pointer row exists (checked under the cache lock). Router top-k runs on
7877    /// device; sel/w are consumed by the `_dev` matvec twins directly; NOTHING crosses PCIe.
7878    /// Same numerics as the fused-router + gdec chain (kernel-level bit-identity, see the
7879    /// MoE PREFILL PAIR-BATCH: host routing (sel/w like the sequential path), then 5 launches
7880    /// TOTAL per layer (quantize z, gate-pairs, up-pairs, silu, act-quantize, down-pairs,
7881    /// scatter) regardless of T or expert count. Bit-identity class: per (pair,row) dot =
7882    /// qmatvec_expert_q8 order; per-token accumulation slot-ordered (scatter kernel).
7883    fn moe_ffn_pairs(
7884        e: &Engine,
7885        m: &MoeWeights,
7886        z: &CudaSlice<f32>,
7887        logits: &CudaSlice<f32>,
7888        t: usize,
7889        cfg: &ModelConfig,
7890    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7891        let moe = cfg.moe.as_ref().unwrap();
7892        let n_embd = cfg.n_embd as usize;
7893        let n_expert = moe.expert_count as usize;
7894        let n_used = moe.expert_used_count as usize;
7895        let n_ff_exp = moe.expert_ff_length as usize;
7896        // This arm has no `il` and every kernel in it fuses PLAIN silu(gate)*up, so a clamped
7897        // model must never reach it. The caller's gate at `moe_ffn_sequential_zq8` denies per
7898        // layer via `swiglu_clamped_at(il)`; assert the whole-model form here so a future caller
7899        // that forgets the gate fails loudly in debug instead of returning wrong logits.
7900        debug_assert!(
7901            !cfg.swiglu_clamped_anywhere(),
7902            "moe_ffn_pairs has no per-layer clamp: fused epilogues are plain SiLU"
7903        );
7904        let dev = m.dev_exps.as_ref().unwrap();
7905        // WALL-GAP ARC: interleaved gate/up slab strides (see moe_ffn_dev).
7906        let (rbg_d, rbu_d) = if dev.gu_il {
7907            let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
7908            (sxx, sxx)
7909        } else {
7910            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
7911        };
7912
7913        let (sel_all, w_all) = Self::moe_route(e, logits, t, n_expert, n_used)?;
7914        let n_pairs = t * n_used;
7915        // pair arrays: pair p = (token p/n_used, slot p%n_used) — ALREADY slot-ordered per token,
7916        // so the CSR is trivial: tok_pair_off[tok] = tok*n_used, ids identity.
7917        let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
7918        let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
7919        let pair_w: Vec<f32> = w_all.clone();
7920        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
7921        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
7922        let pt = e.htod_i32(&pair_tok)?;
7923        let px = e.htod_i32(&pair_ex)?;
7924        let pw = e.htod(&pair_w)?;
7925        let toff = e.htod_i32(&tok_off)?;
7926        let tids = e.htod_i32(&tok_ids)?;
7927
7928        // z quantized ONCE for all tokens; gate/up pair matvecs; silu; act quantize; down; scatter.
7929        // EXPERT-MAJOR CSR (rung 2): pairs grouped by expert -> the kernel reuses each weight
7930        // row across the expert's token group (llama-MMQ's core win). Host grouping is O(pairs).
7931        let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
7932        for p in 0..n_pairs {
7933            by_ex[pair_ex[p] as usize].push(p as i32);
7934        }
7935        let mut ex_ids: Vec<i32> = Vec::new();
7936        let mut ex_off: Vec<i32> = vec![0];
7937        let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
7938        for (ex, list) in by_ex.iter().enumerate() {
7939            if list.is_empty() {
7940                continue;
7941            }
7942            ex_ids.push(ex as i32);
7943            ex_pairs.extend_from_slice(list);
7944            ex_off.push(ex_pairs.len() as i32);
7945        }
7946        let n_active = ex_ids.len();
7947        let exi = e.htod_i32(&ex_ids)?;
7948        let exo = e.htod_i32(&ex_off)?;
7949        let exp_d = e.htod_i32(&ex_pairs)?;
7950        let _ = &px; // pair-major twin keeps it; em path uses CSR
7951
7952        // INT8-MMA EXPERT MMQ (MEMRA_MOE_MMA=1, opt-in): the m16n8k16.s8 tensor-core analog of the
7953        // _dec dp4a kernel (cu/mmq_iq_experts.cu). Same CSR grouping; per-expert matvec runs as a
7954        // 128x128-tile int8 MMA GEMM over the expert's token group. Weight IQ nibbles decode to int8
7955        // at tile-load + per-32 float scale; activation is q8_1_mmq (D4, same quant class as dp4a).
7956        // FP-ORDER differs from dp4a (MMA reduction) — logits SHIFT, gated on argmax/spec/closeness,
7957        // NOT byte-identity (like the W4A8 path). Requires IQ3_S/IQ4_XS + in_f % 256 == 0.
7958        // t >= 16 (GEMM_M-class rule): the MMA tile needs token volume (crossover ~200 tok/expert;
7959        // microbench: dp4a wins at tiny groups). ALSO an exactness requirement — spec verify
7960        // batches (t=2..K+2) must ride the dp4a path whose FP order matches the T=1 decode chain,
7961        // else K=1 self-consistency FAILs (caught 2026-07-06: MMA at T=2 flipped a verify argmax).
7962        // DEFAULT ON (2026-07-06, third flip — this time with the real culprit fixed): the
7963        // "MMA prime breaks spec" failure was the ROUTER's cuBLASLt n-dependence (d994271),
7964        // not MMA's own FP order — both this and the k-quant arms were innocent suspects whose
7965        // margin shifts surfaced the router bug. With the router decode-exact at verify t, the
7966        // full battery is green with MMA on (spec p1/p2/p3 PASS, raw K=1..8 PASS, argmax MATCH,
7967        // pp6257 2862 = 2.1x dec). t>=16 floor still required: verify batches must ride dp4a
7968        // (dispatch parity with the T=1 decode chain). MEMRA_MOE_MMA=0 rollback;
7969        // MEMRA_MOE_MMA_T overrides the floor (bisect seam).
7970        static MMA_T: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7971        let mma_t = *MMA_T.get_or_init(|| {
7972            std::env::var("MEMRA_MOE_MMA_T")
7973                .ok()
7974                .and_then(|v| v.parse().ok())
7975                .unwrap_or(16)
7976        });
7977        let use_mma = std::env::var("MEMRA_MOE_MMA")
7978            .map(|v| v != "0")
7979            .unwrap_or(true)
7980            && t >= mma_t
7981            && q8_expert_dec_supported(m.gate_exps.qtype)
7982            && q8_expert_dec_supported(m.up_exps.qtype)
7983            && q8_expert_dec_supported(m.down_exps.qtype)
7984            && n_embd % 256 == 0
7985            && n_ff_exp % 256 == 0;
7986        // GROUPED f16 LANE admission (MEMRA_MOE_F16G, rounds 46-49): its own door, no longer a
7987        // subset of the MMQ arm — q35's k-quant stragglers (Q6_K/Q4_K down, one Q3_K gate/up
7988        // layer) fail q8_expert_dec_supported but dequant fine to f16, so f16g must be able to
7989        // take a layer the MMQ arm would reject. Same t >= mma_t floor as MMA: decode and
7990        // spec-verify batches must ride the dp4a path whose FP order matches the T=1 chain.
7991        // MODE 2 (sm_120a naked default, lane/f16g-default-rearb 2026-08-02): every layer
7992        // whose three projections pass f16g_proj_ok rides the sk visitor with direct tile
7993        // loaders — with IQ4_XS/IQ3_S direct coverage the sk arm beats the int8-MMA MMQ
7994        // tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7%).
7995        // AUTO-KQUANT (mode 3, MEMRA_MOE_F16G=3, lane/q4k-expert-prefill): admit f16g ONLY
7996        // where the MMA arm can't take the layer's QTYPES — the k-quant expert class whose
7997        // baseline is the per-pair _em fallback (Ornith-35B Q4_K: board-2048 3.14x). Its
7998        // MMA-capable carve-out (IQ3_S/IQ4_XS/Q4_0 banks on MMQ) was priced pre-IQ-direct;
7999        // it survives as the rollback seam. Keyed on qtype capability, NOT use_mma, so
8000        // MEMRA_MOE_MMA=0 stays a pure dp4a rollback seam.
8001        let mma_capable = q8_expert_dec_supported(m.gate_exps.qtype)
8002            && q8_expert_dec_supported(m.up_exps.qtype)
8003            && q8_expert_dec_supported(m.down_exps.qtype)
8004            && n_embd % 256 == 0
8005            && n_ff_exp % 256 == 0;
8006        let f16g_mode = crate::moe_f16g_mode();
8007        let f16g = f16g_mode != 0
8008            && t >= mma_t
8009            && (f16g_mode != 3 || !mma_capable)
8010            && f16g_proj_ok(m.gate_exps.qtype, n_embd)
8011            && f16g_proj_ok(m.up_exps.qtype, n_embd)
8012            && f16g_proj_ok(m.down_exps.qtype, n_ff_exp);
8013        if use_mma || f16g {
8014            // GROUPED f16 LANE (MEMRA_MOE_F16G, rounds 46-49, experimental door): dequant
8015            // the active experts ONCE per projection to f16 and run one grouped f16 GEMM over
8016            // the CSR groups. f16-mirror numeric class — argmax/spec gated before promotion.
8017            // NOTE: the pair-gather kernel needs pair p ordered EXPERT-MAJOR (ex_pairs order);
8018            // the y rows come back in that same CSR order, so gate/up/down all stay pair-major
8019            // in ex_pairs order — but moe_pairs_silu_mul and the scatter consume PAIR-ID order.
8020            // We therefore gather activations per ex_pairs and scatter y back through ex_pairs.
8021            let y_down = if f16g {
8022                // CSR order end-to-end: gather z rows by the pair's TOKEN (pair p's token is
8023                // p / n_used — the trivial CSR above), silu in CSR order (elementwise), one
8024                // permute at the very end back to pair-id order for the scatter.
8025                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
8026                let csr_tok_d = e.htod_i32(&csr_tok)?;
8027                let (z_f16, z_s) = e.moe_f16g_act(z, Some(&csr_tok_d), n_embd, n_pairs)?;
8028                let g_csr = e.moe_f16_grouped(
8029                    &dev.ptr_row,
8030                    0,
8031                    n_expert,
8032                    &exi,
8033                    &ex_off,
8034                    &exo,
8035                    &z_f16,
8036                    &z_s,
8037                    n_embd,
8038                    n_ff_exp,
8039                    n_active,
8040                    n_pairs,
8041                    m.gate_exps.qtype,
8042                    rbg_d,
8043                )?;
8044                let u_csr = e.moe_f16_grouped(
8045                    &dev.ptr_row,
8046                    1,
8047                    n_expert,
8048                    &exi,
8049                    &ex_off,
8050                    &exo,
8051                    &z_f16,
8052                    &z_s,
8053                    n_embd,
8054                    n_ff_exp,
8055                    n_active,
8056                    n_pairs,
8057                    m.up_exps.qtype,
8058                    rbu_d,
8059                )?;
8060                let act_csr = e.moe_pairs_silu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
8061                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
8062                let d_csr = e.moe_f16_grouped(
8063                    &dev.ptr_row,
8064                    2,
8065                    n_expert,
8066                    &exi,
8067                    &ex_off,
8068                    &exo,
8069                    &a_f16,
8070                    &a_s,
8071                    n_ff_exp,
8072                    n_embd,
8073                    n_active,
8074                    n_pairs,
8075                    m.down_exps.qtype,
8076                    m.down_exps.row_bytes,
8077                )?;
8078                e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?
8079            } else {
8080                // gate/up: activation = z, token-major over t tokens; pair_tok gathers the routed row.
8081                let z_scr = e.mmq_iq_quantize_act(z, n_embd, t)?;
8082                let gate = e.mmq_iq_experts(
8083                    &dev.ptr_row,
8084                    0,
8085                    n_expert,
8086                    &exi,
8087                    &exo,
8088                    &exp_d,
8089                    &pt,
8090                    &z_scr,
8091                    n_embd,
8092                    n_ff_exp,
8093                    n_active,
8094                    n_pairs,
8095                    t,
8096                    m.gate_exps.qtype,
8097                    rbg_d,
8098                )?;
8099                let up = e.mmq_iq_experts(
8100                    &dev.ptr_row,
8101                    1,
8102                    n_expert,
8103                    &exi,
8104                    &exo,
8105                    &exp_d,
8106                    &pt,
8107                    &z_scr,
8108                    n_embd,
8109                    n_ff_exp,
8110                    n_active,
8111                    n_pairs,
8112                    t,
8113                    m.up_exps.qtype,
8114                    rbu_d,
8115                )?;
8116                // down: activation = silu(gate)*up, pair-major [n_pairs, n_ff_exp]; pair_tok =
8117                // identity. FUSED ACT-EPILOGUE (default on): one launch computes the activation in
8118                // registers and writes ONLY the quantized scratch — the two-pass chain
8119                // (moe_pairs_silu_mul writes act f32, mmq_iq_quantize_act re-reads it) is the
8120                // MEMRA_MOE_FUSE_ACTQ=0 rollback. Scratch bytes are BYTE-IDENTICAL (kernel-check).
8121                let a_scr = if crate::moe_fuse_actq_on() {
8122                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 0)?
8123                } else {
8124                    let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
8125                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
8126                };
8127                let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
8128                let pself = e.htod_i32(&pair_self)?;
8129                e.mmq_iq_experts(
8130                    &dev.ptr_row,
8131                    2,
8132                    n_expert,
8133                    &exi,
8134                    &exo,
8135                    &exp_d,
8136                    &pself,
8137                    &a_scr,
8138                    n_ff_exp,
8139                    n_embd,
8140                    n_active,
8141                    n_pairs,
8142                    n_pairs,
8143                    m.down_exps.qtype,
8144                    m.down_exps.row_bytes,
8145                )?
8146            };
8147            let mut moe_out = e.uninit(t * n_embd)?;
8148            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
8149            if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8150                (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8151            {
8152                let n_ff_sh = gate_shexp.out_features();
8153                let sg_gate = e.matmul(gate_shexp, z, t)?;
8154                let sg_up = e.matmul(up_shexp, z, t)?;
8155                let mut sa = e.uninit(t * n_ff_sh)?;
8156                Self::ffn_act(e, cfg, &sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8157                let sh = e.matmul(down_shexp, &sa, t)?;
8158                // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8159                // SERVE ISOLATION (lane/concat-prime-exact): the fused sigmoid-dot is the
8160                // m-INVARIANT form — see the sequential arm's note. This is the PAIRS arm,
8161                // i.e. the one real prefill actually takes on a resident-expert MoE model,
8162                // so the concat-prime isolation fix has to land here as well.
8163                let g = match &m.gate_inp_shexp {
8164                    Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
8165                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8166                    }
8167                    Some(gate_inp_shexp) => {
8168                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8169                        let mut g = e.uninit(t)?;
8170                        e.sigmoid(&gs, &mut g, t)?;
8171                        g
8172                    }
8173                    None => e.htod(&vec![1.0f32; t])?,
8174                };
8175                e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8176            }
8177            return Ok(moe_out);
8178        }
8179
8180        // DECODE-ONCE MMQ (rung 3, MEMRA_MOE_DEC=1 default-on): dequant each weight group once per
8181        // (row,group) then dp4a across the expert's tokens. _em re-decoded per token (NEUTRAL).
8182        let dec = std::env::var("MEMRA_MOE_DEC")
8183            .map(|v| v != "0")
8184            .unwrap_or(true);
8185        let matvec = |proj,
8186                      exi: &_,
8187                      exo: &_,
8188                      exp_d: &_,
8189                      pt: &_,
8190                      aq: &_,
8191                      ad: &_,
8192                      inf,
8193                      outf,
8194                      qtype,
8195                      rb|
8196         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8197            // _dec's decode-once extractors are IQ-only; k-quant expert layers take the _em dot path.
8198            let dec = dec && q8_expert_dec_supported(qtype);
8199            if dec {
8200                e.moe_pairs_matvec_q8_dec(
8201                    &dev.ptr_row,
8202                    proj,
8203                    exi,
8204                    exo,
8205                    exp_d,
8206                    pt,
8207                    aq,
8208                    ad,
8209                    inf,
8210                    outf,
8211                    n_expert,
8212                    n_active,
8213                    n_pairs,
8214                    qtype,
8215                    rb,
8216                )
8217            } else {
8218                e.moe_pairs_matvec_q8_em(
8219                    &dev.ptr_row,
8220                    proj,
8221                    exi,
8222                    exo,
8223                    exp_d,
8224                    pt,
8225                    aq,
8226                    ad,
8227                    inf,
8228                    outf,
8229                    n_expert,
8230                    n_active,
8231                    n_pairs,
8232                    qtype,
8233                    rb,
8234                )
8235            }
8236        };
8237        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8238        let gate = matvec(
8239            0,
8240            &exi,
8241            &exo,
8242            &exp_d,
8243            &pt,
8244            &zq,
8245            &zd,
8246            n_embd,
8247            n_ff_exp,
8248            m.gate_exps.qtype,
8249            rbg_d,
8250        )?;
8251        let up = matvec(
8252            1,
8253            &exi,
8254            &exo,
8255            &exp_d,
8256            &pt,
8257            &zq,
8258            &zd,
8259            n_embd,
8260            n_ff_exp,
8261            m.up_exps.qtype,
8262            rbu_d,
8263        )?;
8264        let act = e.moe_pairs_silu_mul(&gate, &up, n_pairs * n_ff_exp)?;
8265        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8266        // down consumes PAIR-major activation rows: pair_tok = identity.
8267        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
8268        let pself = e.htod_i32(&pair_self)?;
8269        let y_down = matvec(
8270            2,
8271            &exi,
8272            &exo,
8273            &exp_d,
8274            &pself,
8275            &aq2,
8276            &ad2,
8277            n_ff_exp,
8278            n_embd,
8279            m.down_exps.qtype,
8280            m.down_exps.row_bytes,
8281        )?;
8282        let mut moe_out = e.uninit(t * n_embd)?; // scatter fully overwrites per (token,col)
8283        e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
8284
8285        // SHARED EXPERT epilogue — same as the other paths.
8286        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8287        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8288        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8289            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8290        {
8291            let n_ff_sh = gate_shexp.out_features();
8292            // These decode-exact forms are required by the new Step resident arm. Keep the
8293            // established grouped shared-expert program for every other architecture: widening
8294            // this to Gemma changed its speculative acceptance despite green argmax gates.
8295            let step_exact = true;
8296            let verify_t = step_exact && t > 1 && t < PRIME_MIN_T;
8297            let (sg_gate, sg_up) = if step_exact && t == 1 {
8298                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, None)?
8299            } else if verify_t {
8300                let mut fused = None;
8301                if crate::spec::spec_fused_t()
8302                    && (2..=4).contains(&t)
8303                    && e.uses_q8_1_fast(gate_shexp)
8304                    && e.uses_q8_1_fast(up_shexp)
8305                {
8306                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8307                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8308                }
8309                match fused {
8310                    Some(pair) => pair,
8311                    None => (
8312                        e.matmul_decode_exact(gate_shexp, z, t)?,
8313                        e.matmul_decode_exact(up_shexp, z, t)?,
8314                    ),
8315                }
8316            } else {
8317                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8318            };
8319            let mut sa = e.uninit(t * n_ff_sh)?;
8320            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8321            let sh = if verify_t {
8322                e.matmul_decode_exact(down_shexp, &sa, t)?
8323            } else {
8324                e.matmul(down_shexp, &sa, t)?
8325            };
8326            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8327            // m-INVARIANT fused sigmoid-dot under router_prefill_exact (serve isolation,
8328            // lane/concat-prime-exact) — every shexp-gate arm shares the same form so a
8329            // dispatch choice cannot change bits.
8330            let g = match &m.gate_inp_shexp {
8331                Some(gate_inp_shexp) if crate::router_prefill_exact_on() => {
8332                    e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8333                }
8334                Some(gate_inp_shexp) => {
8335                    let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8336                    let mut g = e.uninit(t)?;
8337                    e.sigmoid(&gs, &mut g, t)?;
8338                    g
8339                }
8340                None => e.htod(&vec![1.0f32; t])?,
8341            };
8342            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8343        }
8344        Ok(moe_out)
8345    }
8346
8347    /// kernel headers); the shared-expert epilogue is byte-identical to moe_ffn_sequential's.
8348    #[allow(clippy::too_many_arguments)]
8349    #[allow(clippy::too_many_arguments)]
8350    fn moe_ffn_dev(
8351        e: &Engine,
8352        m: &MoeWeights,
8353        z: &CudaSlice<f32>,
8354        zq8: Option<&(CudaSlice<i8>, CudaSlice<f32>)>,
8355        logits: &CudaSlice<f32>,
8356        t: usize,
8357        cfg: &ModelConfig,
8358        il: u16,
8359        max_block: usize,
8360    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8361        let moe = cfg.moe.as_ref().unwrap();
8362        let n_embd = cfg.n_embd as usize;
8363        let n_expert = moe.expert_count as usize;
8364        let n_used = moe.expert_used_count as usize;
8365        let n_ff_exp = moe.expert_ff_length as usize;
8366        // moe_router_topk is SOFTMAX-only (no exp_probs_b bias, no expert_weights_scale) and every
8367        // gate_up kernel here fuses PLAIN silu(gate)*up. `dev_ok` denies sigmoid-router archs and
8368        // clamped layers; assert both so a future caller that skips the gate fails loudly.
8369        debug_assert!(
8370            cfg.sigmoid_router().is_none(),
8371            "moe_ffn_dev routes SOFTMAX: a sigmoid-router arch would pick wrong experts"
8372        );
8373        debug_assert!(
8374            !cfg.swiglu_clamped_at(il as u32),
8375            "moe_ffn_dev's fused epilogue is plain SiLU: no clamped form"
8376        );
8377
8378        // device top-k: sel [t, n_used] i32, w [t, n_used] f32 — stays on device.
8379        let (sel_d, mut w_d) = e.moe_router_topk(logits, t, n_expert, n_used)?;
8380        // Down-projection macro fold (compressed-tensors NVFP4 artifacts): one tiny launch,
8381        // skipped entirely for macro-free experts (every k-quant GGUF).
8382        if m.has_macros {
8383            e.moe_w_scale_by_expert(&mut w_d, &sel_d, &m.dev_macros, n_expert, t * n_used)?;
8384        }
8385
8386        // moe_out rows are FULLY overwritten by moe_down8_fma_dev — uninit (stage-2 rule).
8387        let mut moe_out = e.uninit(t * n_embd)?;
8388
8389        // RESIDENT-EXPERTS arm: the pointer row comes from the load-time slab (no cache, no
8390        // lock). Same kernels/loop as the SLRU arm below — only the row's provenance differs.
8391        if let Some(dev) = m.dev_exps.as_ref() {
8392            // WALL-GAP ARC: interleaved gate/up slab (MEMRA_MOE_GU_IL) -> both projections use
8393            // the combined stride; up's base is offset in the ptr table. Down unchanged.
8394            let (rbg_d, rbu_d) = if dev.gu_il {
8395                let sxx = m.gate_exps.row_bytes + m.up_exps.row_bytes;
8396                (sxx, sxx)
8397            } else {
8398                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
8399            };
8400            let q8 = moe_q8_enabled()
8401                && q8_expert_supported(m.gate_exps.qtype)
8402                && q8_expert_supported(m.up_exps.qtype)
8403                && q8_expert_supported(m.down_exps.qtype);
8404            // SMALL-M ROWS ARM (MEMRA_SPEC_M2, lane/spec-m2): batch the verify token loop —
8405            // ONE batched z-quantize + ONE gate_up rows launch + ONE act quantize + ONE down
8406            // rows launch (4 launches/layer, was 4t). BIT-IDENTICAL per token to the serial
8407            // loop below (rows twins = the _v/w8h2v per-token programs on a grid.z token axis;
8408            // quantize_q8_1 is per-32-block row-independent). Gated to the AUTO kernel modes —
8409            // a custom MEMRA_MOE_DEVQ8_GU/DOWN diagnostic run keeps the serial loop so the
8410            // dispatched kernel stays exactly the env-selected one — and to the w8h2v shape
8411            // (n_ff_exp==512, n_used<=8), the same contract the AUTO down dispatch keys on.
8412            let rows_arm = q8
8413                && t > 1
8414                && crate::spec::spec_m2()
8415                && n_ff_exp == 512
8416                && n_used <= 8
8417                && std::env::var("MEMRA_MOE_DEVQ8_GU")
8418                    .map(|v| v.is_empty() || v == "v")
8419                    .unwrap_or(true)
8420                && std::env::var("MEMRA_MOE_DEVQ8_DOWN")
8421                    .map(|v| v.is_empty() || v == "w8h2v")
8422                    .unwrap_or(true);
8423            // CSR EXPERT-DEDUP gate_up (verify-cost target #1, DEFAULT ON 2026-07-10): the
8424            // owner-scan kernel serves every (token, slot) pair of an expert from ONE block,
8425            // deduping the 38-40% duplicated weight-stream+decode the overlap probe measured
8426            // (gate_up 55.0 -> 39.7us/launch; +1.3-2.1% spec e2e p2, +0.6-1.7% p3, all K).
8427            // Bit-identical to the _rows twins (explicit-intrinsic accumulate — the ULP/fmad
8428            // lesson; =2 byte-compare verified zero diffs). down stays on _rows (CSR down
8429            // measured 23.5 -> 37.5us: 16-group rows can't amortize the serial pair loop).
8430            // MEMRA_MOE_CSR=0 rollback; =2 runs BOTH paths and byte-compares (debug).
8431            let csr_mode = std::env::var("MEMRA_MOE_CSR")
8432                .ok()
8433                .and_then(|v| v.parse::<i32>().ok())
8434                .unwrap_or(1);
8435            // NVFP4 admission REVERTED 2026-08-21 (lane/samplat, decode-batch-gate2 find):
8436            // the csr_nvfp4 kernel drifts last-ULP vs the rows program (11041/32768 ACT
8437            // elements at t=8) and the drift is BATCH-COMPOSITION-DEPENDENT — gate2 (B=8 vs
8438            // isolated) FAILED on the ornith15 artifact, the one-numeric-program law's batch
8439            // axis. Three chain-pinning attempts did not close it (receipts,
8440            // research/samplat-20260821/); a source-verbatim per-pair helper form IS
8441            // bit-identical but loses the dedup win (-3% vs rows). NVFP4 stays on the rows
8442            // twins until a cached form passes gate2 + the =2 byte-compare at t=8. The
8443            // increment-1 qualification hole: =2 ran across run-spec (solo verify shapes),
8444            // never decode-batch-gate at B=8 on the MoE model itself.
8445            // MEMRA_MOE_CSR_NVFP4=1 (lane/orndecode, DIAGNOSTIC PROBE ONLY): re-admits NVFP4
8446            // to the CSR arm and widens it to the exact-16 decode widths, so gate2 B=12/16 +
8447            // the =2 byte-compare can re-adjudicate the cached form at the widths where the
8448            // serial dev loop hurts most (B=16 tick: 1280 launches/step). The v0.100.1
8449            // de-admission verdict above stands until those gates are GREEN on the MoE
8450            // artifact; this door must never default on.
8451            let csr_nvfp4_probe = std::env::var("MEMRA_MOE_CSR_NVFP4").as_deref() == Ok("1");
8452            let csr_qt = |qt: i32| {
8453                qt == crate::QT_IQ4_XS
8454                    || qt == crate::QT_IQ3_S
8455                    || (csr_nvfp4_probe && qt == crate::QT_NVFP4)
8456            };
8457            let csr_t_max = if csr_nvfp4_probe { MOE_DEV_MAX_T } else { 10 };
8458            let csr_uniform = m.gate_exps.qtype == m.up_exps.qtype;
8459            let csr_arm = rows_arm
8460                && csr_mode > 0
8461                && t <= csr_t_max
8462                && csr_uniform
8463                && csr_qt(m.gate_exps.qtype)
8464                && csr_qt(m.up_exps.qtype)
8465                && csr_qt(m.down_exps.qtype);
8466            if csr_arm {
8467                if csr_mode == 2 {
8468                    static ENGAGED: std::sync::Once = std::sync::Once::new();
8469                    ENGAGED.call_once(|| eprintln!("[memra] moe CSR byte-compare mode ON (t={t})"));
8470                }
8471                let n_pairs = t * n_used;
8472                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8473                let act = e.moe_gate_up_silu8_dev_q8_csr(
8474                    &dev.ptr_row,
8475                    &sel_d,
8476                    &zq,
8477                    &zd,
8478                    n_pairs,
8479                    n_embd,
8480                    n_ff_exp,
8481                    n_used,
8482                    n_expert,
8483                    m.gate_exps.qtype,
8484                    m.up_exps.qtype,
8485                    rbg_d,
8486                    rbu_d,
8487                )?;
8488                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
8489                // down stays on the _rows twin — BOTH CSR down variants measured negative
8490                // (v1 serial pairs 23.5->37.5us; v2 warp-parallel+SMEM -14% e2e, K=8 -37%):
8491                // 16-group rows have too little decode to amortize any dedup structure.
8492                e.moe_down8_fma_dev_q8_rows(
8493                    &dev.ptr_row,
8494                    &sel_d,
8495                    &w_d,
8496                    &aq2,
8497                    &ad2,
8498                    &mut moe_out,
8499                    t,
8500                    n_ff_exp,
8501                    n_embd,
8502                    n_used,
8503                    n_expert,
8504                    m.down_exps.qtype,
8505                    m.down_exps.row_bytes,
8506                )?;
8507                if csr_mode == 2 {
8508                    // DEBUG BYTE-COMPARE: run the _rows twins on the same inputs, diff bits.
8509                    let act_r = e.moe_gate_up_silu8_dev_q8_rows(
8510                        &dev.ptr_row,
8511                        &sel_d,
8512                        &zq,
8513                        &zd,
8514                        t,
8515                        n_embd,
8516                        n_ff_exp,
8517                        n_used,
8518                        n_expert,
8519                        m.gate_exps.qtype,
8520                        m.up_exps.qtype,
8521                        rbg_d,
8522                        rbu_d,
8523                        &m.dev_macros,
8524                    )?;
8525                    let mut out_r = e.uninit(t * n_embd)?;
8526                    let (aq2r, ad2r) = e.quantize_q8_1(&act_r, n_pairs, n_ff_exp)?;
8527                    e.moe_down8_fma_dev_q8_rows(
8528                        &dev.ptr_row,
8529                        &sel_d,
8530                        &w_d,
8531                        &aq2r,
8532                        &ad2r,
8533                        &mut out_r,
8534                        t,
8535                        n_ff_exp,
8536                        n_embd,
8537                        n_used,
8538                        n_expert,
8539                        m.down_exps.qtype,
8540                        m.down_exps.row_bytes,
8541                    )?;
8542                    let (a1, a2) = (e.dtoh(&act)?, e.dtoh(&act_r)?);
8543                    let (o1, o2) = (e.dtoh(&moe_out)?, e.dtoh(&out_r)?);
8544                    let ba = a1
8545                        .iter()
8546                        .zip(&a2)
8547                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8548                        .count();
8549                    let bo = o1
8550                        .iter()
8551                        .zip(&o2)
8552                        .filter(|(x, y)| x.to_bits() != y.to_bits())
8553                        .count();
8554                    if ba + bo > 0 {
8555                        eprintln!(
8556                            "[csr-check] il={il} t={t} ACT diffs={ba}/{} OUT diffs={bo}/{}",
8557                            a1.len(),
8558                            o1.len()
8559                        );
8560                        // First 4 differing ACT elements: (pair, o, csr, rows) + that pair's expert
8561                        let sel_h = e.dtoh_i32(&sel_d)?;
8562                        let mut shown = 0;
8563                        for (i, (x, y)) in a1.iter().zip(&a2).enumerate() {
8564                            if x.to_bits() != y.to_bits() && shown < 4 {
8565                                let (p, o) = (i / n_ff_exp, i % n_ff_exp);
8566                                let ex = sel_h[p];
8567                                let npx = sel_h.iter().filter(|&&v| v == ex).count();
8568                                eprintln!(
8569                                    "  ACT p={p} ex={ex} np={npx} o={o} csr={x:e} rows={y:e}"
8570                                );
8571                                shown += 1;
8572                            }
8573                        }
8574                        std::process::exit(3);
8575                    }
8576                }
8577            } else if rows_arm {
8578                // TEMP PROBE (MEMRA_MOE_OVERLAP=1): cross-token expert-activation overlap at
8579                // verify — sizes the CSR dedup win (unique experts vs t*n_used pairs).
8580                if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
8581                    use std::sync::atomic::{AtomicU64, Ordering};
8582                    static PAIRS: AtomicU64 = AtomicU64::new(0);
8583                    static UNIQ: AtomicU64 = AtomicU64::new(0);
8584                    static CALLS: AtomicU64 = AtomicU64::new(0);
8585                    let sel_h = e.dtoh_i32(&sel_d)?;
8586                    let mut u: Vec<i32> = sel_h.clone();
8587                    u.sort_unstable();
8588                    u.dedup();
8589                    PAIRS.fetch_add(sel_h.len() as u64, Ordering::Relaxed);
8590                    UNIQ.fetch_add(u.len() as u64, Ordering::Relaxed);
8591                    let c = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
8592                    if c % 480 == 0 {
8593                        let p = PAIRS.load(Ordering::Relaxed);
8594                        let q = UNIQ.load(Ordering::Relaxed);
8595                        eprintln!(
8596                            "[overlap] calls={c} pairs={p} unique={q} ratio={:.3} (t={t})",
8597                            q as f64 / p as f64
8598                        );
8599                    }
8600                }
8601                let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8602                let act = e.moe_gate_up_silu8_dev_q8_rows(
8603                    &dev.ptr_row,
8604                    &sel_d,
8605                    &zq,
8606                    &zd,
8607                    t,
8608                    n_embd,
8609                    n_ff_exp,
8610                    n_used,
8611                    n_expert,
8612                    m.gate_exps.qtype,
8613                    m.up_exps.qtype,
8614                    rbg_d,
8615                    rbu_d,
8616                    &m.dev_macros,
8617                )?;
8618                let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
8619                e.moe_down8_fma_dev_q8_rows(
8620                    &dev.ptr_row,
8621                    &sel_d,
8622                    &w_d,
8623                    &aq2,
8624                    &ad2,
8625                    &mut moe_out,
8626                    t,
8627                    n_ff_exp,
8628                    n_embd,
8629                    n_used,
8630                    n_expert,
8631                    m.down_exps.qtype,
8632                    m.down_exps.row_bytes,
8633                )?;
8634            } else {
8635                for tok in 0..t {
8636                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8637                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8638                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8639                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8640                    if q8 {
8641                        let (zq, zd) = match (t, zq8) {
8642                            (1, Some((q, d))) => (q.clone(), d.clone()),
8643                            _ => e.quantize_q8_1_view(&zt, 1, n_embd)?,
8644                        };
8645                        let act = e.moe_gate_up_silu8_dev_q8(
8646                            &dev.ptr_row,
8647                            &selt,
8648                            &zq,
8649                            &zd,
8650                            n_embd,
8651                            n_ff_exp,
8652                            n_used,
8653                            n_expert,
8654                            m.gate_exps.qtype,
8655                            m.up_exps.qtype,
8656                            rbg_d,
8657                            rbu_d,
8658                            &m.dev_macros,
8659                        )?;
8660                        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8661                        e.moe_down8_fma_dev_q8(
8662                            &dev.ptr_row,
8663                            &selt,
8664                            &wt,
8665                            &aq2,
8666                            &ad2,
8667                            &mut dst,
8668                            n_ff_exp,
8669                            n_embd,
8670                            n_used,
8671                            n_expert,
8672                            m.down_exps.qtype,
8673                            m.down_exps.row_bytes,
8674                        )?;
8675                    } else {
8676                        let act = e.moe_gate_up_silu8_dev(
8677                            &dev.ptr_row,
8678                            &selt,
8679                            &zt,
8680                            n_embd,
8681                            n_ff_exp,
8682                            n_used,
8683                            n_expert,
8684                            m.gate_exps.qtype,
8685                            m.up_exps.qtype,
8686                            rbg_d,
8687                            rbu_d,
8688                            &m.dev_macros,
8689                        )?;
8690                        e.moe_down8_fma_dev(
8691                            &dev.ptr_row,
8692                            &selt,
8693                            &wt,
8694                            &act,
8695                            &mut dst,
8696                            n_ff_exp,
8697                            n_embd,
8698                            n_used,
8699                            n_expert,
8700                            m.down_exps.qtype,
8701                            m.down_exps.row_bytes,
8702                        )?;
8703                    }
8704                }
8705            }
8706        } else {
8707            // Launch under the cache lock: the row borrow lives as long as the closure, and the
8708            // lock covers only launch ISSUE (µs), same policy as moe_cached_gemm.
8709            // Q8 ARM PARITY (2026-07-06): the SLRU arm ran the f32-dequant _dev kernels only —
8710            // 80us/launch vs the q8 twins' 15us on the SAME shapes (fixed-build profile: 228
8711            // f32 launches = 36ms of the 64-tok window). Same q8 gate + kernels as the resident
8712            // arm above; MEMRA_MOE_Q8=0 restores the byte-identical f32 path.
8713            let q8 = moe_q8_enabled()
8714                && q8_expert_supported(m.gate_exps.qtype)
8715                && q8_expert_supported(m.up_exps.qtype)
8716                && q8_expert_supported(m.down_exps.qtype);
8717            e.with_moe_cache(max_block, |c, eng| {
8718                let row = c
8719                    .layer_dev_row(il, n_expert, eng)?
8720                    .ok_or("moe_ffn_dev: layer row vanished under the lock")?;
8721                for tok in 0..t {
8722                    let zt = z.slice(tok * n_embd..(tok + 1) * n_embd);
8723                    let selt = sel_d.slice(tok * n_used..(tok + 1) * n_used);
8724                    let wt = w_d.slice(tok * n_used..(tok + 1) * n_used);
8725                    let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8726                    if q8 {
8727                        let (zq, zd) = match (t, zq8) {
8728                            (1, Some((q, d))) => (q.clone(), d.clone()),
8729                            _ => eng.quantize_q8_1_view(&zt, 1, n_embd)?,
8730                        };
8731                        let act = eng.moe_gate_up_silu8_dev_q8(
8732                            row,
8733                            &selt,
8734                            &zq,
8735                            &zd,
8736                            n_embd,
8737                            n_ff_exp,
8738                            n_used,
8739                            n_expert,
8740                            m.gate_exps.qtype,
8741                            m.up_exps.qtype,
8742                            m.gate_exps.row_bytes,
8743                            m.up_exps.row_bytes,
8744                            &m.dev_macros,
8745                        )?;
8746                        let (aq2, ad2) = eng.quantize_q8_1(&act, n_used, n_ff_exp)?;
8747                        eng.moe_down8_fma_dev_q8(
8748                            row,
8749                            &selt,
8750                            &wt,
8751                            &aq2,
8752                            &ad2,
8753                            &mut dst,
8754                            n_ff_exp,
8755                            n_embd,
8756                            n_used,
8757                            n_expert,
8758                            m.down_exps.qtype,
8759                            m.down_exps.row_bytes,
8760                        )?;
8761                    } else {
8762                        let act = eng.moe_gate_up_silu8_dev(
8763                            row,
8764                            &selt,
8765                            &zt,
8766                            n_embd,
8767                            n_ff_exp,
8768                            n_used,
8769                            n_expert,
8770                            m.gate_exps.qtype,
8771                            m.up_exps.qtype,
8772                            m.gate_exps.row_bytes,
8773                            m.up_exps.row_bytes,
8774                            &m.dev_macros,
8775                        )?;
8776                        eng.moe_down8_fma_dev(
8777                            row,
8778                            &selt,
8779                            &wt,
8780                            &act,
8781                            &mut dst,
8782                            n_ff_exp,
8783                            n_embd,
8784                            n_used,
8785                            n_expert,
8786                            m.down_exps.qtype,
8787                            m.down_exps.row_bytes,
8788                        )?;
8789                    }
8790                }
8791                // instrumentation parity with the host paths (3 blocks/expert-slot, all hits).
8792                c.hits += (t * 3 * n_used) as u64;
8793                Ok(())
8794            })?;
8795        }
8796
8797        // SHARED EXPERT epilogue — byte-identical to moe_ffn_sequential step 3 (incl. its Q8
8798        // TRUNK-FUSION arm: fused2 is bit-identical to the two matmul calls per (tensor,row)).
8799        // gate_inp_shexp is OPTIONAL: qwen35moe gates the shared expert (sigmoid(gate_inp) x sh);
8800        // MiniMax-M3 (DeepSeek-V3 class) has NO shexp gate — the shared expert adds directly.
8801        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
8802            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
8803        {
8804            let n_ff_sh = gate_shexp.out_features();
8805            // verify-t (2..15) decode-exact arm: this fn now serves the spec verify batches
8806            // (pairs gate moved to t>=PRIME_MIN_T), so the shexp chain must match t==1 per col.
8807            let verify_t = t > 1 && t < PRIME_MIN_T;
8808            let (sg_gate, sg_up) = if t == 1 {
8809                shexp_gate_up_t1(e, gate_shexp, up_shexp, z, zq8)?
8810            } else if verify_t {
8811                // VERIFY-TIER TRUNK FUSION (MEMRA_SPEC_FUSED_T, t=2-4): the shexp gate+up pair
8812                // rides one shared quantize + one fused2 batched launch instead of two
8813                // decode-exact calls. Bit-identical per (tensor,token,row) — see spec_fused_t.
8814                let mut fused = None;
8815                if crate::spec::spec_fused_t()
8816                    && (2..=4).contains(&t)
8817                    && e.uses_q8_1_fast(gate_shexp)
8818                    && e.uses_q8_1_fast(up_shexp)
8819                {
8820                    let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
8821                    fused = e.matmul_q8_fused2_t(gate_shexp, up_shexp, &zq, &zd, t)?;
8822                }
8823                match fused {
8824                    Some(pair) => pair,
8825                    None => (
8826                        e.matmul_decode_exact(gate_shexp, z, t)?,
8827                        e.matmul_decode_exact(up_shexp, z, t)?,
8828                    ),
8829                }
8830            } else {
8831                (e.matmul(gate_shexp, z, t)?, e.matmul(up_shexp, z, t)?)
8832            };
8833            let mut sa = e.uninit(t * n_ff_sh)?; // silu_mul fully overwrites
8834            e.silu_mul(&sg_gate, &sg_up, &mut sa, t * n_ff_sh)?;
8835            let sh = if verify_t {
8836                e.matmul_decode_exact(down_shexp, &sa, t)?
8837            } else {
8838                e.matmul(down_shexp, &sa, t)?
8839            };
8840            // shexp gate: qwen35moe sigmoid-gates; M3 has no gate tensor -> weight 1.0.
8841            // Same fused sigmoid-dot as moe_ffn_sequential step 3 (byte-identity contract
8842            // between the two arms; prefill keeps the batched cuBLASLt linear).
8843            let g = match &m.gate_inp_shexp {
8844                Some(gate_inp_shexp) => {
8845                    // router_prefill_exact_on(): the fused sigmoid-dot is m-INVARIANT and
8846                    // serves EVERY t (serve isolation, lane/concat-prime-exact).
8847                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
8848                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
8849                    } else {
8850                        let gs = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
8851                        let mut g = e.uninit(t)?;
8852                        e.sigmoid(&gs, &mut g, t)?;
8853                        g
8854                    }
8855                }
8856                None => e.htod(&vec![1.0f32; t])?,
8857            };
8858            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, t)?;
8859        }
8860
8861        Ok(moe_out)
8862    }
8863
8864    /// STAGE-2 GROUPED DECODE (2026-07-04): run ONE token's whole routed-expert FFN in TWO
8865    /// launches when every one of its 3*n_used blocks is ALREADY cache-resident. Returns
8866    /// Ok(true) if the grouped path ran (caller skips the sequential loop for this token);
8867    /// Ok(false) on ANY miss (caller falls through — the sequential loop stages/admits as
8868    /// before, so the NEXT occurrence takes the grouped path). Pointer safety: cache slots are
8869    /// fixed-address for the engine's lifetime and the pure-HIT path performs no admission, so
8870    /// the collected raw pointers cannot move between collection and launch (single-threaded
8871    /// decode; the lock is held only for collection, launches are stream-ordered after any
8872    /// prior same-stream staging writes).
8873    #[allow(clippy::too_many_arguments)]
8874    /// q8 twin of moe_gdec_token (dp4a arc): same residency check + 2-launch shape; the fused
8875    /// kernels consume the pre-quantized z-row and re-quantize act per slot batch.
8876    #[allow(clippy::too_many_arguments)]
8877    fn moe_gdec_token_q8(
8878        e: &Engine,
8879        m: &MoeWeights,
8880        il: u16,
8881        max_block: usize,
8882        zq: &CudaSlice<i8>,
8883        zd: &CudaSlice<f32>,
8884        sel: &[u32],
8885        w: &[f32],
8886        moe_out: &mut CudaSlice<f32>,
8887        tok: usize,
8888        n_embd: usize,
8889        n_ff_exp: usize,
8890        n_used: usize,
8891    ) -> Result<bool, Box<dyn std::error::Error>> {
8892        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8893        use cudarc::driver::DevicePtr;
8894        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8895            let mut g = [0u64; 8];
8896            let mut u = [0u64; 8];
8897            let mut d = [0u64; 8];
8898            for (j, &ex) in sel.iter().enumerate() {
8899                let ex = ex as u16;
8900                let (Some(sg), Some(su), Some(sd)) = (
8901                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8902                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8903                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8904                ) else {
8905                    return Ok(None);
8906                };
8907                let __s = eng.stream();
8908                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8909                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8910                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8911                g[j] = pg as u64;
8912                u[j] = pu as u64;
8913                d[j] = pd as u64;
8914            }
8915            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
8916                for &ex in sel {
8917                    let ex = ex as u16;
8918                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
8919                        c.note_profile_hit(BlockId::new(il, proj, ex));
8920                    }
8921                }
8922            }
8923            c.hits += (3 * n_used) as u64;
8924            Ok(Some((g, u, d)))
8925        })?;
8926        let Some((g, u, d)) = ptrs else {
8927            return Ok(false);
8928        };
8929        let mut wv = [0f32; 8];
8930        wv[..n_used].copy_from_slice(w);
8931        let act = e.moe_gate_up_silu8_q8(
8932            crate::WPtr8(g),
8933            crate::WPtr8(u),
8934            zq,
8935            zd,
8936            n_embd,
8937            n_ff_exp,
8938            n_used,
8939            m.gate_exps.qtype,
8940            m.up_exps.qtype,
8941            m.gate_exps.row_bytes,
8942            m.up_exps.row_bytes,
8943        )?;
8944        // per-slot act quantize: [n_used, n_ff] rows in one quantize launch.
8945        let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
8946        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
8947        e.moe_down8_fma_q8(
8948            crate::WPtr8(d),
8949            crate::F32x8(wv),
8950            &aq2,
8951            &ad2,
8952            &mut dst,
8953            n_ff_exp,
8954            n_embd,
8955            n_used,
8956            m.down_exps.qtype,
8957            m.down_exps.row_bytes,
8958        )?;
8959        Ok(true)
8960    }
8961
8962    fn moe_gdec_token(
8963        e: &Engine,
8964        m: &MoeWeights,
8965        il: u16,
8966        max_block: usize,
8967        zt: &cudarc::driver::CudaView<f32>,
8968        sel: &[u32],
8969        w: &[f32],
8970        moe_out: &mut CudaSlice<f32>,
8971        tok: usize,
8972        n_embd: usize,
8973        n_ff_exp: usize,
8974        n_used: usize,
8975    ) -> Result<bool, Box<dyn std::error::Error>> {
8976        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
8977        use cudarc::driver::DevicePtr;
8978        // One lock hold: residency-check all 3*n_used blocks, collect raw slot pointers.
8979        let ptrs = e.with_moe_cache(max_block, |c, eng| {
8980            let mut g = [0u64; 8];
8981            let mut u = [0u64; 8];
8982            let mut d = [0u64; 8];
8983            for (j, &ex) in sel.iter().enumerate() {
8984                let ex = ex as u16;
8985                let (Some(sg), Some(su), Some(sd)) = (
8986                    c.resident(BlockId::new(il, PROJ_GATE, ex)),
8987                    c.resident(BlockId::new(il, PROJ_UP, ex)),
8988                    c.resident(BlockId::new(il, PROJ_DOWN, ex)),
8989                ) else {
8990                    return Ok(None);
8991                };
8992                let __s = eng.stream();
8993                let (pg, _e0) = c.slot(sg).device_ptr(&__s);
8994                let (pu, _e1) = c.slot(su).device_ptr(&__s);
8995                let (pd, _e2) = c.slot(sd).device_ptr(&__s);
8996                g[j] = pg as u64;
8997                u[j] = pu as u64;
8998                d[j] = pd as u64;
8999            }
9000            if cpu_expert_profile_admit_enabled() && !c.is_frozen() {
9001                for &ex in sel {
9002                    let ex = ex as u16;
9003                    for proj in [PROJ_GATE, PROJ_UP, PROJ_DOWN] {
9004                        c.note_profile_hit(BlockId::new(il, proj, ex));
9005                    }
9006                }
9007            }
9008            c.hits += (3 * n_used) as u64; // instrumentation parity with dispatch()
9009            Ok(Some((g, u, d)))
9010        })?;
9011        let Some((g, u, d)) = ptrs else {
9012            return Ok(false);
9013        };
9014        let mut wv = [0f32; 8];
9015        wv[..n_used].copy_from_slice(w);
9016        // 2 launches: (gate+up+silu) x8, then (down + slot-ordered FMA accumulate) x8.
9017        let act = e.moe_gate_up_silu8(
9018            crate::WPtr8(g),
9019            crate::WPtr8(u),
9020            zt,
9021            n_embd,
9022            n_ff_exp,
9023            n_used,
9024            m.gate_exps.qtype,
9025            m.up_exps.qtype,
9026            m.gate_exps.row_bytes,
9027            m.up_exps.row_bytes,
9028        )?;
9029        let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
9030        e.moe_down8_fma_into(
9031            crate::WPtr8(d),
9032            crate::F32x8(wv),
9033            &act,
9034            &mut dst,
9035            n_ff_exp,
9036            n_embd,
9037            n_used,
9038            m.down_exps.qtype,
9039            m.down_exps.row_bytes,
9040        )?;
9041        Ok(true)
9042    }
9043
9044    /// EDGE-1 §B.3: dispatch one expert projection through the SLRU cache, then run the SAME
9045    /// `qmatvec_view` from whichever slot it landed in (resident HIT or staged MISS). `x` is the
9046    /// sliced activation row. `proj` selects the gate/up/down HostExps tensor. Returns y = W_expert @ x.
9047    /// q8 twin of moe_cached_gemm: same dispatch/slot mechanics, dp4a expert kernel.
9048    fn moe_cached_gemm_q8(
9049        e: &Engine,
9050        il: u16,
9051        proj: u8,
9052        ex: usize,
9053        m: &MoeWeights,
9054        max_block: usize,
9055        aq: &CudaSlice<i8>,
9056        ad: &CudaSlice<f32>,
9057    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9058        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
9059        let exps = match proj {
9060            PROJ_GATE => &m.gate_exps,
9061            PROJ_UP => &m.up_exps,
9062            _ => &m.down_exps,
9063        };
9064        let layout = exps.expert_layout(ex);
9065        let id = BlockId::new(il, proj, ex as u16);
9066        let source = exps.expert_source(ex);
9067        e.with_moe_cache(max_block, |c, eng| {
9068            let slot = c.dispatch_source(id, source, eng)?;
9069            let DispatchSlot::Resident(sl) = slot;
9070            let buf = c.slot(sl);
9071            eng.qmatvec_expert_q8(
9072                buf,
9073                0..layout.len,
9074                aq,
9075                ad,
9076                1,
9077                exps.in_f,
9078                exps.out_f,
9079                layout.qtype,
9080                layout.row_bytes,
9081            )
9082        })
9083    }
9084
9085    fn moe_cached_gemm(
9086        e: &Engine,
9087        il: u16,
9088        proj: u8,
9089        ex: usize,
9090        m: &MoeWeights,
9091        max_block: usize,
9092        x: &cudarc::driver::CudaView<f32>,
9093    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9094        use crate::moe_cache::{BlockId, DispatchSlot, PROJ_GATE, PROJ_UP};
9095        let exps = match proj {
9096            PROJ_GATE => &m.gate_exps,
9097            PROJ_UP => &m.up_exps,
9098            _ => &m.down_exps,
9099        };
9100        let layout = exps.expert_layout(ex);
9101        let id = BlockId::new(il, proj, ex as u16);
9102        let source = exps.expert_source(ex);
9103        // dispatch under the lock (lookup/admit/memcpy-issue), then resolve the slot and GEMM.
9104        e.with_moe_cache(max_block, |c, eng| {
9105            let slot = c.dispatch_source(id, source, eng)?;
9106            // resolve the device buffer for this slot; the GEMM is enqueued on the compute stream
9107            // (the same stream the memcpy was issued on, so ordering holds without extra sync).
9108            let DispatchSlot::Resident(sl) = slot;
9109            let buf = c.slot(sl);
9110            eng.qmatvec_view(
9111                buf,
9112                0..layout.len,
9113                x,
9114                1,
9115                exps.in_f,
9116                exps.out_f,
9117                layout.qtype,
9118                layout.row_bytes,
9119            )
9120        })
9121    }
9122
9123    /// Populate the warmup cache for an expert whose current-token output intentionally ran on
9124    /// CPU. No GEMM is launched and callers invoke this only after the CPU result has completed,
9125    /// so the current forward's backend assignment and output remain unchanged.
9126    fn moe_profile_admit_expert(
9127        e: &Engine,
9128        il: u16,
9129        ex: usize,
9130        m: &MoeWeights,
9131        max_block: usize,
9132    ) -> Result<(), Box<dyn std::error::Error>> {
9133        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9134        e.with_moe_cache(max_block, |cache, eng| {
9135            for (proj, exps) in [
9136                (PROJ_GATE, &m.gate_exps),
9137                (PROJ_UP, &m.up_exps),
9138                (PROJ_DOWN, &m.down_exps),
9139            ] {
9140                let id = BlockId::new(il, proj, ex as u16);
9141                let _ = cache.dispatch_source(id, exps.expert_source(ex), eng)?;
9142            }
9143            Ok(())
9144        })
9145    }
9146
9147    /// Read a projection from the immutable residency set when present; otherwise use one
9148    /// transient slot without admitting or evicting anything. Used only by post-freeze prefill.
9149    #[allow(clippy::too_many_arguments)]
9150    fn moe_frozen_gemm(
9151        e: &Engine,
9152        il: u16,
9153        proj: u8,
9154        ex: usize,
9155        m: &MoeWeights,
9156        max_block: usize,
9157        x: &cudarc::driver::CudaView<f32>,
9158        scratch: &mut Option<CudaSlice<u8>>,
9159        scratch_len: usize,
9160    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9161        use crate::moe_cache::{BlockId, PROJ_GATE, PROJ_UP};
9162        let exps = match proj {
9163            PROJ_GATE => &m.gate_exps,
9164            PROJ_UP => &m.up_exps,
9165            _ => &m.down_exps,
9166        };
9167        let layout = exps.expert_layout(ex);
9168        let id = BlockId::new(il, proj, ex as u16);
9169        if let Some(output) = e.with_moe_cache(max_block, |cache, eng| {
9170            let Some(slot) = cache.resident(id) else {
9171                return Ok(None);
9172            };
9173            let buf = cache.slot(slot);
9174            Ok(Some(eng.qmatvec_view(
9175                buf,
9176                0..layout.len,
9177                x,
9178                1,
9179                exps.in_f,
9180                exps.out_f,
9181                layout.qtype,
9182                layout.row_bytes,
9183            )?))
9184        })? {
9185            return Ok(output);
9186        }
9187        if scratch.is_none() {
9188            *scratch = Some(e.alloc_u8_uninit(scratch_len)?);
9189        }
9190        let scratch = scratch.as_mut().unwrap();
9191        e.stage_expert(exps.expert_bytes(ex), scratch, 0)?;
9192        e.qmatvec_view(
9193            scratch,
9194            0..layout.len,
9195            x,
9196            1,
9197            exps.in_f,
9198            exps.out_f,
9199            layout.qtype,
9200            layout.row_bytes,
9201        )
9202    }
9203
9204    fn moe_prefetch_expert(
9205        e: &Engine,
9206        il: u16,
9207        ex: usize,
9208        m: &MoeWeights,
9209        max_block: usize,
9210        keep: &[crate::moe_cache::BlockId],
9211    ) -> Result<(), Box<dyn std::error::Error>> {
9212        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9213        e.with_moe_cache(max_block, |c, eng| {
9214            for (proj, exps) in [
9215                (PROJ_GATE, &m.gate_exps),
9216                (PROJ_UP, &m.up_exps),
9217                (PROJ_DOWN, &m.down_exps),
9218            ] {
9219                let id = BlockId::new(il, proj, ex as u16);
9220                let _ = c.prefetch_source(id, exps.expert_source(ex), keep, eng)?;
9221            }
9222            Ok(())
9223        })
9224    }
9225
9226    /// Worker-mode disk lookahead for grouped prefill. Memory sources are deliberately skipped so
9227    /// selecting `worker` changes only storage scheduling here; all CUDA work stays in dispatch.
9228    fn moe_prefetch_disk_expert(
9229        e: &Engine,
9230        il: u16,
9231        ex: usize,
9232        m: &MoeWeights,
9233        max_block: usize,
9234        keep: &[crate::moe_cache::BlockId],
9235    ) -> Result<(), Box<dyn std::error::Error>> {
9236        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
9237        e.with_moe_cache(max_block, |c, eng| {
9238            for (proj, exps) in [
9239                (PROJ_GATE, &m.gate_exps),
9240                (PROJ_UP, &m.up_exps),
9241                (PROJ_DOWN, &m.down_exps),
9242            ] {
9243                let source = exps.expert_source(ex);
9244                if let crate::model::ExpertSource::Disk { .. } = &source {
9245                    let id = BlockId::new(il, proj, ex as u16);
9246                    let _ = c.prefetch_source(id, source, keep, eng)?;
9247                }
9248            }
9249            Ok(())
9250        })
9251    }
9252
9253    #[inline]
9254    fn moe_prefetch_host_expert(ex: usize, m: &MoeWeights) {
9255        let _ = m.gate_exps.prefetch_expert_pages(ex);
9256        let _ = m.up_exps.prefetch_expert_pages(ex);
9257        let _ = m.down_exps.prefetch_expert_pages(ex);
9258    }
9259}
9260
9261// ================================================================================================
9262// A2: EXPERT-GROUPED MoE PREFILL (MEMRA_MOE_GROUPED=1). Resident-case prototype.
9263//
9264// Instead of the per-token loop (T * 8 experts * 3 projections = 12024 individual m=1 matvecs),
9265// this groups tokens by expert and runs ONE matmul per active expert per projection at m=m_e.
9266// On a 501-token prefill with ~170 active experts, that's ~510 matmuls (vs 12024).
9267//
9268// EXACTNESS: per-token accumulation across its 8 experts is reordered (grouped processes experts
9269// in expert-id order, not the router's top-k order). To preserve bit-identity with the sequential
9270// loop, we use an 8-SLOT scheme: expert outputs are scattered into slots keyed by the token's
9271// top-k position (0..7), then reduced in that fixed order. This makes the f32 addition order
9272// identical to the per-token loop regardless of expert processing order.
9273//
9274// Memory: T * 8 * n_embd * 4 = 501 * 8 * 2048 * 4 = ~32 MB (slot buffer). Fine on 96GB.
9275// ================================================================================================
9276
9277impl HybridModel {
9278    /// Resident-slab fast path for host-routed grouped prefill. Unclamped layers batch the
9279    /// sequential fused q8 program over the token axis; clamped layers use the separate
9280    /// expert-major q8 chain so `ffn_act_lim` remains authoritative.
9281    #[allow(clippy::too_many_arguments)]
9282    fn moe_ffn_grouped_resident_q8(
9283        e: &Engine,
9284        m: &MoeWeights,
9285        z: &CudaSlice<f32>,
9286        t: usize,
9287        cfg: &ModelConfig,
9288        il: u16,
9289        sel_all: &[u32],
9290        w_all: &[f32],
9291        table: &CudaSlice<u64>,
9292        gu_il: bool,
9293    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9294        let moe = cfg.moe.as_ref().unwrap();
9295        let n_embd = cfg.n_embd as usize;
9296        let n_expert = moe.expert_count as usize;
9297        let n_used = moe.expert_used_count as usize;
9298        let n_ff_exp = moe.expert_ff_length as usize;
9299        let n_pairs = t * n_used;
9300        debug_assert_eq!(sel_all.len(), n_pairs);
9301        debug_assert_eq!(w_all.len(), n_pairs);
9302        debug_assert!(
9303            m.gate_exps.macros.is_none()
9304                && m.up_exps.macros.is_none()
9305                && m.down_exps.macros.is_none(),
9306            "resident grouped q8 does not fold per-expert macro scales",
9307        );
9308
9309        // The rows twins run the resident sequential program verbatim on grid.z = token:
9310        // fused gate/up/SiLU per slot, batched activation quantization, then the original
9311        // slot-ordered down/FMA chain. Routing remains the sigmoid selector above (device by
9312        // default; MEMRA_SIG_ROUTER=0 is the host oracle); these kernels consume sel/w only and
9313        // never enter the softmax router.
9314        if !cfg.swiglu_clamped_at(il as u32) {
9315            let sel: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9316            let sel_d = e.htod_i32(&sel)?;
9317            let w_d = e.htod(w_all)?;
9318            let (gate_row_bytes, up_row_bytes) = if gu_il {
9319                let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9320                (combined, combined)
9321            } else {
9322                (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9323            };
9324            let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9325            let act = e.moe_gate_up_silu8_dev_q8_rows(
9326                table,
9327                &sel_d,
9328                &zq,
9329                &zd,
9330                t,
9331                n_embd,
9332                n_ff_exp,
9333                n_used,
9334                n_expert,
9335                m.gate_exps.qtype,
9336                m.up_exps.qtype,
9337                gate_row_bytes,
9338                up_row_bytes,
9339                &m.dev_macros,
9340            )?;
9341            let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9342            let mut moe_out = e.uninit(t * n_embd)?;
9343            e.moe_down8_fma_dev_q8_rows_g(
9344                table,
9345                &sel_d,
9346                &w_d,
9347                &aq2,
9348                &ad2,
9349                &mut moe_out,
9350                t,
9351                n_ff_exp,
9352                n_embd,
9353                n_used,
9354                n_expert,
9355                m.down_exps.qtype,
9356                m.down_exps.row_bytes,
9357            )?;
9358
9359            if std::env::var("MEMRA_MOE_STATS").is_ok() {
9360                let mut counts = vec![0usize; n_expert];
9361                for &expert in sel_all {
9362                    counts[expert as usize] += 1;
9363                }
9364                let mut sizes: Vec<usize> =
9365                    counts.into_iter().filter(|&count| count != 0).collect();
9366                sizes.sort_unstable();
9367                let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9368                println!(
9369                    "moe-grouped il={il} t={t} dispatch=resident-q8-rows active={}/{} \
9370                     m_e: min={} median={} mean={mean:.1} max={}",
9371                    sizes.len(),
9372                    n_expert,
9373                    sizes.first().copied().unwrap_or(0),
9374                    sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9375                    sizes.last().copied().unwrap_or(0),
9376                );
9377            }
9378            return Ok(moe_out);
9379        }
9380
9381        // Clamped layers keep gate/up, activation, and down as separate stages. The pair-major
9382        // matvec body is qmatvec_expert_q8 verbatim, while one launch covers every routed pair.
9383        // Pair ids stay in router slot order so scatter preserves the sequential FMA chain.
9384        let pair_tok: Vec<i32> = (0..n_pairs).map(|pair| (pair / n_used) as i32).collect();
9385        let pair_ex: Vec<i32> = sel_all.iter().map(|&expert| expert as i32).collect();
9386        let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
9387        let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
9388
9389        let mut by_expert: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
9390        for (pair, &expert) in pair_ex.iter().enumerate() {
9391            by_expert[expert as usize].push(pair as i32);
9392        }
9393
9394        let pair_tok_d = e.htod_i32(&pair_tok)?;
9395        let pair_ex_d = e.htod_i32(&pair_ex)?;
9396        let pair_w_d = e.htod(w_all)?;
9397        let tok_off_d = e.htod_i32(&tok_off)?;
9398        let tok_ids_d = e.htod_i32(&tok_ids)?;
9399
9400        let matvec = |proj: i32,
9401                      pair_rows: &CudaSlice<i32>,
9402                      aq: &CudaSlice<i8>,
9403                      ad: &CudaSlice<f32>,
9404                      in_f: usize,
9405                      out_f: usize,
9406                      qtype: i32,
9407                      row_bytes: usize|
9408         -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9409            e.moe_pairs_matvec_q8(
9410                table, proj, pair_rows, &pair_ex_d, aq, ad, in_f, out_f, n_expert, n_pairs, qtype,
9411                row_bytes,
9412            )
9413        };
9414
9415        let (gate_row_bytes, up_row_bytes) = if gu_il {
9416            let combined = m.gate_exps.row_bytes + m.up_exps.row_bytes;
9417            (combined, combined)
9418        } else {
9419            (m.gate_exps.row_bytes, m.up_exps.row_bytes)
9420        };
9421        let (zq, zd) = e.quantize_q8_1(z, t, n_embd)?;
9422        let gate = matvec(
9423            0,
9424            &pair_tok_d,
9425            &zq,
9426            &zd,
9427            n_embd,
9428            n_ff_exp,
9429            m.gate_exps.qtype,
9430            gate_row_bytes,
9431        )?;
9432        let up = matvec(
9433            1,
9434            &pair_tok_d,
9435            &zq,
9436            &zd,
9437            n_embd,
9438            n_ff_exp,
9439            m.up_exps.qtype,
9440            up_row_bytes,
9441        )?;
9442        let mut act = e.uninit(n_pairs * n_ff_exp)?;
9443        Self::ffn_act_lim(
9444            e,
9445            cfg,
9446            &gate,
9447            &up,
9448            1.0,
9449            1.0,
9450            cfg.clamp_exp_at(il as u32),
9451            &mut act,
9452            n_pairs * n_ff_exp,
9453        )?;
9454        let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
9455        let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
9456        let pair_self_d = e.htod_i32(&pair_self)?;
9457        let down = matvec(
9458            2,
9459            &pair_self_d,
9460            &aq2,
9461            &ad2,
9462            n_ff_exp,
9463            n_embd,
9464            m.down_exps.qtype,
9465            m.down_exps.row_bytes,
9466        )?;
9467        let mut moe_out = e.uninit(t * n_embd)?;
9468        e.moe_pairs_scatter(
9469            &down,
9470            &pair_w_d,
9471            &tok_off_d,
9472            &tok_ids_d,
9473            &mut moe_out,
9474            t,
9475            n_embd,
9476        )?;
9477
9478        if std::env::var("MEMRA_MOE_STATS").is_ok() {
9479            let mut sizes: Vec<usize> = by_expert
9480                .iter()
9481                .filter_map(|pairs| (!pairs.is_empty()).then_some(pairs.len()))
9482                .collect();
9483            sizes.sort_unstable();
9484            let mean = sizes.iter().sum::<usize>() as f64 / sizes.len().max(1) as f64;
9485            println!(
9486                "moe-grouped il={il} t={t} dispatch=resident-q8-clamped-pairs active={}/{} \
9487                 m_e: min={} median={} mean={mean:.1} max={}",
9488                sizes.len(),
9489                n_expert,
9490                sizes.first().copied().unwrap_or(0),
9491                sizes.get(sizes.len() / 2).copied().unwrap_or(0),
9492                sizes.last().copied().unwrap_or(0),
9493            );
9494        }
9495        Ok(moe_out)
9496    }
9497
9498    /// MEMRA_SHEXP_SPLIT worker: the shared expert's gate/up/down rows split across both
9499    /// devices (dev1 idles during E3), act halves exchanged both ways, downs row-split —
9500    /// per-element/per-row programs identical, so `sh` is BIT-IDENTICAL to the single-device
9501    /// arm. Process-static workspace pinned by the gate tensor pointer; rank1 holds one-time
9502    /// row-half replicas (~10MB/layer x 42). Returns None when ineligible.
9503    #[allow(clippy::too_many_arguments)]
9504    fn shexp_split_matvec(
9505        e: &Engine,
9506        rank1: &Engine,
9507        wg: &CudaSlice<u8>,
9508        wu: &CudaSlice<u8>,
9509        wd: &CudaSlice<u8>,
9510        z: &CudaSlice<f32>,
9511        lim: Option<f32>,
9512        cfg: &ModelConfig,
9513        il: u16,
9514        n_embd: usize,
9515        n_ff_sh: usize,
9516    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
9517        use cudarc::driver::DevicePtr;
9518        if n_ff_sh % 2 != 0 || n_embd % 2 != 0 {
9519            return Ok(None);
9520        }
9521        let hf = n_ff_sh / 2;
9522        let nd = n_embd / 2;
9523        struct Rep {
9524            wg1: CudaSlice<u8>,
9525            wu1: CudaSlice<u8>,
9526            wd1: CudaSlice<u8>,
9527        }
9528        struct SplitWs {
9529            pin_dev: usize,
9530            // e side
9531            gate0: CudaSlice<f32>,
9532            up0: CudaSlice<f32>,
9533            act: CudaSlice<f32>,
9534            sh_buf: CudaSlice<f32>,
9535            ev_z: cudarc::driver::CudaEvent,
9536            ev_act0: cudarc::driver::CudaEvent,
9537            // rank1 side
9538            z1: CudaSlice<f32>,
9539            g1: CudaSlice<f32>,
9540            u1: CudaSlice<f32>,
9541            a1h: CudaSlice<f32>,
9542            act1: CudaSlice<f32>,
9543            y1: CudaSlice<f32>,
9544            ev_act1: cudarc::driver::CudaEvent,
9545            ev_y1: cudarc::driver::CudaEvent,
9546            raw_act_e: u64,
9547            raw_sh_e: u64,
9548            raw_z1: u64,
9549            raw_a1h: u64,
9550            raw_act1: u64,
9551            raw_y1: u64,
9552        }
9553        static WS: std::sync::Mutex<Option<SplitWs>> = std::sync::Mutex::new(None);
9554        static REPS: std::sync::Mutex<Option<std::collections::HashMap<u64, Rep>>> =
9555            std::sync::Mutex::new(None);
9556        let mut guard = WS.lock().map_err(|_| "shexp split lock is poisoned")?;
9557        let mut reps_guard = REPS.lock().map_err(|_| "shexp reps lock is poisoned")?;
9558        let reps = reps_guard.get_or_insert_with(std::collections::HashMap::new);
9559        let pins = e.ctx().ordinal();
9560        if guard.as_ref().is_none_or(|w| w.pin_dev != pins) {
9561            let (gate0, up0, act, sh_buf, ev_z, ev_act0) = {
9562                let _m = e.gpu.enter_main()?;
9563                (
9564                    e.htod(&vec![0.0f32; hf])?,
9565                    e.htod(&vec![0.0f32; hf])?,
9566                    e.htod(&vec![0.0f32; n_ff_sh])?,
9567                    e.htod(&vec![0.0f32; n_embd])?,
9568                    e.ctx().new_event(None)?,
9569                    e.ctx().new_event(None)?,
9570                )
9571            };
9572            let (z1, g1, u1, a1h, act1, y1, ev_act1, ev_y1) = {
9573                let _r = rank1.gpu.enter_main()?;
9574                (
9575                    rank1.htod(&vec![0.0f32; n_embd])?,
9576                    rank1.htod(&vec![0.0f32; hf])?,
9577                    rank1.htod(&vec![0.0f32; hf])?,
9578                    rank1.htod(&vec![0.0f32; hf])?,
9579                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9580                    rank1.htod(&vec![0.0f32; nd])?,
9581                    rank1.ctx().new_event(None)?,
9582                    rank1.ctx().new_event(None)?,
9583                )
9584            };
9585            let (raw_act_e, raw_sh_e) = {
9586                let _m = e.gpu.enter_main()?;
9587                let stream = e.stream();
9588                let (a, _g0) = act.device_ptr(&stream);
9589                let (b, _g1) = sh_buf.device_ptr(&stream);
9590                (a as u64, b as u64)
9591            };
9592            let (raw_z1, raw_a1h, raw_act1, raw_y1) = {
9593                let _r = rank1.gpu.enter_main()?;
9594                let rs = rank1.stream();
9595                let (a, _g0) = z1.device_ptr(&rs);
9596                let (b, _g1) = a1h.device_ptr(&rs);
9597                let (c, _g2) = act1.device_ptr(&rs);
9598                let (d, _g3) = y1.device_ptr(&rs);
9599                (a as u64, b as u64, c as u64, d as u64)
9600            };
9601            *guard = Some(SplitWs {
9602                pin_dev: pins,
9603                gate0,
9604                up0,
9605                act,
9606                sh_buf,
9607                ev_z,
9608                ev_act0,
9609                z1,
9610                g1,
9611                u1,
9612                a1h,
9613                act1,
9614                y1,
9615                ev_act1,
9616                ev_y1,
9617                raw_act_e,
9618                raw_sh_e,
9619                raw_z1,
9620                raw_a1h,
9621                raw_act1,
9622                raw_y1,
9623            });
9624        }
9625        let ws = guard.as_mut().expect("armed above");
9626        let wg_pin = {
9627            let _m = e.gpu.enter_main()?;
9628            let stream = e.stream();
9629            let (p, _g) = wg.device_ptr(&stream);
9630            p as u64
9631        };
9632        if !reps.contains_key(&wg_pin) {
9633            // One-time per layer: upload rank1's row halves (gate/up rows [hf..], down rows [nd..]).
9634            let mut up = |src: &CudaSlice<u8>,
9635                          off_bytes: usize,
9636                          len: usize|
9637             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9638                use cudarc::driver::sys;
9639                let sptr = {
9640                    let _m = e.gpu.enter_main()?;
9641                    let stream = e.stream();
9642                    let (p, _g) = src.device_ptr(&stream);
9643                    p as u64 + off_bytes as u64
9644                };
9645                let dst = {
9646                    let _r = rank1.gpu.enter_main()?;
9647                    rank1.alloc_u8_uninit(len)?
9648                };
9649                let dptr = {
9650                    let _r = rank1.gpu.enter_main()?;
9651                    let rs = rank1.stream();
9652                    let (p, _g) = dst.device_ptr(&rs);
9653                    p as u64
9654                };
9655                let _r = rank1.gpu.enter_main()?;
9656                let r = unsafe {
9657                    sys::cuMemcpyAsync(
9658                        dptr as sys::CUdeviceptr,
9659                        sptr as sys::CUdeviceptr,
9660                        len,
9661                        rank1.stream().cu_stream() as sys::CUstream,
9662                    )
9663                };
9664                if r != sys::CUresult::CUDA_SUCCESS {
9665                    return Err(format!("shexp split replica upload: {r:?}").into());
9666                }
9667                rank1.stream().synchronize()?;
9668                Ok(dst)
9669            };
9670            let wg1 = up(wg, hf * n_embd * 2, hf * n_embd * 2)?;
9671            let wu1 = up(wu, hf * n_embd * 2, hf * n_embd * 2)?;
9672            let wd1 = up(wd, nd * n_ff_sh * 2, nd * n_ff_sh * 2)?;
9673            reps.insert(wg_pin, Rep { wg1, wu1, wd1 });
9674        }
9675        let _ = il;
9676        // Per token, evented split flow.
9677        let raw_z = {
9678            let _m = e.gpu.enter_main()?;
9679            let stream = e.stream();
9680            let (p, _g) = z.device_ptr(&stream);
9681            ws.ev_z.record(&stream)?;
9682            p as u64
9683        };
9684        // rank1: pull z, its dual half, its act half; push act half to e; pull e's act half.
9685        {
9686            let rep = reps.get(&wg_pin).expect("uploaded above");
9687            let _r = rank1.gpu.enter_main()?;
9688            rank1.stream().wait(&ws.ev_z)?;
9689            crate::tp::raw_copy_bytes(ws.raw_z1, raw_z, n_embd * 4, rank1)?;
9690            let SplitWs {
9691                z1, g1, u1, a1h, ..
9692            } = &mut *ws;
9693            rank1.matvec_bf16_dual_into(&rep.wg1, &rep.wu1, z1, g1, u1, n_embd, hf)?;
9694            Self::ffn_act_lim(rank1, cfg, g1, u1, 1.0, 1.0, lim, a1h, hf)?;
9695            // local place into act1[hf..] + P2P push into e's act[hf..]
9696            crate::tp::raw_copy_bytes(ws.raw_act1 + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9697            crate::tp::raw_copy_bytes(ws.raw_act_e + (hf * 4) as u64, ws.raw_a1h, hf * 4, rank1)?;
9698            ws.ev_act1.record(&rank1.stream())?;
9699        }
9700        // e: its dual half + act half; publish it; then wait rank1's half and run down lo.
9701        {
9702            let _m = e.gpu.enter_main()?;
9703            let SplitWs {
9704                gate0, up0, act, ..
9705            } = &mut *ws;
9706            let wg_lo = wg.slice(0..hf * n_embd * 2);
9707            let wu_lo = wu.slice(0..hf * n_embd * 2);
9708            e.matvec_bf16_dual_view_into(&wg_lo, &wu_lo, z, gate0, up0, n_embd, hf)?;
9709            Self::ffn_act_lim(e, cfg, gate0, up0, 1.0, 1.0, lim, act, hf)?;
9710            ws.ev_act0.record(&e.stream())?;
9711        }
9712        // rank1: pull e's act half into act1[0..hf], run down hi, push y half.
9713        {
9714            let rep = reps.get(&wg_pin).expect("uploaded above");
9715            let _r = rank1.gpu.enter_main()?;
9716            rank1.stream().wait(&ws.ev_act0)?;
9717            crate::tp::raw_copy_bytes(ws.raw_act1, ws.raw_act_e, hf * 4, rank1)?;
9718            let SplitWs { act1, y1, .. } = &mut *ws;
9719            rank1.matvec_bf16_into(&rep.wd1, act1, y1, n_ff_sh, nd)?;
9720            crate::tp::raw_copy_bytes(ws.raw_sh_e + (nd * 4) as u64, ws.raw_y1, nd * 4, rank1)?;
9721            ws.ev_y1.record(&rank1.stream())?;
9722        }
9723        // e: down lo into sh_buf[0..nd]; join rank1's half; hand back an owned sh.
9724        {
9725            let _m = e.gpu.enter_main()?;
9726            e.stream().wait(&ws.ev_act1)?;
9727            let SplitWs { act, sh_buf, .. } = &mut *ws;
9728            let wd_lo = wd.slice(0..nd * n_ff_sh * 2);
9729            e.matvec_bf16_view_into(&wd_lo, act, sh_buf, n_ff_sh, nd)?;
9730            e.stream().wait(&ws.ev_y1)?;
9731            let mut sh = e.uninit(n_embd)?;
9732            {
9733                let mut dst = sh.slice_mut(0..n_embd);
9734                e.stream()
9735                    .memcpy_dtod(&ws.sh_buf.slice(0..n_embd), &mut dst)?;
9736            }
9737            Ok(Some(sh))
9738        }
9739    }
9740
9741    /// SHEXP OVERLAP issue (MEMRA_SHEXP_OVERLAP=1): the shared expert reads only `z`, so
9742    /// its kernels (dual matvec+SwiGLU, down) are issued on e's stream from the routes
9743    /// PREJOIN hook — they execute while the peer rank drains its sweep, filling dev0's
9744    /// join wait. The down lands in ITS OWN row (the pre-#2e split program — receipted
9745    /// bit-identical to the fused down+addscale) and `shexp_overlap_apply` adds it after
9746    /// the join with the exact add_scaled_rows expression: values unchanged.
9747    fn shexp_overlap_issue(
9748        e: &Engine,
9749        m: &MoeWeights,
9750        z: &CudaSlice<f32>,
9751        cfg: &ModelConfig,
9752        il: u16,
9753        n_embd: usize,
9754    ) -> Result<bool, Box<dyn std::error::Error>> {
9755        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9756            return Ok(false);
9757        }
9758        let (
9759            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9760            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9761            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9762        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9763        else {
9764            return Ok(false);
9765        };
9766        let n_ff_sh = m
9767            .gate_shexp
9768            .as_ref()
9769            .expect("matched Some above")
9770            .out_features();
9771        let lim = cfg.clamp_shexp_at(il as u32);
9772        let mut guard = SHEXP_OV_WS
9773            .lock()
9774            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9775        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9776        if guard
9777            .as_ref()
9778            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9779        {
9780            *guard = Some((
9781                pins.0,
9782                pins.1,
9783                pins.2,
9784                e.uninit(n_ff_sh)?,
9785                e.uninit(n_embd)?,
9786            ));
9787        }
9788        let (_, _, _, act, sh) = guard.as_mut().expect("armed above");
9789        e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
9790        e.matvec_bf16_into(wd, act, sh, n_ff_sh, n_embd)?;
9791        drop(guard);
9792        Ok(true)
9793    }
9794
9795    /// SHEXP ON DEV1 issue (MEMRA_SHEXP_DEV1=1): the shared expert runs on rank1 — the
9796    /// idle device — with replica weights (one-time P2P upload), the SAME kernels and the
9797    /// SAME split program as the dev0 overlap (dual matvec+SwiGLU, f32acc down), so the
9798    /// values are bit-identical. z rides one 16KB P2P pull behind an e-stream event; the
9799    /// down row lands root-resident (single P2P store pass); apply waits ev_done on e.
9800    #[allow(clippy::too_many_arguments)]
9801    fn shexp_dev1_issue(
9802        e: &Engine,
9803        rank1: &Engine,
9804        m: &MoeWeights,
9805        z: &CudaSlice<f32>,
9806        cfg: &ModelConfig,
9807        il: u16,
9808        n_embd: usize,
9809    ) -> Result<bool, Box<dyn std::error::Error>> {
9810        use cudarc::driver::DevicePtr;
9811        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9812            return Ok(false);
9813        }
9814        let (
9815            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
9816            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
9817            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
9818        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9819        else {
9820            return Ok(false);
9821        };
9822        let n_ff_sh = m
9823            .gate_shexp
9824            .as_ref()
9825            .expect("matched Some above")
9826            .out_features();
9827        let lim = cfg.clamp_shexp_at(il as u32);
9828        // Shared scratch, geometry-keyed.
9829        let mut ws_guard = SHEXP_D1_WS
9830            .lock()
9831            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9832        if ws_guard
9833            .as_ref()
9834            .is_none_or(|(k, ..)| *k != (n_embd, n_ff_sh))
9835        {
9836            let (act1, z1, ev_done) = {
9837                let _r1 = rank1.gpu.enter_main()?;
9838                (
9839                    rank1.htod(&vec![0.0f32; n_ff_sh])?,
9840                    rank1.htod(&vec![0.0f32; n_embd])?,
9841                    rank1.ctx().new_event(None)?,
9842                )
9843            };
9844            let (sh_root, ev_z) = {
9845                let _main = e.gpu.enter_main()?;
9846                (e.htod(&vec![0.0f32; n_embd])?, e.ctx().new_event(None)?)
9847            };
9848            *ws_guard = Some(((n_embd, n_ff_sh), act1, z1, sh_root, ev_z, ev_done));
9849        }
9850        // Per-LAYER weight replicas (gate/up/down differ per layer): one-time P2P upload.
9851        let mut reps_guard = SHEXP_D1_REPS
9852            .lock()
9853            .map_err(|_| "shexp dev1 replica lock is poisoned")?;
9854        let reps = reps_guard.get_or_insert_with(Default::default);
9855        if !reps.contains_key(&il) {
9856            let (wg1, wu1, wd1) = {
9857                let _r1 = rank1.gpu.enter_main()?;
9858                (
9859                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9860                    rank1.alloc_u8_uninit(n_ff_sh * n_embd * 2)?,
9861                    rank1.alloc_u8_uninit(n_embd * n_ff_sh * 2)?,
9862                )
9863            };
9864            for (src, dst) in [(wg, &wg1), (wu, &wu1), (wd, &wd1)] {
9865                let s_ptr = {
9866                    let _main = e.gpu.enter_main()?;
9867                    let stream = e.stream();
9868                    let (p, _g) = src.device_ptr(&stream);
9869                    p as u64
9870                };
9871                let d_ptr = {
9872                    let _r1 = rank1.gpu.enter_main()?;
9873                    let stream = rank1.stream();
9874                    let (p, _g) = dst.device_ptr(&stream);
9875                    p as u64
9876                };
9877                let _r1 = rank1.gpu.enter_main()?;
9878                crate::tp::raw_copy_bytes(d_ptr, s_ptr, src.len(), rank1)?;
9879            }
9880            {
9881                let _r1 = rank1.gpu.enter_main()?;
9882                rank1.stream().synchronize()?;
9883            }
9884            reps.insert(il, (wg1, wu1, wd1));
9885        }
9886        let (wg1, wu1, wd1) = reps.get(&il).expect("armed above");
9887        let (_, act1, z1, sh_root, ev_z, ev_done) = ws_guard.as_mut().expect("armed above");
9888        // z ready on e's stream -> rank1 pulls it, runs the split shexp, pushes the down
9889        // row root-side (single store pass), rings ev_done.
9890        let (raw_z, raw_sh) = {
9891            let _main = e.gpu.enter_main()?;
9892            let stream = e.stream();
9893            let (a, _g0) = z.device_ptr(&stream);
9894            let (b, _g1) = sh_root.device_ptr(&stream);
9895            ev_z.record(&stream)?;
9896            (a as u64, b as u64)
9897        };
9898        {
9899            let _r1 = rank1.gpu.enter_main()?;
9900            rank1.stream().wait(ev_z)?;
9901            let raw_z1 = {
9902                let stream = rank1.stream();
9903                let (p, _g) = z1.device_ptr(&stream);
9904                p as u64
9905            };
9906            crate::tp::raw_copy_bytes(raw_z1, raw_z, n_embd * 4, rank1)?;
9907            rank1.matvec_bf16_dual_silu_into(wg1, wu1, z1, act1, n_embd, n_ff_sh, lim)?;
9908            // down writes the ROOT-resident row over P2P via the raw-output twin of
9909            // matvec_bf16_into: reuse the view launcher with a slice view is not possible
9910            // cross-device, so launch on the raw pointer.
9911            rank1.matvec_bf16_raw_out(wd1, act1, raw_sh, n_ff_sh, n_embd)?;
9912            ev_done.record(&rank1.stream())?;
9913        }
9914        Ok(true)
9915    }
9916
9917    /// Apply the dev1 shared expert: wait ev_done on e, then the exact add_scaled_rows.
9918    fn shexp_dev1_apply(
9919        e: &Engine,
9920        output: &mut CudaSlice<f32>,
9921        n_embd: usize,
9922    ) -> Result<(), Box<dyn std::error::Error>> {
9923        let guard = SHEXP_D1_WS
9924            .lock()
9925            .map_err(|_| "shexp dev1 workspace lock is poisoned")?;
9926        let (pin, _, _, sh_root, _, ev_done) =
9927            guard.as_ref().ok_or("shexp dev1 apply without issue")?;
9928        if pin.0 != n_embd {
9929            return Err("shexp dev1 width drifted".into());
9930        }
9931        let _main = e.gpu.enter_main()?;
9932        e.stream().wait(ev_done)?;
9933        static ONES_D1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9934            std::sync::Mutex::new(None);
9935        let mut og = ONES_D1.lock().map_err(|_| "ones lock is poisoned")?;
9936        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9937            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9938        }
9939        let ones = &og.as_ref().expect("armed above").1;
9940        e.add_scaled_rows(sh_root, ones, output, n_embd, 1)?;
9941        Ok(())
9942    }
9943
9944    /// MOE TAIL FUSION M1 helper: pre-arm the overlap ws + persistent ones row and
9945    /// return their RAW pointers (None when the overlap is ineligible — the caller then
9946    /// takes the split path). Mirrors shexp_overlap_issue's eligibility exactly.
9947    fn shexp_overlap_tail_ptrs(
9948        e: &Engine,
9949        m: &MoeWeights,
9950        cfg: &ModelConfig,
9951        n_embd: usize,
9952    ) -> Result<Option<(u64, u64)>, Box<dyn std::error::Error>> {
9953        use cudarc::driver::DevicePtr;
9954        if cfg.m3.is_some() || m.gate_inp_shexp.is_some() {
9955            return Ok(None);
9956        }
9957        let (
9958            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9959            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9960            Some(crate::model::GpuTensor::FloatBf16 { .. }),
9961        ) = (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
9962        else {
9963            return Ok(None);
9964        };
9965        let n_ff_sh = m
9966            .gate_shexp
9967            .as_ref()
9968            .expect("matched Some above")
9969            .out_features();
9970        let mut guard = SHEXP_OV_WS
9971            .lock()
9972            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
9973        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
9974        if guard
9975            .as_ref()
9976            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
9977        {
9978            *guard = Some((
9979                pins.0,
9980                pins.1,
9981                pins.2,
9982                e.uninit(n_ff_sh)?,
9983                e.uninit(n_embd)?,
9984            ));
9985        }
9986        let sh_raw = {
9987            let (_, _, _, _, sh) = guard.as_ref().expect("armed above");
9988            let stream = e.stream();
9989            let (p, _g) = sh.device_ptr(&stream);
9990            p as u64
9991        };
9992        static ONES_T3: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
9993            std::sync::Mutex::new(None);
9994        let mut og = ONES_T3.lock().map_err(|_| "ones lock is poisoned")?;
9995        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
9996            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
9997        }
9998        let ones_raw = {
9999            let stream = e.stream();
10000            let (p, _g) = og.as_ref().expect("armed above").1.device_ptr(&stream);
10001            p as u64
10002        };
10003        Ok(Some((sh_raw, ones_raw)))
10004    }
10005
10006    /// Apply the overlapped shared expert: output[r] += sh[r] * 1.0 — the exact
10007    /// add_scaled_rows program the split path used (persistent ones row, no htod).
10008    fn shexp_overlap_apply(
10009        e: &Engine,
10010        output: &mut CudaSlice<f32>,
10011        n_embd: usize,
10012    ) -> Result<(), Box<dyn std::error::Error>> {
10013        let guard = SHEXP_OV_WS
10014            .lock()
10015            .map_err(|_| "shexp overlap workspace lock is poisoned")?;
10016        let (_, ne, _, _, sh) = guard.as_ref().ok_or("shexp overlap apply without issue")?;
10017        if *ne != n_embd {
10018            return Err("shexp overlap width drifted".into());
10019        }
10020        static ONES_OV: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10021            std::sync::Mutex::new(None);
10022        let mut og = ONES_OV.lock().map_err(|_| "ones lock is poisoned")?;
10023        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10024            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10025        }
10026        let ones = &og.as_ref().expect("armed above").1;
10027        e.add_scaled_rows(sh, ones, output, n_embd, 1)?;
10028        Ok(())
10029    }
10030
10031    fn moe_ffn_grouped_add_shared(
10032        e: &Engine,
10033        m: &MoeWeights,
10034        z: &CudaSlice<f32>,
10035        t: usize,
10036        cfg: &ModelConfig,
10037        il: u16,
10038        moe_out: &mut CudaSlice<f32>,
10039    ) -> Result<(), Box<dyn std::error::Error>> {
10040        // MEMRA_STEP_TP_TIMING=1: shared-expert wall (syncs e's stream at exit to bill the
10041        // queued matmuls here rather than at the next host readback).
10042        static SHEXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10043        static SHEXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10044        let shexp_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10045        let shexp_started = shexp_timing.then(std::time::Instant::now);
10046        let result = Self::moe_ffn_grouped_add_shared_inner(e, m, z, t, cfg, il, moe_out);
10047        if let Some(started) = shexp_started {
10048            use std::sync::atomic::Ordering;
10049            e.stream().synchronize()?;
10050            let ns = SHEXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10051                + started.elapsed().as_nanos() as u64;
10052            let calls = SHEXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10053            if calls % 430 == 0 {
10054                eprintln!(
10055                    "[moe-shexp-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10056                    ns as f64 / 1.0e6,
10057                    ns as f64 / calls as f64 / 1.0e3,
10058                );
10059            }
10060        }
10061        result
10062    }
10063
10064    #[allow(clippy::too_many_arguments)]
10065    fn moe_ffn_grouped_add_shared_inner(
10066        e: &Engine,
10067        m: &MoeWeights,
10068        z: &CudaSlice<f32>,
10069        t: usize,
10070        cfg: &ModelConfig,
10071        il: u16,
10072        moe_out: &mut CudaSlice<f32>,
10073    ) -> Result<(), Box<dyn std::error::Error>> {
10074        let n_embd = cfg.n_embd as usize;
10075        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
10076            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
10077        {
10078            let n_ff_sh = gate_shexp.out_features();
10079            let lim = cfg.clamp_shexp_at(il as u32);
10080            // T=1 DECODE FUSION (2026-08-20): the ffn_swiglu_decode fast-path program, ported
10081            // — one shared quantize feeds gate+up (matmul at m=1 quantizes internally per
10082            // call with identical bytes, so sharing it is bit-identical), the dual NVFP4/Q8
10083            // launch covers both when available, and silu_mul_scaled_q8_1 emits down's
10084            // operand pre-quantized (kernel_check-proven identities). This path measured
10085            // 167us/layer as separate matmuls + 5 allocs at decode.
10086            let fused = t == 1
10087                && lim.is_none()
10088                && cfg.m3.is_none()
10089                && e.uses_q8_1_fast(gate_shexp)
10090                && e.uses_q8_1_fast(up_shexp);
10091            // MEMRA_BF16_MMV class: both projections in ONE launch (bit-identical per row to
10092            // the two matvec_bf16 launches matmul would issue).
10093            let bf16_dual = if t == 1 && crate::Engine::bf16_mmv_on() && n_embd % 8 == 0 {
10094                match (gate_shexp, up_shexp) {
10095                    (
10096                        crate::model::GpuTensor::FloatBf16 { data: wg, .. },
10097                        crate::model::GpuTensor::FloatBf16 { data: wu, .. },
10098                    ) => Some((wg, wu)),
10099                    _ => None,
10100                }
10101            } else {
10102                None
10103            };
10104            let sh = if let Some((wg, wu)) = bf16_dual {
10105                // Persistent shared-expert workspace: sizes are constant across every MoE
10106                // layer, so one process-level set pinned by (device, n_embd, n_ff_sh) removes
10107                // the four per-layer allocations. Buffers are fully overwritten each call.
10108                static SHEXP_WS: std::sync::Mutex<
10109                    Option<(
10110                        usize,
10111                        usize,
10112                        usize,
10113                        CudaSlice<f32>,
10114                        CudaSlice<f32>,
10115                        CudaSlice<f32>,
10116                        CudaSlice<f32>,
10117                    )>,
10118                > = std::sync::Mutex::new(None);
10119                let down_bf16 = match down_shexp {
10120                    crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
10121                    _ => None,
10122                };
10123                let mut guard = SHEXP_WS
10124                    .lock()
10125                    .map_err(|_| "shexp workspace lock is poisoned")?;
10126                let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
10127                if guard
10128                    .as_ref()
10129                    .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
10130                {
10131                    *guard = Some((
10132                        pins.0,
10133                        pins.1,
10134                        pins.2,
10135                        e.uninit(n_ff_sh)?,
10136                        e.uninit(n_ff_sh)?,
10137                        e.uninit(n_ff_sh)?,
10138                        e.uninit(n_embd)?,
10139                    ));
10140                }
10141                // MEMRA_SHEXP_SPLIT=1: bit-identical row split across both devices; falls
10142                // through to the single-device arm when ineligible.
10143                {
10144                    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10145                    let split_on = *ON
10146                        .get_or_init(|| std::env::var("MEMRA_SHEXP_SPLIT").as_deref() == Ok("1"));
10147                    if split_on {
10148                        if let (Some(wd), Some(rank1)) = (
10149                            match down_shexp {
10150                                crate::model::GpuTensor::FloatBf16 { data, .. } => Some(data),
10151                                _ => None,
10152                            },
10153                            m.step_tp.as_ref().and_then(|st| st.runtime.rank_engine(1)),
10154                        ) {
10155                            if let Some(sh) = Self::shexp_split_matvec(
10156                                e, rank1, wg, wu, wd, z, lim, cfg, il, n_embd, n_ff_sh,
10157                            )? {
10158                                drop(guard);
10159                                let gate = match &m.gate_inp_shexp {
10160                                    Some(gate_inp_shexp) => e.sigmoid_dot_rows(
10161                                        z,
10162                                        gate_inp_shexp.float_data(),
10163                                        n_embd,
10164                                        t,
10165                                    )?,
10166                                    None => e.htod(&vec![1.0f32; t])?,
10167                                };
10168                                e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
10169                                return Ok(());
10170                            }
10171                        }
10172                    }
10173                }
10174                let (_, _, _, gate, up, act, sh_buf) =
10175                    guard.as_mut().expect("shexp workspace initialized above");
10176                if cfg.m3.is_none() {
10177                    // FUSION #2b: dual matvec + SwiGLU act in one launch — exact dual
10178                    // per-row program + exact silu/clamped expression, bit-identical.
10179                    e.matvec_bf16_dual_silu_into(wg, wu, z, act, n_embd, n_ff_sh, lim)?;
10180                    let _ = (&gate, &up);
10181                } else {
10182                    e.matvec_bf16_dual_into(wg, wu, z, gate, up, n_embd, n_ff_sh)?;
10183                    Self::ffn_act_lim(e, cfg, gate, up, 1.0, 1.0, lim, act, n_ff_sh)?;
10184                }
10185                if let Some(down) = down_bf16 {
10186                    // FUSION #2e (gate-less shexp only, MEMRA_FUSE_DOWN_ADDSCALE=0 reverts):
10187                    // down matvec + scaled accumulate straight into moe_out in ONE launch —
10188                    // exact f32acc per-row program + the exact add_scaled_rows expression
10189                    // (dst[r] += y_r * 1.0). Replaces down + ownership alloc + 16KB copy +
10190                    // add_scaled (3 launches + alloc -> 1 launch); bit-identical because the
10191                    // accumulate consumes the same f32 the split path stored and reloaded.
10192                    static FUSE_DA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10193                    let fuse_da = *FUSE_DA.get_or_init(|| {
10194                        std::env::var("MEMRA_FUSE_DOWN_ADDSCALE").as_deref() != Ok("0")
10195                    });
10196                    if fuse_da && m.gate_inp_shexp.is_none() {
10197                        static ONES1: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10198                            std::sync::Mutex::new(None);
10199                        let mut og = ONES1.lock().map_err(|_| "shexp ones lock is poisoned")?;
10200                        if og.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10201                            *og = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10202                        }
10203                        let ones = &og.as_ref().expect("armed above").1;
10204                        e.matvec_bf16_down_addscale_into(
10205                            down, act, ones, moe_out, n_ff_sh, n_embd,
10206                        )?;
10207                        return Ok(());
10208                    }
10209                    e.matvec_bf16_into(down, act, sh_buf, n_ff_sh, n_embd)?;
10210                    let sh = e.uninit(n_embd)?;
10211                    // One alloc keeps the ownership contract; the copy is 16KB on-stream.
10212                    let mut sh = sh;
10213                    {
10214                        let mut dst = sh.slice_mut(0..n_embd);
10215                        e.stream().memcpy_dtod(&sh_buf.slice(0..n_embd), &mut dst)?;
10216                    }
10217                    sh
10218                } else {
10219                    e.matmul(down_shexp, act, 1)?
10220                }
10221            } else if fused {
10222                let (zq, zd) = e.quantize_q8_1(z, 1, n_embd)?;
10223                let pair = match e.matmul_pre_dual_noscale(gate_shexp, up_shexp, &zq, &zd, 1)? {
10224                    Some((gate, up)) => Some((gate, up)),
10225                    None => {
10226                        match (
10227                            e.matmul_pre_noscale(gate_shexp, &zq, &zd, 1)?,
10228                            e.matmul_pre_noscale(up_shexp, &zq, &zd, 1)?,
10229                        ) {
10230                            (Some(gate), Some(up)) => Some((gate, up)),
10231                            _ => None,
10232                        }
10233                    }
10234                };
10235                match pair {
10236                    Some(((gate, gs), (up, us))) => {
10237                        if e.uses_q8_1_fast(down_shexp) {
10238                            let (aq, ad) = e.silu_mul_scaled_q8_1(&gate, &up, gs, us, n_ff_sh)?;
10239                            e.matmul_pre(down_shexp, &aq, &ad, &gate, 1)?
10240                        } else {
10241                            let mut act = e.uninit(n_ff_sh)?;
10242                            e.silu_mul_scaled(&gate, &up, gs, us, &mut act, n_ff_sh)?;
10243                            e.matmul(down_shexp, &act, 1)?
10244                        }
10245                    }
10246                    None => {
10247                        let gate = e.matmul_pre(gate_shexp, &zq, &zd, z, 1)?;
10248                        let up = e.matmul_pre(up_shexp, &zq, &zd, z, 1)?;
10249                        let mut act = e.uninit(n_ff_sh)?;
10250                        Self::ffn_act(e, cfg, &gate, &up, &mut act, n_ff_sh)?;
10251                        e.matmul(down_shexp, &act, 1)?
10252                    }
10253                }
10254            } else {
10255                let sg_gate = e.matmul(gate_shexp, z, t)?;
10256                let sg_up = e.matmul(up_shexp, z, t)?;
10257                let mut sa = e.uninit(t * n_ff_sh)?;
10258                Self::ffn_act_lim(
10259                    e,
10260                    cfg,
10261                    &sg_gate,
10262                    &sg_up,
10263                    1.0,
10264                    1.0,
10265                    lim,
10266                    &mut sa,
10267                    t * n_ff_sh,
10268                )?;
10269                e.matmul(down_shexp, &sa, t)?
10270            };
10271            let gate = match &m.gate_inp_shexp {
10272                Some(gate_inp_shexp) => {
10273                    if t < PRIME_MIN_T || crate::router_prefill_exact_on() {
10274                        e.sigmoid_dot_rows(z, gate_inp_shexp.float_data(), n_embd, t)?
10275                    } else {
10276                        let raw = e.linear(z, gate_inp_shexp.float_data(), t, n_embd, 1)?;
10277                        let mut gate = e.uninit(t)?;
10278                        e.sigmoid(&raw, &mut gate, t)?;
10279                        gate
10280                    }
10281                }
10282                // t=1 hot path: the per-layer htod of a ones row is a PAGEABLE H2D that
10283                // synchronizes the stream — measured as the biggest per-layer host gap
10284                // (44.6us x 42, eager gap table 2026-08-21). One persistent ones row per
10285                // device serves every layer; larger t (prefill) keeps the plain htod.
10286                None if t == 1 => {
10287                    static ONES: std::sync::Mutex<Option<(usize, CudaSlice<f32>)>> =
10288                        std::sync::Mutex::new(None);
10289                    let mut guard = ONES.lock().map_err(|_| "shexp ones lock is poisoned")?;
10290                    if guard.as_ref().is_none_or(|(d, _)| *d != e.ctx().ordinal()) {
10291                        *guard = Some((e.ctx().ordinal(), e.htod(&[1.0f32])?));
10292                    }
10293                    let ones = &guard.as_ref().expect("armed above").1;
10294                    e.add_scaled_rows(&sh, ones, moe_out, n_embd, t)?;
10295                    return Ok(());
10296                }
10297                None => e.htod(&vec![1.0f32; t])?,
10298            };
10299            e.add_scaled_rows(&sh, &gate, moe_out, n_embd, t)?;
10300        }
10301        Ok(())
10302    }
10303
10304    /// A2 expert-grouped MoE FFN (prefill path, MEMRA_MOE_GROUPED=1). Same semantics as moe_ffn:
10305    /// z [T, n_embd] -> moe_out [T, n_embd]. BIT-IDENTICAL to moe_ffn when using the slot scheme.
10306    pub(crate) fn moe_ffn_grouped(
10307        e: &Engine,
10308        m: &MoeWeights,
10309        z: &CudaSlice<f32>,
10310        t: usize,
10311        cfg: &ModelConfig,
10312        il: u16,
10313        max_block: usize,
10314    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10315        let moe = cfg.moe.as_ref().unwrap();
10316        let n_embd = cfg.n_embd as usize;
10317        let n_expert = moe.expert_count as usize;
10318        let n_used = moe.expert_used_count as usize;
10319        let n_ff_exp = moe.expert_ff_length as usize;
10320        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10321        let lim_exp = cfg.clamp_exp_at(il as u32);
10322
10323        // 1. ROUTER: exactly the same m-invariant sigmoid selector as the sequential path
10324        // (device by default; MEMRA_SIG_ROUTER=0 is the host oracle). The grouped dispatch never
10325        // enters the softmax-only pairs/dev router.
10326        let logits = Self::moe_router_logits(e, m, z, t, cfg)?;
10327        if let Some(sig) = cfg.sigmoid_router() {
10328            Self::trace_sigmoid_router_logits(e, il, t, n_expert, n_used, &logits, m, sig)?;
10329        }
10330        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10331            Self::moe_route_sigmoid_cfg(e, &logits, t, n_expert, n_used, m, sig)?
10332        } else {
10333            Self::moe_route_cfg(e, &logits, t, n_expert, n_used, m.active_experts.as_deref())?
10334        };
10335        crate::moesd::record_host_routes(il, n_expert, n_used, &sel_all)?;
10336        Self::trace_moe_routes(il, t, &sel_all, &w_all)?;
10337        Self::trace_moe_input(e, il, t, n_embd, z)?;
10338
10339        // Fits-VRAM Step35 under the PP stage split: use the expert-major q8 arithmetic directly
10340        // from the owning device's uniform resident slab. This is not `moe_ffn_pairs`: routing
10341        // already happened above and the activation is clamp-aware. Mixed layouts, remote slabs,
10342        // macro-scaled experts, q8-disabled configs, and spill fall through to metadata-aware A2.
10343        let no_exp_macros = m.gate_exps.macros.is_none()
10344            && m.up_exps.macros.is_none()
10345            && m.down_exps.macros.is_none();
10346        let resident_q8 = m.dev_exps.as_ref().filter(|dev| {
10347            m.has_uniform_expert_layout()
10348                && no_exp_macros
10349                && moe_q8_enabled()
10350                && q8_expert_supported(m.gate_exps.qtype)
10351                && q8_expert_supported(m.up_exps.qtype)
10352                && q8_expert_supported(m.down_exps.qtype)
10353                && moe_slab_enabled()
10354                && dev.dev == e.ctx().ordinal()
10355        });
10356        if let Some(dev) = resident_q8 {
10357            let mut moe_out = Self::moe_ffn_grouped_resident_q8(
10358                e,
10359                m,
10360                z,
10361                t,
10362                cfg,
10363                il,
10364                &sel_all,
10365                &w_all,
10366                &dev.ptr_row,
10367                dev.gu_il,
10368            )?;
10369            Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10370            return Ok(moe_out);
10371        }
10372
10373        // 2. BUILD PER-EXPERT TOKEN LISTS (host-side grouping).
10374        // For each expert e, we need: which tokens use it, their positions in z, their top-k
10375        // slot index (for bit-identical accumulation), and their weights.
10376        struct ExpertGroup {
10377            tok_indices: Vec<i32>,  // indices into z rows (0..T-1)
10378            slot_indices: Vec<i32>, // top-k slot (0..n_used-1) for that token-expert pair
10379            weights: Vec<f32>,      // renormalized weight for that token-expert pair
10380        }
10381        let mut groups: Vec<ExpertGroup> = (0..n_expert)
10382            .map(|_| ExpertGroup {
10383                tok_indices: Vec::new(),
10384                slot_indices: Vec::new(),
10385                weights: Vec::new(),
10386            })
10387            .collect();
10388
10389        for tok in 0..t {
10390            for j in 0..n_used {
10391                let ex = sel_all[tok * n_used + j] as usize;
10392                let w = w_all[tok * n_used + j];
10393                groups[ex].tok_indices.push(tok as i32);
10394                groups[ex].slot_indices.push(j as i32);
10395                groups[ex].weights.push(w);
10396            }
10397        }
10398
10399        // 3. ALLOCATE SLOT BUFFER: [T, n_used, n_embd] f32, zero-initialized.
10400        // Each token's 8 expert contributions land in their respective slots.
10401        let mut slot_buf = e.zeros(t * n_used * n_embd)?;
10402        let mut wbuf = e.zeros(t * n_used)?; // [T, n_used] weight buffer for FMA reduce
10403
10404        // Expert weight dimensions (used in both cache and staging paths).
10405        let g_len = m.gate_exps.max_expert_bytes();
10406        let u_len = m.up_exps.max_expert_bytes();
10407        let d_len = m.down_exps.max_expert_bytes();
10408        let moe_q8 = m.has_uniform_expert_layout()
10409            && moe_q8_enabled()
10410            && q8_expert_supported(m.gate_exps.qtype)
10411            && q8_expert_supported(m.up_exps.qtype)
10412            && q8_expert_supported(m.down_exps.qtype);
10413        // The per-expert slab view is legal only for the ordinary contiguous uniform layout.
10414        // Interleaved GU slabs require the pointer-table fast path above.
10415        let slab_local = m
10416            .dev_exps
10417            .as_ref()
10418            .filter(|dev| !dev.gu_il && moe_slab_enabled() && dev.dev == e.ctx().ordinal());
10419        let use_cache =
10420            slab_local.is_none() && Engine::moe_cache_enabled() && !e.moe_cache_frozen();
10421        // The sequential no-cache/frozen staging oracle is f32. Use q8 only where sequential
10422        // also does: a local resident slab or a live SLRU dispatch.
10423        let grouped_q8 = moe_q8 && (slab_local.is_some() || use_cache);
10424
10425        // GPU scratch for staging (only allocated without a local slab or cache).
10426        let (mut scratch_g, mut scratch_u, mut scratch_d) = if slab_local.is_none() && !use_cache {
10427            (
10428                Some(e.alloc_u8(g_len)?),
10429                Some(e.alloc_u8(u_len)?),
10430                Some(e.alloc_u8(d_len)?),
10431            )
10432        } else {
10433            (None, None, None)
10434        };
10435
10436        // 4. PER ACTIVE EXPERT: gather, compute, scatter.
10437        // Processing ORDER: DESCENDING m_e (biggest token batches first) — the concluded winner
10438        // (rig5090 2026-07-04, the ascending-id arm and its MEMRA_MOE_ORDER seam removed): desc is
10439        // a first-forward win at partial cache capacity — the hot (big-m_e) experts are admitted
10440        // to the SLRU before the small-m_e tail can pollute it, so residency converges in ONE
10441        // forward instead of several: auto-cache T=501 126.9 -> 169.9 tok/s (1.34x), cap512
10442        // 119.6 -> 160.8 (and kills the rep-to-rep bimodal); wash (<2%) at cap64 pure-spill and
10443        // at long prompts where every expert stages regardless. Order is FREE to change without
10444        // breaking the byte-identity gate: the slot scheme pins each token's accumulation order
10445        // regardless of expert processing order (the whole point of the slots).
10446        let mut order: Vec<usize> = (0..n_expert)
10447            .filter(|&ex| !groups[ex].tok_indices.is_empty())
10448            .collect();
10449        order.sort_by(|&a, &b| {
10450            groups[b]
10451                .tok_indices
10452                .len()
10453                .cmp(&groups[a].tok_indices.len())
10454                .then(a.cmp(&b))
10455        });
10456        let mut m_dist: Vec<usize> = Vec::new(); // for stats
10457        let page_window = moe_page_prefetch_window();
10458        let worker_disk_prefetch = use_cache && crate::spill_pread::worker_enabled();
10459        if worker_disk_prefetch {
10460            if let Some(first) = grouped_worker_prefetch_position(order.len(), None) {
10461                Self::moe_prefetch_disk_expert(e, il, order[first], m, max_block, &[])?;
10462            }
10463        }
10464        for (order_pos, &ex) in order.iter().enumerate() {
10465            for next in page_prefetch_positions(order_pos, order.len(), page_window) {
10466                Self::moe_prefetch_host_expert(order[next], m);
10467            }
10468            if worker_disk_prefetch {
10469                if let Some(next) = grouped_worker_prefetch_position(order.len(), Some(order_pos)) {
10470                    use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10471                    let keep = [
10472                        BlockId::new(il, PROJ_GATE, ex as u16),
10473                        BlockId::new(il, PROJ_UP, ex as u16),
10474                        BlockId::new(il, PROJ_DOWN, ex as u16),
10475                    ];
10476                    Self::moe_prefetch_disk_expert(e, il, order[next], m, max_block, &keep)?;
10477                }
10478            }
10479            let grp = &groups[ex];
10480            let m_e = grp.tok_indices.len();
10481            m_dist.push(m_e);
10482            let gl = m.gate_exps.expert_layout(ex);
10483            let ul = m.up_exps.expert_layout(ex);
10484            let dl = m.down_exps.expert_layout(ex);
10485
10486            // Upload index/weight arrays to device. The down-proj per-expert macro-scale
10487            // (ModelOpt weight_scale_2) folds into the scatter weights — post-matmul linear,
10488            // same fold as the sequential loop's `w[j] * macro_scale(ex)`. 1.0 for GGUF experts.
10489            let tok_idx_d = e.htod_i32(&grp.tok_indices)?;
10490            let slot_idx_d = e.htod_i32(&grp.slot_indices)?;
10491            let dmac = m.down_exps.macro_scale(ex);
10492            let weight_d = if dmac == 1.0 {
10493                e.htod(&grp.weights)?
10494            } else {
10495                let scaled: Vec<f32> = grp.weights.iter().map(|&w| w * dmac).collect();
10496                e.htod(&scaled)?
10497            };
10498
10499            // GATHER: collect m_e activation rows from z into a contiguous buffer.
10500            let mut gathered = e.zeros(m_e * n_embd)?;
10501            e.gather_rows(z, &tok_idx_d, &mut gathered, n_embd, m_e)?;
10502            let gv = gathered.slice(0..m_e * n_embd);
10503
10504            // Compute gate/up/down from the local slab, metadata-aware cache, or staging. The q8
10505            // form keeps each gathered row in the sequential arithmetic class at `m=m_e`.
10506            let y = if let Some(dev) = slab_local {
10507                let gate_start = ex * m.gate_exps.expert_stride;
10508                let up_start = ex * m.up_exps.expert_stride;
10509                let down_start = ex * m.down_exps.expert_stride;
10510                if grouped_q8 {
10511                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10512                    let gate = e.qmatvec_expert_q8(
10513                        &dev.gate,
10514                        gate_start..gate_start + gl.len,
10515                        &zq,
10516                        &zd,
10517                        m_e,
10518                        m.gate_exps.in_f,
10519                        m.gate_exps.out_f,
10520                        gl.qtype,
10521                        gl.row_bytes,
10522                    )?;
10523                    let up = e.qmatvec_expert_q8(
10524                        &dev.up,
10525                        up_start..up_start + ul.len,
10526                        &zq,
10527                        &zd,
10528                        m_e,
10529                        m.up_exps.in_f,
10530                        m.up_exps.out_f,
10531                        ul.qtype,
10532                        ul.row_bytes,
10533                    )?;
10534                    let mut act = e.uninit(m_e * n_ff_exp)?;
10535                    Self::ffn_act_lim(
10536                        e,
10537                        cfg,
10538                        &gate,
10539                        &up,
10540                        m.gate_exps.macro_scale(ex),
10541                        m.up_exps.macro_scale(ex),
10542                        lim_exp,
10543                        &mut act,
10544                        m_e * n_ff_exp,
10545                    )?;
10546                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10547                    e.qmatvec_expert_q8(
10548                        &dev.down,
10549                        down_start..down_start + dl.len,
10550                        &aq2,
10551                        &ad2,
10552                        m_e,
10553                        m.down_exps.in_f,
10554                        m.down_exps.out_f,
10555                        dl.qtype,
10556                        dl.row_bytes,
10557                    )?
10558                } else {
10559                    let gate = e.qmatvec_view(
10560                        &dev.gate,
10561                        gate_start..gate_start + gl.len,
10562                        &gv,
10563                        m_e,
10564                        m.gate_exps.in_f,
10565                        m.gate_exps.out_f,
10566                        gl.qtype,
10567                        gl.row_bytes,
10568                    )?;
10569                    let up = e.qmatvec_view(
10570                        &dev.up,
10571                        up_start..up_start + ul.len,
10572                        &gv,
10573                        m_e,
10574                        m.up_exps.in_f,
10575                        m.up_exps.out_f,
10576                        ul.qtype,
10577                        ul.row_bytes,
10578                    )?;
10579                    let mut act = e.uninit(m_e * n_ff_exp)?;
10580                    Self::ffn_act_lim(
10581                        e,
10582                        cfg,
10583                        &gate,
10584                        &up,
10585                        m.gate_exps.macro_scale(ex),
10586                        m.up_exps.macro_scale(ex),
10587                        lim_exp,
10588                        &mut act,
10589                        m_e * n_ff_exp,
10590                    )?;
10591                    let actv = act.slice(0..m_e * n_ff_exp);
10592                    e.qmatvec_view(
10593                        &dev.down,
10594                        down_start..down_start + dl.len,
10595                        &actv,
10596                        m_e,
10597                        m.down_exps.in_f,
10598                        m.down_exps.out_f,
10599                        dl.qtype,
10600                        dl.row_bytes,
10601                    )?
10602                }
10603            } else if use_cache {
10604                use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10605                if grouped_q8 {
10606                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10607                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10608                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10609                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10610                        eng.qmatvec_expert_q8(
10611                            cache.buf(slot),
10612                            0..gl.len,
10613                            &zq,
10614                            &zd,
10615                            m_e,
10616                            m.gate_exps.in_f,
10617                            m.gate_exps.out_f,
10618                            gl.qtype,
10619                            gl.row_bytes,
10620                        )
10621                    })?;
10622                    let up = e.with_moe_cache(max_block, |cache, eng| {
10623                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10624                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10625                        eng.qmatvec_expert_q8(
10626                            cache.buf(slot),
10627                            0..ul.len,
10628                            &zq,
10629                            &zd,
10630                            m_e,
10631                            m.up_exps.in_f,
10632                            m.up_exps.out_f,
10633                            ul.qtype,
10634                            ul.row_bytes,
10635                        )
10636                    })?;
10637                    let mut act = e.uninit(m_e * n_ff_exp)?;
10638                    Self::ffn_act_lim(
10639                        e,
10640                        cfg,
10641                        &gate,
10642                        &up,
10643                        m.gate_exps.macro_scale(ex),
10644                        m.up_exps.macro_scale(ex),
10645                        lim_exp,
10646                        &mut act,
10647                        m_e * n_ff_exp,
10648                    )?;
10649                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10650                    e.with_moe_cache(max_block, |cache, eng| {
10651                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10652                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10653                        eng.qmatvec_expert_q8(
10654                            cache.buf(slot),
10655                            0..dl.len,
10656                            &aq2,
10657                            &ad2,
10658                            m_e,
10659                            m.down_exps.in_f,
10660                            m.down_exps.out_f,
10661                            dl.qtype,
10662                            dl.row_bytes,
10663                        )
10664                    })?
10665                } else {
10666                    let gate = e.with_moe_cache(max_block, |cache, eng| {
10667                        let id = BlockId::new(il, PROJ_GATE, ex as u16);
10668                        let slot = cache.dispatch_source(id, m.gate_exps.expert_source(ex), eng)?;
10669                        eng.qmatvec_view(
10670                            cache.buf(slot),
10671                            0..gl.len,
10672                            &gv,
10673                            m_e,
10674                            m.gate_exps.in_f,
10675                            m.gate_exps.out_f,
10676                            gl.qtype,
10677                            gl.row_bytes,
10678                        )
10679                    })?;
10680                    let up = e.with_moe_cache(max_block, |cache, eng| {
10681                        let id = BlockId::new(il, PROJ_UP, ex as u16);
10682                        let slot = cache.dispatch_source(id, m.up_exps.expert_source(ex), eng)?;
10683                        eng.qmatvec_view(
10684                            cache.buf(slot),
10685                            0..ul.len,
10686                            &gv,
10687                            m_e,
10688                            m.up_exps.in_f,
10689                            m.up_exps.out_f,
10690                            ul.qtype,
10691                            ul.row_bytes,
10692                        )
10693                    })?;
10694                    let mut act = e.uninit(m_e * n_ff_exp)?;
10695                    Self::ffn_act_lim(
10696                        e,
10697                        cfg,
10698                        &gate,
10699                        &up,
10700                        m.gate_exps.macro_scale(ex),
10701                        m.up_exps.macro_scale(ex),
10702                        lim_exp,
10703                        &mut act,
10704                        m_e * n_ff_exp,
10705                    )?;
10706                    let actv = act.slice(0..m_e * n_ff_exp);
10707                    e.with_moe_cache(max_block, |cache, eng| {
10708                        let id = BlockId::new(il, PROJ_DOWN, ex as u16);
10709                        let slot = cache.dispatch_source(id, m.down_exps.expert_source(ex), eng)?;
10710                        eng.qmatvec_view(
10711                            cache.buf(slot),
10712                            0..dl.len,
10713                            &actv,
10714                            m_e,
10715                            m.down_exps.in_f,
10716                            m.down_exps.out_f,
10717                            dl.qtype,
10718                            dl.row_bytes,
10719                        )
10720                    })?
10721                }
10722            } else {
10723                let sg = scratch_g.as_mut().unwrap();
10724                let su = scratch_u.as_mut().unwrap();
10725                let sd = scratch_d.as_mut().unwrap();
10726                e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
10727                e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
10728                e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
10729                if grouped_q8 {
10730                    let (zq, zd) = e.quantize_q8_1(&gathered, m_e, n_embd)?;
10731                    let gate = e.qmatvec_expert_q8(
10732                        sg,
10733                        0..gl.len,
10734                        &zq,
10735                        &zd,
10736                        m_e,
10737                        m.gate_exps.in_f,
10738                        m.gate_exps.out_f,
10739                        gl.qtype,
10740                        gl.row_bytes,
10741                    )?;
10742                    let up = e.qmatvec_expert_q8(
10743                        su,
10744                        0..ul.len,
10745                        &zq,
10746                        &zd,
10747                        m_e,
10748                        m.up_exps.in_f,
10749                        m.up_exps.out_f,
10750                        ul.qtype,
10751                        ul.row_bytes,
10752                    )?;
10753                    let mut act = e.uninit(m_e * n_ff_exp)?;
10754                    Self::ffn_act_lim(
10755                        e,
10756                        cfg,
10757                        &gate,
10758                        &up,
10759                        m.gate_exps.macro_scale(ex),
10760                        m.up_exps.macro_scale(ex),
10761                        lim_exp,
10762                        &mut act,
10763                        m_e * n_ff_exp,
10764                    )?;
10765                    let (aq2, ad2) = e.quantize_q8_1(&act, m_e, n_ff_exp)?;
10766                    e.qmatvec_expert_q8(
10767                        sd,
10768                        0..dl.len,
10769                        &aq2,
10770                        &ad2,
10771                        m_e,
10772                        m.down_exps.in_f,
10773                        m.down_exps.out_f,
10774                        dl.qtype,
10775                        dl.row_bytes,
10776                    )?
10777                } else {
10778                    let gate = e.qmatvec_view(
10779                        sg,
10780                        0..gl.len,
10781                        &gv,
10782                        m_e,
10783                        m.gate_exps.in_f,
10784                        m.gate_exps.out_f,
10785                        gl.qtype,
10786                        gl.row_bytes,
10787                    )?;
10788                    let up = e.qmatvec_view(
10789                        su,
10790                        0..ul.len,
10791                        &gv,
10792                        m_e,
10793                        m.up_exps.in_f,
10794                        m.up_exps.out_f,
10795                        ul.qtype,
10796                        ul.row_bytes,
10797                    )?;
10798                    let mut act = e.uninit(m_e * n_ff_exp)?;
10799                    Self::ffn_act_lim(
10800                        e,
10801                        cfg,
10802                        &gate,
10803                        &up,
10804                        m.gate_exps.macro_scale(ex),
10805                        m.up_exps.macro_scale(ex),
10806                        lim_exp,
10807                        &mut act,
10808                        m_e * n_ff_exp,
10809                    )?;
10810                    let actv = act.slice(0..m_e * n_ff_exp);
10811                    e.qmatvec_view(
10812                        sd,
10813                        0..dl.len,
10814                        &actv,
10815                        m_e,
10816                        m.down_exps.in_f,
10817                        m.down_exps.out_f,
10818                        dl.qtype,
10819                        dl.row_bytes,
10820                    )?
10821                }
10822            };
10823
10824            // SCATTER into slot buffer: each row goes to slot_buf[tok, slot, :].
10825            e.scatter_slot(
10826                &y,
10827                &tok_idx_d,
10828                &slot_idx_d,
10829                &weight_d,
10830                &mut slot_buf,
10831                &mut wbuf,
10832                n_embd,
10833                n_used,
10834                m_e,
10835            )?;
10836        }
10837
10838        // 5. REDUCE SLOTS: sum the 8 slots per token into the final moe_out.
10839        let mut moe_out = e.zeros(t * n_embd)?;
10840        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, t)?;
10841
10842        // STATS: print m-distribution when MEMRA_MOE_STATS is set.
10843        if std::env::var("MEMRA_MOE_STATS").is_ok() && !m_dist.is_empty() {
10844            m_dist.sort_unstable();
10845            let active = m_dist.len();
10846            let mean = m_dist.iter().sum::<usize>() as f64 / active as f64;
10847            let median = m_dist[active / 2];
10848            let max_m = *m_dist.last().unwrap();
10849            let min_m = m_dist[0];
10850            let above16 = m_dist.iter().filter(|&&x| x >= 16).count();
10851            println!(
10852                "moe-grouped il={il} t={t} active={active}/{n_expert} \
10853                      m_e: min={min_m} median={median} mean={mean:.1} max={max_m} \
10854                      above_gemm_threshold(>=16)={above16}/{active}"
10855            );
10856        }
10857
10858        Self::moe_ffn_grouped_add_shared(e, m, z, t, cfg, il, &mut moe_out)?;
10859        Ok(moe_out)
10860    }
10861
10862    /// Lane-3 M2: cross-stream MoE for lockstep decode. Routes all m stream rows in one
10863    /// batch, executes fully-HBM-resident experts through the grouped gather/GEMM/scatter
10864    /// machinery at m_e>1 (weight reads amortized across streams), and assigns any expert
10865    /// with a missing projection to that row's CPU companion call (whole-expert granularity,
10866    /// same rule as the sequential frozen path). Slot-pinned accumulation keeps each row's
10867    /// expert-sum order identical to the sequential path.
10868    pub(crate) fn moe_ffn_lockstep(
10869        &self,
10870        e: &Engine,
10871        m: &MoeWeights,
10872        zbatch: &CudaSlice<f32>,
10873        mrows: usize,
10874        il: u16,
10875        max_block: usize,
10876    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10877        use crate::moe_cache::{BlockId, PROJ_DOWN, PROJ_GATE, PROJ_UP};
10878        let cfg = &self.cfg;
10879        let moe = cfg.moe.as_ref().unwrap();
10880        let n_embd = cfg.n_embd as usize;
10881        let n_expert = moe.expert_count as usize;
10882        let n_used = moe.expert_used_count as usize;
10883        let n_ff_exp = moe.expert_ff_length as usize;
10884        // step35 per-layer SwiGLU clamp; None on every other arch / unclamped layer.
10885        let lim_exp = cfg.clamp_exp_at(il as u32);
10886        let lim_shexp = cfg.clamp_shexp_at(il as u32);
10887
10888        let logits = e.matmul(&m.gate_inp, zbatch, mrows)?;
10889        if let Some(sig) = cfg.sigmoid_router() {
10890            Self::trace_sigmoid_router_logits(e, il, mrows, n_expert, n_used, &logits, m, sig)?;
10891        }
10892        let (sel_all, w_all) = if let Some(sig) = cfg.sigmoid_router() {
10893            Self::moe_route_sigmoid_cfg(e, &logits, mrows, n_expert, n_used, m, sig)?
10894        } else {
10895            Self::moe_route_cfg(
10896                e,
10897                &logits,
10898                mrows,
10899                n_expert,
10900                n_used,
10901                m.active_experts.as_deref(),
10902            )?
10903        };
10904        Self::trace_moe_routes(il, mrows, &sel_all, &w_all)?;
10905
10906        // Residency split at whole-expert granularity against the (frozen) cache.
10907        let resident_expert: Vec<bool> = e.with_moe_cache(max_block, |c, _| {
10908            Ok((0..n_expert)
10909                .map(|ex| {
10910                    [PROJ_GATE, PROJ_UP, PROJ_DOWN]
10911                        .into_iter()
10912                        .all(|p| c.resident(BlockId::new(il, p, ex as u16)).is_some())
10913                })
10914                .collect())
10915        })?;
10916
10917        struct Group {
10918            rows: Vec<i32>,
10919            slots: Vec<i32>,
10920            weights: Vec<f32>,
10921        }
10922        let mut groups: std::collections::HashMap<usize, Group> = Default::default();
10923        let mut cpu_rows: Vec<Vec<(usize, f32)>> = vec![Vec::new(); mrows];
10924        let mut cpu_by_expert: std::collections::HashMap<usize, Vec<(usize, f32)>> =
10925            Default::default();
10926        for row in 0..mrows {
10927            for j in 0..n_used {
10928                let ex = sel_all[row * n_used + j] as usize;
10929                let w = w_all[row * n_used + j];
10930                if resident_expert[ex] {
10931                    let group = groups.entry(ex).or_insert_with(|| Group {
10932                        rows: Vec::new(),
10933                        slots: Vec::new(),
10934                        weights: Vec::new(),
10935                    });
10936                    group.rows.push(row as i32);
10937                    group.slots.push(j as i32);
10938                    group.weights.push(w);
10939                } else {
10940                    crate::cpu_experts::record_incomplete_gpu_residency(0);
10941                    cpu_rows[row].push((ex, w));
10942                    cpu_by_expert.entry(ex).or_default().push((row, w));
10943                }
10944            }
10945        }
10946
10947        // CPU tickets first: reads/compute overlap the GPU grouped work below. Experts routed
10948        // by >=2 streams go through the multi-row ABI (weight decode amortized across rows);
10949        // each row's remaining experts stay one ordinary per-row call. Contribution FP-sum
10950        // order per row differs from the sequential single-call chunk — part of the
10951        // documented lockstep numeric class.
10952        let host_rows = e.dtoh(zbatch)?;
10953        let rows_ok = crate::cpu_experts::rows_supported();
10954        enum CpuPart {
10955            Single { row: usize },
10956            Rows { rows: Vec<usize> },
10957        }
10958        let mut tickets: Vec<(CpuPart, crate::cpu_experts::CpuExpertTicket)> = Vec::new();
10959        let mut rows_served: std::collections::HashSet<(usize, usize)> = Default::default();
10960        if rows_ok {
10961            let mut shared: Vec<(usize, Vec<(usize, f32)>)> = cpu_by_expert
10962                .into_iter()
10963                .filter(|(_, rows)| rows.len() >= 2)
10964                .collect();
10965            shared.sort_by_key(|(ex, _)| *ex);
10966            for (ex, mut row_weights) in shared {
10967                row_weights.sort_by_key(|(row, _)| *row);
10968                let inputs: Vec<(&[f32], f32)> = row_weights
10969                    .iter()
10970                    .map(|&(row, w)| (&host_rows[row * n_embd..(row + 1) * n_embd], w))
10971                    .collect();
10972                let job = crate::cpu_experts::prepare_rows_job(m, ex, &inputs)
10973                    .map_err(std::io::Error::other)?;
10974                for &(row, _) in &row_weights {
10975                    rows_served.insert((row, ex));
10976                }
10977                tickets.push((
10978                    CpuPart::Rows {
10979                        rows: row_weights.iter().map(|&(row, _)| row).collect(),
10980                    },
10981                    crate::cpu_experts::submit_rows(job).map_err(std::io::Error::other)?,
10982                ));
10983            }
10984        }
10985        for (row, selected) in cpu_rows.iter().enumerate() {
10986            let leftover: Vec<(usize, f32)> = selected
10987                .iter()
10988                .copied()
10989                .filter(|&(ex, _)| !rows_served.contains(&(row, ex)))
10990                .collect();
10991            if leftover.is_empty() {
10992                continue;
10993            }
10994            let host_row = &host_rows[row * n_embd..(row + 1) * n_embd];
10995            let job = crate::cpu_experts::prepare_job(m, il, &leftover, host_row)
10996                .map_err(std::io::Error::other)?;
10997            tickets.push((
10998                CpuPart::Single { row },
10999                crate::cpu_experts::submit(job).map_err(std::io::Error::other)?,
11000            ));
11001        }
11002
11003        let mut slot_buf = e.zeros(mrows * n_used * n_embd)?;
11004        let mut wbuf = e.zeros(mrows * n_used)?;
11005        let mut order: Vec<usize> = groups.keys().copied().collect();
11006        order.sort_by(|&a, &b| {
11007            groups[&b]
11008                .rows
11009                .len()
11010                .cmp(&groups[&a].rows.len())
11011                .then(a.cmp(&b))
11012        });
11013        for &ex in &order {
11014            let group = &groups[&ex];
11015            let m_e = group.rows.len();
11016            let gl = m.gate_exps.expert_layout(ex);
11017            let ul = m.up_exps.expert_layout(ex);
11018            let dl = m.down_exps.expert_layout(ex);
11019            let row_idx_d = e.htod_i32(&group.rows)?;
11020            let slot_idx_d = e.htod_i32(&group.slots)?;
11021            let dmac = m.down_exps.macro_scale(ex);
11022            let weight_d = if dmac == 1.0 {
11023                e.htod(&group.weights)?
11024            } else {
11025                let scaled: Vec<f32> = group.weights.iter().map(|&w| w * dmac).collect();
11026                e.htod(&scaled)?
11027            };
11028            let mut gathered = e.zeros(m_e * n_embd)?;
11029            e.gather_rows(zbatch, &row_idx_d, &mut gathered, n_embd, m_e)?;
11030            let gv = gathered.slice(0..m_e * n_embd);
11031            let gate = e.with_moe_cache(max_block, |c, eng| {
11032                let slot = c
11033                    .resident(BlockId::new(il, PROJ_GATE, ex as u16))
11034                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11035                eng.qmatvec_view(
11036                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11037                    0..gl.len,
11038                    &gv,
11039                    m_e,
11040                    m.gate_exps.in_f,
11041                    m.gate_exps.out_f,
11042                    gl.qtype,
11043                    gl.row_bytes,
11044                )
11045            })?;
11046            let up = e.with_moe_cache(max_block, |c, eng| {
11047                let slot = c
11048                    .resident(BlockId::new(il, PROJ_UP, ex as u16))
11049                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11050                eng.qmatvec_view(
11051                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11052                    0..ul.len,
11053                    &gv,
11054                    m_e,
11055                    m.up_exps.in_f,
11056                    m.up_exps.out_f,
11057                    ul.qtype,
11058                    ul.row_bytes,
11059                )
11060            })?;
11061            let mut act = e.zeros(m_e * n_ff_exp)?;
11062            Self::ffn_act_lim(
11063                e,
11064                cfg,
11065                &gate,
11066                &up,
11067                m.gate_exps.macro_scale(ex),
11068                m.up_exps.macro_scale(ex),
11069                lim_exp,
11070                &mut act,
11071                m_e * n_ff_exp,
11072            )?;
11073            let actv = act.slice(0..m_e * n_ff_exp);
11074            let y = e.with_moe_cache(max_block, |c, eng| {
11075                let slot = c
11076                    .resident(BlockId::new(il, PROJ_DOWN, ex as u16))
11077                    .ok_or("lockstep resident expert vanished (cache not frozen?)")?;
11078                eng.qmatvec_view(
11079                    c.buf(crate::moe_cache::DispatchSlot::Resident(slot)),
11080                    0..dl.len,
11081                    &actv,
11082                    m_e,
11083                    m.down_exps.in_f,
11084                    m.down_exps.out_f,
11085                    dl.qtype,
11086                    dl.row_bytes,
11087                )
11088            })?;
11089            e.scatter_slot(
11090                &y,
11091                &row_idx_d,
11092                &slot_idx_d,
11093                &weight_d,
11094                &mut slot_buf,
11095                &mut wbuf,
11096                n_embd,
11097                n_used,
11098                m_e,
11099            )?;
11100        }
11101        let mut moe_out = e.zeros(mrows * n_embd)?;
11102        e.reduce_slots(&slot_buf, &wbuf, &mut moe_out, n_embd, n_used, mrows)?;
11103
11104        // CPU contributions join BEFORE the shared expert (the sequential path's placement).
11105        let mut row_sums: Vec<Option<Vec<f32>>> = vec![None; mrows];
11106        for (part, ticket) in tickets {
11107            let cpu_output = ticket.wait().map_err(std::io::Error::other)?;
11108            let mut add_row = |row: usize, chunk: &[f32]| {
11109                let sum = row_sums[row].get_or_insert_with(|| vec![0.0f32; n_embd]);
11110                for (accumulator, value) in sum.iter_mut().zip(chunk) {
11111                    *accumulator += value;
11112                }
11113            };
11114            match part {
11115                CpuPart::Single { row } => add_row(row, &cpu_output),
11116                CpuPart::Rows { rows } => {
11117                    for (slot, row) in rows.into_iter().enumerate() {
11118                        add_row(row, &cpu_output[slot * n_embd..(slot + 1) * n_embd]);
11119                    }
11120                }
11121            }
11122        }
11123        for (row, sum) in row_sums.into_iter().enumerate() {
11124            let Some(sum) = sum else { continue };
11125            let cpu_output = e.htod(&sum)?;
11126            let mut dst = moe_out.slice_mut(row * n_embd..(row + 1) * n_embd);
11127            e.axpy_into(&cpu_output, 1.0, &mut dst, n_embd)?;
11128        }
11129
11130        if let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
11131            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
11132        {
11133            let n_ff_sh = gate_shexp.out_features();
11134            let sg_gate = e.matmul(gate_shexp, zbatch, mrows)?;
11135            let sg_up = e.matmul(up_shexp, zbatch, mrows)?;
11136            let mut sa = e.zeros(mrows * n_ff_sh)?;
11137            Self::ffn_act_lim(
11138                e,
11139                cfg,
11140                &sg_gate,
11141                &sg_up,
11142                1.0,
11143                1.0,
11144                lim_shexp,
11145                &mut sa,
11146                mrows * n_ff_sh,
11147            )?;
11148            let sh = e.matmul(down_shexp, &sa, mrows)?;
11149            // lockstep rows ARE decode tokens: fused sigmoid-dot per row so batched serving
11150            // decode matches the single-sequence decode chain bit-for-bit.
11151            let g = match &m.gate_inp_shexp {
11152                Some(gate_inp_shexp) => {
11153                    e.sigmoid_dot_rows(zbatch, gate_inp_shexp.float_data(), n_embd, mrows)?
11154                }
11155                None => e.htod(&vec![1.0f32; mrows])?,
11156            };
11157            e.add_scaled_rows(&sh, &g, &mut moe_out, n_embd, mrows)?;
11158        }
11159
11160        Ok(moe_out)
11161    }
11162}
11163
11164// ============================ gemma4 (R8 verified wiring) ==================================
11165// Node-for-node vs llama.cpp src/models/gemma4.cpp:180-405 (HANDOVER "R8 VERIFIED WIRING").
11166// v0 bring-up: full attention everywhere (exact for prompts < sliding_window 1024 — R6 masking
11167// later), sdpa_naive (hd 512 has no FA stamp), sequential host-staged MoE (the perf arms grow
11168// gemma variants after the correctness gate).
11169impl HybridModel {
11170    /// Per-layer attention geometry (R5): (head_dim, n_kv, n_head, rope_base, scale, is_swa).
11171    /// Rotary width the gemma-4 config DECLARES for layer `il`, per SWA/global class — the
11172    /// `n_rot` argument of the fused `rms_norm_qkv_rope*` kernels
11173    /// (`Engine::full_width_rope_only`, lane/graph-s-key-exactness-20260819).
11174    ///
11175    /// It exists so the fusion's full-width assumption is checked against the CONFIG instead of
11176    /// against itself: passing `hd` twice would guard nothing. On every gemma-4 artifact both
11177    /// routes set `rope_dims_{global,swa} == key_length_{global,swa}` (GGUF reads
11178    /// `rope.dimension_count(_swa)`, the safetensors route derives them from
11179    /// `global_head_dim`/`head_dim`), which is exactly WHY the fusion is legal here — gemma's
11180    /// "partial" rotary is per-dimension frequency scaling through the `rope_freqs` factors
11181    /// tensor, not an n_rot truncation. An arch that truncates instead (qwen3.5: 64 of 256;
11182    /// step35 full-attn: 64 of 128) must not reach these kernels, and now cannot silently.
11183    pub(crate) fn gemma4_rope_dims(&self, il: usize) -> usize {
11184        let g = self
11185            .cfg
11186            .gemma4
11187            .as_ref()
11188            .expect("gemma4_rope_dims on a non-gemma4 config");
11189        if g.swa_pattern[il] {
11190            g.rope_dims_swa as usize
11191        } else {
11192            g.rope_dims_global as usize
11193        }
11194    }
11195
11196    pub(crate) fn gemma4_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
11197        let g = self.cfg.gemma4.as_ref().unwrap();
11198        let swa = g.swa_pattern[il];
11199        let hd = if swa {
11200            g.key_length_swa
11201        } else {
11202            g.key_length_global
11203        } as usize;
11204        // attention scale = 1.0 (llama gemma4.cpp:11 "Gemma4 uses self.scaling = 1.0" — q/k are
11205        // per-head rms-normed; NOT the 1/sqrt(hd) default. Bring-up bug: 1/sqrt(hd) left token-0
11206        // rows exact (softmax over one element) while every later position drifted).
11207        (
11208            hd,
11209            g.head_count_kv[il] as usize,
11210            self.cfg.n_head as usize,
11211            if swa {
11212                g.rope_base_swa
11213            } else {
11214                g.rope_base_global
11215            },
11216            1.0,
11217            swa,
11218        )
11219    }
11220
11221    /// Suppress-token mask over t logits rows (tokenizer.ggml.suppress_tokens; no-op when the
11222    /// model ships none). NOT monotonic like softcap — must run before every argmax/sample, so
11223    /// every gemma4 logits tail (forward/prime/decode/dc/verify/e4b) calls this on device ld.
11224    pub(crate) fn gemma4_suppress(
11225        &self,
11226        e: &Engine,
11227        ld: &mut CudaSlice<f32>,
11228        t: usize,
11229    ) -> Result<(), Box<dyn std::error::Error>> {
11230        if let Some((ids, n)) = self.gemma4_aux.as_ref().and_then(|a| a.suppress_d.as_ref()) {
11231            // suppress_d is primary-owned by the audited invariant (serving picks the last/head
11232            // stage as primary, and this tail runs only after the last stage). The assert turns
11233            // that argued invariant into a checked one: any topology violating primary==head
11234            // trips here in debug instead of silently peer-reading a device-0 buffer.
11235            #[cfg(debug_assertions)]
11236            crate::debug_assert_tensor_stream_device(
11237                ids,
11238                &e.stream(),
11239                "gemma4_suppress.suppress_d",
11240            );
11241            e.mask_ids_rows(ld, ids, *n, self.output.out_features(), t)?;
11242        }
11243        Ok(())
11244    }
11245
11246    /// gemma4 attention (R5 geometry, R7 weightless V-norm on the RAW K projection, R9 dual rope).
11247    /// `cache`: Some => PRIME mode — append the T post-rope K / normed V rows into the quantized
11248    /// KV cache (same per-row quantize math as the decode append) and advance len. Fresh-prompt
11249    /// only (v0): attends within `tokens` via the f32 sdpa.
11250    #[allow(clippy::too_many_arguments)]
11251    /// ONE-PROGRAM seam (lane/gemma-restore-exactness-20260819, DEFAULT OFF): route dense
11252    /// gemma SWA prefill attention through the WINDOWED arm at every prompt length instead of
11253    /// switching program at `t > sliding_window`. The door is the measured cause of the
11254    /// prefix-restore exactness violation: below it the SWA layers attend unwindowed on f32
11255    /// operands, above it windowed on bf16 operands, so rows[0..932) of a 932-row prime and of
11256    /// a 1048-row prime differ in 59 of 60 layers. `=1` collapses it to one program so the
11257    /// published prefix KV stops depending on the total prompt length. Off by default because
11258    /// collapsing changes the bytes short prompts get today; the flip is an owner call.
11259    fn gemma_fa_one_program() -> bool {
11260        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11261        *ON.get_or_init(|| std::env::var("MEMRA_GEMMA_FA_ONE_PROGRAM").as_deref() == Ok("1"))
11262    }
11263
11264    fn gemma4_attn_prime(
11265        &self,
11266        e: &Engine,
11267        fa: &crate::hybrid::FullAttnLayer,
11268        il: usize,
11269        h: &CudaSlice<f32>,
11270        pos_d: &CudaSlice<i32>,
11271        t: usize,
11272        cache: Option<&mut Cache>,
11273        island: Option<&CudaSlice<i32>>,
11274    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11275        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
11276        let eps = self.cfg.rms_eps;
11277        let aux = self.gemma4_aux.as_ref().unwrap();
11278        let ones = aux.ones(e);
11279        #[cfg(debug_assertions)]
11280        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_attn_prime.ones");
11281
11282        // quantize-once window: q/k/v share `h` — the MMQ D4 activation quantizes once
11283        // (h stays borrowed across the triple, so the cache key can't go stale).
11284        e.mmq_act_begin();
11285        let q0 = e.matmul(&fa.wq, h, t)?; // [t, nh*hd]
11286        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
11287            let v = e.dtoh(&q0)?;
11288            let nan = v.iter().filter(|x| x.is_nan()).count();
11289            let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
11290            eprintln!(
11291                "[g4-prime-trace] L0 q0: nan={nan}/{} amax={amax:.3}",
11292                v.len()
11293            );
11294        }
11295        let k0 = e.matmul(&fa.wk, h, t)?; // [t, nkv*hd]
11296        // globals ship no v_proj (wv := wk at load) — V input is the SAME projection output;
11297        // reuse k0 instead of re-running the identical matmul (K=V dedup, 5 layers).
11298        let v0 = if swa {
11299            e.matmul(&fa.wv, h, t)?
11300        } else {
11301            e.clone_dtod(&k0)?
11302        };
11303        if il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
11304            for (tag, buf) in [("k0", &k0), ("v0", &v0)] {
11305                let v = e.dtoh(buf)?;
11306                let nan = v.iter().filter(|x| x.is_nan()).count();
11307                let amax = v.iter().fold(0f32, |a, x| a.max(x.abs()));
11308                eprintln!(
11309                    "[g4-prime-trace] L0 {tag}: nan={nan}/{} amax={amax:.3}",
11310                    v.len()
11311                );
11312            }
11313        }
11314
11315        let mut q = e.uninit(t * nh * hd)?;
11316        let mut k = e.uninit(t * nkv * hd)?;
11317        // R7: V = weightless rms_norm of the raw projection; NEVER roped.
11318        let mut v = e.uninit(t * nkv * hd)?;
11319        // 31B glue lane: producers emit the bf16 FA operands (norm emits vb; rope emits qb/kb
11320        // post-rope) — kills 3 f32->bf16 converts + re-reads per layer. Bit-identical operands
11321        // (same __float2bfloat16); MEMRA_FA_EMIT=0 reverts to the convert-in-FA path.
11322        static EMIT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11323        // Island primes take the mask-capable naive kernel below; keep the operands f32
11324        // (the bf16 FA emit path has no island consumer). Text-only keeps emit unchanged.
11325        let emit = island.is_none()
11326            && t >= 16
11327            && crate::Engine::qkvnorm_w_on_prefill(nh * t + 2 * nkv * t, hd)
11328            && *EMIT.get_or_init(|| {
11329                std::env::var("MEMRA_FA_EMIT")
11330                    .map(|s| s != "0")
11331                    .unwrap_or(true)
11332            });
11333        let mut qb = e.alloc_uninit::<u8>(if emit { t * nh * hd * 2 } else { 1 })?;
11334        let mut kb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
11335        let mut vb = e.alloc_uninit::<u8>(if emit { t * nkv * hd * 2 } else { 1 })?;
11336        // f16-P/V door: emit V as f16 straight from the norm when this layer's FA consumer
11337        // reads f16 — kills the per-layer bf16->f16 re-encode (1.4GB/pass on 31B SWA).
11338        let v_f16 = emit
11339            && crate::fa_f16pv_on()
11340            && match hd {
11341                512 => true,
11342                256 => swa && crate::faw_hp_on() && nh % 2 == 0 && (nh / nkv) % 2 == 0,
11343                _ => false,
11344            };
11345        if emit {
11346            e.rms_norm_qkv_w4b(
11347                &q0,
11348                &k0,
11349                &v0,
11350                fa.q_norm.float_data(),
11351                fa.k_norm.float_data(),
11352                ones,
11353                &mut q,
11354                &mut k,
11355                &mut v,
11356                &mut vb,
11357                hd,
11358                nh * t,
11359                nkv * t,
11360                eps,
11361                v_f16,
11362            )?;
11363        } else {
11364            e.rms_norm_qkv(
11365                &q0,
11366                &k0,
11367                &v0,
11368                fa.q_norm.float_data(),
11369                fa.k_norm.float_data(),
11370                ones,
11371                &mut q,
11372                &mut k,
11373                &mut v,
11374                hd,
11375                nh * t,
11376                nkv * t,
11377                eps,
11378            )?;
11379        }
11380
11381        let ff = if swa {
11382            None
11383        } else {
11384            Some(
11385                aux.rope_freqs(e)
11386                    .expect("gemma4 global rope needs rope_freqs.weight"),
11387            )
11388        };
11389        #[cfg(debug_assertions)]
11390        if let Some(ff) = ff {
11391            crate::debug_assert_tensor_stream_device(
11392                ff,
11393                &e.stream(),
11394                "gemma4_attn_prime.rope_freqs",
11395            );
11396        }
11397        if emit {
11398            e.rope_neox2_bf16e(
11399                &mut q, &mut k, &mut qb, &mut kb, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff,
11400            )?;
11401        } else {
11402            e.rope_neox2(&mut q, &mut k, pos_d, hd, hd, nh, nkv, t, base, 1.0, ff)?;
11403        }
11404
11405        if let Some(cache) = cache {
11406            let kvl = cache.kv[il].as_mut().unwrap();
11407            assert_eq!(kvl.len, 0, "gemma4 prime is fresh-prompt only (v0)");
11408            e.append_kv_quantized_rows(
11409                &k,
11410                &v,
11411                &mut kvl.k,
11412                &mut kvl.v,
11413                kvl.len,
11414                t,
11415                kvl.kv_dim_k,
11416                kvl.kv_dim_v,
11417                kvl.k_tok_bytes,
11418                kvl.v_tok_bytes,
11419                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
11420            )?;
11421            kvl.len += t;
11422        }
11423        let mut attn = e.zeros(t * nh * hd)?;
11424        // R6: SWA layers mask keys older than sliding_window once the prompt exceeds it
11425        // (windowed naive twin; fa windowed stamps later). Under the window, full attention
11426        // is exact — SWA rides fa_prefill (hd-256 stamp), the hd-512 globals stay naive.
11427        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
11428        if let Some(span) = island {
11429            // Masked-prefill arm: every layer routes through the island-aware naive
11430            // kernel (correctness-first, same posture as the vision tower v1). The
11431            // window argument keeps the R6 shortcut: 0 while the prompt fits the
11432            // window, the real window beyond it.
11433            let w = if swa && t > win { win } else { 0 };
11434            e.sdpa_naive_island(&q, &k, &v, &mut attn, span, hd, nh, nkv, t, t, scale, w)?;
11435        } else if swa && (t > win || Self::gemma_fa_one_program()) {
11436            if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11437                if emit {
11438                    e.fa_prefill_w_pre(
11439                        &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, win, v_f16,
11440                    )?;
11441                } else {
11442                    e.fa_prefill_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11443                }
11444            } else {
11445                e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
11446            }
11447        } else if hd == 256 && std::env::var("MEMRA_NOFA").is_err() {
11448            e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11449        } else if hd == 512 && std::env::var("MEMRA_NOFA").is_err() {
11450            if emit {
11451                e.fa_prefill_hd512_pre(
11452                    &qb, &kb, &vb, &mut attn, hd, nh, nkv, t, t, scale, true, v_f16,
11453                )?;
11454            } else {
11455                e.fa_prefill_hd512(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11456            }
11457        } else {
11458            e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
11459        }
11460        Ok(e.matmul(&fa.wo, &attn, t)?)
11461    }
11462
11463    /// Back-compat wrapper (pure prefill, no cache).
11464    fn gemma4_attn(
11465        &self,
11466        e: &Engine,
11467        fa: &crate::hybrid::FullAttnLayer,
11468        il: usize,
11469        h: &CudaSlice<f32>,
11470        pos_d: &CudaSlice<i32>,
11471        t: usize,
11472    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11473        self.gemma4_attn_prime(e, fa, il, h, pos_d, t, None, None)
11474    }
11475
11476    /// gemma4 MoE with the expert input PRE-QUANTIZED (the q8z tail fusion). Caller guarantees
11477    /// the fast-arm conditions (resident dev slabs + dp4a qtypes) — decode t=1 and verify rows
11478    /// arms only, per-token kernel chains identical to the f32-input path (same quantize bytes:
11479    /// the q8z epilogue is quantize_q8_1 verbatim).
11480    fn gemma4_moe_q8(
11481        &self,
11482        e: &Engine,
11483        m: &crate::hybrid::MoeWeights,
11484        bits: &crate::hybrid::Gemma4MoeBits,
11485        mq: &(CudaSlice<i8>, CudaSlice<f32>),
11486        router_in: &CudaSlice<f32>,
11487        t: usize,
11488    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11489        let cfg = &self.cfg;
11490        let moe = cfg.moe.as_ref().unwrap();
11491        let n_embd = cfg.n_embd as usize;
11492        let n_expert = moe.expert_count as usize;
11493        let n_used = moe.expert_used_count as usize;
11494        let n_ff_exp = moe.expert_ff_length as usize;
11495        // router stays the two-launch pair: THREE fuse variants measured worse (serial-dot
11496        // -50%, 1024-thread warp-parallel -12% on topk sync overhead — jsonl 2026-07-11);
11497        // the pair's 12us is kernel time, not launch gaps.
11498        let logits = if crate::router_kernel_on() {
11499            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11500        } else {
11501            e.matmul(&m.gate_inp, router_in, t)?
11502        };
11503        let dev = m.dev_exps.as_ref().unwrap();
11504        let (sel_d, w_d) =
11505            e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11506        let (zq, zd) = mq;
11507        if t == 1 {
11508            let selv = sel_d.slice(0..n_used);
11509            let wv = w_d.slice(0..n_used);
11510            let act = e.moe_gate_up_gelu8_dev_q8(
11511                &dev.ptr_row,
11512                &selv,
11513                zq,
11514                zd,
11515                n_embd,
11516                n_ff_exp,
11517                n_used,
11518                n_expert,
11519                m.gate_exps.qtype,
11520                m.up_exps.qtype,
11521                m.gate_exps.row_bytes,
11522                m.up_exps.row_bytes,
11523            )?;
11524            let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11525            let mut moe_out = e.uninit(n_embd)?;
11526            e.moe_down8_fma_dev_q8(
11527                &dev.ptr_row,
11528                &selv,
11529                &wv,
11530                &aq2,
11531                &ad2,
11532                &mut moe_out.slice_mut(0..n_embd),
11533                n_ff_exp,
11534                n_embd,
11535                n_used,
11536                n_expert,
11537                m.down_exps.qtype,
11538                m.down_exps.row_bytes,
11539            )?;
11540            return Ok(moe_out);
11541        }
11542        let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11543        let act = if csr {
11544            e.moe_gate_up_gelu8_dev_q8_csr(
11545                &dev.ptr_row,
11546                &sel_d,
11547                zq,
11548                zd,
11549                t * n_used,
11550                n_embd,
11551                n_ff_exp,
11552                n_used,
11553                n_expert,
11554                m.gate_exps.qtype,
11555                m.up_exps.qtype,
11556                m.gate_exps.row_bytes,
11557                m.up_exps.row_bytes,
11558            )?
11559        } else {
11560            e.moe_gate_up_gelu8_dev_q8_rows(
11561                &dev.ptr_row,
11562                &sel_d,
11563                zq,
11564                zd,
11565                t,
11566                n_embd,
11567                n_ff_exp,
11568                n_used,
11569                n_expert,
11570                m.gate_exps.qtype,
11571                m.up_exps.qtype,
11572                m.gate_exps.row_bytes,
11573                m.up_exps.row_bytes,
11574            )?
11575        };
11576        let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11577        let mut moe_out = e.uninit(t * n_embd)?;
11578        // down stays rows_g: the CSR dedup twin measured NEGATIVE at nsb=22 too (189.4 vs
11579        // 207.0 depth spec, bitwise-exact — jsonl 2026-07-10; qwen nsb=16 same verdict).
11580        e.moe_down8_fma_dev_q8_rows_g(
11581            &dev.ptr_row,
11582            &sel_d,
11583            &w_d,
11584            &aq2,
11585            &ad2,
11586            &mut moe_out,
11587            t,
11588            n_ff_exp,
11589            n_embd,
11590            n_used,
11591            n_expert,
11592            m.down_exps.qtype,
11593            m.down_exps.row_bytes,
11594        )?;
11595        Ok(moe_out)
11596    }
11597
11598    /// gemma4 MoE (R2 router prologue input supplied by caller, R3 per-expert output scale).
11599    /// Sequential host-staged v0 — softmax gating + renorm (moe_route, the qwen recipe), GELU
11600    /// experts, scale folded into the accumulate weight (post-matmul linear scale, exact fold).
11601    fn gemma4_moe(
11602        &self,
11603        e: &Engine,
11604        m: &crate::hybrid::MoeWeights,
11605        bits: &crate::hybrid::Gemma4MoeBits,
11606        moe_in: &CudaSlice<f32>,
11607        router_in: &CudaSlice<f32>,
11608        t: usize,
11609    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11610        let cfg = &self.cfg;
11611        let moe = cfg.moe.as_ref().unwrap();
11612        let n_embd = cfg.n_embd as usize;
11613        let n_expert = moe.expert_count as usize;
11614        let n_used = moe.expert_used_count as usize;
11615        let n_ff_exp = moe.expert_ff_length as usize;
11616
11617        // Router: the in-house GEMV for ALL small t (decode AND verify ride the same per-column
11618        // kernel — the cuBLASLt n-dependence flipped top-k at verify t on the 27B, d994271);
11619        // batched matmul only at real prefill.
11620        let logits = if t < PRIME_MIN_T && crate::router_kernel_on() {
11621            e.router_gemv(m.gate_inp.float_data(), router_in, n_embd, n_expert, t)?
11622        } else {
11623            e.matmul(&m.gate_inp, router_in, t)?
11624        };
11625
11626        // FAST SMALL-T ARM (decode t=1 AND spec verify t=2..15): device softmax-topk router,
11627        // then PER TOKEN the same fused gate_up GELU + down8 FMA launch pair over the resident
11628        // dev slabs — verify rides the EXACT decode kernel chain per token (dispatch-parity
11629        // law; the qwen "verify must be kernel-dispatch-identical to decode" lesson).
11630        if t < PRIME_MIN_T
11631            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11632            && expert_dp4a_supported(m.gate_exps.qtype)
11633            && expert_dp4a_supported(m.up_exps.qtype)
11634            && expert_dp4a_supported(m.down_exps.qtype)
11635            && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
11636        {
11637            let dev = m.dev_exps.as_ref().unwrap();
11638            let (sel_d, w_d) =
11639                e.moe_router_topk_scaled(&logits, t, n_expert, n_used, &bits.per_expert_scale_d)?;
11640            if t == 1 {
11641                let (zq, zd) = e.quantize_q8_1(moe_in, 1, n_embd)?;
11642                let selv = sel_d.slice(0..n_used);
11643                let wv = w_d.slice(0..n_used);
11644                let act = e.moe_gate_up_gelu8_dev_q8(
11645                    &dev.ptr_row,
11646                    &selv,
11647                    &zq,
11648                    &zd,
11649                    n_embd,
11650                    n_ff_exp,
11651                    n_used,
11652                    n_expert,
11653                    m.gate_exps.qtype,
11654                    m.up_exps.qtype,
11655                    m.gate_exps.row_bytes,
11656                    m.up_exps.row_bytes,
11657                )?;
11658                let (aq2, ad2) = e.quantize_q8_1(&act, n_used, n_ff_exp)?;
11659                let mut moe_out = e.uninit(n_embd)?;
11660                e.moe_down8_fma_dev_q8(
11661                    &dev.ptr_row,
11662                    &selv,
11663                    &wv,
11664                    &aq2,
11665                    &ad2,
11666                    &mut moe_out.slice_mut(0..n_embd),
11667                    n_ff_exp,
11668                    n_embd,
11669                    n_used,
11670                    n_expert,
11671                    m.down_exps.qtype,
11672                    m.down_exps.row_bytes,
11673                )?;
11674                return Ok(moe_out);
11675            }
11676            // VERIFY ROWS TWINS (t=2..15): ONE launch pair for all tokens; per (token,row,slot)
11677            // bodies are the t=1 kernels VERBATIM (bit-identical to the per-token loop).
11678            // gate_up rides the CSR owner-scan dedup when t <= 10 (each duplicated expert's
11679            // weight stream decoded once; the qwen CSR class). MEMRA_GEMMA_CSR=0 -> rows.
11680            let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11681            let csr = t <= 10 && std::env::var("MEMRA_GEMMA_CSR").as_deref() != Ok("0");
11682            let act = if csr {
11683                e.moe_gate_up_gelu8_dev_q8_csr(
11684                    &dev.ptr_row,
11685                    &sel_d,
11686                    &zq,
11687                    &zd,
11688                    t * n_used,
11689                    n_embd,
11690                    n_ff_exp,
11691                    n_used,
11692                    n_expert,
11693                    m.gate_exps.qtype,
11694                    m.up_exps.qtype,
11695                    m.gate_exps.row_bytes,
11696                    m.up_exps.row_bytes,
11697                )?
11698            } else {
11699                e.moe_gate_up_gelu8_dev_q8_rows(
11700                    &dev.ptr_row,
11701                    &sel_d,
11702                    &zq,
11703                    &zd,
11704                    t,
11705                    n_embd,
11706                    n_ff_exp,
11707                    n_used,
11708                    n_expert,
11709                    m.gate_exps.qtype,
11710                    m.up_exps.qtype,
11711                    m.gate_exps.row_bytes,
11712                    m.up_exps.row_bytes,
11713                )?
11714            };
11715            let (aq2, ad2) = e.quantize_q8_1(&act, t * n_used, n_ff_exp)?;
11716            let mut moe_out = e.uninit(t * n_embd)?;
11717            e.moe_down8_fma_dev_q8_rows_g(
11718                &dev.ptr_row,
11719                &sel_d,
11720                &w_d,
11721                &aq2,
11722                &ad2,
11723                &mut moe_out,
11724                t,
11725                n_ff_exp,
11726                n_embd,
11727                n_used,
11728                n_expert,
11729                m.down_exps.qtype,
11730                m.down_exps.row_bytes,
11731            )?;
11732            return Ok(moe_out);
11733        }
11734
11735        let (sel_all, mut w_all) = Self::moe_route(e, &logits, t, n_expert, n_used)?;
11736        for (i, &sx) in sel_all.iter().enumerate() {
11737            w_all[i] *= bits.per_expert_scale[sx as usize];
11738        }
11739
11740        // PREFILL PAIRS ARM (t >= 16): expert-major CSR over the resident slabs — ONE launch per
11741        // projection covers ALL (token,expert) pairs (the qwen pairs recipe, _em dot = expert_dot_g
11742        // which has the Q4_0 body; GELU pairs epilogue; R3 scale folded into pair_w).
11743        if t >= PRIME_MIN_T
11744            && m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
11745            && expert_dp4a_supported(m.gate_exps.qtype)
11746            && expert_dp4a_supported(m.up_exps.qtype)
11747            && expert_dp4a_supported(m.down_exps.qtype)
11748            && std::env::var("MEMRA_GEMMA_MOE_PAIRS").as_deref() != Ok("0")
11749        {
11750            let dev = m.dev_exps.as_ref().unwrap();
11751            let n_pairs = t * n_used;
11752            let pair_ex: Vec<i32> = sel_all.iter().map(|&x| x as i32).collect();
11753            let pair_tok: Vec<i32> = (0..n_pairs).map(|p| (p / n_used) as i32).collect();
11754            let tok_off: Vec<i32> = (0..=t).map(|tok| (tok * n_used) as i32).collect();
11755            let tok_ids: Vec<i32> = (0..n_pairs as i32).collect();
11756            let pt = e.htod_i32(&pair_tok)?;
11757            let pw = e.htod(&w_all)?;
11758            let toff = e.htod_i32(&tok_off)?;
11759            let tids = e.htod_i32(&tok_ids)?;
11760            let mut by_ex: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11761            for p in 0..n_pairs {
11762                by_ex[pair_ex[p] as usize].push(p as i32);
11763            }
11764            let mut ex_ids: Vec<i32> = Vec::new();
11765            let mut ex_off: Vec<i32> = vec![0];
11766            let mut ex_pairs: Vec<i32> = Vec::with_capacity(n_pairs);
11767            for (ex, list) in by_ex.iter().enumerate() {
11768                if list.is_empty() {
11769                    continue;
11770                }
11771                ex_ids.push(ex as i32);
11772                ex_pairs.extend_from_slice(list);
11773                ex_off.push(ex_pairs.len() as i32);
11774            }
11775            let n_active = ex_ids.len();
11776            let exi = e.htod_i32(&ex_ids)?;
11777            let exo = e.htod_i32(&ex_off)?;
11778            let exp_d = e.htod_i32(&ex_pairs)?;
11779            // GROUPED f16 LANE (MEMRA_MOE_F16G=1, round 46 arc 2): dequant active experts to
11780            // f16 once per projection + one grouped f16 GEMM over the CSR groups; CSR order
11781            // end-to-end (gelu is elementwise), one row permute before the scatter. The
11782            // ragged down k (704) needs no padding here — cublas takes any k.
11783            // f16-mirror numeric class, argmax/spec gated. PER-MODEL default OFF (round 50):
11784            // the gelu class regressed g26 board-2048 prefill -8.3% under the round-49
11785            // Hopper default — see moe_f16g_gemma_on.
11786            if crate::moe_f16g_gemma_on()
11787                && f16g_proj_ok(m.gate_exps.qtype, n_embd)
11788                && f16g_proj_ok(m.up_exps.qtype, n_embd)
11789                && f16g_proj_ok(m.down_exps.qtype, n_ff_exp)
11790            {
11791                let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11792                let csr_tok_d = e.htod_i32(&csr_tok)?;
11793                let (z_f16, z_s) = e.moe_f16g_act(moe_in, Some(&csr_tok_d), n_embd, n_pairs)?;
11794                let g_csr = e.moe_f16_grouped(
11795                    &dev.ptr_row,
11796                    0,
11797                    n_expert,
11798                    &exi,
11799                    &ex_off,
11800                    &exo,
11801                    &z_f16,
11802                    &z_s,
11803                    n_embd,
11804                    n_ff_exp,
11805                    n_active,
11806                    n_pairs,
11807                    m.gate_exps.qtype,
11808                    m.gate_exps.row_bytes,
11809                )?;
11810                let u_csr = e.moe_f16_grouped(
11811                    &dev.ptr_row,
11812                    1,
11813                    n_expert,
11814                    &exi,
11815                    &ex_off,
11816                    &exo,
11817                    &z_f16,
11818                    &z_s,
11819                    n_embd,
11820                    n_ff_exp,
11821                    n_active,
11822                    n_pairs,
11823                    m.up_exps.qtype,
11824                    m.up_exps.row_bytes,
11825                )?;
11826                let act_csr = e.moe_pairs_gelu_mul(&g_csr, &u_csr, n_pairs * n_ff_exp)?;
11827                let (a_f16, a_s) = e.moe_f16g_act(&act_csr, None, n_ff_exp, n_pairs)?;
11828                let d_csr = e.moe_f16_grouped(
11829                    &dev.ptr_row,
11830                    2,
11831                    n_expert,
11832                    &exi,
11833                    &ex_off,
11834                    &exo,
11835                    &a_f16,
11836                    &a_s,
11837                    n_ff_exp,
11838                    n_embd,
11839                    n_active,
11840                    n_pairs,
11841                    m.down_exps.qtype,
11842                    m.down_exps.row_bytes,
11843                )?;
11844                let y_down = e.rows_permute(&d_csr, &exp_d, n_pairs, n_embd)?;
11845                let mut moe_out = e.uninit(t * n_embd)?;
11846                e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
11847                if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
11848                    let scan = |v: &[f32]| v.iter().filter(|x| !x.is_finite()).count();
11849                    let (yd, mo) = (e.dtoh(&y_down)?, e.dtoh(&moe_out)?);
11850                    eprintln!(
11851                        "[f16g-debug] post-permute bad={} post-scatter bad={}",
11852                        scan(&yd),
11853                        scan(&mo)
11854                    );
11855                }
11856                return Ok(moe_out);
11857            }
11858            // gate/up: int8-MMA expert GEMM (in_f = n_embd 2816 = 11x256 tiles ok); down keeps
11859            // the decode-once dp4a (in_f 704 fails the 256-superblock tiling).
11860            let mma =
11861                n_embd % 256 == 0 && std::env::var("MEMRA_GEMMA_MOE_MMA").as_deref() != Ok("0");
11862            let (gate, up) = if mma {
11863                let z_scr = e.mmq_iq_quantize_act(moe_in, n_embd, t)?;
11864                (
11865                    e.mmq_iq_experts(
11866                        &dev.ptr_row,
11867                        0,
11868                        n_expert,
11869                        &exi,
11870                        &exo,
11871                        &exp_d,
11872                        &pt,
11873                        &z_scr,
11874                        n_embd,
11875                        n_ff_exp,
11876                        n_active,
11877                        n_pairs,
11878                        t,
11879                        m.gate_exps.qtype,
11880                        m.gate_exps.row_bytes,
11881                    )?,
11882                    e.mmq_iq_experts(
11883                        &dev.ptr_row,
11884                        1,
11885                        n_expert,
11886                        &exi,
11887                        &exo,
11888                        &exp_d,
11889                        &pt,
11890                        &z_scr,
11891                        n_embd,
11892                        n_ff_exp,
11893                        n_active,
11894                        n_pairs,
11895                        t,
11896                        m.up_exps.qtype,
11897                        m.up_exps.row_bytes,
11898                    )?,
11899                )
11900            } else {
11901                let (zq, zd) = e.quantize_q8_1(moe_in, t, n_embd)?;
11902                (
11903                    e.moe_pairs_matvec_q8_dec(
11904                        &dev.ptr_row,
11905                        0,
11906                        &exi,
11907                        &exo,
11908                        &exp_d,
11909                        &pt,
11910                        &zq,
11911                        &zd,
11912                        n_embd,
11913                        n_ff_exp,
11914                        n_expert,
11915                        n_active,
11916                        n_pairs,
11917                        m.gate_exps.qtype,
11918                        m.gate_exps.row_bytes,
11919                    )?,
11920                    e.moe_pairs_matvec_q8_dec(
11921                        &dev.ptr_row,
11922                        1,
11923                        &exi,
11924                        &exo,
11925                        &exp_d,
11926                        &pt,
11927                        &zq,
11928                        &zd,
11929                        n_embd,
11930                        n_ff_exp,
11931                        n_expert,
11932                        n_active,
11933                        n_pairs,
11934                        m.up_exps.qtype,
11935                        m.up_exps.row_bytes,
11936                    )?,
11937                )
11938            };
11939            let pair_self: Vec<i32> = (0..n_pairs as i32).collect();
11940            let pself = e.htod_i32(&pair_self)?;
11941            // DOWN through the int8-MMA expert GEMM (2026-07-31, g26 prefill lever): the
11942            // ragged k (n_ff_exp=704 on the 26B) rides a PADDED k-walk — in_f rounds up
11943            // to the 256-val superblock (768) while the act quantizer's zero padding
11944            // makes every padded-k product exactly zero (weight overread bytes multiply
11945            // zero int8 act values; the dev slab carries 144B tail slack for the OOB).
11946            // The old dp4a matvec was 11.3ms/call at m=T (the 0.07x prefill wall).
11947            // MEMRA_GEMMA_MOE_MMA=0 reverts down together with gate/up.
11948            // FUSED ACT-EPILOGUE (default on): gelu_tanh(gate)*up + D4 quantize in one
11949            // launch — no f32 act buffer (the fused kernel zero-pads the ragged tail
11950            // exactly like the two-pass quantizer). MEMRA_MOE_FUSE_ACTQ=0 rollback.
11951            // Scratch bytes are BYTE-IDENTICAL (kernel-check gated).
11952            let y_down = if mma {
11953                let in_pad = n_ff_exp.div_ceil(256) * 256;
11954                let a_scr = if crate::moe_fuse_actq_on() {
11955                    e.mmq_iq_fused_act_quant(&gate, &up, n_ff_exp, n_pairs, 1)?
11956                } else {
11957                    let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11958                    e.mmq_iq_quantize_act(&act, n_ff_exp, n_pairs)?
11959                };
11960                e.mmq_iq_experts(
11961                    &dev.ptr_row,
11962                    2,
11963                    n_expert,
11964                    &exi,
11965                    &exo,
11966                    &exp_d,
11967                    &pself,
11968                    &a_scr,
11969                    in_pad,
11970                    n_embd,
11971                    n_active,
11972                    n_pairs,
11973                    n_pairs,
11974                    m.down_exps.qtype,
11975                    m.down_exps.row_bytes,
11976                )?
11977            } else {
11978                let act = e.moe_pairs_gelu_mul(&gate, &up, n_pairs * n_ff_exp)?;
11979                let (aq2, ad2) = e.quantize_q8_1(&act, n_pairs, n_ff_exp)?;
11980                e.moe_pairs_matvec_q8_dec(
11981                    &dev.ptr_row,
11982                    2,
11983                    &exi,
11984                    &exo,
11985                    &exp_d,
11986                    &pself,
11987                    &aq2,
11988                    &ad2,
11989                    n_ff_exp,
11990                    n_embd,
11991                    n_expert,
11992                    n_active,
11993                    n_pairs,
11994                    m.down_exps.qtype,
11995                    m.down_exps.row_bytes,
11996                )?
11997            };
11998            let mut moe_out = e.uninit(t * n_embd)?;
11999            e.moe_pairs_scatter(&y_down, &pw, &toff, &tids, &mut moe_out, t, n_embd)?;
12000            return Ok(moe_out);
12001        }
12002
12003        let g_len = m.gate_exps.expert_stride;
12004        let u_len = m.up_exps.expert_stride;
12005        let d_len = m.down_exps.expert_stride;
12006        // Resident dev slabs (fits-VRAM regime): read each expert straight from the device slab
12007        // at ex*stride — zero H2D, SAME qmatvec_view kernel/bytes as the staged path. Staging is
12008        // the spill fallback.
12009        let dev = m.dev_exps.as_ref().filter(|d| !d.gu_il);
12010        let (mut sg, mut su, mut sd) = if dev.is_some() {
12011            (None, None, None)
12012        } else {
12013            (
12014                Some(e.alloc_u8_uninit(g_len)?),
12015                Some(e.alloc_u8_uninit(u_len)?),
12016                Some(e.alloc_u8_uninit(d_len)?),
12017            )
12018        };
12019        let mut moe_out = e.zeros(t * n_embd)?;
12020        for tok in 0..t {
12021            let sel = &sel_all[tok * n_used..(tok + 1) * n_used];
12022            let w = &w_all[tok * n_used..(tok + 1) * n_used];
12023            let zt = moe_in.slice(tok * n_embd..(tok + 1) * n_embd);
12024            for (j, &ex) in sel.iter().enumerate() {
12025                let ex = ex as usize;
12026                let gate = match dev {
12027                    Some(d) => e.qmatvec_view(
12028                        &d.gate,
12029                        ex * g_len..(ex + 1) * g_len,
12030                        &zt,
12031                        1,
12032                        m.gate_exps.in_f,
12033                        m.gate_exps.out_f,
12034                        m.gate_exps.qtype,
12035                        m.gate_exps.row_bytes,
12036                    )?,
12037                    None => {
12038                        let sg = sg.as_mut().unwrap();
12039                        e.stage_expert(m.gate_exps.expert_bytes(ex), sg, 0)?;
12040                        e.qmatvec_view(
12041                            sg,
12042                            0..g_len,
12043                            &zt,
12044                            1,
12045                            m.gate_exps.in_f,
12046                            m.gate_exps.out_f,
12047                            m.gate_exps.qtype,
12048                            m.gate_exps.row_bytes,
12049                        )?
12050                    }
12051                };
12052                let up = match dev {
12053                    Some(d) => e.qmatvec_view(
12054                        &d.up,
12055                        ex * u_len..(ex + 1) * u_len,
12056                        &zt,
12057                        1,
12058                        m.up_exps.in_f,
12059                        m.up_exps.out_f,
12060                        m.up_exps.qtype,
12061                        m.up_exps.row_bytes,
12062                    )?,
12063                    None => {
12064                        let su = su.as_mut().unwrap();
12065                        e.stage_expert(m.up_exps.expert_bytes(ex), su, 0)?;
12066                        e.qmatvec_view(
12067                            su,
12068                            0..u_len,
12069                            &zt,
12070                            1,
12071                            m.up_exps.in_f,
12072                            m.up_exps.out_f,
12073                            m.up_exps.qtype,
12074                            m.up_exps.row_bytes,
12075                        )?
12076                    }
12077                };
12078                let mut act = e.uninit(n_ff_exp)?;
12079                e.gelu_tanh_mul(&gate, &up, &mut act, n_ff_exp)?;
12080                let actv = act.slice(0..n_ff_exp);
12081                let y = match dev {
12082                    Some(d) => e.qmatvec_view(
12083                        &d.down,
12084                        ex * d_len..(ex + 1) * d_len,
12085                        &actv,
12086                        1,
12087                        m.down_exps.in_f,
12088                        m.down_exps.out_f,
12089                        m.down_exps.qtype,
12090                        m.down_exps.row_bytes,
12091                    )?,
12092                    None => {
12093                        let sd = sd.as_mut().unwrap();
12094                        e.stage_expert(m.down_exps.expert_bytes(ex), sd, 0)?;
12095                        e.qmatvec_view(
12096                            sd,
12097                            0..d_len,
12098                            &actv,
12099                            1,
12100                            m.down_exps.in_f,
12101                            m.down_exps.out_f,
12102                            m.down_exps.qtype,
12103                            m.down_exps.row_bytes,
12104                        )?
12105                    }
12106                };
12107                let mut dst = moe_out.slice_mut(tok * n_embd..(tok + 1) * n_embd);
12108                e.axpy_into(&y, w[j], &mut dst, n_embd)?;
12109            }
12110        }
12111        Ok(moe_out)
12112    }
12113
12114    /// One gemma4 trunk layer (R8): x -> x_next.
12115    fn gemma4_layer(
12116        &self,
12117        e: &Engine,
12118        il: usize,
12119        layer: &crate::hybrid::HybridLayer,
12120        x: &CudaSlice<f32>,
12121        pos_d: &CudaSlice<i32>,
12122        t: usize,
12123    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12124        let n_embd = self.cfg.n_embd as usize;
12125        let eps = self.cfg.rms_eps;
12126
12127        let mut h = e.zeros(t * n_embd)?;
12128        e.rms_norm(x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12129        let Mixer::Full(fa) = &layer.mixer else {
12130            panic!("gemma4 layer {il} not full-attn")
12131        };
12132        let o = self.gemma4_attn(e, fa, il, &h, pos_d, t)?;
12133        // gemma order: post_attention_norm applies to the ATTENTION OUTPUT, then the residual.
12134        let mut cur = e.zeros(t * n_embd)?;
12135        e.rms_norm(
12136            &o,
12137            layer.post_attn_norm.float_data(),
12138            &mut cur,
12139            n_embd,
12140            t,
12141            eps,
12142        )?;
12143        self.gemma4_layer_tail_add(e, layer, &cur, x, t)
12144    }
12145
12146    /// Everything after the attention output in a gemma4 layer: the residual add (cur + x ->
12147    /// attn_out) FUSED with the three attn_out norms, then shared FFN + router + MoE + combine +
12148    /// layer scale — shared verbatim by the prefill, decode and verify paths.
12149    fn gemma4_layer_tail_add(
12150        &self,
12151        e: &Engine,
12152        layer: &crate::hybrid::HybridLayer,
12153        cur: &CudaSlice<f32>,
12154        x: &CudaSlice<f32>,
12155        t: usize,
12156    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12157        Ok(self.gemma4_layer_tail_add_n(e, layer, cur, x, t, None)?.0)
12158    }
12159
12160    /// tail_add with the NEXT layer's attn_norm fused into the closing add+scale (one launch
12161    /// produces both x_next and h_next — the cross-layer fusion). `next_norm` None = last layer.
12162    fn gemma4_layer_tail_add_n(
12163        &self,
12164        e: &Engine,
12165        layer: &crate::hybrid::HybridLayer,
12166        cur: &CudaSlice<f32>,
12167        x: &CudaSlice<f32>,
12168        t: usize,
12169        next_norm: Option<&CudaSlice<f32>>,
12170    ) -> Result<(CudaSlice<f32>, Option<CudaSlice<f32>>), Box<dyn std::error::Error>> {
12171        let n_embd = self.cfg.n_embd as usize;
12172        let bits = layer.gemma4.as_ref().unwrap();
12173        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12174        let mut xn = e.uninit(t * n_embd)?;
12175        match next_norm {
12176            Some(w) => {
12177                let mut hn = e.uninit(t * n_embd)?;
12178                e.add_scale_rms_norm(
12179                    &sn,
12180                    &attn_out,
12181                    bits.layer_scale,
12182                    w,
12183                    &mut xn,
12184                    &mut hn,
12185                    n_embd,
12186                    t,
12187                    self.cfg.rms_eps,
12188                )?;
12189                Ok((xn, Some(hn)))
12190            }
12191            None => {
12192                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12193                Ok((xn, None))
12194            }
12195        }
12196    }
12197
12198    /// Tail core: attn_out = cur+x (fused with the 3 norms), both FFN branches, the combine
12199    /// norm — returns (sn, attn_out) for the closing add+scale variants.
12200    fn gemma4_layer_tail_core(
12201        &self,
12202        e: &Engine,
12203        layer: &crate::hybrid::HybridLayer,
12204        cur: &CudaSlice<f32>,
12205        x: &CudaSlice<f32>,
12206        t: usize,
12207    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12208        self.gemma4_layer_tail_core_pn(e, layer, cur, x, t, None, false)
12209    }
12210
12211    /// tail_core with an optional PRE-NORM fold (glue-fusion lane): `pre_norm = Some(wa)`
12212    /// means `cur` is the RAW attention output and the dense entry runs
12213    /// rms(cur, wa) + residual-add + ffn_norm as ONE launch (E4B's post-attn norm fold).
12214    /// `defer_post_norm`: dense-arm exit returns RAW f0 (ffn_down output) instead of
12215    /// sn = rms(f0, post_ffw) — the caller fuses the post-norm into its residual emit
12216    /// (rms_pre_add_q8_1, E4B glue wave 5). MoE arm ignores it.
12217    fn gemma4_layer_tail_core_pn(
12218        &self,
12219        e: &Engine,
12220        layer: &crate::hybrid::HybridLayer,
12221        cur: &CudaSlice<f32>,
12222        x: &CudaSlice<f32>,
12223        t: usize,
12224        pre_norm: Option<&CudaSlice<f32>>,
12225        defer_post_norm: bool,
12226    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12227        let n_embd = self.cfg.n_embd as usize;
12228        let eps = self.cfg.rms_eps;
12229        let bits = layer.gemma4.as_ref().unwrap();
12230
12231        // DENSE gemma4 variants (31B/E4B — no MoE, no parallel branch): attn_out = cur + x
12232        // fused with the single ffn_norm; GELU_PAR ffn; post_ffw_norm.
12233        let Some(mbits) = bits.moe_bits.as_ref() else {
12234            let crate::hybrid::Ffn::Dense {
12235                ffn_gate,
12236                ffn_up,
12237                ffn_down,
12238            } = &layer.ffn
12239            else {
12240                panic!("gemma4 dense layer without Dense ffn")
12241            };
12242            let mut attn_out = e.uninit(t * n_embd)?;
12243            let mut zsh = e.uninit(t * n_embd)?;
12244            // wave-2: with the pre-norm fold active the entry ALSO emits zsh q8_1 — the
12245            // t=1 fused2 gate/up consume the pair with no standalone quantize launch.
12246            let mut zpair: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
12247            match pre_norm {
12248                Some(wa) if t == 1 => {
12249                    zpair = Some(e.rms_pre_add_rms_norm_q8z(
12250                        cur,
12251                        wa,
12252                        x,
12253                        bits.ffn_norm.float_data(),
12254                        &mut attn_out,
12255                        &mut zsh,
12256                        n_embd,
12257                        t,
12258                        eps,
12259                    )?);
12260                }
12261                Some(wa) => e.rms_pre_add_rms_norm(
12262                    cur,
12263                    wa,
12264                    x,
12265                    bits.ffn_norm.float_data(),
12266                    &mut attn_out,
12267                    &mut zsh,
12268                    n_embd,
12269                    t,
12270                    eps,
12271                )?,
12272                None => e.add_rms_norm(
12273                    cur,
12274                    x,
12275                    bits.ffn_norm.float_data(),
12276                    &mut attn_out,
12277                    &mut zsh,
12278                    n_embd,
12279                    t,
12280                    eps,
12281                )?,
12282            }
12283            let n_ff = ffn_gate.out_features();
12284            // FFN persistent slab (counter-barrier form) FALSIFIED here 2026-07-14
12285            // (falsification #7, jsonl row): PDL glue already hides the launch boundaries
12286            // it fused; barrier + worst-segment occupancy net −0.3% (31B depth) / −2.3%
12287            // (E4B spec). Down's act dependency is all-to-all, so sentinel sync cannot
12288            // rescue segment C — the megakernel front is closed for the dense tail.
12289            let (gate, up) = if t == 1 {
12290                let (zq, zd) = match zpair {
12291                    Some(p) => p,
12292                    None => e.quantize_q8_1(&zsh, 1, n_embd)?,
12293                };
12294                match e.matmul_q4_fused2(ffn_gate, ffn_up, &zq, &zd)? {
12295                    Some(p) => p,
12296                    // NVFP4mix dense trunk: gate/up are both NVFP4 (down stays Q8_0).
12297                    None => match e.matmul_nvfp4_fused2(ffn_gate, ffn_up, &zq, &zd, 1)? {
12298                        Some(p) => p,
12299                        None => (
12300                            e.matmul_pre(ffn_gate, &zq, &zd, &zsh, 1)?,
12301                            e.matmul_pre(ffn_up, &zq, &zd, &zsh, 1)?,
12302                        ),
12303                    },
12304                }
12305            } else {
12306                // BATCHED FUSED2 (DEFAULT ON 2026-07-13, MEMRA_F2B=0 seam): one segmented
12307                // launch for the verify's gate+up — the up segment's blocks fill SMs as
12308                // the gate segment drains (the launch-tail mechanism behind the b-tier
12309                // plateau; first positive after six falsified in-kernel variants).
12310                static F2B: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12311                let f2b = *F2B.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
12312                let fused = if f2b {
12313                    let (zq, zd) = e.quantize_q8_1(&zsh, t, n_embd)?;
12314                    e.matmul_q4_fused2_batched(ffn_gate, ffn_up, &zq, &zd, t)?
12315                } else {
12316                    None
12317                };
12318                match fused {
12319                    Some(p) => p,
12320                    None => {
12321                        // quantize-once window: gate/up share `zsh` (borrowed across the pair).
12322                        e.mmq_act_begin();
12323                        (e.matmul(ffn_gate, &zsh, t)?, e.matmul(ffn_up, &zsh, t)?)
12324                    }
12325                }
12326            };
12327            let mut act = e.uninit(t * n_ff)?;
12328            // act quantize folds into the GELU epilogue (bit-identical q8_1 rounding);
12329            // ffn_down rides matmul_pre — one quantize launch fewer per layer.
12330            let f0 = if e.uses_q8_1_fast(ffn_down) {
12331                let upv = e.view(&up, t * n_ff);
12332                let up_all = upv.slice(0..t * n_ff);
12333                let (aq, ad) = e.gelu_tanh_mul_q8_1(&gate, &up_all, &mut act, n_ff, t)?;
12334                e.matmul_pre(ffn_down, &aq, &ad, &act, t)?
12335            } else {
12336                e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12337                e.matmul(ffn_down, &act, t)?
12338            };
12339            if defer_post_norm {
12340                return Ok((f0, attn_out));
12341            }
12342            let mut sn = e.uninit(t * n_embd)?;
12343            e.rms_norm(
12344                &f0,
12345                bits.post_ffw_norm.float_data(),
12346                &mut sn,
12347                n_embd,
12348                t,
12349                eps,
12350            )?;
12351            return Ok((sn, attn_out));
12352        };
12353
12354        assert!(pre_norm.is_none(), "pre-norm fold is dense-entry only");
12355        // MoE variant (26B): attn_out = cur + x fused with the three attn_out norms
12356        // (ffn_norm + router-scale + pre_ffw_norm_2): ONE launch, chains verbatim. At small t
12357        // (decode + verify) the zsh and moe_in outputs are EMITTED q8_1 (both consumers are
12358        // quantized matmuls — two quantize launches + two f32 round-trips fold away).
12359        let mut attn_out = e.uninit(t * n_embd)?;
12360        let mut router_in = e.uninit(t * n_embd)?;
12361        let fast_moe = match &layer.ffn {
12362            crate::hybrid::Ffn::Moe(m) => {
12363                m.dev_exps.as_ref().is_some_and(|d| !d.gu_il)
12364                    && expert_dp4a_supported(m.gate_exps.qtype)
12365                    && expert_dp4a_supported(m.up_exps.qtype)
12366                    && expert_dp4a_supported(m.down_exps.qtype)
12367                    && std::env::var("MEMRA_GEMMA_MOE_FAST").as_deref() != Ok("0")
12368            }
12369            _ => false,
12370        };
12371        let q8z = t < PRIME_MIN_T && fast_moe;
12372        let (zsh_f32, zsh_q8, moe_q8) = if q8z {
12373            let (z0, m2) = e.add_rms_norm3_q8z(
12374                cur,
12375                x,
12376                bits.ffn_norm.float_data(),
12377                &mbits.router_scale_pre,
12378                mbits.pre_ffw_norm_2.float_data(),
12379                &mut attn_out,
12380                &mut router_in,
12381                n_embd,
12382                t,
12383                eps,
12384            )?;
12385            (None, Some(z0), Some(m2))
12386        } else {
12387            let mut zsh = e.uninit(t * n_embd)?;
12388            let mut moe_in = e.uninit(t * n_embd)?;
12389            e.add_rms_norm3(
12390                cur,
12391                x,
12392                bits.ffn_norm.float_data(),
12393                &mbits.router_scale_pre,
12394                mbits.pre_ffw_norm_2.float_data(),
12395                &mut attn_out,
12396                &mut zsh,
12397                &mut router_in,
12398                &mut moe_in,
12399                n_embd,
12400                t,
12401                eps,
12402            )?;
12403            (Some((zsh, moe_in)), None, None)
12404        };
12405        let attn_out2 = attn_out;
12406        #[allow(unused_variables)]
12407        let attn_out = &attn_out2;
12408        let n_ff = mbits.shared_gate.out_features();
12409        let (gate, up) = if let Some((zq, zd)) = zsh_q8.as_ref() {
12410            if t == 1 {
12411                match e.matmul_q4_fused2(&mbits.shared_gate, &mbits.shared_up, zq, zd)? {
12412                    Some(p) => p,
12413                    None => match e.matmul_nvfp4_fused2(
12414                        &mbits.shared_gate,
12415                        &mbits.shared_up,
12416                        zq,
12417                        zd,
12418                        1,
12419                    )? {
12420                        Some(p) => p,
12421                        None => {
12422                            let h0 = e.zeros(0)?;
12423                            (
12424                                e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, 1)?,
12425                                e.matmul_pre(&mbits.shared_up, zq, zd, &h0, 1)?,
12426                            )
12427                        }
12428                    },
12429                }
12430            } else {
12431                // verify t 2..15: the batched mmvq twins consume the pre-quantized pair.
12432                let h0 = e.zeros(0)?;
12433                (
12434                    e.matmul_pre(&mbits.shared_gate, zq, zd, &h0, t)?,
12435                    e.matmul_pre(&mbits.shared_up, zq, zd, &h0, t)?,
12436                )
12437            }
12438        } else {
12439            let (zsh, _) = zsh_f32.as_ref().unwrap();
12440            (
12441                e.matmul(&mbits.shared_gate, zsh, t)?,
12442                e.matmul(&mbits.shared_up, zsh, t)?,
12443            )
12444        };
12445        let mut act = e.uninit(t * n_ff)?;
12446        e.gelu_tanh_mul(&gate, &up, &mut act, t * n_ff)?;
12447        let mlp0 = e.matmul(&mbits.shared_down, &act, t)?;
12448        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
12449            panic!("gemma4 layer not MoE")
12450        };
12451        let moe0 = match (&moe_q8, &zsh_f32) {
12452            (Some(mq), _) => self.gemma4_moe_q8(e, m, mbits, mq, &router_in, t)?,
12453            (None, Some((_, moe_in))) => self.gemma4_moe(e, m, mbits, moe_in, &router_in, t)?,
12454            _ => unreachable!(),
12455        };
12456        // post_ffw_norm_1(mlp0) + post_ffw_norm_2(moe0): one fused launch, per-row verbatim.
12457        let mut mlp = e.uninit(t * n_embd)?;
12458        let mut moe = e.uninit(t * n_embd)?;
12459        e.rms_norm2x(
12460            &mlp0,
12461            &moe0,
12462            mbits.post_ffw_norm_1.float_data(),
12463            mbits.post_ffw_norm_2.float_data(),
12464            &mut mlp,
12465            &mut moe,
12466            n_embd,
12467            t,
12468            eps,
12469        )?;
12470
12471        // combine: rms_norm(mlp + moe, post_ffw_norm) + attn_out, then the layer output scalar.
12472        // add+norm fused (add_rms_norm == add then rms_norm, kernel-check-pinned identity).
12473        let mut sum = e.uninit(t * n_embd)?;
12474        let mut sn = e.uninit(t * n_embd)?;
12475        e.add_rms_norm(
12476            &mlp,
12477            &moe,
12478            bits.post_ffw_norm.float_data(),
12479            &mut sum,
12480            &mut sn,
12481            n_embd,
12482            t,
12483            eps,
12484        )?;
12485        Ok((sn, attn_out2))
12486    }
12487
12488    /// tail_add with the next attn_norm emitted PRE-QUANTIZED q8_1 (decode/verify loops).
12489    /// pn-fold front of `gemma4_layer_tail_add_nq` (MEMRA_G4_PNFOLD, GAP-DIAGNOSIS
12490    /// verdict 7): takes the RAW attention output `o` and, on the dense trunk, folds
12491    /// post_attn_norm into the tail entry and post_ffw_norm into the residual exit —
12492    /// the E4B glue chain (13594) backported to the decode/verify trio. Kills the
12493    /// standalone rms_norm(o) + rms_norm(f0) (+ffn quantize) launches per layer.
12494    /// BITS-CHANGING vs the two-launch chain; every caller rides this ONE front so
12495    /// decode == verify == graph parity holds by construction at either seam value.
12496    /// Non-dense (26B MoE) and seam-off fall to the unfused chain unchanged.
12497    pub(crate) fn gemma4_layer_tail_add_nq_pn(
12498        &self,
12499        e: &Engine,
12500        layer: &crate::hybrid::HybridLayer,
12501        o: &CudaSlice<f32>,
12502        x: &CudaSlice<f32>,
12503        t: usize,
12504        next_norm: Option<&CudaSlice<f32>>,
12505    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12506    {
12507        let n_embd = self.cfg.n_embd as usize;
12508        let eps = self.cfg.rms_eps;
12509        let bits = layer.gemma4.as_ref().unwrap();
12510        if Engine::g4_pnfold_on() && matches!(layer.ffn, crate::hybrid::Ffn::Dense { .. }) {
12511            let (f0, attn_out) = self.gemma4_layer_tail_core_pn(
12512                e,
12513                layer,
12514                o,
12515                x,
12516                t,
12517                Some(layer.post_attn_norm.float_data()),
12518                true,
12519            )?;
12520            let mut xn = e.uninit(t * n_embd)?;
12521            return match next_norm {
12522                Some(w) => {
12523                    let pair = e.rms_pre_add_scale_rms_norm_q8_1(
12524                        &f0,
12525                        bits.post_ffw_norm.float_data(),
12526                        &attn_out,
12527                        bits.layer_scale,
12528                        w,
12529                        &mut xn,
12530                        n_embd,
12531                        t,
12532                        eps,
12533                    )?;
12534                    Ok((xn, Some(pair)))
12535                }
12536                None => {
12537                    let mut sn = e.uninit(t * n_embd)?;
12538                    e.rms_norm(
12539                        &f0,
12540                        bits.post_ffw_norm.float_data(),
12541                        &mut sn,
12542                        n_embd,
12543                        t,
12544                        eps,
12545                    )?;
12546                    e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12547                    Ok((xn, None))
12548                }
12549            };
12550        }
12551        let mut cur = e.uninit(t * n_embd)?;
12552        e.rms_norm(
12553            o,
12554            layer.post_attn_norm.float_data(),
12555            &mut cur,
12556            n_embd,
12557            t,
12558            eps,
12559        )?;
12560        self.gemma4_layer_tail_add_nq(e, layer, &cur, x, t, next_norm)
12561    }
12562
12563    pub(crate) fn gemma4_layer_tail_add_nq(
12564        &self,
12565        e: &Engine,
12566        layer: &crate::hybrid::HybridLayer,
12567        cur: &CudaSlice<f32>,
12568        x: &CudaSlice<f32>,
12569        t: usize,
12570        next_norm: Option<&CudaSlice<f32>>,
12571    ) -> Result<(CudaSlice<f32>, Option<(CudaSlice<i8>, CudaSlice<f32>)>), Box<dyn std::error::Error>>
12572    {
12573        let n_embd = self.cfg.n_embd as usize;
12574        let bits = layer.gemma4.as_ref().unwrap();
12575        let (sn, attn_out) = self.gemma4_layer_tail_core(e, layer, cur, x, t)?;
12576        let mut xn = e.uninit(t * n_embd)?;
12577        match next_norm {
12578            Some(w) => {
12579                let pair = e.add_scale_rms_norm_q8_1(
12580                    &sn,
12581                    &attn_out,
12582                    bits.layer_scale,
12583                    w,
12584                    &mut xn,
12585                    n_embd,
12586                    t,
12587                    self.cfg.rms_eps,
12588                )?;
12589                Ok((xn, Some(pair)))
12590            }
12591            None => {
12592                e.add_scale(&sn, &attn_out, bits.layer_scale, &mut xn, t * n_embd)?;
12593                Ok((xn, None))
12594            }
12595        }
12596    }
12597
12598    /// gemma4 prefill: `last_only` = forward_last semantics (lm_head on the final row only).
12599    /// R4: final logits softcapped 30*tanh(l/30) on host (monotonic — argmax unaffected).
12600    fn gemma4_forward(
12601        &self,
12602        e: &Engine,
12603        tokens: &[u32],
12604        last_only: bool,
12605    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12606        // E4B routes to its own forward regardless of the caller's entry point (forward /
12607        // forward_last / prime paths all funnel here for gemma4).
12608        if self.is_gemma4_e4b() {
12609            return self.gemma4_e4b_forward(e, tokens, last_only);
12610        }
12611        let n_embd = self.cfg.n_embd as usize;
12612        let t = tokens.len();
12613        let pos: Vec<i32> = (0..t as i32).collect();
12614        let pos_d = e.htod_i32(&pos)?;
12615
12616        let mut x = self.embed(e, tokens)?;
12617        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12618        // MEMRA_GEMMA_PROBE=1: per-layer trunk stats (host rms + max of the LAST token row) —
12619        // the bring-up bisect vs llama-eval-callback node stats.
12620        let probe = std::env::var("MEMRA_GEMMA_PROBE").is_ok();
12621        let stat =
12622            |e: &Engine, x: &CudaSlice<f32>, tag: &str| -> Result<(), Box<dyn std::error::Error>> {
12623                let h = e.dtoh(x)?;
12624                let bad = h.iter().filter(|v| !v.is_finite()).count();
12625                let mx = h
12626                    .iter()
12627                    .filter(|v| v.is_finite())
12628                    .fold(0.0f32, |m, v| m.max(v.abs()));
12629                eprintln!(
12630                    "[gemma-probe] {tag}: tok0_first3={:?} bad={bad} max={mx:.3e}",
12631                    &h[..3]
12632                );
12633                Ok(())
12634            };
12635        if probe {
12636            stat(e, &x, "embed")?;
12637        }
12638        for (il, layer) in self.layers.iter().enumerate() {
12639            x = self.gemma4_layer(e, il, layer, &x, &pos_d, t)?;
12640            if probe {
12641                stat(e, &x, &format!("L{il}"))?;
12642            }
12643        }
12644        let mut hn = e.zeros(t * n_embd)?;
12645        e.rms_norm(
12646            &x,
12647            self.output_norm.float_data(),
12648            &mut hn,
12649            n_embd,
12650            t,
12651            self.cfg.rms_eps,
12652        )?;
12653        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12654        let n_vocab = self.output.out_features();
12655        let logits = if last_only {
12656            let hv = e.view(&hn, t * n_embd);
12657            let last_row = hv.slice((t - 1) * n_embd..t * n_embd);
12658            let mut hlast = e.zeros(n_embd)?;
12659            e.copy_view_into(&mut hlast, 0, &last_row, n_embd)?;
12660            let mut ld = e.matmul(&self.output, &hlast, 1)?;
12661            e.softcap(&mut ld, cap, n_vocab)?;
12662            self.gemma4_suppress(e, &mut ld, 1)?;
12663            e.dtoh(&ld)?
12664        } else {
12665            let mut ld = e.matmul(&self.output, &hn, t)?;
12666            e.softcap(&mut ld, cap, t * n_vocab)?;
12667            self.gemma4_suppress(e, &mut ld, t)?;
12668            e.dtoh(&ld)?
12669        };
12670        Ok(logits)
12671    }
12672
12673    /// gemma4 BATCHED PROMPT PRIME v0 (fresh cache only): the prefill graph over the whole
12674    /// prompt with each layer's post-rope K / weightless-normed V appended into the quantized
12675    /// KV cache (decode-append row math). Returns (last-row softcapped logits, h_seed = last
12676    /// pre-output_norm hidden, hiddens = full pre-output_norm stack [T, n_embd]).
12677    pub(crate) fn gemma4_prime(
12678        &self,
12679        e: &Engine,
12680        tokens: &[u32],
12681        cache: &mut Cache,
12682        overlay: Option<&crate::vision::EmbedOverlay>,
12683    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12684        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): a served gemma4 prompt longer
12685        // than the worker's prefill tick used to chunk here, and chunk 2 (pos > 0) killed the
12686        // whole worker process on this line. The worker now primes gemma4 monolithically and
12687        // routes continuation suffixes tokenwise; this is the per-request backstop.
12688        if cache.pos != 0 {
12689            return Err(
12690                "gemma4 prime v0 is fresh-prompt only (no continuation/chunked prime) \
12691                        — prime the full prompt in one call or decode tokenwise"
12692                    .into(),
12693            );
12694        }
12695        let n_embd = self.cfg.n_embd as usize;
12696        let eps = self.cfg.rms_eps;
12697        let t = tokens.len();
12698        let pos: Vec<i32> = (0..t as i32).collect();
12699        let pos_d = e.htod_i32(&pos)?;
12700        let mut x = self.embed(e, tokens)?;
12701        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
12702        // Masked-prefill arm (lane/gemma-vision): splice the tower rows AFTER the
12703        // sqrt(n_embd) text scale — the reference scales token batches only
12704        // (llama.cpp gemma4.cpp:182 `ubatch.token ? sqrtf(n_embd) : 1.0f`), so image
12705        // embeddings enter the trunk raw. Each span becomes an attention ISLAND:
12706        // bidirectional within itself, causal+SWA everywhere else, matching the
12707        // reference's llama_set_causal_attn(false) image batch exactly.
12708        let island: Option<CudaSlice<i32>> = match overlay {
12709            Some(ov) => {
12710                let mut span_id = vec![-1i32; t];
12711                for (i, &(pos, row_off, n_rows)) in ov.spans.iter().enumerate() {
12712                    if pos + n_rows > t {
12713                        return Err(format!(
12714                            "gemma4 overlay span {i} [{pos}, {}) exceeds the prompt ({t})",
12715                            pos + n_rows
12716                        )
12717                        .into());
12718                    }
12719                    let view = ov.rows.slice(row_off * n_embd..(row_off + n_rows) * n_embd);
12720                    e.copy_view_into(&mut x, pos * n_embd, &view, n_rows * n_embd)?;
12721                    for s in span_id.iter_mut().skip(pos).take(n_rows) {
12722                        *s = i as i32;
12723                    }
12724                }
12725                // MEMRA_GV_FORCE_CAUSAL=1: WRONG-ARM probe seam — splice the rows but
12726                // keep the plain causal mask. Exists only so the decisive probe can show
12727                // the island mask itself changes the answer; never on in serving.
12728                if std::env::var("MEMRA_GV_FORCE_CAUSAL").as_deref() == Ok("1") {
12729                    None
12730                } else {
12731                    Some(e.htod_i32(&span_id)?)
12732                }
12733            }
12734            None => None,
12735        };
12736        for (il, layer) in self.layers.iter().enumerate() {
12737            let mut h = e.zeros(t * n_embd)?;
12738            e.rms_norm(&x, layer.attn_norm.float_data(), &mut h, n_embd, t, eps)?;
12739            let Mixer::Full(fa) = &layer.mixer else {
12740                panic!("gemma4 layer not full-attn")
12741            };
12742            let trace = il == 0 && std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1");
12743            if trace {
12744                let v = e.dtoh(&h)?;
12745                let nan = v.iter().filter(|x| x.is_nan()).count();
12746                eprintln!("[g4-prime-trace] L0 post-attn_norm: nan={nan}/{}", v.len());
12747            }
12748            let o =
12749                self.gemma4_attn_prime(e, fa, il, &h, &pos_d, t, Some(cache), island.as_ref())?;
12750            if trace {
12751                let v = e.dtoh(&o)?;
12752                let nan = v.iter().filter(|x| x.is_nan()).count();
12753                eprintln!("[g4-prime-trace] L0 post-attn: nan={nan}/{}", v.len());
12754            }
12755            let mut cur = e.zeros(t * n_embd)?;
12756            e.rms_norm(
12757                &o,
12758                layer.post_attn_norm.float_data(),
12759                &mut cur,
12760                n_embd,
12761                t,
12762                eps,
12763            )?;
12764            x = self.gemma4_layer_tail_add(e, layer, &cur, &x, t)?;
12765            self.dflash_tap(e, cache, il, &x, t)?;
12766            // MEMRA_G4_PRIME_TRACE=1: per-layer NaN hunt (recipe-probe diagnostic; off = zero cost)
12767            if std::env::var("MEMRA_G4_PRIME_TRACE").as_deref() == Ok("1") {
12768                let h = e.dtoh(&x)?;
12769                let nan = h.iter().filter(|v| v.is_nan()).count();
12770                let amax = h.iter().fold(0f32, |a, v| a.max(v.abs()));
12771                eprintln!(
12772                    "[g4-prime-trace] layer {il}: nan={nan}/{} amax={amax:.3}",
12773                    h.len()
12774                );
12775                if nan > 0 {
12776                    return Err(format!("g4-prime-trace: first NaN at layer {il}").into());
12777                }
12778            }
12779        }
12780        cache.pos += t;
12781        let hiddens = e.clone_dtod(&x)?;
12782        let xv = e.view(&x, t * n_embd);
12783        let last_row = xv.slice((t - 1) * n_embd..t * n_embd);
12784        let mut h_seed = e.zeros(n_embd)?;
12785        e.copy_view_into(&mut h_seed, 0, &last_row, n_embd)?;
12786        let mut hn = e.uninit(n_embd)?;
12787        e.rms_norm(
12788            &h_seed,
12789            self.output_norm.float_data(),
12790            &mut hn,
12791            n_embd,
12792            1,
12793            eps,
12794        )?;
12795        let mut ld = e.matmul(&self.output, &hn, 1)?;
12796        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
12797        e.softcap(&mut ld, cap, self.output.out_features())?;
12798        self.gemma4_suppress(e, &mut ld, 1)?;
12799        let logits = e.dtoh(&ld)?;
12800        Ok((logits, h_seed, hiddens))
12801    }
12802
12803    /// gemma4 T=1 decode attention: per-layer geometry, quantized-KV append + fa_decode
12804    /// (vec kernels at hd 256, generic scalar at the globals' hd 512), weightless V-norm,
12805    /// dual rope, scale 1.0. Takes the attn-normed input PRE-QUANTIZED (the cross-layer
12806    /// fused norm emits q8 directly — the f32 h never materializes).
12807    fn gemma4_decode_attn(
12808        &self,
12809        e: &Engine,
12810        fa: &crate::hybrid::FullAttnLayer,
12811        il: usize,
12812        hq: &CudaSlice<i8>,
12813        hdq: &CudaSlice<f32>,
12814        pos_d: &CudaSlice<i32>,
12815        cache: &mut Cache,
12816    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12817        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
12818        let eps = self.cfg.rms_eps;
12819        let aux = self.gemma4_aux.as_ref().unwrap();
12820        let ones = aux.ones(e);
12821        #[cfg(debug_assertions)]
12822        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn.ones");
12823        let (hq, hdq) = (hq, hdq);
12824        let h0 = e.zeros(0)?;
12825        let h = &h0;
12826        let (q0, k0, v0) = if swa {
12827            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
12828                Some(t3) => t3,
12829                // NVFP4mix trio is MIXED-type (wv stays Q8_0), so fused3 can never
12830                // match — fuse the uniform (q,k) pair and take v as its own single.
12831                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12832                    Some((q0, k0)) => {
12833                        let v0 = e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?;
12834                        (q0, k0, v0)
12835                    }
12836                    None => (
12837                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12838                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12839                        e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
12840                    ),
12841                },
12842            }
12843        } else {
12844            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, &hq, &hdq)? {
12845                Some(p) => p,
12846                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, &hq, &hdq, 1)? {
12847                    Some(p) => p,
12848                    None => (
12849                        e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
12850                        e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
12851                    ),
12852                },
12853            };
12854            let v0 = e.clone_dtod(&k0)?;
12855            (q0, k0, v0)
12856        };
12857        let mut q = e.uninit(nh * hd)?;
12858        let mut k = e.uninit(nkv * hd)?;
12859        let mut v = e.uninit(nkv * hd)?;
12860        // E4B wave-3 fold, m=1 completion (2026-07-23): the rows arms took this fold at
12861        // 550fcfa5; the decode-step trio kept 2 launches/layer it doesn't need.
12862        let ff = if swa {
12863            None
12864        } else {
12865            Some(
12866                aux.rope_freqs(e)
12867                    .expect("gemma4 global rope needs rope_freqs.weight"),
12868            )
12869        };
12870        #[cfg(debug_assertions)]
12871        if let Some(ff) = ff {
12872            crate::debug_assert_tensor_stream_device(
12873                ff,
12874                &e.stream(),
12875                "gemma4_decode_attn.rope_freqs",
12876            );
12877        }
12878        let kvl = cache.kv[il].as_mut().unwrap();
12879        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
12880        if crate::Engine::qkv_append_on() {
12881            // append fold, eager completion (zoo-fusion arc): the dc arms took this fold
12882            // 2026-07-23; the eager step kept the 2-launch pair it doesn't need. Host-len
12883            // twin of the dc fold — bit-identical bodies, one launch per layer.
12884            e.rms_norm_qkv_rope_append(
12885                &q0,
12886                &k0,
12887                &v0,
12888                fa.q_norm.float_data(),
12889                fa.k_norm.float_data(),
12890                ones,
12891                &mut q,
12892                &mut k,
12893                &mut v,
12894                hd,
12895                self.gemma4_rope_dims(il),
12896                nh,
12897                nkv,
12898                pos_d,
12899                nh,
12900                nkv,
12901                base,
12902                1.0,
12903                ff,
12904                eps,
12905                &mut kvl.k,
12906                &mut kvl.v,
12907                kvl.len,
12908                kvl.k_tok_bytes,
12909                kvl.v_tok_bytes,
12910                kv_fp8,
12911            )?;
12912        } else {
12913            e.rms_norm_qkv_rope(
12914                &q0,
12915                &k0,
12916                &v0,
12917                fa.q_norm.float_data(),
12918                fa.k_norm.float_data(),
12919                ones,
12920                &mut q,
12921                &mut k,
12922                &mut v,
12923                hd,
12924                self.gemma4_rope_dims(il),
12925                nh,
12926                nkv,
12927                pos_d,
12928                nh,
12929                nkv,
12930                base,
12931                1.0,
12932                ff,
12933                eps,
12934            )?;
12935            e.append_kv_quantized(
12936                &k,
12937                &v,
12938                &mut kvl.k,
12939                &mut kvl.v,
12940                kvl.len,
12941                kvl.kv_dim_k,
12942                kvl.kv_dim_v,
12943                kvl.k_tok_bytes,
12944                kvl.v_tok_bytes,
12945                kv_fp8,
12946            )?;
12947        }
12948        kvl.len += 1;
12949        // R6 decode: SWA layers attend only the last `sliding_window` keys — a token-aligned
12950        // VIEW OFFSET into the quantized cache (keys carry absolute rope; the mask is purely
12951        // positional). Globals attend the full history.
12952        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
12953        let mut attn = e.uninit(nh * hd)?;
12954        // global layers: SAME hd512 rows twin as verify with t=1 (parity law).
12955        if !swa
12956            && hd == 512
12957            && kvl.len >= crate::fa512_min_tkv()
12958            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12959        {
12960            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12961            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12962            // device-len: eager syncs the counter (async arg-store), dc keeps it live.
12963            let base = kvl.len as i32;
12964            e.i32_set_k(&mut kvl.len_d, base)?;
12965            e.fa_decode_rows(
12966                &q,
12967                &kp,
12968                &vp,
12969                &mut attn,
12970                hd,
12971                nh,
12972                nkv,
12973                kvl.len - 1,
12974                1,
12975                scale,
12976                kvl.k_tok_bytes,
12977                kvl.v_tok_bytes,
12978                Some((&kvl.len_d, -1)),
12979                false,
12980                false,
12981                None,
12982            )?;
12983            return Ok(e.matmul(&fa.wo, &attn, 1)?);
12984        }
12985        // windowed regime: SAME rows_w kernel as verify with t=1 (parity law — see verify_attn).
12986        if swa
12987            && kvl.len > win
12988            && hd == 256
12989            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
12990        {
12991            let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
12992            let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
12993            let base = kvl.len as i32;
12994            e.i32_set_k(&mut kvl.len_d, base)?;
12995            e.fa_decode_rows_w(
12996                &q,
12997                &kp,
12998                &vp,
12999                &mut attn,
13000                hd,
13001                nh,
13002                nkv,
13003                &kvl.len_d,
13004                -1,
13005                1,
13006                scale,
13007                win,
13008                kvl.k_tok_bytes,
13009                kvl.v_tok_bytes,
13010                None,
13011            )?;
13012            return Ok(e.matmul(&fa.wo, &attn, 1)?);
13013        }
13014        let (off_tok, t_kv) = if swa && kvl.len > win {
13015            (kvl.len - win, win)
13016        } else {
13017            (0, kvl.len)
13018        };
13019        let k_view = e.view_u8_range(
13020            &kvl.k,
13021            off_tok * kvl.k_tok_bytes,
13022            (off_tok + t_kv) * kvl.k_tok_bytes,
13023        );
13024        let v_view = e.view_u8_range(
13025            &kvl.v,
13026            off_tok * kvl.v_tok_bytes,
13027            (off_tok + t_kv) * kvl.v_tok_bytes,
13028        );
13029        e.fa_decode_kvmod(
13030            &q,
13031            &k_view,
13032            &v_view,
13033            &mut attn,
13034            hd,
13035            nh,
13036            nkv,
13037            t_kv,
13038            scale,
13039            kvl.k_tok_bytes,
13040            kvl.v_tok_bytes,
13041            swa && crate::Engine::wkv_on(),
13042        )?;
13043        Ok(e.matmul(&fa.wo, &attn, 1)?)
13044    }
13045
13046    /// gemma4 DEVICE-COUNTER decode step (graph arc): token id + rope pos + KV lengths live in
13047    /// device counters; ZERO varying host kernel args. `cap_bucket_max` = Some(bucket) for graph
13048    /// capture (host mirrors untouched, n_splits from bucket, full-buffer KV views) / None for
13049    /// the eager-dc gate path (host mirrors advanced, live geometry — bit-identical target =
13050    /// gemma4_decode_step_h's token stream). V1 scope: t_kv <= sliding_window (no window views
13051    /// in-graph; the driver gates).
13052    #[allow(clippy::too_many_arguments)]
13053    pub fn gemma4_decode_step_dc(
13054        &self,
13055        e: &Engine,
13056        token_d: &CudaSlice<u32>,
13057        pos_d: &mut CudaSlice<i32>,
13058        embd_gpu: &CudaSlice<u8>,
13059        embd_qt: i32,
13060        embd_rb: usize,
13061        cache: &mut Cache,
13062        n_vocab: usize,
13063        cap_bucket_max: Option<(usize, usize)>,
13064    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
13065        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
13066        self.gemma4_decode_step_dc_into(
13067            e,
13068            token_d,
13069            pos_d,
13070            embd_gpu,
13071            embd_qt,
13072            embd_rb,
13073            cache,
13074            n_vocab,
13075            cap_bucket_max,
13076            &mut tok_out,
13077        )?;
13078        Ok(tok_out)
13079    }
13080
13081    /// CAPTURE body: argmax lands in the PERSISTENT `tok_out` (same buffer = same address on
13082    /// every replay; pass `token_d` itself for the self-feeding graph loop).
13083    #[allow(clippy::too_many_arguments)]
13084    pub fn gemma4_decode_step_dc_into(
13085        &self,
13086        e: &Engine,
13087        token_d: &CudaSlice<u32>,
13088        pos_d: &mut CudaSlice<i32>,
13089        embd_gpu: &CudaSlice<u8>,
13090        embd_qt: i32,
13091        embd_rb: usize,
13092        cache: &mut Cache,
13093        n_vocab: usize,
13094        cap_bucket_max: Option<(usize, usize)>,
13095        tok_out: &mut CudaSlice<u32>,
13096    ) -> Result<(), Box<dyn std::error::Error>> {
13097        let n_embd = self.cfg.n_embd as usize;
13098        let eps = self.cfg.rms_eps;
13099        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
13100        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
13101        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13102        let n_layers = self.layers.len();
13103        for (il, layer) in self.layers.iter().enumerate() {
13104            let (hq, hdq) = match h_carry.take() {
13105                Some(p) => p,
13106                None => {
13107                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
13108                }
13109            };
13110            let Mixer::Full(fa) = &layer.mixer else {
13111                panic!("gemma4 layer {il} not full-attn")
13112            };
13113            let o =
13114                self.gemma4_decode_attn_dc(e, fa, il, &hq, &hdq, pos_d, cache, cap_bucket_max)?;
13115            let next_norm = if il + 1 < n_layers {
13116                Some(self.layers[il + 1].attn_norm.float_data())
13117            } else {
13118                None
13119            };
13120            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
13121            x = xn;
13122            h_carry = hn;
13123        }
13124        let mut hn = e.uninit(n_embd)?;
13125        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
13126        let mut logits = e.matmul(&self.output, &hn, 1)?;
13127        self.gemma4_suppress(e, &mut logits, 1)?; // cap skipped (monotonic); the mask is not
13128        e.argmax_token_device_into(&logits, tok_out, n_vocab)?;
13129        e.inc_seqlen(pos_d)?;
13130        if cap_bucket_max.is_none() {
13131            cache.pos += 1;
13132        }
13133        Ok(())
13134    }
13135
13136    /// Persistent transient slots for the ALLOC-FREE captured dc step (the graph door):
13137    /// every buffer the step produces per token lives here, allocated ONCE pre-capture, so
13138    /// the captured graph carries zero cuMemAllocAsync/Free nodes (the 226us/launch tax,
13139    /// osrt 2026-07-23). Sized for the model's max per-layer shapes.
13140
13141    /// Build the slot set (call OUTSIDE any capture).
13142    pub fn g4_dc_slots(&self, e: &Engine) -> Result<G4DcSlots, Box<dyn std::error::Error>> {
13143        let n_embd = self.cfg.n_embd as usize;
13144        let n_vocab = self.output.out_features();
13145        let n_layers = self.layers.len();
13146        let (mut qmax, mut kvmax, mut ffmax) = (0usize, 0usize, 0usize);
13147        for il in 0..n_layers {
13148            let (hd, nkv, nh, _b, _s, _w) = self.gemma4_geom(il);
13149            qmax = qmax.max(nh * hd);
13150            kvmax = kvmax.max(nkv * hd);
13151            if let crate::hybrid::Ffn::Dense { ffn_gate, .. } = &self.layers[il].ffn {
13152                ffmax = ffmax.max(ffn_gate.out_features());
13153            }
13154        }
13155        Ok(G4DcSlots {
13156            x: e.uninit(n_embd)?,
13157            xn: e.uninit(n_embd)?,
13158            cur: e.uninit(n_embd)?,
13159            hq: e.alloc_i8_uninit(n_embd)?,
13160            hd_: e.uninit(n_embd / 32)?,
13161            q0: e.uninit(qmax)?,
13162            k0: e.uninit(kvmax)?,
13163            v0: e.uninit(kvmax)?,
13164            q: e.uninit(qmax)?,
13165            k: e.uninit(kvmax)?,
13166            v: e.uninit(kvmax)?,
13167            attn: e.uninit(qmax)?,
13168            o: e.uninit(n_embd)?,
13169            attn_out: e.uninit(n_embd)?,
13170            zsh: e.uninit(n_embd)?,
13171            // zq/zd feed the wo matvec (nh*hd rows — 4096 on hd512 globals > n_embd),
13172            // the ffn entry (n_embd) and the lm_head (n_embd): size for the max.
13173            zq: e.alloc_i8_uninit(n_embd.max(qmax))?,
13174            zd: e.uninit(n_embd.max(qmax) / 32)?,
13175            gate: e.uninit(ffmax)?,
13176            up: e.uninit(ffmax)?,
13177            act: e.uninit(ffmax)?,
13178            actq: e.alloc_i8_uninit(ffmax)?,
13179            actd: e.uninit(ffmax / 32)?,
13180            f0: e.uninit(n_embd)?,
13181            sn: e.uninit(n_embd)?,
13182            hn: e.uninit(n_embd)?,
13183            logits: e.uninit(n_vocab)?,
13184        })
13185    }
13186
13187    /// m=1 pre-quantized matvec into a slot — mirrors matmul_pre's m=1 mmvq route exactly
13188    /// (rp4-mirror bytes; mmvq_supports guaranteed for gemma4 q4_0/q6_K).
13189    fn g4_matvec_m1_into(
13190        &self,
13191        e: &Engine,
13192        w: &crate::model::GpuTensor,
13193        aq: &CudaSlice<i8>,
13194        ad: &CudaSlice<f32>,
13195        y: &mut CudaSlice<f32>,
13196    ) -> Result<(), Box<dyn std::error::Error>> {
13197        use crate::model::GpuTensor;
13198        let (bytes, qtype, row_bytes, scale, rp) = match w {
13199            GpuTensor::Quant {
13200                bytes,
13201                qtype,
13202                row_bytes,
13203                scale,
13204                rp,
13205                ..
13206            } => (bytes, *qtype, *row_bytes, *scale, *rp),
13207            _ => return Err("g4_matvec_m1_into: non-quant tensor".into()),
13208        };
13209        let (mbytes, mrp) = match w {
13210            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13211            _ => (bytes, rp),
13212        };
13213        e.qmatvec_mmvq_into(
13214            mbytes,
13215            aq,
13216            ad,
13217            1,
13218            w.in_features(),
13219            w.out_features(),
13220            qtype,
13221            row_bytes,
13222            scale,
13223            mrp,
13224            y,
13225        )
13226    }
13227
13228    /// ALLOC-FREE dc step (capture body): kernel-for-kernel mirror of
13229    /// `gemma4_decode_step_dc_into` at t=1 with every transient slot-fed. Dense gemma4 only
13230    /// (12B/31B; uniform q4_0 trunk guarantees the fused2/3 arms).
13231    #[allow(clippy::too_many_arguments)]
13232    pub fn gemma4_decode_step_dc_slotted(
13233        &self,
13234        e: &Engine,
13235        token_d: &CudaSlice<u32>,
13236        pos_d: &mut CudaSlice<i32>,
13237        embd_gpu: &CudaSlice<u8>,
13238        embd_qt: i32,
13239        embd_rb: usize,
13240        cache: &mut Cache,
13241        n_vocab: usize,
13242        cap_bucket_max: Option<(usize, usize)>,
13243        sl: &mut G4DcSlots,
13244        tok_out: &mut CudaSlice<u32>,
13245        ring: Option<(&mut CudaSlice<u32>, usize)>,
13246    ) -> Result<(), Box<dyn std::error::Error>> {
13247        let n_embd = self.cfg.n_embd as usize;
13248        let eps = self.cfg.rms_eps;
13249        e.embed_gather_device_into(embd_gpu, token_d, &mut sl.x, n_embd, embd_qt, embd_rb)?;
13250        e.scale_inplace(&mut sl.x, (n_embd as f32).sqrt(), n_embd)?;
13251        let n_layers = self.layers.len();
13252        let mut has_carry = false;
13253        for il in 0..n_layers {
13254            if !has_carry {
13255                e.rms_norm_q8_1_into(
13256                    &sl.x,
13257                    self.layers[il].attn_norm.float_data(),
13258                    n_embd,
13259                    1,
13260                    eps,
13261                    &mut sl.hq,
13262                    &mut sl.hd_,
13263                )?;
13264            }
13265            has_carry = true;
13266            let layer = &self.layers[il];
13267            let Mixer::Full(fa) = &layer.mixer else {
13268                panic!("gemma4 layer {il} not full-attn")
13269            };
13270            self.gemma4_decode_attn_dc_slotted(e, fa, il, pos_d, cache, cap_bucket_max, sl)?;
13271            // pn-fold: the tail's entry launch consumes RAW sl.o (post_attn folded there);
13272            // the standalone norm only survives on the unfused seam arm.
13273            if !Engine::g4_pnfold_on() {
13274                e.rms_norm(
13275                    &sl.o,
13276                    layer.post_attn_norm.float_data(),
13277                    &mut sl.cur,
13278                    n_embd,
13279                    1,
13280                    eps,
13281                )?;
13282            }
13283            let next_norm = if il + 1 < n_layers {
13284                Some(self.layers[il + 1].attn_norm.float_data())
13285            } else {
13286                None
13287            };
13288            self.gemma4_layer_tail_slotted(e, layer, next_norm, sl)?;
13289            std::mem::swap(&mut sl.x, &mut sl.xn);
13290        }
13291        e.rms_norm(
13292            &sl.x,
13293            self.output_norm.float_data(),
13294            &mut sl.hn,
13295            n_embd,
13296            1,
13297            eps,
13298        )?;
13299        e.quantize_q8_1_into(&sl.hn, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13300        // lm_head via the same m=1 mmvq route as matmul (q6_K on the gemma4 family).
13301        {
13302            let (zq, zd) = (&sl.zq, &sl.zd);
13303            let zq = unsafe { &*(zq as *const CudaSlice<i8>) };
13304            let zd = unsafe { &*(zd as *const CudaSlice<f32>) };
13305            self.g4_matvec_m1_into(e, &self.output, zq, zd, &mut sl.logits)?;
13306        }
13307        self.gemma4_suppress(e, &mut sl.logits, 1)?;
13308        e.argmax_token_device_into(&sl.logits, tok_out, n_vocab)?;
13309        if let Some((ring, base)) = ring {
13310            // in-graph token parking: slot = (pos - base) % ring.len(); the door drains
13311            // with ONE sync per chunk instead of a per-token dtoh (the launch-serialization
13312            // fix — llama's 885us/launch overlaps GPU work because nothing waits per token).
13313            e.plain_tok_ring(tok_out, pos_d, base, ring)?;
13314        }
13315        e.inc_seqlen(pos_d)?;
13316        if cap_bucket_max.is_none() {
13317            cache.pos += 1;
13318        }
13319        Ok(())
13320    }
13321
13322    /// Slot-fed dc attention: mirrors gemma4_decode_attn_dc's CAPTURE arm kernel-for-kernel.
13323    #[allow(clippy::too_many_arguments)]
13324    fn gemma4_decode_attn_dc_slotted(
13325        &self,
13326        e: &Engine,
13327        fa: &crate::hybrid::FullAttnLayer,
13328        il: usize,
13329        pos_d: &CudaSlice<i32>,
13330        cache: &mut Cache,
13331        cap_bucket_max: Option<(usize, usize)>,
13332        sl: &mut G4DcSlots,
13333    ) -> Result<(), Box<dyn std::error::Error>> {
13334        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13335        let eps = self.cfg.rms_eps;
13336        let aux = self.gemma4_aux.as_ref().unwrap();
13337        let ones = aux.ones(e);
13338        #[cfg(debug_assertions)]
13339        crate::debug_assert_tensor_stream_device(
13340            ones,
13341            &e.stream(),
13342            "gemma4_decode_attn_dc_slotted.ones",
13343        );
13344        {
13345            let hq = unsafe { &*(&sl.hq as *const CudaSlice<i8>) };
13346            let hdq = unsafe { &*(&sl.hd_ as *const CudaSlice<f32>) };
13347            if swa {
13348                if !e.matmul_q4_fused3_into(
13349                    &fa.wq, &fa.wk, &fa.wv, hq, hdq, &mut sl.q0, &mut sl.k0, &mut sl.v0,
13350                )? {
13351                    // NVFP4mix trio is MIXED-type (wv stays Q8_0): fuse the uniform
13352                    // (q,k) pair, v through the generic m1 slot matvec — the same two
13353                    // bodies the eager arm runs, slot-fed for the alloc-free capture.
13354                    if e.matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13355                    {
13356                        self.g4_matvec_m1_into(e, &fa.wv, hq, hdq, &mut sl.v0)?;
13357                    } else {
13358                        return Err("slotted step: fused3 unavailable (non-uniform trunk)".into());
13359                    }
13360                }
13361            } else {
13362                if !e.matmul_q4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13363                    && !e
13364                        .matmul_nvfp4_fused2_into(&fa.wq, &fa.wk, hq, hdq, &mut sl.q0, &mut sl.k0)?
13365                {
13366                    return Err("slotted step: fused2 unavailable".into());
13367                }
13368                let k0r = unsafe { &*(&sl.k0 as *const CudaSlice<f32>) };
13369                e.copy_into(&mut sl.v0, 0, k0r, nkv * hd)?;
13370            }
13371        }
13372        // E4B wave-3 fold, m=1 completion (2026-07-23) — MUST mirror the dc_into arm
13373        // kernel-for-kernel (graph stream-identity gate).
13374        let ff = if swa {
13375            None
13376        } else {
13377            Some(
13378                aux.rope_freqs(e)
13379                    .expect("gemma4 global rope needs rope_freqs.weight"),
13380            )
13381        };
13382        #[cfg(debug_assertions)]
13383        if let Some(ff) = ff {
13384            crate::debug_assert_tensor_stream_device(
13385                ff,
13386                &e.stream(),
13387                "gemma4_decode_attn_dc_slotted.rope_freqs",
13388            );
13389        }
13390        let kvl = cache.kv[il].as_mut().unwrap();
13391        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13392        if crate::Engine::qkv_append_on() {
13393            // append fold (2026-07-23): mirrors dc_into.
13394            e.rms_norm_qkv_rope_append_dc(
13395                &sl.q0,
13396                &sl.k0,
13397                &sl.v0,
13398                fa.q_norm.float_data(),
13399                fa.k_norm.float_data(),
13400                ones,
13401                &mut sl.q,
13402                &mut sl.k,
13403                &mut sl.v,
13404                hd,
13405                self.gemma4_rope_dims(il),
13406                nh,
13407                nkv,
13408                pos_d,
13409                nh,
13410                nkv,
13411                base,
13412                1.0,
13413                ff,
13414                eps,
13415                &mut kvl.k,
13416                &mut kvl.v,
13417                &kvl.len_d,
13418                kvl.k_tok_bytes,
13419                kvl.v_tok_bytes,
13420                kv_fp8,
13421            )?;
13422        } else {
13423            e.rms_norm_qkv_rope(
13424                &sl.q0,
13425                &sl.k0,
13426                &sl.v0,
13427                fa.q_norm.float_data(),
13428                fa.k_norm.float_data(),
13429                ones,
13430                &mut sl.q,
13431                &mut sl.k,
13432                &mut sl.v,
13433                hd,
13434                self.gemma4_rope_dims(il),
13435                nh,
13436                nkv,
13437                pos_d,
13438                nh,
13439                nkv,
13440                base,
13441                1.0,
13442                ff,
13443                eps,
13444            )?;
13445            e.append_kv_quantized_dc(
13446                &sl.k,
13447                &sl.v,
13448                &mut kvl.k,
13449                &mut kvl.v,
13450                &kvl.len_d,
13451                kvl.kv_dim_k,
13452                kvl.kv_dim_v,
13453                kvl.k_tok_bytes,
13454                kvl.v_tok_bytes,
13455                kv_fp8,
13456            )?;
13457        }
13458        e.inc_seqlen(&mut kvl.len_d)?;
13459        let (b_swa, b_glob) = cap_bucket_max.expect("slotted step is capture-only");
13460        let k_view = e.view_u8(&kvl.k, kvl.k.len());
13461        let v_view = e.view_u8(&kvl.v, kvl.v.len());
13462        let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13463        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13464        // combine-q8 emit (wave-5b m=1 port): the rows arms quantize inside the combine —
13465        // the standalone quantize launch runs only on the non-rows fallback. MUST mirror
13466        // the dc_into arm branch-for-branch (stream gate).
13467        let mut fa_q8 = false;
13468        if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13469            e.fa_decode_rows(
13470                &sl.q,
13471                &k_view,
13472                &v_view,
13473                &mut sl.attn,
13474                hd,
13475                nh,
13476                nkv,
13477                b_glob - 1,
13478                1,
13479                scale,
13480                kvl.k_tok_bytes,
13481                kvl.v_tok_bytes,
13482                Some((&kvl.len_d, -1)),
13483                false,
13484                false,
13485                Some((&mut sl.zq, &mut sl.zd)),
13486            )?;
13487            fa_q8 = true;
13488        } else if swa && b_swa > win && hd == 256 && rows_on {
13489            e.fa_decode_rows_w(
13490                &sl.q,
13491                &k_view,
13492                &v_view,
13493                &mut sl.attn,
13494                hd,
13495                nh,
13496                nkv,
13497                &kvl.len_d,
13498                -1,
13499                1,
13500                scale,
13501                win,
13502                kvl.k_tok_bytes,
13503                kvl.v_tok_bytes,
13504                Some((&mut sl.zq, &mut sl.zd)),
13505            )?;
13506            fa_q8 = true;
13507        } else {
13508            let b = if swa { b_swa } else { b_glob };
13509            e.fa_decode_dc(
13510                &sl.q,
13511                &k_view,
13512                &v_view,
13513                &mut sl.attn,
13514                hd,
13515                nh,
13516                nkv,
13517                &kvl.len_d,
13518                b,
13519                scale,
13520                kvl.k_tok_bytes,
13521                kvl.v_tok_bytes,
13522                swa && crate::Engine::wkv_on(),
13523            )?;
13524        }
13525        if !fa_q8 {
13526            let aq = unsafe { &*(&sl.attn as *const CudaSlice<f32>) };
13527            e.quantize_q8_1_into(aq, 1, nh * hd, &mut sl.zq, &mut sl.zd)?;
13528        }
13529        {
13530            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13531            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13532            self.g4_matvec_m1_into(e, &fa.wo, zq, zd, &mut sl.o)?;
13533        }
13534        Ok(())
13535    }
13536
13537    /// Slot-fed dense layer tail: mirrors gemma4_layer_tail_core (t=1, no pre-norm fold) +
13538    /// tail_add_nq kernel-for-kernel; the next layer's (hq, hd_) carry lands in the slots.
13539    fn gemma4_layer_tail_slotted(
13540        &self,
13541        e: &Engine,
13542        layer: &crate::hybrid::HybridLayer,
13543        next_norm: Option<&CudaSlice<f32>>,
13544        sl: &mut G4DcSlots,
13545    ) -> Result<(), Box<dyn std::error::Error>> {
13546        let n_embd = self.cfg.n_embd as usize;
13547        let eps = self.cfg.rms_eps;
13548        let bits = layer.gemma4.as_ref().unwrap();
13549        let crate::hybrid::Ffn::Dense {
13550            ffn_gate,
13551            ffn_up,
13552            ffn_down,
13553        } = &layer.ffn
13554        else {
13555            return Err("slotted tail: dense ffn only".into());
13556        };
13557        let pnfold = Engine::g4_pnfold_on();
13558        if pnfold {
13559            // pn-fold entry (E4B glue, slot-fed): rms(o, post_attn) + residual + ffn_norm
13560            // + q8 emit in ONE launch — replaces rms_norm + add_rms_norm + quantize.
13561            let or = unsafe { &*(&sl.o as *const CudaSlice<f32>) };
13562            let xr = unsafe { &*(&sl.x as *const CudaSlice<f32>) };
13563            e.rms_pre_add_rms_norm_q8z_into(
13564                or,
13565                layer.post_attn_norm.float_data(),
13566                xr,
13567                bits.ffn_norm.float_data(),
13568                &mut sl.attn_out,
13569                &mut sl.zsh,
13570                n_embd,
13571                1,
13572                eps,
13573                &mut sl.zq,
13574                &mut sl.zd,
13575            )?;
13576        } else {
13577            e.add_rms_norm(
13578                &sl.cur,
13579                &sl.x,
13580                bits.ffn_norm.float_data(),
13581                &mut sl.attn_out,
13582                &mut sl.zsh,
13583                n_embd,
13584                1,
13585                eps,
13586            )?;
13587        }
13588        let n_ff = ffn_gate.out_features();
13589        if !pnfold {
13590            let zshr = unsafe { &*(&sl.zsh as *const CudaSlice<f32>) };
13591            e.quantize_q8_1_into(zshr, 1, n_embd, &mut sl.zq, &mut sl.zd)?;
13592        }
13593        {
13594            let zq = unsafe { &*(&sl.zq as *const CudaSlice<i8>) };
13595            let zd = unsafe { &*(&sl.zd as *const CudaSlice<f32>) };
13596            if !e.matmul_q4_fused2_into(ffn_gate, ffn_up, zq, zd, &mut sl.gate, &mut sl.up)?
13597                && !e.matmul_nvfp4_fused2_into(
13598                    ffn_gate,
13599                    ffn_up,
13600                    zq,
13601                    zd,
13602                    &mut sl.gate,
13603                    &mut sl.up,
13604                )?
13605            {
13606                return Err("slotted tail: ffn fused2 unavailable".into());
13607            }
13608        }
13609        debug_assert!(e.uses_q8_1_fast(ffn_down));
13610        {
13611            let upr = unsafe { &*(&sl.up as *const CudaSlice<f32>) };
13612            let upv = e.view(upr, n_ff);
13613            let up_all = upv.slice(0..n_ff);
13614            let gr = unsafe { &*(&sl.gate as *const CudaSlice<f32>) };
13615            e.gelu_tanh_mul_q8_1_into(
13616                gr,
13617                &up_all,
13618                &mut sl.act,
13619                n_ff,
13620                1,
13621                &mut sl.actq,
13622                &mut sl.actd,
13623            )?;
13624        }
13625        {
13626            let aq = unsafe { &*(&sl.actq as *const CudaSlice<i8>) };
13627            let ad = unsafe { &*(&sl.actd as *const CudaSlice<f32>) };
13628            self.g4_matvec_m1_into(e, ffn_down, aq, ad, &mut sl.f0)?;
13629        }
13630        if pnfold {
13631            // pn-fold exit: rms(f0, post_ffw) + scaled residual + next attn_norm + q8
13632            // emit in ONE launch — replaces rms_norm + add_scale_rms_norm_q8_1.
13633            if let Some(w) = next_norm {
13634                let f0r = unsafe { &*(&sl.f0 as *const CudaSlice<f32>) };
13635                let aor = unsafe { &*(&sl.attn_out as *const CudaSlice<f32>) };
13636                e.rms_pre_add_scale_rms_norm_q8_1_into(
13637                    f0r,
13638                    bits.post_ffw_norm.float_data(),
13639                    aor,
13640                    bits.layer_scale,
13641                    w,
13642                    &mut sl.xn,
13643                    n_embd,
13644                    1,
13645                    eps,
13646                    &mut sl.hq,
13647                    &mut sl.hd_,
13648                )?;
13649                return Ok(());
13650            }
13651        }
13652        e.rms_norm(
13653            &sl.f0,
13654            bits.post_ffw_norm.float_data(),
13655            &mut sl.sn,
13656            n_embd,
13657            1,
13658            eps,
13659        )?;
13660        match next_norm {
13661            Some(w) => {
13662                e.add_scale_rms_norm_q8_1_into(
13663                    &sl.sn,
13664                    &sl.attn_out,
13665                    bits.layer_scale,
13666                    w,
13667                    &mut sl.xn,
13668                    n_embd,
13669                    1,
13670                    eps,
13671                    &mut sl.hq,
13672                    &mut sl.hd_,
13673                )?;
13674            }
13675            None => {
13676                e.add_scale(&sl.sn, &sl.attn_out, bits.layer_scale, &mut sl.xn, n_embd)?;
13677            }
13678        }
13679        Ok(())
13680    }
13681
13682    /// dc attention: same math as gemma4_decode_attn, KV slot/lengths from device counters.
13683    #[allow(clippy::too_many_arguments)]
13684    fn gemma4_decode_attn_dc(
13685        &self,
13686        e: &Engine,
13687        fa: &crate::hybrid::FullAttnLayer,
13688        il: usize,
13689        hq: &CudaSlice<i8>,
13690        hdq: &CudaSlice<f32>,
13691        pos_d: &CudaSlice<i32>,
13692        cache: &mut Cache,
13693        cap_bucket_max: Option<(usize, usize)>,
13694    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13695        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
13696        let eps = self.cfg.rms_eps;
13697        let aux = self.gemma4_aux.as_ref().unwrap();
13698        let ones = aux.ones(e);
13699        #[cfg(debug_assertions)]
13700        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_decode_attn_dc.ones");
13701        let (q0, k0, v0) = if swa {
13702            match e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
13703                Some(t3) => t3,
13704                // NVFP4mix: wv is Q8_0 (mixed trio) — fuse the (q,k) pair, v single.
13705                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13706                    Some((q0, k0)) => {
13707                        let h0 = e.zeros(0)?;
13708                        let v0 = e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?;
13709                        (q0, k0, v0)
13710                    }
13711                    None => {
13712                        let h0 = e.zeros(0)?;
13713                        (
13714                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13715                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13716                            e.matmul_pre(&fa.wv, hq, hdq, &h0, 1)?,
13717                        )
13718                    }
13719                },
13720            }
13721        } else {
13722            let (q0, k0) = match e.matmul_q4_fused2(&fa.wq, &fa.wk, hq, hdq)? {
13723                Some(p) => p,
13724                None => match e.matmul_nvfp4_fused2(&fa.wq, &fa.wk, hq, hdq, 1)? {
13725                    Some(p) => p,
13726                    None => {
13727                        let h0 = e.zeros(0)?;
13728                        (
13729                            e.matmul_pre(&fa.wq, hq, hdq, &h0, 1)?,
13730                            e.matmul_pre(&fa.wk, hq, hdq, &h0, 1)?,
13731                        )
13732                    }
13733                },
13734            };
13735            let v0 = e.clone_dtod(&k0)?;
13736            (q0, k0, v0)
13737        };
13738        let mut q = e.uninit(nh * hd)?;
13739        let mut k = e.uninit(nkv * hd)?;
13740        let mut v = e.uninit(nkv * hd)?;
13741        // E4B wave-3 fold, m=1 completion (2026-07-23): mirrored by the slotted arm.
13742        let ff = if swa {
13743            None
13744        } else {
13745            Some(
13746                aux.rope_freqs(e)
13747                    .expect("gemma4 global rope needs rope_freqs.weight"),
13748            )
13749        };
13750        #[cfg(debug_assertions)]
13751        if let Some(ff) = ff {
13752            crate::debug_assert_tensor_stream_device(
13753                ff,
13754                &e.stream(),
13755                "gemma4_decode_attn_dc.rope_freqs",
13756            );
13757        }
13758        let kvl = cache.kv[il].as_mut().unwrap();
13759        let kv_fp8 = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
13760        if crate::Engine::qkv_append_on() {
13761            // append fold (2026-07-23): norm+rope+cache-append in ONE launch.
13762            e.rms_norm_qkv_rope_append_dc(
13763                &q0,
13764                &k0,
13765                &v0,
13766                fa.q_norm.float_data(),
13767                fa.k_norm.float_data(),
13768                ones,
13769                &mut q,
13770                &mut k,
13771                &mut v,
13772                hd,
13773                self.gemma4_rope_dims(il),
13774                nh,
13775                nkv,
13776                pos_d,
13777                nh,
13778                nkv,
13779                base,
13780                1.0,
13781                ff,
13782                eps,
13783                &mut kvl.k,
13784                &mut kvl.v,
13785                &kvl.len_d,
13786                kvl.k_tok_bytes,
13787                kvl.v_tok_bytes,
13788                kv_fp8,
13789            )?;
13790        } else {
13791            e.rms_norm_qkv_rope(
13792                &q0,
13793                &k0,
13794                &v0,
13795                fa.q_norm.float_data(),
13796                fa.k_norm.float_data(),
13797                ones,
13798                &mut q,
13799                &mut k,
13800                &mut v,
13801                hd,
13802                self.gemma4_rope_dims(il),
13803                nh,
13804                nkv,
13805                pos_d,
13806                nh,
13807                nkv,
13808                base,
13809                1.0,
13810                ff,
13811                eps,
13812            )?;
13813            e.append_kv_quantized_dc(
13814                &k,
13815                &v,
13816                &mut kvl.k,
13817                &mut kvl.v,
13818                &kvl.len_d,
13819                kvl.kv_dim_k,
13820                kvl.kv_dim_v,
13821                kvl.k_tok_bytes,
13822                kvl.v_tok_bytes,
13823                kv_fp8,
13824            )?;
13825        }
13826        e.inc_seqlen(&mut kvl.len_d)?;
13827        let mut attn = e.uninit(nh * hd)?;
13828        // combine-q8 emit carrier (wave-5b m=1 port): rows arms fill this; the tail then
13829        // rides g4_matvec_m1_into instead of matmul's internal quantize.
13830        let mut fa_q8: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
13831        // Weight prefetch NOT wired here (26B/31B/qwen probes 2026-07-13: 26B flat
13832        // 196.6/196.0 vs 196.4/196.1 — MoE ffn dilutes wo; 31B −0.2% — dense decode sits
13833        // at the DRAM wall, no idle window to front-load into). E4B keeps the arm
13834        // (gemma4_e4b_attn, +0.65% valid window).
13835        match cap_bucket_max {
13836            None => {
13837                // dc-EAGER: host knows the length — R6 window views EXACTLY like the eager
13838                // decode (SWA layers attend the last `sliding_window` keys); the device
13839                // counters carry only the append slot + the graph seam.
13840                kvl.len += 1;
13841                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13842                if !swa
13843                    && hd == 512
13844                    && kvl.len >= crate::fa512_min_tkv()
13845                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13846                {
13847                    // global layers: SAME hd512 rows twin as verify, t=1 (parity law).
13848                    // dc: len_d is live (inc_seqlen) — device-len rides it, plus=-1.
13849                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13850                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13851                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13852                    e.fa_decode_rows(
13853                        &q,
13854                        &kp,
13855                        &vp,
13856                        &mut attn,
13857                        hd,
13858                        nh,
13859                        nkv,
13860                        kvl.len - 1,
13861                        1,
13862                        scale,
13863                        kvl.k_tok_bytes,
13864                        kvl.v_tok_bytes,
13865                        Some((&kvl.len_d, -1)),
13866                        false,
13867                        false,
13868                        Some((&mut aq8, &mut ad8)),
13869                    )?;
13870                    fa_q8 = Some((aq8, ad8));
13871                } else if swa
13872                    && kvl.len > win
13873                    && hd == 256
13874                    && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
13875                {
13876                    // windowed regime: SAME rows_w kernel as verify, t=1 (parity law).
13877                    let kp = e.view_u8(&kvl.k, kvl.len * kvl.k_tok_bytes);
13878                    let vp = e.view_u8(&kvl.v, kvl.len * kvl.v_tok_bytes);
13879                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13880                    e.fa_decode_rows_w(
13881                        &q,
13882                        &kp,
13883                        &vp,
13884                        &mut attn,
13885                        hd,
13886                        nh,
13887                        nkv,
13888                        &kvl.len_d,
13889                        -1,
13890                        1,
13891                        scale,
13892                        win,
13893                        kvl.k_tok_bytes,
13894                        kvl.v_tok_bytes,
13895                        Some((&mut aq8, &mut ad8)),
13896                    )?;
13897                    fa_q8 = Some((aq8, ad8));
13898                } else {
13899                    let (off_tok, t_kv) = if swa && kvl.len > win {
13900                        (kvl.len - win, win)
13901                    } else {
13902                        (0, kvl.len)
13903                    };
13904                    let k_view = e.view_u8_range(
13905                        &kvl.k,
13906                        off_tok * kvl.k_tok_bytes,
13907                        (off_tok + t_kv) * kvl.k_tok_bytes,
13908                    );
13909                    let v_view = e.view_u8_range(
13910                        &kvl.v,
13911                        off_tok * kvl.v_tok_bytes,
13912                        (off_tok + t_kv) * kvl.v_tok_bytes,
13913                    );
13914                    e.fa_decode_kvmod(
13915                        &q,
13916                        &k_view,
13917                        &v_view,
13918                        &mut attn,
13919                        hd,
13920                        nh,
13921                        nkv,
13922                        t_kv,
13923                        scale,
13924                        kvl.k_tok_bytes,
13925                        kvl.v_tok_bytes,
13926                        swa && crate::Engine::wkv_on(),
13927                    )?;
13928                }
13929            }
13930            Some((b_swa, b_glob)) => {
13931                // capture: full-buffer views + device length. PARITY LAW port (graph arc step
13932                // 3): the SAME regime branches as dc-eager, pinned per arm — b_swa is the
13933                // exact-key t_kv for the dc family (n_splits constant per bucket), b_glob is
13934                // the RUNG max for the rows family (kernels derive per-replay splits from
13935                // kvl.len_d; grid/partials sized for the rung end, excess splits exit).
13936                let k_view = e.view_u8(&kvl.k, kvl.k.len());
13937                let v_view = e.view_u8(&kvl.v, kvl.v.len());
13938                let rows_on = std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0");
13939                let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
13940                if !swa && hd == 512 && b_glob >= crate::fa512_min_tkv() && rows_on {
13941                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13942                    e.fa_decode_rows(
13943                        &q,
13944                        &k_view,
13945                        &v_view,
13946                        &mut attn,
13947                        hd,
13948                        nh,
13949                        nkv,
13950                        b_glob - 1,
13951                        1,
13952                        scale,
13953                        kvl.k_tok_bytes,
13954                        kvl.v_tok_bytes,
13955                        Some((&kvl.len_d, -1)),
13956                        false,
13957                        false,
13958                        Some((&mut aq8, &mut ad8)),
13959                    )?;
13960                    fa_q8 = Some((aq8, ad8));
13961                } else if swa && b_swa > win && hd == 256 && rows_on {
13962                    let (mut aq8, mut ad8) = e.uninit_q8_pair(nh * hd)?;
13963                    e.fa_decode_rows_w(
13964                        &q,
13965                        &k_view,
13966                        &v_view,
13967                        &mut attn,
13968                        hd,
13969                        nh,
13970                        nkv,
13971                        &kvl.len_d,
13972                        -1,
13973                        1,
13974                        scale,
13975                        win,
13976                        kvl.k_tok_bytes,
13977                        kvl.v_tok_bytes,
13978                        Some((&mut aq8, &mut ad8)),
13979                    )?;
13980                    fa_q8 = Some((aq8, ad8));
13981                } else {
13982                    let b = if swa { b_swa } else { b_glob };
13983                    e.fa_decode_dc(
13984                        &q,
13985                        &k_view,
13986                        &v_view,
13987                        &mut attn,
13988                        hd,
13989                        nh,
13990                        nkv,
13991                        &kvl.len_d,
13992                        b,
13993                        scale,
13994                        kvl.k_tok_bytes,
13995                        kvl.v_tok_bytes,
13996                        swa && crate::Engine::wkv_on(),
13997                    )?;
13998                }
13999            }
14000        }
14001        // combine-q8 emit (wave-5b m=1 port): the rows arms produced the wo activation pair
14002        // in-combine — ride the slotted arm's exact matvec route (parity by construction).
14003        if let Some((aq8, ad8)) = fa_q8 {
14004            let mut y = e.uninit(fa.wo.out_features())?;
14005            self.g4_matvec_m1_into(e, &fa.wo, &aq8, &ad8, &mut y)?;
14006            return Ok(y);
14007        }
14008        Ok(e.matmul(&fa.wo, &attn, 1)?)
14009    }
14010
14011    /// gemma4 GRAPH-REPLAY greedy loop: per (swa-key, global-key) fa bucket, capture ONE full
14012    /// dc step (self-feeding: argmax writes token_d in-graph) and replay it — one graph launch
14013    /// per token, one 4B dtoh. V1 scope: whole generation under the sliding window (no window
14014    /// views in-graph); caller gates and falls back to the dc-eager loop.
14015    pub fn gemma4_generate_graph(
14016        &self,
14017        e: &Engine,
14018        prompt_pos: usize,
14019        first_token: u32,
14020        cache: &mut Cache,
14021        max_new: usize,
14022        eos: &[u32],
14023        mut on_token: impl FnMut(u32) -> bool,
14024    ) -> Result<(Vec<u32>, crate::decode::StopReason), Box<dyn std::error::Error>> {
14025        if self.is_gemma4_e4b() {
14026            return Err(
14027                "E4B graph serving is unwired (HANDOVER-E4B.md) — dc-eager is the serving arm"
14028                    .into(),
14029            );
14030        }
14031        use crate::decode::StopReason;
14032        let n_vocab = self.output.out_features();
14033        let n_embd = self.cfg.n_embd as usize;
14034        let embd_gpu = self
14035            .embd_gpu
14036            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14037        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14038        for kvl in cache.kv.iter_mut().flatten() {
14039            e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
14040        }
14041        let mut token_d = e.stream().clone_htod(&[first_token])?;
14042        let mut pos_d = e.htod_i32(&[prompt_pos as i32])?;
14043        let g4 = self.cfg.gemma4.as_ref().unwrap();
14044        let (hd_s, hd_g) = (g4.key_length_swa as usize, g4.key_length_global as usize);
14045        // per-layer nkv: swa vs global counts from the pattern (uniform within class).
14046        let nkv_s = g4
14047            .head_count_kv
14048            .iter()
14049            .zip(g4.swa_pattern.iter())
14050            .find(|p| *p.1)
14051            .map(|p| *p.0 as usize)
14052            .unwrap_or(8);
14053        let nkv_g = g4
14054            .head_count_kv
14055            .iter()
14056            .zip(g4.swa_pattern.iter())
14057            .find(|p| !*p.1)
14058            .map(|p| *p.0 as usize)
14059            .unwrap_or(2);
14060        let mut graphs: std::collections::HashMap<
14061            ((bool, usize), (bool, usize), bool, bool),
14062            (
14063                cudarc::driver::CudaGraph,
14064                Vec<Box<dyn std::any::Any + Send>>,
14065            ),
14066        > = Default::default();
14067        // ALLOC-FREE capture: persistent transient slots — the captured graph carries zero
14068        // mem nodes (the 226us/launch tax). Slots must outlive every cached graph.
14069        let mut slots = self.g4_dc_slots(e)?;
14070        // Chunked replay ring: tokens park on-device; ONE drain sync per chunk. ring_base is
14071        // baked at the door entry (the modulo keeps every capture valid indefinitely).
14072        const RING: usize = 64;
14073        // DRAIN pinned at 1 (2026-07-23): relaunching the SAME graph exec before its prior
14074        // launch completes is ILLEGAL (chunk=4 -> ILLEGAL_ADDRESS; chunk=1 clean). Pipelining
14075        // needs alternating exec instances — and the measured payoff was ~0 (the ~200us
14076        // cuGraphLaunch host cost already overlaps its own launch's GPU work; llama pays
14077        // 885us/launch the same way). MEMRA_GRAPH_DRAIN raises it only for experiments.
14078        const DRAIN: usize = 1;
14079        let mut ring = e.stream().alloc_zeros::<u32>(RING)?;
14080        let ring_base = prompt_pos;
14081        let mut out = Vec::with_capacity(max_new);
14082        let mut reason = StopReason::MaxNew;
14083        let mut next = first_token;
14084        let mut captures = 0usize;
14085        for _ in 0..max_new {
14086            out.push(next);
14087            if eos.contains(&next) {
14088                reason = StopReason::Eos;
14089                break;
14090            }
14091            if !on_token(next) {
14092                reason = StopReason::Callback;
14093                break;
14094            }
14095            let t_kv = cache.pos + 1;
14096            // Bucket key per ARM (graph arc step 3):
14097            //  - swa component: dc family under the window (exact (fa_vec, n_splits) key —
14098            //    n_splits must be constant per bucket); rows_w above it (window-constant, so
14099            //    the component collapses to a single marker).
14100            //  - global component: dc family under the fa512 floor (exact key); rows_dpl16
14101            //    at/above it — the kernel derives splits from len_d per replay, so buckets
14102            //    are power-of-2 RUNGS (one capture per doubling; grid sized for the rung end).
14103            let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14104            let f512 = crate::fa512_min_tkv();
14105            let key_s = if t_kv > win {
14106                (true, usize::MAX)
14107            } else {
14108                e.fa_bucket_key(t_kv, hd_s, nkv_s, crate::Engine::wkv_on())
14109            };
14110            let (key_g, rung_end) = if t_kv >= f512 {
14111                // strict upper bound: the rung must ROLL at exact powers (t_kv==1024 starts
14112                // the [1024,2048) bucket) — sizing covers every replayed T_kv < end.
14113                let end = (t_kv + 1).next_power_of_two().max(f512 * 2);
14114                ((true, end), end)
14115            } else {
14116                (e.fa_bucket_key(t_kv, hd_g, nkv_g, false), t_kv)
14117            };
14118            let key = (key_s, key_g, t_kv >= f512, t_kv > win);
14119            if !graphs.contains_key(&key) {
14120                let bucket_max = (t_kv, rung_end);
14121                // snapshot device+host state (the 3 capture-warmup runs leave no residue).
14122                let snap = cache.snapshot(e)?;
14123                let pos_save = e.dtoh_i32_one(&pos_d)?;
14124                let len_save: Vec<Option<i32>> = cache
14125                    .kv
14126                    .iter()
14127                    .map(|k| k.as_ref().map(|kvl| e.dtoh_i32_one(&kvl.len_d).unwrap()))
14128                    .collect();
14129                let tok_save = e.dtoh_u32_one(&token_d)?;
14130                // RETAINED capture (2026-07-23): the plain capture_graph left pool-transient
14131                // clones as dead COPY NODES replayed every launch — the E4B 0.74ms/token
14132                // regression class, and this door's measured -8.8%. The keeper pins warmup
14133                // transients so the captured graph holds kernel nodes only.
14134                let graph = {
14135                    let tok_ref = &mut token_d;
14136                    let pos_ref = &mut pos_d;
14137                    let cache_ref = &mut *cache;
14138                    let slots_ref = &mut slots;
14139                    let ring_ref = &mut ring;
14140                    e.capture_graph_retained_flags(
14141                        cudarc::driver::sys::CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY,
14142                        |e| {
14143                        // self-feeding: the argmax writes token_d itself.
14144                        let tok_in = unsafe { &*(tok_ref as *const CudaSlice<u32>) };
14145                        let sl = unsafe { &mut *(slots_ref as *mut G4DcSlots) };
14146                        let rg = unsafe { &mut *(ring_ref as *mut CudaSlice<u32>) };
14147                        self.gemma4_decode_step_dc_slotted(e, tok_in, pos_ref, embd_gpu, qt, rb,
14148                                                           cache_ref, n_vocab, Some(bucket_max),
14149                                                           sl, tok_ref, Some((rg, ring_base)))
14150                    })?
14151                };
14152                cache.rollback(e, &snap, 0)?;
14153                e.set_i32_one(&mut pos_d, pos_save)?;
14154                for (il, ls) in len_save.iter().enumerate() {
14155                    if let (Some(kvl), Some(v)) = (cache.kv[il].as_mut(), ls) {
14156                        e.set_i32_one(&mut kvl.len_d, *v)?;
14157                    }
14158                }
14159                e.set_u32_one(&mut token_d, tok_save)?;
14160                if std::env::var("MEMRA_GRAPH_CENSUS").as_deref() == Ok("1") {
14161                    if let Ok(c) = crate::graph_update::node_census(&graph.0) {
14162                        eprintln!("[graph-census] {c:?}");
14163                    }
14164                }
14165                graphs.insert(key, graph);
14166                captures += 1;
14167            }
14168            // CHUNKED REPLAY: enqueue up to DRAIN launches back-to-back (the ~200us host
14169            // cost of each cuGraphLaunch overlaps the PREVIOUS launch's ~11ms GPU work),
14170            // then ONE sync + ring drain. The chunk must stay inside this bucket key and
14171            // the budget; capture warmups already emitted their tokens through the ring.
14172            let mut chunk = 1usize;
14173            let drain_cap: usize = std::env::var("MEMRA_GRAPH_DRAIN")
14174                .ok()
14175                .and_then(|v| v.parse().ok())
14176                .unwrap_or(DRAIN);
14177            while chunk < drain_cap && out.len() + chunk < max_new {
14178                let t_next = cache.pos + 1 + chunk;
14179                let key_s2 = if t_next > win {
14180                    (true, usize::MAX)
14181                } else {
14182                    e.fa_bucket_key(t_next, hd_s, nkv_s, crate::Engine::wkv_on())
14183                };
14184                let key_g2 = if t_next >= f512 {
14185                    (true, (t_next + 1).next_power_of_two().max(f512 * 2))
14186                } else {
14187                    e.fa_bucket_key(t_next, hd_g, nkv_g, false)
14188                };
14189                if (key_s2, key_g2, t_next >= f512, t_next > win) != key {
14190                    break;
14191                }
14192                chunk += 1;
14193            }
14194            let g = &graphs.get(&key).unwrap().0;
14195            for _ in 0..chunk {
14196                g.launch()?;
14197            }
14198            e.stream().synchronize()?;
14199            let ringh = e.dtoh_u32(&ring)?;
14200            for j in 0..chunk {
14201                let pos_j = cache.pos + j;
14202                let tok_j = ringh[(pos_j - ring_base) % RING];
14203                cache.pos += 0; // advanced below in one shot
14204                if j + 1 == chunk {
14205                    next = tok_j;
14206                } else {
14207                    out.push(tok_j);
14208                    if eos.contains(&tok_j) || !on_token(tok_j) {
14209                        reason = if eos.contains(&tok_j) {
14210                            StopReason::Eos
14211                        } else {
14212                            StopReason::Callback
14213                        };
14214                        // roll device/host state back to the stop point.
14215                        let keep = cache.pos + j + 1;
14216                        e.set_i32_one(&mut pos_d, keep as i32)?;
14217                        for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
14218                            e.set_i32_one(&mut kvl.len_d, keep as i32)?;
14219                            kvl.len = keep;
14220                        }
14221                        cache.pos = keep;
14222                        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
14223                            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
14224                        }
14225                        return Ok((out, reason));
14226                    }
14227                }
14228            }
14229            cache.pos += chunk;
14230            for kvl in cache.kv.iter_mut().filter_map(|k| k.as_mut()) {
14231                kvl.len += chunk;
14232            }
14233        }
14234        if std::env::var("MEMRA_GRAPH_STATS").is_ok() {
14235            eprintln!("[gemma-graph] captures={captures} buckets={}", graphs.len());
14236        }
14237        Ok((out, reason))
14238    }
14239
14240    /// gemma4 VERIFY step (spec decode): t tokens batched through the trunk at positions
14241    /// pos0..pos0+t-1, K/V rows appended to the quantized cache (caller rolls back rejected
14242    /// rows via kvl.len), per-token causal windowed attend (fa_decode per token — the same
14243    /// kernel family as T=1 decode at each token's t_kv). Returns [t, n_vocab] softcapped
14244    /// logits (host) + advances cache.pos by t.
14245    pub(crate) fn gemma4_decode_step_t(
14246        &self,
14247        e: &Engine,
14248        tokens: &[u32],
14249        pos0: usize,
14250        cache: &mut Cache,
14251    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
14252        Ok(self.gemma4_decode_step_t_h(e, tokens, pos0, cache)?.0)
14253    }
14254
14255    /// GREEDY verify: per-row DEVICE argmax (t x 4B host traffic instead of the t x 1MB logits
14256    /// stack — softcap skipped: tanh is monotonic, per-row argmax unaffected). Returns
14257    /// (argmax ids [t], post-output_norm hidden stack [t, n_embd]).
14258    pub(crate) fn gemma4_decode_step_t_am(
14259        &self,
14260        e: &Engine,
14261        tokens: &[u32],
14262        pos0: usize,
14263        cache: &mut Cache,
14264    ) -> Result<(Vec<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14265        let (ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
14266        let t = tokens.len();
14267        let n_vocab = self.output.out_features();
14268        let mut toks = e.stream().alloc_zeros::<u32>(t)?;
14269        for i in 0..t {
14270            e.argmax_token_device_col(&ld, i, n_vocab, &mut toks, i)?;
14271        }
14272        Ok((e.dtoh_u32(&toks)?, hn))
14273    }
14274
14275    /// Device-token verify (async spec round): tokens live in tok_d[0..t]; per-row argmax
14276    /// lands in a DEVICE buffer (no host logits). Returns (vam_d [t] u32 device, hn stack).
14277    pub(crate) fn gemma4_decode_step_t_am_dev(
14278        &self,
14279        e: &Engine,
14280        tok_d: &CudaSlice<u32>,
14281        t: usize,
14282        pos0: usize,
14283        cache: &mut Cache,
14284    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14285        let (ld, hn) = self.gemma4_verify_trunk(e, &vec![0u32; t], pos0, cache, Some(tok_d))?;
14286        let n_vocab = self.output.out_features();
14287        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14288        for i in 0..t {
14289            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14290        }
14291        Ok((vam, hn))
14292    }
14293
14294    /// gemma4 verify + the POST-output_norm hidden stack [t, n_embd] (the drafter's h input —
14295    /// llama's h_nextn convention).
14296    pub(crate) fn gemma4_decode_step_t_h(
14297        &self,
14298        e: &Engine,
14299        tokens: &[u32],
14300        pos0: usize,
14301        cache: &mut Cache,
14302    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14303        let (mut ld, hn) = self.gemma4_verify_trunk(e, tokens, pos0, cache, None)?;
14304        let t = tokens.len();
14305        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
14306        e.softcap(&mut ld, cap, t * self.output.out_features())?;
14307        Ok((e.dtoh(&ld)?, hn))
14308    }
14309
14310    /// Persistent device scratch for the BURST verify stream (one per generation): rope pos
14311    /// rows [cap] + per-row t_kv counters (device-filled from the round counter, zero H2D).
14312    pub(crate) fn verify_stream_scratch(
14313        &self,
14314        e: &Engine,
14315        cap: usize,
14316    ) -> Result<VerifyStreamScratch, Box<dyn std::error::Error>> {
14317        Ok(VerifyStreamScratch {
14318            pos_d: e.htod_i32(&vec![0i32; cap])?,
14319            row_ctrs: (0..cap)
14320                .map(|_| e.htod_i32(&[0]))
14321                .collect::<Result<_, _>>()?,
14322        })
14323    }
14324
14325    /// BURST verify trunk (device-slot twin): tokens from a device buffer, rope positions
14326    /// iota'd from `ctr`, appends/attention at the layers' len_d counters (verify_attn_stream),
14327    /// NO host cache.pos/len advance. Returns (per-row device argmaxes [t], post-norm hidden
14328    /// stack [t, n_embd]) — the burst's accept/seed inputs, zero host readbacks.
14329    /// `scr` = PERSISTENT per-gen scratch (pos rows + per-row t_kv counters): the first cut
14330    /// htod_i32-allocated these per call — 9 pageable H2D copies per round, each a stream
14331    /// sync, exactly the turnaround the burst exists to remove.
14332    pub(crate) fn gemma4_verify_t_am_stream(
14333        &self,
14334        e: &Engine,
14335        tok_d: &CudaSlice<u32>,
14336        t: usize,
14337        ctr: &CudaSlice<i32>,
14338        hint: usize,
14339        cache: &mut Cache,
14340        scr: &mut VerifyStreamScratch,
14341    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14342        let n_embd = self.cfg.n_embd as usize;
14343        let eps = self.cfg.rms_eps;
14344        assert!(t <= scr.row_ctrs.len() && t <= 64);
14345        e.i32_iota_from(ctr, &mut scr.pos_d, t)?;
14346        for i in 0..t {
14347            e.i32_copy_add(ctr, &mut scr.row_ctrs[i], (i + 1) as i32)?;
14348        }
14349        let (pos_d, row_ctrs) = (&scr.pos_d, &scr.row_ctrs);
14350        let embd_gpu = self
14351            .embd_gpu
14352            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14353        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14354        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
14355        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14356        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14357        let n_layers = self.layers.len();
14358        for (il, layer) in self.layers.iter().enumerate() {
14359            let (hq, hdq) = match h_carry.take() {
14360                Some(p) => p,
14361                None => {
14362                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14363                }
14364            };
14365            let Mixer::Full(fa) = &layer.mixer else {
14366                panic!("gemma4 layer {il} not full-attn")
14367            };
14368            let o = self
14369                .gemma4_verify_attn_stream(e, fa, il, &hq, &hdq, pos_d, t, cache, hint, row_ctrs)?;
14370            let next_norm = if il + 1 < n_layers {
14371                Some(self.layers[il + 1].attn_norm.float_data())
14372            } else {
14373                None
14374            };
14375            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14376            x = xn;
14377            h_carry = hn;
14378            self.dflash_tap(e, cache, il, &x, t)?;
14379        }
14380        let mut hn = e.uninit(t * n_embd)?;
14381        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14382        let ld = e.matmul(&self.output, &hn, t)?;
14383        let n_vocab = self.output.out_features();
14384        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
14385        for i in 0..t {
14386            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
14387        }
14388        Ok((vam, hn))
14389    }
14390
14391    /// Verify trunk core: returns (UN-softcapped logits device [t, n_vocab], post-output_norm
14392    /// hidden stack [t, n_embd]); appends KV rows + advances cache.pos.
14393    /// DFlash tap write (dflash lane): copy the post-layer residual rows of `x` into the
14394    /// armed sink at the tap slot for `il` (row-major [t, n_taps*hidden]). Per-row D2D
14395    /// copies — t <= block_size on verify; prime pays t*n_taps once per prompt (dedicated
14396    /// kernel later if it shows in the profile).
14397    pub(crate) fn dflash_tap(
14398        &self,
14399        e: &Engine,
14400        cache: &mut Cache,
14401        il: usize,
14402        x: &CudaSlice<f32>,
14403        t: usize,
14404    ) -> Result<(), Box<dyn std::error::Error>> {
14405        let Some(taps) = cache.dflash_taps.as_mut() else {
14406            return Ok(());
14407        };
14408        let Some(slot) = taps.layer_ids.iter().position(|&l| l == il) else {
14409            return Ok(());
14410        };
14411        let h = taps.hidden;
14412        let n_taps = taps.layer_ids.len();
14413        let base = taps.base;
14414        debug_assert!(
14415            base + t <= taps.t,
14416            "tap window {base}+{t} exceeds sink {}",
14417            taps.t
14418        );
14419        let xv = e.view(x, t * h);
14420        for r in 0..t {
14421            let row = xv.slice(r * h..(r + 1) * h);
14422            e.copy_view_into(&mut taps.buf, (base + r) * n_taps * h + slot * h, &row, h)?;
14423        }
14424        Ok(())
14425    }
14426
14427    fn gemma4_verify_trunk(
14428        &self,
14429        e: &Engine,
14430        tokens: &[u32],
14431        pos0: usize,
14432        cache: &mut Cache,
14433        tok_dev: Option<&CudaSlice<u32>>,
14434    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14435        let n_embd = self.cfg.n_embd as usize;
14436        let eps = self.cfg.rms_eps;
14437        let t = tokens.len();
14438        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
14439        let pos_d = e.htod_i32(&pos)?;
14440        let mut x = match tok_dev {
14441            Some(td) => {
14442                let embd_gpu = self
14443                    .embd_gpu
14444                    .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
14445                let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
14446                e.embed_gather_device_td(embd_gpu, td, t, n_embd, qt, rb)?
14447            }
14448            None => e.htod(&self.embd.gather(n_embd, tokens))?,
14449        };
14450        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
14451        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
14452        let n_layers = self.layers.len();
14453        for (il, layer) in self.layers.iter().enumerate() {
14454            let (hq, hdq) = match h_carry.take() {
14455                Some(p) => p,
14456                None => {
14457                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, t, eps)?
14458                }
14459            };
14460            let Mixer::Full(fa) = &layer.mixer else {
14461                panic!("gemma4 layer {il} not full-attn")
14462            };
14463            let o = self.gemma4_verify_attn(e, fa, il, &hq, &hdq, &pos_d, t, cache)?;
14464            let next_norm = if il + 1 < n_layers {
14465                Some(self.layers[il + 1].attn_norm.float_data())
14466            } else {
14467                None
14468            };
14469            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, t, next_norm)?;
14470            x = xn;
14471            h_carry = hn;
14472            self.dflash_tap(e, cache, il, &x, t)?;
14473        }
14474        let mut hn = e.uninit(t * n_embd)?;
14475        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
14476        let mut ld = e.matmul(&self.output, &hn, t)?;
14477        self.gemma4_suppress(e, &mut ld, t)?; // before the per-row argmax consumers
14478        cache.pos += t;
14479        Ok((ld, hn))
14480    }
14481
14482    /// Verify attention: project/norm/rope t rows, append them to the cache, then attend each
14483    /// row causally over [win_off_i .. base+i] via the SAME fa_decode dispatch as T=1 decode.
14484    /// BURST verify attention (device-slot twin of `gemma4_verify_attn`): the append lands
14485    /// at the layer's len_d counter (rows_dc), attention bases ride the counter (rows /
14486    /// rows_w with base_dev), and NO host len is read or bumped — `hint` is a host UPPER
14487    /// bound on base_len used only for split sizing and arm gating (the burst loop passes
14488    /// pos0 + burst slack). Host len mirrors re-sync at the burst drain.
14489    #[allow(clippy::too_many_arguments)]
14490    fn gemma4_verify_attn_stream(
14491        &self,
14492        e: &Engine,
14493        fa: &crate::hybrid::FullAttnLayer,
14494        il: usize,
14495        hq: &CudaSlice<i8>,
14496        hdq: &CudaSlice<f32>,
14497        pos_d: &CudaSlice<i32>,
14498        t: usize,
14499        cache: &mut Cache,
14500        hint: usize,
14501        row_ctrs: &[CudaSlice<i32>],
14502    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14503        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14504        let eps = self.cfg.rms_eps;
14505        let aux = self.gemma4_aux.as_ref().unwrap();
14506        let ones = aux.ones(e);
14507        #[cfg(debug_assertions)]
14508        crate::debug_assert_tensor_stream_device(
14509            ones,
14510            &e.stream(),
14511            "gemma4_verify_attn_stream.ones",
14512        );
14513        let h0 = e.zeros(0)?;
14514        let h = &h0;
14515        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14516        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14517        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14518        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14519        let fused_qkv = if f2b {
14520            if swa {
14521                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14522                    .map(|(a, b, c)| (a, b, Some(c)))
14523            } else {
14524                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14525                    .map(|(a, b)| (a, b, None))
14526            }
14527        } else {
14528            None
14529        };
14530        let (q0, k0, v0) = match fused_qkv {
14531            Some((a, b, cv)) => {
14532                let v = match cv {
14533                    Some(c) => c,
14534                    None => e.clone_dtod(&b)?,
14535                };
14536                (a, b, v)
14537            }
14538            None => {
14539                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14540                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14541                let v0 = if swa {
14542                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14543                } else {
14544                    e.clone_dtod(&k0)?
14545                };
14546                (q0, k0, v0)
14547            }
14548        };
14549        let mut q = e.uninit(t * nh * hd)?;
14550        let mut k = e.uninit(t * nkv * hd)?;
14551        let mut v = e.uninit(t * nkv * hd)?;
14552        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14553        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14554        let ff = if swa {
14555            None
14556        } else {
14557            Some(
14558                aux.rope_freqs(e)
14559                    .expect("gemma4 global rope needs rope_freqs.weight"),
14560            )
14561        };
14562        #[cfg(debug_assertions)]
14563        if let Some(ff) = ff {
14564            crate::debug_assert_tensor_stream_device(
14565                ff,
14566                &e.stream(),
14567                "gemma4_verify_attn_stream.rope_freqs",
14568            );
14569        }
14570        e.rms_norm_qkv_rope(
14571            &q0,
14572            &k0,
14573            &v0,
14574            fa.q_norm.float_data(),
14575            fa.k_norm.float_data(),
14576            ones,
14577            &mut q,
14578            &mut k,
14579            &mut v,
14580            hd,
14581            self.gemma4_rope_dims(il),
14582            nh * t,
14583            nkv * t,
14584            pos_d,
14585            nh,
14586            nkv,
14587            base,
14588            1.0,
14589            ff,
14590            eps,
14591        )?;
14592        let kvl = cache.kv[il].as_mut().unwrap();
14593        // append at the DEVICE slot; the counter advances by t on-device.
14594        e.append_kv_quantized_rows_dc(
14595            &k,
14596            &v,
14597            &mut kvl.k,
14598            &mut kvl.v,
14599            &kvl.len_d,
14600            t,
14601            kvl.kv_dim_k,
14602            kvl.kv_dim_v,
14603            kvl.k_tok_bytes,
14604            kvl.v_tok_bytes,
14605            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14606        )?;
14607        // len_d is NOT advanced here: the burst's device rollback (spec_rollback_stream) is
14608        // the sole len writer after this round's attention (base stays = old len, plus = 0).
14609        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14610        let mut attn = e.uninit(t * nh * hd)?;
14611        let k_view = e.view_u8(&kvl.k, kvl.k.len());
14612        let v_view = e.view_u8(&kvl.v, kvl.v.len());
14613        // arm gating on the host HINT (upper bound; burst entry guards hint >= the vec floor
14614        // and a stable window regime — the same rung/regime keys as the draft graph).
14615        if swa && hint + 1 >= win {
14616            // fully-windowed rows: per-row window geometry from the counter (plus = 0: len_d
14617            // still holds the pre-append len; row r's T_kv = ctr + r + 1).
14618            e.fa_decode_rows_w(
14619                &q,
14620                &k_view,
14621                &v_view,
14622                &mut attn,
14623                hd,
14624                nh,
14625                nkv,
14626                &kvl.len_d,
14627                0,
14628                t,
14629                scale,
14630                win,
14631                kvl.k_tok_bytes,
14632                kvl.v_tok_bytes,
14633                None,
14634            )?;
14635        } else if hd == 512 && hint + t < crate::fa512_min_tkv() {
14636            // globals UNDER the fa512 crossover: eager runs the per-row fa_decode_kvmod
14637            // fallback there (parity with t=1 decode) — mirror it with per-row fa_decode_dc
14638            // (bit-correct for any t_kv <= bucket; the drafter dc arc proved the pairing).
14639            // Burst entry gates the horizon onto one side of the crossover, so hint decides
14640            // for every row.
14641            // NO .max(512): fa_decode_dc gates its hd512 dpl16-vs-scalar pick on bucket_max
14642            // (mirroring eager's fa512 floor) — forcing 512 here flipped the arm to dpl16
14643            // while eager ran scalar (il=5 KV drift, the burst's 4/128). SAME LAW capped
14644            // from above (2026-07-13): a regime-pinned hint near the floor (round-graph
14645            // captures use f512-1) pow2-rounds PAST it — clamp under the floor, or the arm
14646            // re-flips to dpl16 (the round-graph 4/64). The scalar unified self-splits, so
14647            // any bucket >= the live length is exact.
14648            let bucket = (hint + t + 2)
14649                .next_power_of_two()
14650                .min(crate::fa512_min_tkv().saturating_sub(1));
14651            let qv = e.view(&q, t * nh * hd);
14652            for i in 0..t {
14653                let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
14654                let mut q_one = e.uninit(nh * hd)?;
14655                e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14656                let mut a_one = e.uninit(nh * hd)?;
14657                e.fa_decode_dc(
14658                    &q_one,
14659                    &k_view,
14660                    &v_view,
14661                    &mut a_one,
14662                    hd,
14663                    nh,
14664                    nkv,
14665                    &row_ctrs[i],
14666                    bucket,
14667                    scale,
14668                    kvl.k_tok_bytes,
14669                    kvl.v_tok_bytes,
14670                    false,
14671                )?;
14672                e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
14673            }
14674        } else if hd == 512 {
14675            // globals past the crossover: dpl16 dc twin via the shared rows wrapper (hint
14676            // sizes splits — upper bound; splits beyond the device len exit in-kernel).
14677            e.fa_decode_rows(
14678                &q,
14679                &k_view,
14680                &v_view,
14681                &mut attn,
14682                hd,
14683                nh,
14684                nkv,
14685                hint,
14686                t,
14687                scale,
14688                kvl.k_tok_bytes,
14689                kvl.v_tok_bytes,
14690                Some((&kvl.len_d, 0)),
14691                false,
14692                false,
14693                None,
14694            )?;
14695        } else {
14696            // hd256 under-window: v4 device-len rows twin.
14697            e.fa_decode_rows_dc(
14698                &q,
14699                &k_view,
14700                &v_view,
14701                &mut attn,
14702                hd,
14703                nh,
14704                nkv,
14705                &kvl.len_d,
14706                hint + t,
14707                t,
14708                scale,
14709                kvl.k_tok_bytes,
14710                kvl.v_tok_bytes,
14711                0,
14712                swa && crate::Engine::wkv_on(),
14713            )?;
14714        }
14715        Ok(e.matmul(&fa.wo, &attn, t)?)
14716    }
14717
14718    fn gemma4_verify_attn(
14719        &self,
14720        e: &Engine,
14721        fa: &crate::hybrid::FullAttnLayer,
14722        il: usize,
14723        hq: &CudaSlice<i8>,
14724        hdq: &CudaSlice<f32>,
14725        pos_d: &CudaSlice<i32>,
14726        t: usize,
14727        cache: &mut Cache,
14728    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14729        let (hd, nkv, nh, base, scale, swa) = self.gemma4_geom(il);
14730        let eps = self.cfg.rms_eps;
14731        let aux = self.gemma4_aux.as_ref().unwrap();
14732        let ones = aux.ones(e);
14733        #[cfg(debug_assertions)]
14734        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_verify_attn.ones");
14735        let n_embd = self.cfg.n_embd as usize;
14736        let _ = n_embd;
14737
14738        let h0 = e.zeros(0)?;
14739        let h = &h0;
14740        // BATCHED FUSED qkv (MEMRA_F2B=1, megakernel microcosm): swa layers fuse all three,
14741        // globals fuse q,k (v := k clone). Bit-identical per row; segments tail-fill.
14742        static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14743        let f2b = *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0"));
14744        let fused_qkv = if f2b {
14745            if swa {
14746                e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
14747                    .map(|(a, b, c)| (a, b, Some(c)))
14748            } else {
14749                e.matmul_q4_fused2_batched(&fa.wq, &fa.wk, hq, hdq, t)?
14750                    .map(|(a, b)| (a, b, None))
14751            }
14752        } else {
14753            None
14754        };
14755        let (q0, k0, v0) = match fused_qkv {
14756            Some((a, b, cv)) => {
14757                let v = match cv {
14758                    Some(c) => c,
14759                    None => e.clone_dtod(&b)?,
14760                };
14761                (a, b, v)
14762            }
14763            None => {
14764                let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
14765                let k0 = e.matmul_pre(&fa.wk, hq, hdq, h, t)?;
14766                let v0 = if swa {
14767                    e.matmul_pre(&fa.wv, hq, hdq, h, t)?
14768                } else {
14769                    e.clone_dtod(&k0)?
14770                };
14771                (q0, k0, v0)
14772            }
14773        };
14774        let mut q = e.uninit(t * nh * hd)?;
14775        let mut k = e.uninit(t * nkv * hd)?;
14776        let mut v = e.uninit(t * nkv * hd)?;
14777        // E4B wave-3 fold (2026-07-23 decode-dust port): q/k/v norms + q/k rope in ONE
14778        // launch — rope math verbatim on the normed rows; V ones-rms, never roped.
14779        let ff = if swa {
14780            None
14781        } else {
14782            Some(
14783                aux.rope_freqs(e)
14784                    .expect("gemma4 global rope needs rope_freqs.weight"),
14785            )
14786        };
14787        #[cfg(debug_assertions)]
14788        if let Some(ff) = ff {
14789            crate::debug_assert_tensor_stream_device(
14790                ff,
14791                &e.stream(),
14792                "gemma4_verify_attn.rope_freqs",
14793            );
14794        }
14795        e.rms_norm_qkv_rope(
14796            &q0,
14797            &k0,
14798            &v0,
14799            fa.q_norm.float_data(),
14800            fa.k_norm.float_data(),
14801            ones,
14802            &mut q,
14803            &mut k,
14804            &mut v,
14805            hd,
14806            self.gemma4_rope_dims(il),
14807            nh * t,
14808            nkv * t,
14809            pos_d,
14810            nh,
14811            nkv,
14812            base,
14813            1.0,
14814            ff,
14815            eps,
14816        )?;
14817        let kvl = cache.kv[il].as_mut().unwrap();
14818        let base_len = kvl.len;
14819        e.append_kv_quantized_rows(
14820            &k,
14821            &v,
14822            &mut kvl.k,
14823            &mut kvl.v,
14824            base_len,
14825            t,
14826            kvl.kv_dim_k,
14827            kvl.kv_dim_v,
14828            kvl.k_tok_bytes,
14829            kvl.v_tok_bytes,
14830            (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
14831        )?;
14832        kvl.len += t;
14833        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
14834        let mut attn = e.uninit(t * nh * hd)?;
14835        // ROWS twin when no window offset is in play (globals are hd512-ineligible; SWA rows
14836        // under the window need no offset): ONE launch, per-row causal == the per-token loop.
14837        let rows_ok = (hd == 256 && base_len + 1 >= crate::fa_vec_min_tkv())
14838            // gemma globals: hd512 rows twin in the dpl16 vec regime (row 0 gates the batch);
14839            // decode rides the SAME symbol at t=1 (parity law).
14840            || (hd == 512 && !swa && base_len + 1 >= crate::fa512_min_tkv());
14841        if rows_ok && (!swa || base_len + t <= win) {
14842            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14843            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14844            if hd == 512 {
14845                // device-len twin: sync the counter to the verify base (async arg-store).
14846                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14847                e.fa_decode_rows(
14848                    &q,
14849                    &k_view,
14850                    &v_view,
14851                    &mut attn,
14852                    hd,
14853                    nh,
14854                    nkv,
14855                    base_len,
14856                    t,
14857                    scale,
14858                    kvl.k_tok_bytes,
14859                    kvl.v_tok_bytes,
14860                    Some((&kvl.len_d, 0)),
14861                    false,
14862                    swa && crate::Engine::wkv_on(),
14863                    None,
14864                )?;
14865            } else {
14866                // hd256: the SAME v4_dc symbol the burst verify launches (PARITY LAW — the
14867                // host-base rows_v4 twin compiles apart from v4_dc and the two-symbol split
14868                // drifted the burst's persisted KV at il=8; one symbol, counter arg-stored).
14869                e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14870                e.fa_decode_rows_dc(
14871                    &q,
14872                    &k_view,
14873                    &v_view,
14874                    &mut attn,
14875                    hd,
14876                    nh,
14877                    nkv,
14878                    &kvl.len_d,
14879                    base_len + t,
14880                    t,
14881                    scale,
14882                    kvl.k_tok_bytes,
14883                    kvl.v_tok_bytes,
14884                    0,
14885                    swa && crate::Engine::wkv_on(),
14886                )?;
14887            }
14888            return Ok(e.matmul(&fa.wo, &attn, t)?);
14889        }
14890        // WINDOWED rows twin (deep ctx, every row fully windowed): one launch, ABSOLUTE-index
14891        // per-row geometry over the prefix view. PARITY LAW (2026-07-10 root-cause): textually
14892        // identical kernels do NOT compile bit-identically (nvcc unrolls fa_decode_vec_q 2x vs
14893        // its rows_w clone — SASS-proven; the unpinned score `+=` chain then rounds apart), so
14894        // decode-vs-verify parity comes from BOTH sides launching THIS SAME rows_w kernel
14895        // (decode passes t=1) — bitwise equal per position by symbol identity, any lane.
14896        // MEMRA_GEMMA_ROWS_W=0 -> per-token loop (decode falls back to fa_decode views too).
14897        if hd == 256
14898            && swa
14899            && base_len + 1 >= win
14900            && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14901        {
14902            let k_view = e.view_u8(&kvl.k, (base_len + t) * kvl.k_tok_bytes);
14903            let v_view = e.view_u8(&kvl.v, (base_len + t) * kvl.v_tok_bytes);
14904            e.i32_set_k(&mut kvl.len_d, base_len as i32)?;
14905            e.fa_decode_rows_w(
14906                &q,
14907                &k_view,
14908                &v_view,
14909                &mut attn,
14910                hd,
14911                nh,
14912                nkv,
14913                &kvl.len_d,
14914                0,
14915                t,
14916                scale,
14917                win,
14918                kvl.k_tok_bytes,
14919                kvl.v_tok_bytes,
14920                None,
14921            )?;
14922            return Ok(e.matmul(&fa.wo, &attn, t)?);
14923        }
14924        for i in 0..t {
14925            let avail = base_len + i + 1;
14926            let (off_tok, t_kv) = if swa && avail > win {
14927                (avail - win, win)
14928            } else {
14929                (0, avail)
14930            };
14931            let k_view = e.view_u8_range(
14932                &kvl.k,
14933                off_tok * kvl.k_tok_bytes,
14934                (off_tok + t_kv) * kvl.k_tok_bytes,
14935            );
14936            let v_view = e.view_u8_range(
14937                &kvl.v,
14938                off_tok * kvl.v_tok_bytes,
14939                (off_tok + t_kv) * kvl.v_tok_bytes,
14940            );
14941            let qi = e.view(&q, t * nh * hd);
14942            let q_row = qi.slice(i * nh * hd..(i + 1) * nh * hd);
14943            let mut q_one = e.uninit(nh * hd)?;
14944            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
14945            let mut a_one = e.uninit(nh * hd)?;
14946            // straddle rounds: windowed rows must ride the SAME rows_w kernel decode uses
14947            // (parity law above); global hd512 rows past the fa512 floor likewise ride the
14948            // rows_dpl16 twin; remaining rows keep the gated fa_decode pair.
14949            if swa
14950                && avail > win
14951                && hd == 256
14952                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14953            {
14954                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14955                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14956                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14957                e.fa_decode_rows_w(
14958                    &q_one,
14959                    &kp,
14960                    &vp,
14961                    &mut a_one,
14962                    hd,
14963                    nh,
14964                    nkv,
14965                    &kvl.len_d,
14966                    0,
14967                    1,
14968                    scale,
14969                    win,
14970                    kvl.k_tok_bytes,
14971                    kvl.v_tok_bytes,
14972                    None,
14973                )?;
14974            } else if !swa
14975                && hd == 512
14976                && avail >= crate::fa512_min_tkv()
14977                && std::env::var("MEMRA_GEMMA_ROWS_W").as_deref() != Ok("0")
14978            {
14979                let kp = e.view_u8(&kvl.k, avail * kvl.k_tok_bytes);
14980                let vp = e.view_u8(&kvl.v, avail * kvl.v_tok_bytes);
14981                e.i32_set_k(&mut kvl.len_d, (avail - 1) as i32)?;
14982                e.fa_decode_rows(
14983                    &q_one,
14984                    &kp,
14985                    &vp,
14986                    &mut a_one,
14987                    hd,
14988                    nh,
14989                    nkv,
14990                    avail - 1,
14991                    1,
14992                    scale,
14993                    kvl.k_tok_bytes,
14994                    kvl.v_tok_bytes,
14995                    Some((&kvl.len_d, 0)),
14996                    false,
14997                    false,
14998                    None,
14999                )?;
15000            } else {
15001                e.fa_decode_kvmod(
15002                    &q_one,
15003                    &k_view,
15004                    &v_view,
15005                    &mut a_one,
15006                    hd,
15007                    nh,
15008                    nkv,
15009                    t_kv,
15010                    scale,
15011                    kvl.k_tok_bytes,
15012                    kvl.v_tok_bytes,
15013                    swa && crate::Engine::wkv_on(),
15014                )?;
15015            }
15016            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
15017        }
15018        Ok(e.matmul(&fa.wo, &attn, t)?)
15019    }
15020
15021    /// gemma4 T=1 decode step: R8 layer graph over the cache; returns (softcapped logits host,
15022    /// h_seed = pre-output_norm hidden). Advances cache.pos.
15023    pub(crate) fn gemma4_decode_step_h(
15024        &self,
15025        e: &Engine,
15026        token: u32,
15027        cache: &mut Cache,
15028    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15029        // M1-PP2 door (crate::pp): 2-stage split with an explicit activation handoff.
15030        // Default OFF — unset env means this branch is never taken. The gemma4 arm stays
15031        // 2-stage in M2 (the N-stage gate model is the generic-arm 9B); N>2 warns + runs
15032        // unsplit rather than guessing a fence.
15033        if let Some(split) = crate::pp::pp2_split(self.layers.len()) {
15034            return self.gemma4_decode_step_h_pp2(e, token, cache, split);
15035        }
15036        if crate::pp::pp_cuts(self.layers.len()).is_some() {
15037            crate::pp::warn_unwired_once("gemma4 eager decode (N>2)");
15038        }
15039        let n_embd = self.cfg.n_embd as usize;
15040        let eps = self.cfg.rms_eps;
15041        let pos_d = e.htod_i32(&[cache.pos as i32])?;
15042        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
15043        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15044        // cross-layer fusion: each tail's closing add+scale also EMITS the next layer's
15045        // attn-normed input pre-quantized q8_1 (the mixer consumes only quantized matmuls).
15046        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
15047        let n_layers = self.layers.len();
15048        for (il, layer) in self.layers.iter().enumerate() {
15049            let (hq, hdq) = match h_carry.take() {
15050                Some(p) => p,
15051                None => {
15052                    e.rms_norm_q8_1(&x, self.layers[0].attn_norm.float_data(), n_embd, 1, eps)?
15053                }
15054            };
15055            let Mixer::Full(fa) = &layer.mixer else {
15056                panic!("gemma4 layer {il} not full-attn")
15057            };
15058            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, &pos_d, cache)?;
15059            let next_norm = if il + 1 < n_layers {
15060                Some(self.layers[il + 1].attn_norm.float_data())
15061            } else {
15062                None
15063            };
15064            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
15065            x = xn;
15066            h_carry = hn;
15067        }
15068        let mut hn = e.uninit(n_embd)?;
15069        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15070        let h_seed = e.clone_dtod(&x)?;
15071        let mut ld = e.matmul(&self.output, &hn, 1)?;
15072        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15073        e.softcap(&mut ld, cap, self.output.out_features())?; // R4 on device (262k host tanh ~ms/step)
15074        self.gemma4_suppress(e, &mut ld, 1)?;
15075        let logits = e.dtoh(&ld)?;
15076        cache.pos += 1;
15077        Ok((logits, h_seed))
15078    }
15079
15080    /// M1-PP2 stage subgraph (gemma4 arm): layers [lo, hi) of the gemma4 T=1 walk with the
15081    /// pre-quantized next-norm carry LOCAL to the range. Enters with a materialized residual
15082    /// `x` (the range head runs its own `rms_norm_q8_1` against ITS layer's attn_norm — for
15083    /// lo == 0 that is exactly the unsplit loop's il==0 arm); exits with the residual
15084    /// materialized (range tail passes next_norm = None, exactly the unsplit last-layer arm).
15085    /// Bit-identity of the cut relies on the kernel-check-pinned `add_scale_rms_norm_q8_1 ==
15086    /// add_scale then rms_norm_q8_1` identity (`pp2-gate` verifies end-to-end).
15087    fn gemma4_decode_layers(
15088        &self,
15089        e: &Engine,
15090        mut x: CudaSlice<f32>,
15091        lo: usize,
15092        hi: usize,
15093        pos_d: &CudaSlice<i32>,
15094        cache: &mut Cache,
15095    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15096        let n_embd = self.cfg.n_embd as usize;
15097        let eps = self.cfg.rms_eps;
15098        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
15099        for il in lo..hi {
15100            let layer = &self.layers[il];
15101            let (hq, hdq) = match h_carry.take() {
15102                Some(p) => p,
15103                // range head: il == lo — norm against THIS layer's attn_norm.
15104                None => {
15105                    e.rms_norm_q8_1(&x, self.layers[il].attn_norm.float_data(), n_embd, 1, eps)?
15106                }
15107            };
15108            let Mixer::Full(fa) = &layer.mixer else {
15109                panic!("gemma4 layer {il} not full-attn")
15110            };
15111            let o = self.gemma4_decode_attn(e, fa, il, &hq, &hdq, pos_d, cache)?;
15112            let next_norm = if il + 1 < hi {
15113                Some(self.layers[il + 1].attn_norm.float_data())
15114            } else {
15115                None
15116            };
15117            let (xn, hn) = self.gemma4_layer_tail_add_nq_pn(e, layer, &o, &x, 1, next_norm)?;
15118            x = xn;
15119            h_carry = hn;
15120        }
15121        Ok(x)
15122    }
15123
15124    /// M1-PP2 (increment 2): `gemma4_decode_step_h` as TWO stage subgraphs, each on its
15125    /// own stream (and device, under MEMRA_PP_DEVICES) with the transport-selected
15126    /// boundary handoff — same choreography as the generic arm (decode.rs), same
15127    /// ownership contract (crate::pp). Stage 0 = embed+scale + layers [0, split);
15128    /// stage 1 = layers [split, n) + output_norm + softcapped head.
15129    /// Each stage uploads its own copy of the step's position scalar on its own stream.
15130    /// MEMRA_PP_STREAMS=0 = the increment-1 same-stream seam. Gate: `pp2-gate`.
15131    fn gemma4_decode_step_h_pp2(
15132        &self,
15133        e: &Engine,
15134        token: u32,
15135        cache: &mut Cache,
15136        split: usize,
15137    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15138        if crate::pp::pp2_streams_off() {
15139            return self.gemma4_decode_step_h_pp2_samestream(e, token, cache, split);
15140        }
15141        let rt = crate::pp::Pp2Rt::get(e)?;
15142        let e0 = rt.engine(0, e);
15143        let e1 = rt.engine(1, e);
15144        let n_embd = self.cfg.n_embd as usize;
15145        let eps = self.cfg.rms_eps;
15146        let pos = cache.pos as i32;
15147
15148        // ---- STAGE 0 (its own stream): embed + sqrt(n_embd) scale + layers [0, split) ----
15149        let slot = {
15150            let _st0 = rt.enter(0);
15151            let pos_d = e0.htod_i32(&[pos])?;
15152            #[cfg(debug_assertions)]
15153            crate::debug_assert_tensor_stream_device(
15154                &pos_d,
15155                &e0.stream(),
15156                "gemma4_decode_step_h_pp2.stage0.pos_d",
15157            );
15158            let mut x = e0.htod(&self.embd.gather(n_embd, &[token]))?;
15159            e0.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15160            let x = self.gemma4_decode_layers(e0, x, 0, split, &pos_d, cache)?;
15161            rt.tx(0, &x, n_embd)?
15162        };
15163
15164        // ---- STAGE 1 (its own stream): RX + layers [split, n) + softcapped head ----
15165        let _st1 = rt.enter(1);
15166        let pos_d = e1.htod_i32(&[pos])?;
15167        #[cfg(debug_assertions)]
15168        crate::debug_assert_tensor_stream_device(
15169            &pos_d,
15170            &e1.stream(),
15171            "gemma4_decode_step_h_pp2.stage1.pos_d",
15172        );
15173        let x = rt.rx(0, slot, n_embd)?;
15174        let x = self.gemma4_decode_layers(e1, x, split, self.layers.len(), &pos_d, cache)?;
15175
15176        let mut hn = e1.uninit(n_embd)?;
15177        e1.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15178        let h_seed = e1.clone_dtod(&x)?;
15179        let mut ld = e1.matmul(&self.output, &hn, 1)?;
15180        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15181        e1.softcap(&mut ld, cap, self.output.out_features())?;
15182        self.gemma4_suppress(e1, &mut ld, 1)?;
15183        let logits = e1.dtoh(&ld)?;
15184        cache.pos += 1;
15185        Ok((logits, h_seed))
15186    }
15187
15188    /// MEMRA_PP_STREAMS=0 rollback seam: the increment-1 gemma4 pp2 body verbatim — both
15189    /// stage subgraphs on the ambient compute stream, boundary = two plain dtod copies.
15190    fn gemma4_decode_step_h_pp2_samestream(
15191        &self,
15192        e: &Engine,
15193        token: u32,
15194        cache: &mut Cache,
15195        split: usize,
15196    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15197        let n_embd = self.cfg.n_embd as usize;
15198        let eps = self.cfg.rms_eps;
15199        let pos_d = e.htod_i32(&[cache.pos as i32])?;
15200
15201        // ---- STAGE 0: embed + sqrt(n_embd) scale + layers [0, split) ----
15202        let mut x = e.htod(&self.embd.gather(n_embd, &[token]))?;
15203        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
15204        let x = self.gemma4_decode_layers(e, x, 0, split, &pos_d, cache)?;
15205
15206        // ---- STAGE BOUNDARY: explicit [n_embd] activation handoff (TX copy, RX copy) ----
15207        let boundary_tx = e.clone_dtod(&x)?;
15208        let boundary_rx = e.clone_dtod(&boundary_tx)?;
15209
15210        // ---- STAGE 1: layers [split, n) + output_norm + softcapped head ----
15211        let x =
15212            self.gemma4_decode_layers(e, boundary_rx, split, self.layers.len(), &pos_d, cache)?;
15213
15214        let mut hn = e.uninit(n_embd)?;
15215        e.rms_norm(&x, self.output_norm.float_data(), &mut hn, n_embd, 1, eps)?;
15216        let h_seed = e.clone_dtod(&x)?;
15217        let mut ld = e.matmul(&self.output, &hn, 1)?;
15218        let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
15219        e.softcap(&mut ld, cap, self.output.out_features())?;
15220        self.gemma4_suppress(e, &mut ld, 1)?;
15221        let logits = e.dtoh(&ld)?;
15222        cache.pos += 1;
15223        Ok((logits, h_seed))
15224    }
15225}
15226
15227// ============================ step35 (Step-3.7-Flash) ==================================
15228// Node-for-node vs llama.cpp src/models/step35.cpp:216-300. WHY THIS IS A DEDICATED MIXER
15229// FAMILY and not a few branches inside the generic `full_attn*` chain:
15230//
15231//   1. `n_head` IS PER LAYER (`step35.attention.head_count` is an ARRAY: 64 on full-attn
15232//      layers, 96 on SWA). Every generic site reads the `cfg.n_head` scalar, so wq/wo/attn_gate
15233//      shapes and the FA head counts would be wrong on 33 of 45 layers.
15234//   2. `cfg.rope_dim_count` is 128 for this arch, but FULL-attn layers rotate only 64 dims
15235//      (upstream halves `n_rot_full` after the generic loader defaults it). A generic mixer
15236//      would rotate 128 dims on the full layers — silently wrong, plausible-looking logits.
15237//   3. Dual rope base (5e6 full / 1e4 SWA) + `rope_freqs.weight` on FULL layers ONLY.
15238//   4. SWA 3:1 (`sliding_window` 512, pattern [false,true,true,true]).
15239//   5. The head-wise gate is a SEPARATE `attn_gate.weight [n_embd, n_head_l]` (one pre-sigmoid
15240//      scalar per head, broadcast over head_dim) applied to the attention output BEFORE wo —
15241//      not the qwen35 fused-in-wq per-DIM gate that `attn_out_gate()`/`q_gate_split` handle.
15242//      Its input is the POST-attn_norm hidden (`cur`), not the residual.
15243//
15244// Attention scale is the DEFAULT 1/sqrt(n_embd_head_k) (step35.cpp:255) — NOT gemma4's 1.0.
15245impl HybridModel {
15246    /// Step35's declarative geometry row. Missing rows are artifact/config errors; never
15247    /// synthesize a drafter or trunk layer from a neighboring class.
15248    pub(crate) fn step35_geom(&self, il: usize) -> memra_gguf::config::LayerGeometry {
15249        let geometry = self
15250            .cfg
15251            .layer_geometry(il as u32)
15252            .unwrap_or_else(|| panic!("step35 layer {il} has no geometry-table row"));
15253        debug_assert_eq!(
15254            geometry.attention_gate,
15255            memra_gguf::config::AttentionGateKind::SeparateHead
15256        );
15257        geometry
15258    }
15259
15260    /// step35 attention core: everything from the q/k/v projection outputs through the head-wise
15261    /// gate, returning the GATED attention output PRE-`wo` (the caller runs wo, so this composes
15262    /// with both the plain `matmul(&fa.wo, ..)` sites and the f16/into-slab GEMM sites).
15263    ///
15264    /// `hg` = the POST-attn_norm hidden state (upstream's `cur`) — the gate projection's input
15265    /// on single-sequence paths. `gt_pre` supplies the already-projected per-head gate for a
15266    /// cross-request batch; exactly one of `hg` or `gt_pre` must be present.
15267    /// `cache`:
15268    ///   * `Some` => PRIME mode: append this chunk's post-rope K / raw V rows into the resident
15269    ///     quantized cache and attend THROUGH the cache view, exactly like the generic
15270    ///     `full_attn_prime_fa_dispatch` (quantize-then-attend on chunk 0 too, so chunk size
15271    ///     cannot decide where a precision edge falls — the grain-free chunk-invariance
15272    ///     contract, lane/chunkinv-flip).
15273    ///   * `None` => pure prefill (forward/forward_last/t2probe): attend over this batch's f32
15274    ///     q/k/v, no cache side effect.
15275    ///
15276    /// SWA WINDOW, and why prefill needs `sdpa_naive_w_quantized_view`: the view is trimmed to
15277    /// the oldest key ANY query in this chunk can reach (`off = base_len - (win-1)`), which
15278    /// bounds the view at `win-1+t` rows, but the mask is still required — inside the chunk,
15279    /// query `qt` may only see view keys `[qt, qt+win-1]`, so the earlier keys the trimmed view
15280    /// still contains must be masked per query. memra's window convention
15281    /// (`sdpa_naive_w_f32`: mask `t < q_pos-(window-1)`, `q_pos = (T_kv-T)+qt`) is bit-for-bit
15282    /// upstream's `LLAMA_SWA_TYPE_STANDARD` (`llama-hparams.h:359`: mask `p1-p0 >= n_swa`), and
15283    /// step35.cpp:6 sets exactly that swa_type — verified verbatim on both sides.
15284    ///
15285    /// Note the f32 `sdpa_naive_w` floor's shared memory is `t_kv*4` bytes, so the SWA arm needs
15286    /// `t_kv = win-1+t <= 12287` — i.e. it relies on chunked prefill (MEMRA_PRIME_CHUNK, default
15287    /// 4096). A monolithic 32k prime would exceed the 48 KiB dynamic-smem default.
15288    ///
15289    /// SWA ARM SELECTION IS KEYED ON `seq_end`, NOT ON THE CHUNK'S OWN `t_kv` (lane/step35-chunkfix,
15290    /// 2026-08-07). `seq_end` = the ABSOLUTE end position of the whole prime call
15291    /// (`cache.pos + prompt_len` at entry; `t` on the cacheless prefill path) — a property of the
15292    /// REQUEST, identical at every MEMRA_PRIME_CHUNK value. The old predicate `swa && t_kv > win`
15293    /// read the chunk's own extent, which made kernel selection — and therefore the logits, the
15294    /// hidden rows, and the generated text — a function of the chunk size:
15295    ///   a chunk [b,e) has off = max(0, b-(win-1)), t_kv = e-off, so b <= win-1 => off=0 => FA iff
15296    ///   e <= win, while every later chunk has t_kv = t+(win-1) > win. The FA rows were therefore a
15297    ///   contiguous PREFIX [0,P) with P = c*floor(win/c) for c <= win (else 0), and the output
15298    ///   depended only on P. Measured (research/step37-p2-20260806, closed-form receipt
15299    ///   `raw/chunkinv-step35-GAP2-CONFIRMED-20260807.txt`): at T=4883, c=512 and c=64 (P=512) are
15300    ///   byte-identical to each other but DIFFER from the c=4096 default (P=0) by maxdiff 1.813e0
15301    ///   with greedy text diverging at step 6; c=513 (P=0) is bit-identical to the default. A
15302    ///   one-token change in a documented machine-config knob changed the answer.
15303    /// So the pre-fix comment "same cache bytes, same numeric class" was false as written: same
15304    /// bytes, DIFFERENT numeric class — swapping `fa_prefill_view_ws` for the f32 windowed floor on
15305    /// the same rows moves the logits by ~1.8.
15306    ///
15307    /// Keying on `seq_end` makes P identically 0 for every chunk size, so MEMRA_PRIME_CHUNK is a
15308    /// pure memory/transient knob again (research/step35-chunkfix-20260807). It is
15309    /// correct-by-construction rather than a tolerance argument — the windowed kernel computes the
15310    /// same masked attention the FA arm computed (for e <= win the window mask is a no-op under
15311    /// causal, which is exactly why FA was legal there), it is now simply the ONLY arm used once
15312    /// the request passes the window. It also cannot move the shipped default: at chunk=4096 every
15313    /// chunk of a `seq_end > win` prime ALREADY had t_kv > win (chunk 0 has t_kv = min(chunk,
15314    /// seq_end) > win; every later chunk has t_kv >= t+win-1 > win since `PRIME_MIN_T` keeps
15315    /// t >= 16), and a `seq_end <= win` prime keeps every chunk on FA exactly as before — so this
15316    /// is a no-op on both default regimes and only removes the FA prefix at small chunk values.
15317    /// The `t_kv <= 12287` smem ceiling is respected: the chunks whose arm changes are precisely
15318    /// those with t_kv <= win = 512.
15319    #[allow(clippy::too_many_arguments)]
15320    fn step35_attn_pre_wo(
15321        &self,
15322        e: &Engine,
15323        fa: &FullAttnLayer,
15324        mut g3: Vec<CudaSlice<f32>>,
15325        hg: Option<&CudaSlice<f32>>,
15326        gt_pre: Option<&CudaSlice<f32>>,
15327        pos_d: &CudaSlice<i32>,
15328        t: usize,
15329        cache: Option<&mut Cache>,
15330        il: usize,
15331        seq_end: usize,
15332    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15333        let geometry = self.step35_geom(il);
15334        let hd = geometry.head_dim_k as usize;
15335        let nkv = geometry.n_head_kv as usize;
15336        let nh = geometry.n_head as usize;
15337        let rbase = geometry.rope_base;
15338        let scale = geometry.attention_scale();
15339        let swa = geometry.window.is_some();
15340        let eps = self.cfg.rms_eps;
15341        let win = geometry.window.unwrap_or(0) as usize;
15342        let n_rot = geometry.n_rot as usize;
15343
15344        let v = g3.pop().unwrap();
15345        let k0 = g3.pop().unwrap();
15346        let q0 = g3.pop().unwrap();
15347
15348        // q/k RMSNorm over head_dim rows (attn_q_norm/attn_k_norm [128] F32), then the
15349        // per-layer PARTIAL NEOX rope: n_rot = 64 on full layers (dims 64..127 pass through
15350        // unrotated), 128 on SWA. rope_freqs (llama3-style per-dim factors) on FULL only.
15351        let mut q = e.uninit(t * nh * hd)?;
15352        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh * t, eps)?;
15353        let mut k = e.uninit(t * nkv * hd)?;
15354        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv * t, eps)?;
15355        let ff = if geometry.rope_factors {
15356            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
15357        } else {
15358            None
15359        };
15360        #[cfg(debug_assertions)]
15361        if let Some(ff) = ff {
15362            crate::debug_assert_tensor_stream_device(
15363                ff,
15364                &e.stream(),
15365                "step35_attn_pre_wo.rope_freqs",
15366            );
15367        }
15368        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, t, rbase, 1.0, ff)?;
15369
15370        let mut attn = e.uninit(t * nh * hd)?;
15371        match cache {
15372            Some(cache) => {
15373                let base_len = cache.kv[il].as_ref().unwrap().len;
15374                // Read per layer call, never in a measured default.
15375                let legacy_tkv = std::env::var("MEMRA_STEP35_SWA_TKV").as_deref() == Ok("1");
15376                let legacy_calllocal = std::env::var("MEMRA_PRIME_CALLLOCAL").as_deref() == Ok("1");
15377                let off = if swa {
15378                    let raw = base_len.saturating_sub(win - 1);
15379                    if legacy_tkv || legacy_calllocal {
15380                        raw
15381                    } else {
15382                        raw & !31usize
15383                    }
15384                } else {
15385                    0
15386                };
15387                {
15388                    let kvl = cache.kv[il].as_mut().unwrap();
15389                    assert!(kvl.len + t <= cache.max_ctx, "step35 prime: KV overflow");
15390                    let write_row = e.prepare_kv_append(kvl, off, t)?;
15391                    e.append_kv_quantized_rows(
15392                        &k,
15393                        &v,
15394                        &mut kvl.k,
15395                        &mut kvl.v,
15396                        write_row,
15397                        t,
15398                        kvl.kv_dim_k,
15399                        kvl.kv_dim_v,
15400                        kvl.k_tok_bytes,
15401                        kvl.v_tok_bytes,
15402                        crate::Engine::kv_fp8_on(),
15403                    )?;
15404                    kvl.len += t;
15405                    let new_len = kvl.len as i32;
15406                    e.set_i32_one(&mut kvl.len_d, new_len)?;
15407                }
15408                let kvl = cache.kv[il].as_ref().unwrap();
15409                // Both segmentation seams restore the FULL pre-fix arithmetic: their legacy
15410                // arm predicate (`t_kv` below, per-call `seq_end` in prime_cache) AND the
15411                // unaligned view offset here. Both halves are load-bearing for the canaries:
15412                // on the FA default the predicate arms agree bitwise wherever they can differ
15413                // (a t_kv<=win view has no maskable key, so windowed==unwindowed FA bit-for-bit).
15414                // That made predicate-only seams INERT under Lever A — first chunkinv35c, then
15415                // tickinv35c. The raw offset restores the live segmentation-variant mechanism
15416                // on the current FA path: its tile grid starts at the chunk/call boundary.
15417                // SWA: trim the view to the oldest key any query in this chunk can reach —
15418                // ALIGNED DOWN to the FA tile size (BK=32). The raw trim is chunk-DEPENDENT
15419                // (off = base_len-(win-1), and base_len is a chunk boundary), and the FA
15420                // kernel's online-softmax recurrence groups keys into BK tiles relative to
15421                // the VIEW START — so an unaligned off regroups the same absolute keys into
15422                // different tiles at different chunk sizes = different (m,l) rounding =
15423                // chunk-dependent bits. chunkinv35 caught exactly this on the first FA-arm
15424                // battery (first_div == chunk size; raw/leverA-gates-20260807T135541Z.log).
15425                // Aligning off to 32 pins tiles to ABSOLUTE key positions for every chunk
15426                // size; the <=31 extra leading keys are older than EVERY query's window
15427                // (all queries sit at >= base_len, so keys < base_len-(win-1) are masked
15428                // for all of them) and a fully-masked key is an exact-0.0 no-op in both
15429                // kernels (NEG_INF -> p=0.0; l+=0.0 and O+=0.0 are bitwise identity), so
15430                // the floor arm's bits do not move either (gated: G2f, battery 2).
15431                let t_kv = base_len + t - off;
15432                let physical = kvl.physical_rows(off, off + t_kv)?;
15433                let k_view = e.view_u8_range(
15434                    &kvl.k,
15435                    physical.start * kvl.k_tok_bytes,
15436                    physical.end * kvl.k_tok_bytes,
15437                );
15438                let v_view = e.view_u8_range(
15439                    &kvl.v,
15440                    physical.start * kvl.v_tok_bytes,
15441                    physical.end * kvl.v_tok_bytes,
15442                );
15443                // CHUNK-INVARIANT PREDICATE: `seq_end` (the request's absolute end position), not
15444                // this chunk's `t_kv`. See the doc note — keying on t_kv made P = c*floor(win/c)
15445                // rows take FA and the output a function of MEMRA_PRIME_CHUNK.
15446                // MEMRA_STEP35_SWA_TKV=1 is the ROLLBACK SEAM to the pre-fix arithmetic — BOTH
15447                // halves: this predicate AND the unaligned view offset above (`legacy_tkv`).
15448                // It is what gives the step35 chunkinv gate its canary teeth: chunk-VARIANT by
15449                // construction, so the invariance assertion MUST break under it (the seam whose
15450                // absence made the original canary inert — GAP 1 in research/step37-p2-20260806;
15451                // and a predicate-only seam went inert AGAIN under the FA default, battery 2's
15452                // CANARY UNEXPECTEDLY MATCHED — see the offset comment). Read per call, not
15453                // cached (probes flip it in-process). Never on in a measured default run.
15454                let swa_naive = if legacy_tkv {
15455                    t_kv > win
15456                } else {
15457                    seq_end > win
15458                };
15459                if swa && swa_naive {
15460                    // Windowed mask needed (see the doc note). DEFAULT since lane/pp-prefill
15461                    // 2026-08-07: the windowed hd128 FA stamp (`fa_prefill_view_ws_w_hd128`) —
15462                    // the anatomy profile measured the f32 floor at 565 ms/layer on a pp4096
15463                    // (41% of the whole prime) while the unwindowed hd128 FA family did the
15464                    // strictly harder causal-4096 in 3.3 ms. NOTE t_kv can be <= win here
15465                    // (a chunk-0 view on a small chunk); the windowed kernel handles that
15466                    // identically to the unwindowed one modulo the mask, which is the point.
15467                    // NEW NUMERIC CLASS vs the floor (bf16-MMA online softmax vs f32 serial),
15468                    // selected on `seq_end` like every arm here, so the class is uniform for
15469                    // the whole request at every MEMRA_PRIME_CHUNK — chunkinv holds by the
15470                    // same construction as the chunkfix. MEMRA_STEP35_SWA_FA=0 = rollback to
15471                    // the f32 floor (the previous numeric config, kept as the A/B seam).
15472                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
15473                        e.sdpa_naive_w_quantized_view(
15474                            &q,
15475                            &k_view,
15476                            &v_view,
15477                            &mut attn,
15478                            hd,
15479                            nh,
15480                            nkv,
15481                            t,
15482                            t_kv,
15483                            scale,
15484                            true,
15485                            win,
15486                            kvl.k_tok_bytes,
15487                            kvl.v_tok_bytes,
15488                        )?;
15489                    } else {
15490                        e.fa_prefill_view_ws_w_hd128(
15491                            &q,
15492                            &k_view,
15493                            &v_view,
15494                            &mut attn,
15495                            hd,
15496                            nh,
15497                            nkv,
15498                            t,
15499                            t_kv,
15500                            scale,
15501                            true,
15502                            win,
15503                            kvl.k_tok_bytes,
15504                            kvl.v_tok_bytes,
15505                        )?;
15506                    }
15507                } else if std::env::var("MEMRA_NOFA").is_ok() {
15508                    e.sdpa_naive_quantized_view(
15509                        &q,
15510                        &k_view,
15511                        &v_view,
15512                        &mut attn,
15513                        hd,
15514                        nh,
15515                        nkv,
15516                        t,
15517                        t_kv,
15518                        scale,
15519                        true,
15520                        kvl.k_tok_bytes,
15521                        kvl.v_tok_bytes,
15522                    )?;
15523                } else {
15524                    // seq_end <= win (or a full-attn layer): no query in the WHOLE request can
15525                    // reach past the window, so the window mask is a no-op under causal and every
15526                    // chunk rides the hd128 dequant-once FA prefill — one arm for the whole
15527                    // request either way, which is what makes the chunk size arithmetic-free.
15528                    e.fa_prefill_view_ws(
15529                        &q,
15530                        &k_view,
15531                        &v_view,
15532                        &mut attn,
15533                        hd,
15534                        nh,
15535                        nkv,
15536                        t,
15537                        t_kv,
15538                        scale,
15539                        true,
15540                        kvl.k_tok_bytes,
15541                        kvl.v_tok_bytes,
15542                        crate::Engine::kv_fp8_on(),
15543                    )?;
15544                }
15545            }
15546            None => {
15547                // Cacheless prefill (forward/forward_last/t2probe) is MONOLITHIC — there is no
15548                // chunk loop on this path, so seq_end == t and the predicate is unchanged. The
15549                // assert pins that: if a chunked cacheless prefill is ever added, it must thread
15550                // seq_end here too or it re-opens the same door.
15551                debug_assert_eq!(
15552                    seq_end, t,
15553                    "step35 cacheless prefill is monolithic (seq_end == t)"
15554                );
15555                if swa && seq_end > win {
15556                    e.sdpa_naive_w(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
15557                } else if std::env::var("MEMRA_NOFA").is_ok() {
15558                    e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15559                } else {
15560                    e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, t, t, scale, true)?;
15561                }
15562            }
15563        }
15564
15565        // HEAD-WISE GATE (step35.cpp:267-285): gate = attn_gate(cur) -> [t, n_head_l];
15566        // attn *= sigmoid(gate) broadcast over head_dim. BEFORE wo.
15567        let gw = fa
15568            .attn_gate
15569            .as_ref()
15570            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
15571        let gt_owned = if gt_pre.is_none() {
15572            Some(e.matmul(
15573                gw,
15574                hg.ok_or("step35 attention needs hg when gt_pre is absent")?,
15575                t,
15576            )?)
15577        } else {
15578            None
15579        };
15580        let gt = gt_pre.or(gt_owned.as_ref()).unwrap();
15581        let mut ag = e.uninit(t * nh * hd)?;
15582        e.attn_head_gate(&attn, gt, &mut ag, None, hd, nh, t)?;
15583        Ok(ag)
15584    }
15585
15586    /// step35 PREFILL mixer, no cache side effect (the `full_attn` contract: `forward`,
15587    /// `forward_last`, t2probe). Post-`wo`.
15588    pub(crate) fn step35_attn(
15589        &self,
15590        e: &Engine,
15591        fa: &FullAttnLayer,
15592        h: &CudaSlice<f32>,
15593        pos_d: &CudaSlice<i32>,
15594        t: usize,
15595        il: usize,
15596    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15597        let g3 = match self.step35_tp_qkv(e, fa, h, t)? {
15598            Some(g3) => g3,
15599            None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15600        };
15601        // Monolithic path: the request ends at t (no chunk loop). See step35_attn_pre_wo's note.
15602        let ag = self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, None, il, t)?;
15603        self.step35_o(e, fa, &ag, t)
15604    }
15605
15606    /// step35 PRIME mixer (the `full_attn_prime` contract: append this chunk's K/V into the
15607    /// resident quantized cache, attend through the cache view). Post-`wo`.
15608    ///
15609    /// `seq_end` = the ABSOLUTE end position of the whole prime request (chunk-invariant); it
15610    /// selects the SWA arm. See `step35_attn_pre_wo`'s doc note for why it cannot be this chunk's
15611    /// own extent.
15612    #[allow(clippy::too_many_arguments)]
15613    pub(crate) fn step35_attn_prime(
15614        &self,
15615        e: &Engine,
15616        fa: &FullAttnLayer,
15617        h: &CudaSlice<f32>,
15618        hx: Option<&CudaSlice<u8>>,
15619        pos_d: &CudaSlice<i32>,
15620        t: usize,
15621        cache: &mut Cache,
15622        il: usize,
15623        seq_end: usize,
15624    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15625        if step_tp_prefill_enabled()? && fa.step_tp_qkv.is_some() {
15626            if hx.is_some() {
15627                return Err(
15628                    "rank-local Step prefill preserves BF16 activations and refuses the q8_1 \
15629                     pre-quantized prime path"
15630                        .into(),
15631                );
15632            }
15633            return self.step35_tp_prefill_attn_resident(e, fa, il, h, pos_d, t, cache, seq_end);
15634        }
15635        let g3 = if fa.step_tp_qkv.is_some() {
15636            if hx.is_some() {
15637                return Err(
15638                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
15639                     pre-quantized prime path"
15640                        .into(),
15641                );
15642            }
15643            self.step35_tp_qkv(e, fa, h, t)?
15644                .expect("Step Q/K/V TP disappeared after the presence check")
15645        } else {
15646            match hx {
15647                Some(xh) => e.matmul_group_xh(&[&fa.wq, &fa.wk, &fa.wv], h, xh, t)?,
15648                None => e.matmul_group(&[&fa.wq, &fa.wk, &fa.wv], h, t)?,
15649            }
15650        };
15651        let ag =
15652            self.step35_attn_pre_wo(e, fa, g3, Some(h), None, pos_d, t, Some(cache), il, seq_end)?;
15653        self.step35_o(e, fa, &ag, t)
15654    }
15655
15656    fn ensure_step_tp_kv_cache(
15657        &self,
15658        e: &Engine,
15659        fa: &FullAttnLayer,
15660        il: usize,
15661        cache: &mut Cache,
15662    ) -> Result<bool, Box<dyn std::error::Error>> {
15663        let tp = fa
15664            .step_tp_qkv
15665            .as_ref()
15666            .ok_or("Step TP cache hydration lost its resident projections")?;
15667        let geometry = self.step35_geom(il);
15668        let window = geometry.window.map(|window| window as usize);
15669        let ranks = tp.runtime.devices().len();
15670        let head_dim = geometry.head_dim_k as usize;
15671        let kv_heads = geometry.n_head_kv as usize;
15672        let max_ctx = cache.max_ctx;
15673
15674        if cache.tp_kv[il].is_some() {
15675            return Ok(false);
15676        }
15677        let local = cache.kv[il]
15678            .as_ref()
15679            .ok_or_else(|| format!("Step TP layer {il} has no owning-stage KV cache"))?;
15680        if local.kv_dim_k != kv_heads * head_dim || local.kv_dim_v != kv_heads * head_dim {
15681            return Err(format!(
15682                "Step TP layer {il} local KV geometry k={} v={} != {}",
15683                local.kv_dim_k,
15684                local.kv_dim_v,
15685                kv_heads * head_dim
15686            )
15687            .into());
15688        }
15689        let resident_start = window
15690            .map(|window| local.len.saturating_sub(window.saturating_sub(1)) & !31usize)
15691            .unwrap_or(0);
15692        let resident_rows = local.len - resident_start;
15693        let physical = local.physical_rows(resident_start, local.len)?;
15694        let k_rows = if resident_rows == 0 {
15695            Vec::new()
15696        } else {
15697            e.dtoh_u8_view(&e.view_u8_range(
15698                &local.k,
15699                physical.start * local.k_tok_bytes,
15700                physical.end * local.k_tok_bytes,
15701            ))?
15702        };
15703        let v_rows = if resident_rows == 0 {
15704            Vec::new()
15705        } else {
15706            e.dtoh_u8_view(&e.view_u8_range(
15707                &local.v,
15708                physical.start * local.v_tok_bytes,
15709                physical.end * local.v_tok_bytes,
15710            ))?
15711        };
15712        let mut distributed = match window {
15713            Some(window) => tp.runtime.allocate_tp_swa_kv_cache(
15714                kv_heads * head_dim,
15715                kv_heads * head_dim,
15716                max_ctx,
15717                window,
15718            )?,
15719            None => tp.runtime.allocate_tp_kv_cache(
15720                kv_heads * head_dim,
15721                kv_heads * head_dim,
15722                max_ctx,
15723            )?,
15724        };
15725        if distributed.k_tok_bytes() * ranks != local.k_tok_bytes
15726            || distributed.v_tok_bytes() * ranks != local.v_tok_bytes
15727        {
15728            return Err(format!(
15729                "Step TP layer {il} distributed/local KV token bytes disagree: \
15730                 k={}x{ranks}/{} v={}x{ranks}/{}",
15731                distributed.k_tok_bytes(),
15732                local.k_tok_bytes,
15733                distributed.v_tok_bytes(),
15734                local.v_tok_bytes,
15735            )
15736            .into());
15737        }
15738        tp.runtime.hydrate_tp_kv_cache_from(
15739            &mut distributed,
15740            local.len,
15741            resident_start,
15742            &k_rows,
15743            &v_rows,
15744        )?;
15745        cache.tp_kv[il] = Some(distributed);
15746        Ok(true)
15747    }
15748
15749    #[allow(clippy::too_many_arguments)]
15750    fn step35_tp_prefill_attn_resident(
15751        &self,
15752        e: &Engine,
15753        fa: &FullAttnLayer,
15754        il: usize,
15755        h: &CudaSlice<f32>,
15756        pos_d: &CudaSlice<i32>,
15757        tokens: usize,
15758        cache: &mut Cache,
15759        seq_end: usize,
15760    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15761        let tp = fa
15762            .step_tp_qkv
15763            .as_ref()
15764            .ok_or("Step TP prefill lost its resident projections")?;
15765        let attention = tp
15766            .attention
15767            .as_ref()
15768            .ok_or("Step TP prefill lost its resident attention auxiliaries")?;
15769        let ranks = tp.runtime.devices().len();
15770        if !step_tp_prefill_shape(
15771            true,
15772            tokens,
15773            ranks,
15774            tp.runtime.native_p2p(),
15775            true,
15776            crate::Engine::kv_fp8_on(),
15777        ) {
15778            return Err(format!(
15779                "rank-local Step prefill requires tokens>={PRIME_MIN_T}, TP2/TP4 native P2P, \
15780                 rank-local attention, and q8_0/q5_1 KV; got tokens={tokens} ranks={ranks} \
15781                 native_p2p={} fp8_kv={}",
15782                tp.runtime.native_p2p(),
15783                crate::Engine::kv_fp8_on(),
15784            )
15785            .into());
15786        }
15787        for seam in [
15788            "MEMRA_STEP35_SWA_TKV",
15789            "MEMRA_PRIME_CALLLOCAL",
15790            "MEMRA_PRIME_F32CHUNK0",
15791        ] {
15792            if std::env::var(seam).as_deref() == Ok("1") {
15793                return Err(format!(
15794                    "rank-local Step prefill has not qualified the legacy seam {seam}=1"
15795                )
15796                .into());
15797            }
15798        }
15799
15800        let geometry = self.step35_geom(il);
15801        let window = geometry.window.map(|window| window as usize);
15802        let head_dim = geometry.head_dim_k as usize;
15803        let heads = geometry.n_head as usize;
15804        let kv_heads = geometry.n_head_kv as usize;
15805        if heads % ranks != 0 || kv_heads % ranks != 0 {
15806            return Err(format!(
15807                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
15808            )
15809            .into());
15810        }
15811        let local_heads = heads / ranks;
15812        let local_kv_heads = kv_heads / ranks;
15813        let local_kv_dim = local_kv_heads * head_dim;
15814        let hidden = self.cfg.n_embd as usize;
15815        let expected_input = tokens
15816            .checked_mul(hidden)
15817            .ok_or("Step TP prefill input size overflow")?;
15818        if h.len() < expected_input {
15819            return Err(format!(
15820                "Step TP prefill input {} is shorter than {tokens}x{hidden}",
15821                h.len()
15822            )
15823            .into());
15824        }
15825        let positions = e.dtoh_i32(pos_d)?;
15826        if positions.len() != tokens {
15827            return Err(format!(
15828                "rank-local Step prefill positions {} != tokens {tokens}",
15829                positions.len()
15830            )
15831            .into());
15832        }
15833
15834        let mut active_input = e.uninit(expected_input)?;
15835        e.copy_view_into(
15836            &mut active_input,
15837            0,
15838            &h.slice(0..expected_input),
15839            expected_input,
15840        )?;
15841        let mut input = tp.runtime.allocate_replicated_device_rows(tokens, hidden)?;
15842        // PRODUCER FENCE (2026-08-20 flake fix): active_input was written on the MODEL engine's
15843        // stream; the refresh below reads it from the runtime root engine's stream (same device,
15844        // different stream). Unfenced, the peer read can overtake the in-flight copy — the
15845        // layer-count-amplified arm of the boot flake.
15846        e.stream().synchronize()?;
15847        tp.runtime
15848            .refresh_replicated_device_rows_from_root(&mut input, &active_input)?;
15849        let q_raw = tp
15850            .runtime
15851            .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &input)?;
15852        let k_raw = tp
15853            .runtime
15854            .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &input)?;
15855        let v_raw = tp
15856            .runtime
15857            .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &input)?;
15858        let mut q = Vec::with_capacity(ranks);
15859        let mut k = Vec::with_capacity(ranks);
15860        for rank in 0..ranks {
15861            let engine = tp
15862                .runtime
15863                .rank_engine(rank)
15864                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15865            let _main = engine.gpu.enter_main()?;
15866            let mut q_rank = engine.uninit(tokens * local_heads * head_dim)?;
15867            engine.rms_norm(
15868                &q_raw[rank],
15869                &attention.q_norm[rank],
15870                &mut q_rank,
15871                head_dim,
15872                tokens * local_heads,
15873                self.cfg.rms_eps,
15874            )?;
15875            let mut k_rank = engine.uninit(tokens * local_kv_dim)?;
15876            engine.rms_norm(
15877                &k_raw[rank],
15878                &attention.k_norm[rank],
15879                &mut k_rank,
15880                head_dim,
15881                tokens * local_kv_heads,
15882                self.cfg.rms_eps,
15883            )?;
15884            let position = engine.htod_i32(&positions)?;
15885            let rope_freqs = if geometry.rope_factors {
15886                self.step35_aux
15887                    .as_ref()
15888                    .and_then(|aux| aux.rope_freqs(engine))
15889            } else {
15890                None
15891            };
15892            engine.rope_neox2(
15893                &mut q_rank,
15894                &mut k_rank,
15895                &position,
15896                head_dim,
15897                geometry.n_rot as usize,
15898                local_heads,
15899                local_kv_heads,
15900                tokens,
15901                geometry.rope_base,
15902                1.0,
15903                rope_freqs,
15904            )?;
15905            q.push(q_rank);
15906            k.push(k_rank);
15907        }
15908
15909        let gate_weight = fa
15910            .attn_gate
15911            .as_ref()
15912            .ok_or("step35 layer is missing attn_gate.weight")?;
15913        let gate = e.dtoh(&e.matmul(gate_weight, h, tokens)?)?;
15914        if gate.len() != tokens * heads {
15915            return Err(format!(
15916                "Step TP layer {il} gate output {} != {tokens}x{heads}",
15917                gate.len()
15918            )
15919            .into());
15920        }
15921
15922        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
15923        let base_len = cache.kv[il]
15924            .as_ref()
15925            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
15926            .len;
15927        let distributed = cache.tp_kv[il]
15928            .as_ref()
15929            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
15930        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
15931            return Err(format!(
15932                "Step TP layer {il} cache lengths diverged before prefill: \
15933                 local={base_len} distributed={}/{}",
15934                distributed.committed_len(),
15935                distributed.staged_len()
15936            )
15937            .into());
15938        }
15939        let target_len = base_len
15940            .checked_add(tokens)
15941            .ok_or("Step TP prefill cache length overflow")?;
15942        if target_len > cache.max_ctx {
15943            return Err(format!(
15944                "Step TP layer {il} prefill exceeds cache: {base_len}+{tokens}>{}",
15945                cache.max_ctx
15946            )
15947            .into());
15948        }
15949        if seq_end < target_len {
15950            return Err(format!(
15951                "Step TP layer {il} request end {seq_end} precedes chunk end {target_len}"
15952            )
15953            .into());
15954        }
15955
15956        let transaction = cache.tp_kv[il]
15957            .as_mut()
15958            .expect("distributed cache checked above")
15959            .begin_transaction()?;
15960        if let Err(error) = tp.runtime.append_tp_kv_transaction(
15961            cache.tp_kv[il]
15962                .as_mut()
15963                .expect("distributed cache checked above"),
15964            transaction,
15965            &k,
15966            &v_raw,
15967            tokens,
15968        ) {
15969            let _ = tp.runtime.rollback_tp_kv_transaction(
15970                cache.tp_kv[il]
15971                    .as_mut()
15972                    .expect("distributed cache checked above"),
15973                transaction,
15974            );
15975            return Err(error);
15976        }
15977
15978        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15979            let distributed = cache.tp_kv[il]
15980                .as_ref()
15981                .expect("distributed cache checked above");
15982            let staged_len = distributed.staged_len();
15983            let view_start = window
15984                .map(|window| base_len.saturating_sub(window.saturating_sub(1)) & !31usize)
15985                .unwrap_or(0);
15986            let physical = distributed.physical_range(view_start, staged_len)?;
15987            let t_kv = staged_len - view_start;
15988            let swa_naive = window.is_some_and(|window| seq_end > window);
15989            let mut gated = Vec::with_capacity(ranks);
15990            for rank in 0..ranks {
15991                let engine = tp
15992                    .runtime
15993                    .rank_engine(rank)
15994                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
15995                let _main = engine.gpu.enter_main()?;
15996                let rank_cache = distributed
15997                    .rank(rank)
15998                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
15999                let k_view = engine.view_u8_range(
16000                    rank_cache.k(),
16001                    physical.start * distributed.k_tok_bytes(),
16002                    physical.end * distributed.k_tok_bytes(),
16003                );
16004                let v_view = engine.view_u8_range(
16005                    rank_cache.v(),
16006                    physical.start * distributed.v_tok_bytes(),
16007                    physical.end * distributed.v_tok_bytes(),
16008                );
16009                let mut attention_out = engine.uninit(tokens * local_heads * head_dim)?;
16010                if swa_naive {
16011                    let window = window.expect("SWA predicate requires a window");
16012                    if std::env::var("MEMRA_STEP35_SWA_FA").as_deref() == Ok("0") {
16013                        engine.sdpa_naive_w_quantized_view(
16014                            &q[rank],
16015                            &k_view,
16016                            &v_view,
16017                            &mut attention_out,
16018                            head_dim,
16019                            local_heads,
16020                            local_kv_heads,
16021                            tokens,
16022                            t_kv,
16023                            geometry.attention_scale(),
16024                            true,
16025                            window,
16026                            distributed.k_tok_bytes(),
16027                            distributed.v_tok_bytes(),
16028                        )?;
16029                    } else {
16030                        engine.fa_prefill_view_ws_w_hd128(
16031                            &q[rank],
16032                            &k_view,
16033                            &v_view,
16034                            &mut attention_out,
16035                            head_dim,
16036                            local_heads,
16037                            local_kv_heads,
16038                            tokens,
16039                            t_kv,
16040                            geometry.attention_scale(),
16041                            true,
16042                            window,
16043                            distributed.k_tok_bytes(),
16044                            distributed.v_tok_bytes(),
16045                        )?;
16046                    }
16047                } else if std::env::var("MEMRA_NOFA").is_ok() {
16048                    engine.sdpa_naive_quantized_view(
16049                        &q[rank],
16050                        &k_view,
16051                        &v_view,
16052                        &mut attention_out,
16053                        head_dim,
16054                        local_heads,
16055                        local_kv_heads,
16056                        tokens,
16057                        t_kv,
16058                        geometry.attention_scale(),
16059                        true,
16060                        distributed.k_tok_bytes(),
16061                        distributed.v_tok_bytes(),
16062                    )?;
16063                } else {
16064                    engine.fa_prefill_view_ws(
16065                        &q[rank],
16066                        &k_view,
16067                        &v_view,
16068                        &mut attention_out,
16069                        head_dim,
16070                        local_heads,
16071                        local_kv_heads,
16072                        tokens,
16073                        t_kv,
16074                        geometry.attention_scale(),
16075                        true,
16076                        distributed.k_tok_bytes(),
16077                        distributed.v_tok_bytes(),
16078                        false,
16079                    )?;
16080                }
16081
16082                let gate_start = rank * local_heads;
16083                let mut gate_rank = Vec::with_capacity(tokens * local_heads);
16084                for token in 0..tokens {
16085                    let start = token * heads + gate_start;
16086                    gate_rank.extend_from_slice(&gate[start..start + local_heads]);
16087                }
16088                let gate_rank = engine.htod(&gate_rank)?;
16089                let mut gated_rank = engine.uninit(tokens * local_heads * head_dim)?;
16090                engine.attn_head_gate(
16091                    &attention_out,
16092                    &gate_rank,
16093                    &mut gated_rank,
16094                    None,
16095                    head_dim,
16096                    local_heads,
16097                    tokens,
16098                )?;
16099                gated.push(gated_rank);
16100            }
16101            for rank in 1..ranks {
16102                let engine = tp
16103                    .runtime
16104                    .rank_engine(rank)
16105                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16106                let _main = engine.gpu.enter_main()?;
16107                engine.stream().synchronize()?;
16108            }
16109
16110            let (output, k_shadow, v_shadow) = if tp.runtime.bulk_p2p() {
16111                let output = tp
16112                    .runtime
16113                    .step_bf16_row_parallel_resident_root_device(&tp.o, &gated, tokens)?;
16114                let k_shadow =
16115                    tp.runtime
16116                        .gather_native_column_shards_device(&k, tokens, local_kv_dim)?;
16117                let v_shadow =
16118                    tp.runtime
16119                        .gather_native_column_shards_device(&v_raw, tokens, local_kv_dim)?;
16120                let root = tp
16121                    .runtime
16122                    .rank_engine(0)
16123                    .ok_or("Step TP prefill lost its root engine")?;
16124                let _main = root.gpu.enter_main()?;
16125                root.stream().synchronize()?;
16126                (output, k_shadow, v_shadow)
16127            } else {
16128                let attention = tp.runtime.gather_native_column_shards(
16129                    &gated,
16130                    tokens,
16131                    local_heads * head_dim,
16132                )?;
16133                let output = tp
16134                    .runtime
16135                    .step_bf16_row_parallel_resident_native(&tp.o, &attention, tokens)?;
16136                let k_shadow = tp
16137                    .runtime
16138                    .gather_native_column_shards(&k, tokens, local_kv_dim)?;
16139                let v_shadow =
16140                    tp.runtime
16141                        .gather_native_column_shards(&v_raw, tokens, local_kv_dim)?;
16142                (e.htod(&output)?, e.htod(&k_shadow)?, e.htod(&v_shadow)?)
16143            };
16144            let local = cache.kv[il]
16145                .as_mut()
16146                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16147            if local.len != base_len {
16148                return Err(format!(
16149                    "Step TP layer {il} local cache changed during prefill: \
16150                     len={} base={base_len}",
16151                    local.len
16152                )
16153                .into());
16154            }
16155            let retain_from = window
16156                .map(|window| {
16157                    let staged_retain = staged_len.saturating_sub(window) & !31usize;
16158                    let rollback_retain =
16159                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16160                    staged_retain.min(rollback_retain)
16161                })
16162                .unwrap_or(0);
16163            let write_row = e.prepare_kv_append(local, retain_from, tokens)?;
16164            e.append_kv_quantized_rows(
16165                &k_shadow,
16166                &v_shadow,
16167                &mut local.k,
16168                &mut local.v,
16169                write_row,
16170                tokens,
16171                local.kv_dim_k,
16172                local.kv_dim_v,
16173                local.k_tok_bytes,
16174                local.v_tok_bytes,
16175                false,
16176            )?;
16177            local.len = staged_len;
16178            e.set_i32_one(&mut local.len_d, staged_len as i32)?;
16179            Ok(output)
16180        })();
16181
16182        let output = match staged {
16183            Ok(output) => output,
16184            Err(error) => {
16185                let _ = tp.runtime.rollback_tp_kv_transaction(
16186                    cache.tp_kv[il]
16187                        .as_mut()
16188                        .expect("distributed cache checked above"),
16189                    transaction,
16190                );
16191                if let Some(local) = cache.kv[il].as_mut() {
16192                    local.len = base_len;
16193                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16194                }
16195                return Err(error);
16196            }
16197        };
16198        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16199            cache.tp_kv[il]
16200                .as_mut()
16201                .expect("distributed cache checked above"),
16202            transaction,
16203            tokens,
16204        ) {
16205            let _ = tp.runtime.rollback_tp_kv_transaction(
16206                cache.tp_kv[il]
16207                    .as_mut()
16208                    .expect("distributed cache checked above"),
16209                transaction,
16210            );
16211            let local = cache.kv[il].as_mut().expect("local cache checked above");
16212            local.len = base_len;
16213            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16214            return Err(error);
16215        }
16216
16217        let committed = cache.tp_kv[il]
16218            .as_ref()
16219            .expect("distributed cache checked above")
16220            .committed_len();
16221        let local_len = cache.kv[il]
16222            .as_ref()
16223            .expect("local cache checked above")
16224            .len;
16225        if committed != local_len {
16226            return Err(format!(
16227                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16228            )
16229            .into());
16230        }
16231        eprintln!(
16232            "[step-tp-prefill-attn] execute layer={} devices={:?} tokens={tokens} \
16233             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16234             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16235             attention_scope={} input_path=root-device-replicated gate_tensor_parallel=false \
16236             gate_shards=host-canonical o_tensor_parallel=true local_cache_shadow=true \
16237             cache_commit=chunk transport={} native_p2p=true bulk_p2p={} \
16238             output={} performance_claim=false",
16239            tp.layer,
16240            tp.devices,
16241            hydrated,
16242            if window.is_some() {
16243                "rank-local-swa-ring"
16244            } else {
16245                "rank-local-global"
16246            },
16247            tp.runtime.transport_label(),
16248            tp.runtime.bulk_p2p(),
16249            if tp.runtime.bulk_p2p() {
16250                "root-device"
16251            } else {
16252                "root-readback"
16253            },
16254        );
16255        Ok(output)
16256    }
16257
16258    fn step35_tp_decode_attn_resident(
16259        &self,
16260        e: &Engine,
16261        fa: &FullAttnLayer,
16262        il: usize,
16263        h: &CudaSlice<f32>,
16264        pos_d: &CudaSlice<i32>,
16265        cache: &mut Cache,
16266    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16267        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of the rank-local TP attention decode,
16268        // printed every ~10 decode steps' worth of layers — the wall-decomposition twin of the
16269        // nvfp4-dev-routes counter.
16270        static ATTN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16271        static ATTN_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16272        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
16273        let started = timing.then(std::time::Instant::now);
16274        let result = if crate::tp::step_tp_decode_v2_enabled()? {
16275            self.step35_tp_decode_attn_resident_v2(e, fa, il, h, pos_d, cache)
16276        } else {
16277            self.step35_tp_decode_attn_resident_inner(e, fa, il, h, pos_d, cache)
16278        };
16279        if let Some(started) = started {
16280            use std::sync::atomic::Ordering;
16281            let ns = ATTN_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
16282                + started.elapsed().as_nanos() as u64;
16283            let calls = ATTN_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16284            if calls % 430 == 0 {
16285                eprintln!(
16286                    "[step-tp-attn-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
16287                    ns as f64 / 1.0e6,
16288                    ns as f64 / calls as f64 / 1.0e3,
16289                );
16290            }
16291        }
16292        result
16293    }
16294
16295    #[allow(clippy::too_many_arguments)]
16296    fn step35_tp_decode_attn_resident_inner(
16297        &self,
16298        e: &Engine,
16299        fa: &FullAttnLayer,
16300        il: usize,
16301        h: &CudaSlice<f32>,
16302        pos_d: &CudaSlice<i32>,
16303        cache: &mut Cache,
16304    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16305        // MEMRA_STEP_TP_TIMING=1 phase decomposition of the 1550us/layer decode wall. Each lap
16306        // drains every stream so queued async work is billed to the phase that queued it — the
16307        // drains perturb absolute wall, but v1 already ends most phases on a host sync, so the
16308        // relative split is honest. Timing OFF is the measured configuration: zero extra syncs.
16309        static T_POS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16310        static T_QKV: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16311        static T_NORMROPE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16312        static T_GATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16313        static T_APPEND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16314        static T_ATTN: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16315        static T_OPROJ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16316        static T_SHADOW: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16317        static T_PHASE_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
16318        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
16319        fn lap(
16320            runtime: &crate::tp::TpE4m3HostBounce,
16321            e: &Engine,
16322            timer: &std::sync::atomic::AtomicU64,
16323            started: &mut Option<std::time::Instant>,
16324        ) -> Result<(), Box<dyn std::error::Error>> {
16325            let Some(start) = started.as_mut() else {
16326                return Ok(());
16327            };
16328            for rank in 0..runtime.devices().len() {
16329                if let Some(engine) = runtime.rank_engine(rank) {
16330                    let _main = engine.gpu.enter_main()?;
16331                    engine.stream().synchronize()?;
16332                }
16333            }
16334            e.stream().synchronize()?;
16335            timer.fetch_add(
16336                start.elapsed().as_nanos() as u64,
16337                std::sync::atomic::Ordering::Relaxed,
16338            );
16339            *start = std::time::Instant::now();
16340            Ok(())
16341        }
16342        let tp = fa
16343            .step_tp_qkv
16344            .as_ref()
16345            .ok_or("Step TP decode lost its resident projections")?;
16346        let attention = tp
16347            .attention
16348            .as_ref()
16349            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
16350        if !tp.runtime.native_p2p() {
16351            return Err("rank-local Step attention requires native P2P".into());
16352        }
16353        if crate::Engine::kv_fp8_on() {
16354            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
16355        }
16356
16357        let geometry = self.step35_geom(il);
16358        let window = geometry.window.map(|window| window as usize);
16359        let ranks = tp.runtime.devices().len();
16360        let head_dim = geometry.head_dim_k as usize;
16361        let heads = geometry.n_head as usize;
16362        let kv_heads = geometry.n_head_kv as usize;
16363        if heads % ranks != 0 || kv_heads % ranks != 0 {
16364            return Err(format!(
16365                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
16366            )
16367            .into());
16368        }
16369        let local_heads = heads / ranks;
16370        let local_kv_heads = kv_heads / ranks;
16371        let local_kv_dim = local_kv_heads * head_dim;
16372        let max_ctx = cache.max_ctx;
16373
16374        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
16375
16376        let base_len = cache.kv[il]
16377            .as_ref()
16378            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
16379            .len;
16380        let distributed = cache.tp_kv[il]
16381            .as_ref()
16382            .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
16383        if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
16384            return Err(format!(
16385                "Step TP layer {il} cache lengths diverged before decode: \
16386                 local={base_len} distributed={}/{}",
16387                distributed.committed_len(),
16388                distributed.staged_len()
16389            )
16390            .into());
16391        }
16392
16393        let mut lap_start = timing.then(std::time::Instant::now);
16394        let positions = e.dtoh_i32(pos_d)?;
16395        if positions.len() != 1 {
16396            return Err(format!(
16397                "rank-local Step decode requires one position, got {}",
16398                positions.len()
16399            )
16400            .into());
16401        }
16402        lap(&tp.runtime, e, &T_POS, &mut lap_start)?;
16403        let (q_raw, k_raw, v_raw, input_path) = if let Some(decode_input) =
16404            attention.decode_input.as_ref()
16405        {
16406            let mut decode_input = decode_input
16407                .lock()
16408                .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
16409            // PRODUCER FENCE (2026-08-20 flake fix): h is the layer hidden written on the MODEL
16410            // engine's stream; the refresh reads it from the runtime root engine's stream. This
16411            // per-layer-per-token seam is the layer-count-amplified arm of the boot flake.
16412            e.stream().synchronize()?;
16413            tp.runtime
16414                .refresh_replicated_device_rows_from_root(&mut decode_input, h)?;
16415            let q_raw = tp
16416                .runtime
16417                .bf16_column_parallel_resident_replicated_device_shards(&tp.q, &decode_input)?;
16418            let k_raw = tp
16419                .runtime
16420                .bf16_column_parallel_resident_replicated_device_shards(&tp.k, &decode_input)?;
16421            let v_raw = tp
16422                .runtime
16423                .bf16_column_parallel_resident_replicated_device_shards(&tp.v, &decode_input)?;
16424            (q_raw, k_raw, v_raw, "root-device-replicated")
16425        } else {
16426            let activation = e.dtoh(h)?;
16427            let q_raw =
16428                tp.runtime
16429                    .bf16_column_parallel_resident_device_shards(&tp.q, &activation, 1)?;
16430            let k_raw =
16431                tp.runtime
16432                    .bf16_column_parallel_resident_device_shards(&tp.k, &activation, 1)?;
16433            let v_raw =
16434                tp.runtime
16435                    .bf16_column_parallel_resident_device_shards(&tp.v, &activation, 1)?;
16436            (q_raw, k_raw, v_raw, "host-replicated")
16437        };
16438        lap(&tp.runtime, e, &T_QKV, &mut lap_start)?;
16439        let mut q = Vec::with_capacity(ranks);
16440        let mut k = Vec::with_capacity(ranks);
16441        for rank in 0..ranks {
16442            let engine = tp
16443                .runtime
16444                .rank_engine(rank)
16445                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16446            let _main = engine.gpu.enter_main()?;
16447            let mut q_rank = engine.uninit(local_heads * head_dim)?;
16448            engine.rms_norm(
16449                &q_raw[rank],
16450                &attention.q_norm[rank],
16451                &mut q_rank,
16452                head_dim,
16453                local_heads,
16454                self.cfg.rms_eps,
16455            )?;
16456            let mut k_rank = engine.uninit(local_kv_dim)?;
16457            engine.rms_norm(
16458                &k_raw[rank],
16459                &attention.k_norm[rank],
16460                &mut k_rank,
16461                head_dim,
16462                local_kv_heads,
16463                self.cfg.rms_eps,
16464            )?;
16465            let position = engine.htod_i32(&positions)?;
16466            let rope_freqs = if geometry.rope_factors {
16467                self.step35_aux
16468                    .as_ref()
16469                    .and_then(|aux| aux.rope_freqs(engine))
16470            } else {
16471                None
16472            };
16473            engine.rope_neox2(
16474                &mut q_rank,
16475                &mut k_rank,
16476                &position,
16477                head_dim,
16478                geometry.n_rot as usize,
16479                local_heads,
16480                local_kv_heads,
16481                1,
16482                geometry.rope_base,
16483                1.0,
16484                rope_freqs,
16485            )?;
16486            q.push(q_rank);
16487            k.push(k_rank);
16488        }
16489        lap(&tp.runtime, e, &T_NORMROPE, &mut lap_start)?;
16490
16491        let gate_weight = fa
16492            .attn_gate
16493            .as_ref()
16494            .ok_or("step35 layer is missing attn_gate.weight")?;
16495        let gate = e.matmul(gate_weight, h, 1)?;
16496        let gate = e.dtoh(&gate)?;
16497        if gate.len() != heads {
16498            return Err(format!("Step TP layer {il} gate output {} != {heads}", gate.len()).into());
16499        }
16500        lap(&tp.runtime, e, &T_GATE, &mut lap_start)?;
16501
16502        let transaction = cache.tp_kv[il]
16503            .as_mut()
16504            .expect("distributed cache checked above")
16505            .begin_transaction()?;
16506        if let Err(error) = tp.runtime.append_tp_kv_transaction(
16507            cache.tp_kv[il]
16508                .as_mut()
16509                .expect("distributed cache checked above"),
16510            transaction,
16511            &k,
16512            &v_raw,
16513            1,
16514        ) {
16515            let _ = tp.runtime.rollback_tp_kv_transaction(
16516                cache.tp_kv[il]
16517                    .as_mut()
16518                    .expect("distributed cache checked above"),
16519                transaction,
16520            );
16521            return Err(error);
16522        }
16523        lap(&tp.runtime, e, &T_APPEND, &mut lap_start)?;
16524
16525        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16526            let distributed = cache.tp_kv[il]
16527                .as_ref()
16528                .expect("distributed cache checked above");
16529            let staged_len = distributed.staged_len();
16530            let view_start = window
16531                .map(|window| staged_len.saturating_sub(window))
16532                .unwrap_or(0);
16533            let physical = distributed.physical_range(view_start, staged_len)?;
16534            let t_kv = staged_len - view_start;
16535            let mut gated = Vec::with_capacity(ranks);
16536            for rank in 0..ranks {
16537                let engine = tp
16538                    .runtime
16539                    .rank_engine(rank)
16540                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
16541                let _main = engine.gpu.enter_main()?;
16542                let rank_cache = distributed
16543                    .rank(rank)
16544                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
16545                let k_view = engine.view_u8_range(
16546                    rank_cache.k(),
16547                    physical.start * distributed.k_tok_bytes(),
16548                    physical.end * distributed.k_tok_bytes(),
16549                );
16550                let v_view = engine.view_u8_range(
16551                    rank_cache.v(),
16552                    physical.start * distributed.v_tok_bytes(),
16553                    physical.end * distributed.v_tok_bytes(),
16554                );
16555                let mut attention_out = engine.uninit(local_heads * head_dim)?;
16556                engine.fa_decode_kvmod(
16557                    &q[rank],
16558                    &k_view,
16559                    &v_view,
16560                    &mut attention_out,
16561                    head_dim,
16562                    local_heads,
16563                    local_kv_heads,
16564                    t_kv,
16565                    geometry.attention_scale(),
16566                    distributed.k_tok_bytes(),
16567                    distributed.v_tok_bytes(),
16568                    false,
16569                )?;
16570                let gate_start = rank * local_heads;
16571                let gate_rank = engine.htod(&gate[gate_start..gate_start + local_heads])?;
16572                let mut gated_rank = engine.uninit(local_heads * head_dim)?;
16573                engine.attn_head_gate(
16574                    &attention_out,
16575                    &gate_rank,
16576                    &mut gated_rank,
16577                    None,
16578                    head_dim,
16579                    local_heads,
16580                    1,
16581                )?;
16582                gated.push(gated_rank);
16583            }
16584            lap(&tp.runtime, e, &T_ATTN, &mut lap_start)?;
16585
16586            let gathered =
16587                tp.runtime
16588                    .gather_native_column_shards(&gated, 1, local_heads * head_dim)?;
16589            let output = tp
16590                .runtime
16591                .step_bf16_row_parallel_resident_native(&tp.o, &gathered, 1)?;
16592            let output = e.htod(&output)?;
16593            lap(&tp.runtime, e, &T_OPROJ, &mut lap_start)?;
16594
16595            let k_shadow = tp
16596                .runtime
16597                .gather_native_column_shards(&k, 1, local_kv_dim)?;
16598            let v_shadow = tp
16599                .runtime
16600                .gather_native_column_shards(&v_raw, 1, local_kv_dim)?;
16601            let k_shadow = e.htod(&k_shadow)?;
16602            let v_shadow = e.htod(&v_shadow)?;
16603            let local = cache.kv[il]
16604                .as_mut()
16605                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
16606            if local.len != base_len || base_len + 1 > max_ctx {
16607                return Err(format!(
16608                    "Step TP layer {il} local cache changed during decode: \
16609                     len={} base={base_len} max={max_ctx}",
16610                    local.len
16611                )
16612                .into());
16613            }
16614            let retain_from = window
16615                .map(|window| {
16616                    let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
16617                    let rollback_retain =
16618                        base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
16619                    staged_retain.min(rollback_retain)
16620                })
16621                .unwrap_or(0);
16622            let write_row = e.prepare_kv_append(local, retain_from, 1)?;
16623            e.append_kv_quantized(
16624                &k_shadow,
16625                &v_shadow,
16626                &mut local.k,
16627                &mut local.v,
16628                write_row,
16629                local.kv_dim_k,
16630                local.kv_dim_v,
16631                local.k_tok_bytes,
16632                local.v_tok_bytes,
16633                false,
16634            )?;
16635            local.len = base_len + 1;
16636            e.set_i32_one(&mut local.len_d, local.len as i32)?;
16637            Ok(output)
16638        })();
16639
16640        let output = match staged {
16641            Ok(output) => output,
16642            Err(error) => {
16643                let _ = tp.runtime.rollback_tp_kv_transaction(
16644                    cache.tp_kv[il]
16645                        .as_mut()
16646                        .expect("distributed cache checked above"),
16647                    transaction,
16648                );
16649                if let Some(local) = cache.kv[il].as_mut() {
16650                    local.len = base_len;
16651                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
16652                }
16653                return Err(error);
16654            }
16655        };
16656        if let Err(error) = tp.runtime.commit_tp_kv_transaction(
16657            cache.tp_kv[il]
16658                .as_mut()
16659                .expect("distributed cache checked above"),
16660            transaction,
16661            1,
16662        ) {
16663            let _ = tp.runtime.rollback_tp_kv_transaction(
16664                cache.tp_kv[il]
16665                    .as_mut()
16666                    .expect("distributed cache checked above"),
16667                transaction,
16668            );
16669            let local = cache.kv[il].as_mut().expect("local cache checked above");
16670            local.len = base_len;
16671            e.set_i32_one(&mut local.len_d, base_len as i32)?;
16672            return Err(error);
16673        }
16674
16675        let committed = cache.tp_kv[il]
16676            .as_ref()
16677            .expect("distributed cache checked above")
16678            .committed_len();
16679        let local_len = cache.kv[il]
16680            .as_ref()
16681            .expect("local cache checked above")
16682            .len;
16683        if committed != local_len {
16684            return Err(format!(
16685                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
16686            )
16687            .into());
16688        }
16689        lap(&tp.runtime, e, &T_SHADOW, &mut lap_start)?;
16690        if timing {
16691            use std::sync::atomic::Ordering;
16692            let calls = T_PHASE_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
16693            if calls % 430 == 0 {
16694                let avg = |t: &std::sync::atomic::AtomicU64| {
16695                    t.load(Ordering::Relaxed) as f64 / calls as f64 / 1.0e3
16696                };
16697                eprintln!(
16698                    "[step-tp-attn-phase] calls={calls} avg_us pos={:.1} qkv={:.1} \
16699                     normrope={:.1} gate={:.1} append={:.1} attn={:.1} oproj={:.1} shadow={:.1}",
16700                    avg(&T_POS),
16701                    avg(&T_QKV),
16702                    avg(&T_NORMROPE),
16703                    avg(&T_GATE),
16704                    avg(&T_APPEND),
16705                    avg(&T_ATTN),
16706                    avg(&T_OPROJ),
16707                    avg(&T_SHADOW),
16708                );
16709            }
16710        }
16711        eprintln!(
16712            "[step-tp-attn] execute layer={} devices={:?} tokens=1 \
16713             qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
16714             kv_cache_distributed=true kv_cache_hydrated={} attention_tensor_parallel=true \
16715             attention_scope={} input_path={} kv_physical_rows={} \
16716             gate_tensor_parallel=false gate_shards=host-canonical o_tensor_parallel=true \
16717             local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
16718             bulk_p2p={} output=root-readback performance_claim=false",
16719            tp.layer,
16720            tp.devices,
16721            hydrated,
16722            if window.is_some() {
16723                "rank-local-swa-ring"
16724            } else {
16725                "rank-local-global"
16726            },
16727            input_path,
16728            cache.tp_kv[il]
16729                .as_ref()
16730                .expect("distributed cache checked above")
16731                .physical_capacity(),
16732            tp.runtime.transport_label(),
16733            tp.runtime.bulk_p2p(),
16734        );
16735        Ok(output)
16736    }
16737
16738    /// v2 rank-local decode attention (MEMRA_STEP_TP_DECODE_V2): the same kernels, operand
16739    /// values, and canonical reduction order as `step35_tp_decode_attn_resident_inner`,
16740    /// restructured onto a persistent per-runtime workspace with evented cross-stream ordering.
16741    /// Per layer per token this path performs exactly one cuMemAlloc (the returned e-context
16742    /// output row), no host round-trip, and no host stream synchronize — the phase timers
16743    /// measured v1 spending 81% of its 1550us/layer wall on those three classes.
16744    #[allow(clippy::too_many_arguments)]
16745    /// T-COLUMN verify precompute for layer `il`: weight-amortized QKV(+gate) over the T
16746    /// verify columns into the ws slabs (per-column rope/append/fa run later through the
16747    /// unmodified t=1 program via the col-select door). Ok(false) when the layer is not on
16748    /// the resident fused TP2 class (caller falls back to the per-row walk).
16749    pub(crate) fn step35_verify_qkv_precompute(
16750        &self,
16751        e: &Engine,
16752        il: usize,
16753        h_t: &CudaSlice<f32>,
16754        t: usize,
16755    ) -> Result<bool, Box<dyn std::error::Error>> {
16756        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16757            return Ok(false);
16758        };
16759        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16760            return Ok(false);
16761        };
16762        let Some(attention) = tp.attention.as_ref() else {
16763            return Ok(false);
16764        };
16765        if !tp.runtime.native_p2p() || !crate::tp::step_tp_qkv_fused_enabled()? {
16766            return Ok(false);
16767        }
16768        let geometry = self.step35_geom(il);
16769        let heads = geometry.n_head as usize;
16770        let ws_index = tp
16771            .runtime
16772            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16773        let gate_shards = attention
16774            .gate_shards_bf16
16775            .as_deref()
16776            .map(crate::tp::StepTpGateShards::Bf16);
16777        tp.runtime.decode_v2_input_qkv_tcol(
16778            ws_index,
16779            e,
16780            h_t,
16781            t,
16782            &tp.q,
16783            &tp.k,
16784            &tp.v,
16785            gate_shards,
16786        )?;
16787        Ok(true)
16788    }
16789
16790    /// MEMRA_TCOL_OPROJ join for the verify walk: after every column of layer `il`
16791    /// stashed its `gated` rows, produce the [t, o_out] `mixed` slab on `e` via the
16792    /// weight-amortized b4_tcol + slab join. Callers only reach this after the stash
16793    /// flag confirmed the defer engaged for every column.
16794    pub(crate) fn step35_verify_oproj_tcol(
16795        &self,
16796        e: &Engine,
16797        il: usize,
16798        t: usize,
16799    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16800        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16801            return Err("tcol o_proj join expects full attention".into());
16802        };
16803        let tp = fa
16804            .step_tp_qkv
16805            .as_ref()
16806            .ok_or("tcol o_proj join lost its resident projections")?;
16807        let heads = self.step35_geom(il).n_head as usize;
16808        let ws_index = tp
16809            .runtime
16810            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
16811        tp.runtime.decode_v2_oproj_tcol(ws_index, e, &tp.o, t)
16812    }
16813
16814    /// MEMRA_SPEC_FA2 precheck: decide BEFORE arming the defer whether both verify
16815    /// columns of layer `il` will take the dcw arm AND the T=2 launch is bit-safe —
16816    /// stashing is unrecoverable (no per-column output exists), so every dynamic input
16817    /// to the engine-side dcw decision is evaluated here, plus the equal-partition
16818    /// guard fa_decode_dcw2's contract requires. Boundary rounds return false and the
16819    /// walk runs the ordinary per-column program.
16820    pub(crate) fn step35_spec_fa2_precheck(
16821        &self,
16822        cache: &Cache,
16823        il: usize,
16824        pos0: usize,
16825    ) -> Result<bool, Box<dyn std::error::Error>> {
16826        // MEMRA_SPEC_FA2_DEBUG=1: print the first failing clause once per clause id —
16827        // a silently-vacuous door is indistinguishable from a slow one without this.
16828        fn nope(clause: &str, il: usize, pos0: usize) -> bool {
16829            static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16830            static SEEN: std::sync::Mutex<Vec<&'static str>> = std::sync::Mutex::new(Vec::new());
16831            if *DBG.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_DEBUG").as_deref() == Ok("1")) {
16832                let mut seen = SEEN.lock().unwrap();
16833                if !seen.iter().any(|c| *c == clause) {
16834                    // leak: bounded by the clause-id set
16835                    seen.push(Box::leak(clause.to_string().into_boxed_str()));
16836                    eprintln!("[spec-fa2] precheck FAIL clause={clause} il={il} pos0={pos0}");
16837                }
16838            }
16839            false
16840        }
16841        // MEMRA_SPEC_FA2_LAYER=<il>: engage on ONE layer only (divergence bisection).
16842        static ONLY: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
16843        if let Some(only) =
16844            ONLY.get_or_init(|| std::env::var("MEMRA_SPEC_FA2_LAYER").ok()?.parse().ok())
16845        {
16846            if *only != il {
16847                return Ok(false);
16848            }
16849        }
16850        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16851            return Ok(nope("mixer", il, pos0));
16852        };
16853        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16854            return Ok(nope("step_tp", il, pos0));
16855        };
16856        let Some(attention) = tp.attention.as_ref() else {
16857            return Ok(nope("attention", il, pos0));
16858        };
16859        if !tp.runtime.native_p2p()
16860            || crate::Engine::kv_fp8_on()
16861            || !crate::tp::step_tp_dcw_enabled()?
16862            || !crate::tp::step_tp_qkv_fused_enabled()?
16863            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16864        {
16865            return Ok(nope("runtime-doors", il, pos0));
16866        }
16867        let geometry = self.step35_geom(il);
16868        let head_dim = geometry.head_dim_k as usize;
16869        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16870            return Ok(nope("fa-class", il, pos0));
16871        }
16872        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16873            return Ok(nope("tp-kv", il, pos0));
16874        };
16875        if distributed.staged_len() != pos0 {
16876            return Ok(nope("staged-len", il, pos0));
16877        }
16878        // Both appends must land without a ring rebase (rebase columns take the
16879        // host-row path, which cannot stash).
16880        let (_, would_rebase) = distributed.peek_append_ring(2)?;
16881        if would_rebase {
16882            return Ok(nope("rebase", il, pos0));
16883        }
16884        let window = geometry.window.map(|w| w as usize);
16885        // Capped SWA is REDUCTION-CLASS in the joined kernel (the two rows' windows
16886        // shift by one key, so one shared tile grid cannot reproduce both rows'
16887        // per-column FP grouping) — and drifted verify logits change accept decisions,
16888        // breaking the spec==target contract. Engage only when BOTH rows' views start
16889        // at 0 (global, or SWA still inside its window): bitwise per row under the
16890        // partition guard below. At agentic ctx this keeps the global layers — ~3/4 of
16891        // the per-key fa work — and leaves capped-SWA layers on the per-column program.
16892        if let Some(w) = window {
16893            if pos0 + 2 > w {
16894                return Ok(nope("swa-capped", il, pos0));
16895            }
16896        }
16897        // Row r's own per-column launch sees the POST-append view: T_r = pos0 + 1 + r
16898        // (kernel T_kv = len_dev - lstart; the host bucket matches it — the one-partition
16899        // law). Both dcw eligibility (t_kv_eff >= 96) and the vec floor key off T0.
16900        let (t0, t1) = (pos0 + 1, pos0 + 2);
16901        if t0 < 96 {
16902            return Ok(nope("dcw-floor", il, pos0));
16903        }
16904        if std::env::var("MEMRA_NO_FA_VEC").is_ok() || t0 < crate::fa_vec_min_tkv() {
16905            return Ok(nope("vec-floor", il, pos0));
16906        }
16907        // Equal-partition guard, on the KERNEL's derivation: split width (sp), effective
16908        // count (ns = ceil(T/sp)) and stride (per = ceil(T/ns)) must all match between
16909        // the two rows' own launches — the joined kernel derives one grid from T1 and
16910        // row0 inherits it, so any difference shifts row0's split boundaries and changes
16911        // the combine's merge rounding. Boundary rounds fall back per column.
16912        let ranks = tp.runtime.devices().len();
16913        let local_kv_heads = (geometry.n_head_kv as usize / ranks).max(1);
16914        let sp0 = crate::fa_split_keys_pub(t0, local_kv_heads);
16915        let sp1 = crate::fa_split_keys_pub(t1, local_kv_heads);
16916        if sp0 != sp1 {
16917            return Ok(nope("partition-sp", il, pos0));
16918        }
16919        let (ns0, ns1) = (t0.div_ceil(sp0), t1.div_ceil(sp1));
16920        if ns0 != ns1 {
16921            return Ok(nope("partition-ns", il, pos0));
16922        }
16923        if t0.div_ceil(ns0) != t1.div_ceil(ns1) {
16924            return Ok(nope("partition-per", il, pos0));
16925        }
16926        Ok(true)
16927    }
16928
16929    /// T-ROW fa precheck (the rows kernel supersedes the dcw2 pair-join): every dynamic
16930    /// input of the engine-side dcw decision must hold for EVERY row — stashing is
16931    /// unrecoverable — plus the rows-launcher guards (big-rig ladder, no env split
16932    /// overrides). No partition or capped-SWA clauses: each row derives its OWN geometry.
16933    pub(crate) fn step35_fa_rows_precheck(
16934        &self,
16935        cache: &Cache,
16936        il: usize,
16937        pos0: usize,
16938        t: usize,
16939    ) -> Result<bool, Box<dyn std::error::Error>> {
16940        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
16941            return Ok(false);
16942        };
16943        let Some(tp) = fa.step_tp_qkv.as_ref() else {
16944            return Ok(false);
16945        };
16946        let Some(attention) = tp.attention.as_ref() else {
16947            return Ok(false);
16948        };
16949        if !tp.runtime.native_p2p()
16950            || crate::Engine::kv_fp8_on()
16951            || !crate::tp::step_tp_dcw_enabled()?
16952            || !crate::tp::step_tp_qkv_fused_enabled()?
16953            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
16954        {
16955            return Ok(false);
16956        }
16957        let geometry = self.step35_geom(il);
16958        let head_dim = geometry.head_dim_k as usize;
16959        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
16960            return Ok(false);
16961        }
16962        if crate::fa_sm_count() < 128
16963            || std::env::var("MEMRA_FA_SPLIT").is_ok()
16964            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
16965            || std::env::var("MEMRA_FA_SP16").is_ok()
16966            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
16967        {
16968            return Ok(false);
16969        }
16970        let Some(distributed) = cache.tp_kv[il].as_ref() else {
16971            return Ok(false);
16972        };
16973        if distributed.staged_len() != pos0 {
16974            return Ok(false);
16975        }
16976        let (_, would_rebase) = distributed.peek_append_ring(t)?;
16977        if would_rebase {
16978            return Ok(false);
16979        }
16980        // Row 0 sees the smallest view: its post-append effective t_kv must clear both
16981        // the dcw floor and the vec-class floor (later rows only grow).
16982        let window = geometry.window.map(|w| w as usize);
16983        let t0 = window.map(|w| (pos0 + 1).min(w)).unwrap_or(pos0 + 1);
16984        if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
16985            return Ok(false);
16986        }
16987        Ok(true)
16988    }
16989
16990    /// T-ROW fa join for the verify walk (same-session rows: shared ring/len with
16991    /// len_back = t-1-r). Tables stage once per (layer, rank, ring, t) and live on the
16992    /// owning rank.
16993    pub(crate) fn step35_verify_fa_rows_join(
16994        &self,
16995        e: &Engine,
16996        il: usize,
16997        cache: &Cache,
16998        pos0: usize,
16999        t: usize,
17000    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17001        use cudarc::driver::DevicePtr;
17002        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17003            return Err("fa rows join expects full attention".into());
17004        };
17005        let tp = fa
17006            .step_tp_qkv
17007            .as_ref()
17008            .ok_or("fa rows join lost its resident projections")?;
17009        let geometry = self.step35_geom(il);
17010        let heads = geometry.n_head as usize;
17011        let head_dim = geometry.head_dim_k as usize;
17012        let window = geometry.window.map(|w| w as usize);
17013        let distributed = cache.tp_kv[il]
17014            .as_ref()
17015            .ok_or("fa rows join lost its distributed KV cache")?;
17016        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17017        // Host mirror of the kernel's big-rig ladder (launcher-guarded identical).
17018        let ladder = |t_kv: usize| -> usize {
17019            if t_kv <= 2048 {
17020                16
17021            } else if t_kv <= 16384 {
17022                64
17023            } else {
17024                128
17025            }
17026        };
17027        let mut max_ns = 1usize;
17028        for r in 0..t {
17029            let t_kv = window
17030                .map(|w| (pos0 + r + 1).min(w))
17031                .unwrap_or(pos0 + r + 1);
17032            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17033        }
17034        // Rebuild the tiny raw-pointer table from the live distributed cache immediately
17035        // before launch. A process-lifetime map cannot prove allocation generation: CUDA may
17036        // recycle len/base independently of the large K/V rings, making a pointer-key cache
17037        // hit refer to another session (Hermes `11339f5cd3c132a3`).
17038        let ranks = tp.runtime.devices().len();
17039        let mut tables = Vec::with_capacity(ranks);
17040        for rank in 0..ranks {
17041            let engine = tp
17042                .runtime
17043                .rank_engine(rank)
17044                .ok_or("fa rows join lost a rank engine")?;
17045            let rank_cache = distributed
17046                .rank(rank)
17047                .ok_or("fa rows join lost a KV cache rank")?;
17048            let _main = engine.gpu.enter_main()?;
17049            let s = engine.stream();
17050            let (kp, _g0) = rank_cache.k().device_ptr(&s);
17051            let (vp, _g1) = rank_cache.v().device_ptr(&s);
17052            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17053            let bp = match rank_cache.base_d() {
17054                Some(b) => {
17055                    let (p, _g) = b.device_ptr(&s);
17056                    p as u64
17057                }
17058                None => 0u64,
17059            };
17060            let mut host = Vec::with_capacity(t * 6);
17061            for r in 0..t {
17062                host.extend_from_slice(&[
17063                    kp as u64,
17064                    vp as u64,
17065                    lp as u64,
17066                    bp,
17067                    0u64,
17068                    (t - 1 - r) as u64,
17069                ]);
17070            }
17071            tables.push(engine.stream().clone_htod(&host)?);
17072        }
17073        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17074        let ws_index = tp
17075            .runtime
17076            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17077        tp.runtime.decode_v2_fa_rows_join(
17078            ws_index,
17079            e,
17080            &tp.o,
17081            &tabs,
17082            t,
17083            head_dim,
17084            window.unwrap_or(0),
17085            max_ns,
17086            geometry.attention_scale(),
17087            k_tok_bytes,
17088            v_tok_bytes,
17089        )
17090    }
17091
17092    /// Multi-session t-row fa precheck (the batched serving walk): the static doors of
17093    /// the rows kernel plus per-SESSION dynamic checks — every row's own cache must be
17094    /// hydrated, in sync, rebase-free and above both floors.
17095    pub(crate) fn step35_batch_fa_rows_precheck(
17096        &self,
17097        caches: &[&mut Cache],
17098        row_to_cache: impl Fn(usize) -> usize,
17099        positions: &[i32],
17100        il: usize,
17101    ) -> Result<bool, Box<dyn std::error::Error>> {
17102        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17103            return Ok(false);
17104        };
17105        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17106            return Ok(false);
17107        };
17108        let Some(attention) = tp.attention.as_ref() else {
17109            return Ok(false);
17110        };
17111        if !tp.runtime.native_p2p()
17112            || crate::Engine::kv_fp8_on()
17113            || !crate::tp::step_tp_dcw_enabled()?
17114            || !crate::tp::step_tp_qkv_fused_enabled()?
17115            || (attention.gate_shards.is_none() && attention.gate_shards_bf16.is_none())
17116        {
17117            return Ok(false);
17118        }
17119        let geometry = self.step35_geom(il);
17120        let head_dim = geometry.head_dim_k as usize;
17121        if head_dim > 256 || head_dim % 32 != 0 || !crate::fa_v3_on() {
17122            return Ok(false);
17123        }
17124        if crate::fa_sm_count() < 128
17125            || std::env::var("MEMRA_FA_SPLIT").is_ok()
17126            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
17127            || std::env::var("MEMRA_FA_SP16").is_ok()
17128            || std::env::var("MEMRA_NO_FA_VEC").is_ok()
17129        {
17130            return Ok(false);
17131        }
17132        let window = geometry.window.map(|w| w as usize);
17133        for (r, &pos) in positions.iter().enumerate() {
17134            let cache = &caches[row_to_cache(r)];
17135            let Some(distributed) = cache.tp_kv[il].as_ref() else {
17136                return Ok(false);
17137            };
17138            if distributed.staged_len() != pos as usize {
17139                return Ok(false);
17140            }
17141            if distributed.peek_append_ring(1)?.1 {
17142                return Ok(false);
17143            }
17144            let t0 = window
17145                .map(|w| (pos as usize + 1).min(w))
17146                .unwrap_or(pos as usize + 1);
17147            if t0 < 96 || t0 < crate::fa_vec_min_tkv() {
17148                return Ok(false);
17149            }
17150        }
17151        Ok(true)
17152    }
17153
17154    /// FULL t-row attention pass for the VERIFY walk (same-session rows): rope/append +
17155    /// fa + combine + o_proj join in 3 launches/rank/layer. Row r appends at slot
17156    /// len-base+r and one last block advances len by t; the fa rows read len_back =
17157    /// t-1-r. Returns None when the fused-rope class does not hold (the walk keeps the
17158    /// per-column stash flow). Caller has passed `step35_fa_rows_precheck`.
17159    pub(crate) fn step35_verify_rope_fa_pass(
17160        &self,
17161        e: &Engine,
17162        il: usize,
17163        cache: &Cache,
17164        pos0: usize,
17165        t: usize,
17166        stage_pos: bool,
17167    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17168        use cudarc::driver::DevicePtr;
17169        if !crate::tp::fuse_rope_append_on() {
17170            return Ok(None);
17171        }
17172        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17173            return Ok(None);
17174        };
17175        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17176            return Ok(None);
17177        };
17178        let Some(attention) = tp.attention.as_ref() else {
17179            return Ok(None);
17180        };
17181        let geometry = self.step35_geom(il);
17182        let head_dim = geometry.head_dim_k as usize;
17183        if head_dim != 128 {
17184            return Ok(None);
17185        }
17186        let heads = geometry.n_head as usize;
17187        let window = geometry.window.map(|w| w as usize);
17188        let ranks = tp.runtime.devices().len();
17189        let Some(distributed) = cache.tp_kv[il].as_ref() else {
17190            return Ok(None);
17191        };
17192        if distributed.kv_dim_k() != distributed.kv_dim_v() {
17193            return Ok(None);
17194        }
17195        {
17196            let rank0 = distributed.rank(0).ok_or("verify rope pass lost rank 0")?;
17197            if rank0.base_d().is_none()
17198                && distributed.staged_len() + t > distributed.physical_capacity()
17199            {
17200                return Ok(None);
17201            }
17202        }
17203        let mut rope_freqs = Vec::with_capacity(ranks);
17204        for rank in 0..ranks {
17205            let engine = tp
17206                .runtime
17207                .rank_engine(rank)
17208                .ok_or("verify rope pass lost a rank engine")?;
17209            rope_freqs.push(if geometry.rope_factors {
17210                match self
17211                    .step35_aux
17212                    .as_ref()
17213                    .and_then(|aux| aux.rope_freqs(engine))
17214                {
17215                    Some(f) => Some(f),
17216                    None => return Ok(None),
17217                }
17218            } else {
17219                None
17220            });
17221        }
17222        let ladder = |t_kv: usize| -> usize {
17223            if t_kv <= 2048 {
17224                16
17225            } else if t_kv <= 16384 {
17226                64
17227            } else {
17228                128
17229            }
17230        };
17231        let (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17232        let mut max_ns = 1usize;
17233        let mut positions = Vec::with_capacity(t);
17234        for r in 0..t {
17235            positions.push((pos0 + r) as i32);
17236            let t_kv = window
17237                .map(|w| (pos0 + r + 1).min(w))
17238                .unwrap_or(pos0 + r + 1);
17239            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17240        }
17241        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17242        let mut tab_keys = vec![0u64; ranks];
17243        for rank in 0..ranks {
17244            let engine = tp
17245                .runtime
17246                .rank_engine(rank)
17247                .ok_or("verify rope pass lost a rank engine")?;
17248            let rank_cache = distributed
17249                .rank(rank)
17250                .ok_or("verify rope pass lost a KV cache rank")?;
17251            let _main = engine.gpu.enter_main()?;
17252            let s = engine.stream();
17253            let (kp, _g0) = rank_cache.k().device_ptr(&s);
17254            let (vp, _g1) = rank_cache.v().device_ptr(&s);
17255            let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17256            let bp = match rank_cache.base_d() {
17257                Some(b) => {
17258                    let (p, _g) = b.device_ptr(&s);
17259                    p as u64
17260                }
17261                None => 0u64,
17262            };
17263            tab_keys[rank] = (kp as u64)
17264                .rotate_left(17)
17265                .wrapping_add(bp)
17266                .wrapping_add((il as u64) << 32)
17267                .wrapping_add(t as u64)
17268                .wrapping_add(1 << 63);
17269            for _r in 0..t {
17270                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17271            }
17272        }
17273        let ws_index = tp
17274            .runtime
17275            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17276        tp.runtime
17277            .decode_v2_rope_fa_rows(
17278                ws_index,
17279                e,
17280                &tp.o,
17281                &session_parts,
17282                &tab_keys,
17283                &positions,
17284                stage_pos,
17285                true,
17286                &attention.q_norm,
17287                &attention.k_norm,
17288                &rope_freqs,
17289                t,
17290                head_dim,
17291                geometry.n_rot as usize,
17292                window.unwrap_or(0),
17293                max_ns,
17294                geometry.attention_scale(),
17295                k_tok_bytes,
17296                v_tok_bytes,
17297                self.cfg.rms_eps,
17298                geometry.rope_base,
17299            )
17300            .map(Some)
17301    }
17302
17303    /// FULL t-row attention pass for the batched walk (rope/append + fa + combine +
17304    /// o_proj join, 3 launches/rank/layer): returns None when the fused-rope class does
17305    /// not hold — the caller falls back to the per-row stash flow. The caller has
17306    /// already passed `step35_batch_fa_rows_precheck`.
17307    #[allow(clippy::too_many_arguments)]
17308    pub(crate) fn step35_batch_rope_fa_pass(
17309        &self,
17310        e: &Engine,
17311        il: usize,
17312        caches: &[&mut Cache],
17313        row_to_cache: impl Fn(usize) -> usize,
17314        positions: &[i32],
17315        t: usize,
17316        stage_pos: bool,
17317    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17318        use cudarc::driver::DevicePtr;
17319        if !crate::tp::fuse_rope_append_on() {
17320            return Ok(None);
17321        }
17322        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17323            return Ok(None);
17324        };
17325        let Some(tp) = fa.step_tp_qkv.as_ref() else {
17326            return Ok(None);
17327        };
17328        let Some(attention) = tp.attention.as_ref() else {
17329            return Ok(None);
17330        };
17331        let geometry = self.step35_geom(il);
17332        let head_dim = geometry.head_dim_k as usize;
17333        if head_dim != 128 {
17334            return Ok(None);
17335        }
17336        let heads = geometry.n_head as usize;
17337        let window = geometry.window.map(|w| w as usize);
17338        let ranks = tp.runtime.devices().len();
17339        // The rows kernels never arm base_d; refuse once a ring could have rebased
17340        // without an armed base (the table would read base=0 after a real rebase).
17341        for r in 0..t {
17342            let cache = &caches[row_to_cache(r)];
17343            let Some(distributed) = cache.tp_kv[il].as_ref() else {
17344                return Ok(None);
17345            };
17346            if distributed.kv_dim_k() != distributed.kv_dim_v() {
17347                return Ok(None);
17348            }
17349            let rank0 = distributed.rank(0).ok_or("rope fa pass lost rank 0")?;
17350            if rank0.base_d().is_none()
17351                && distributed.staged_len() + t > distributed.physical_capacity()
17352            {
17353                return Ok(None);
17354            }
17355        }
17356        let mut rope_freqs = Vec::with_capacity(ranks);
17357        for rank in 0..ranks {
17358            let engine = tp
17359                .runtime
17360                .rank_engine(rank)
17361                .ok_or("rope fa pass lost a rank engine")?;
17362            rope_freqs.push(if geometry.rope_factors {
17363                match self
17364                    .step35_aux
17365                    .as_ref()
17366                    .and_then(|aux| aux.rope_freqs(engine))
17367                {
17368                    Some(f) => Some(f),
17369                    None => return Ok(None),
17370                }
17371            } else {
17372                None
17373            });
17374        }
17375        let ladder = |t_kv: usize| -> usize {
17376            if t_kv <= 2048 {
17377                16
17378            } else if t_kv <= 16384 {
17379                64
17380            } else {
17381                128
17382            }
17383        };
17384        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17385        let mut session_parts: Vec<Vec<[u64; 4]>> = vec![Vec::with_capacity(t); ranks];
17386        let mut tab_keys = vec![0u64; ranks];
17387        for (r, &pos) in positions.iter().enumerate().take(t) {
17388            let cache = &caches[row_to_cache(r)];
17389            let distributed = cache.tp_kv[il]
17390                .as_ref()
17391                .ok_or("rope fa pass lost a distributed KV cache")?;
17392            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17393            let t_kv = window
17394                .map(|w| (pos as usize + 1).min(w))
17395                .unwrap_or(pos as usize + 1);
17396            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17397            for rank in 0..ranks {
17398                let engine = tp
17399                    .runtime
17400                    .rank_engine(rank)
17401                    .ok_or("rope fa pass lost a rank engine")?;
17402                let rank_cache = distributed
17403                    .rank(rank)
17404                    .ok_or("rope fa pass lost a KV cache rank")?;
17405                let _main = engine.gpu.enter_main()?;
17406                let s = engine.stream();
17407                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17408                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17409                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17410                let bp = match rank_cache.base_d() {
17411                    Some(b) => {
17412                        let (p, _g) = b.device_ptr(&s);
17413                        p as u64
17414                    }
17415                    None => 0u64,
17416                };
17417                tab_keys[rank] = tab_keys[rank]
17418                    .rotate_left(9)
17419                    .wrapping_add(kp as u64)
17420                    .wrapping_add(bp)
17421                    .wrapping_add(il as u64);
17422                session_parts[rank].push([kp as u64, vp as u64, lp as u64, bp]);
17423            }
17424        }
17425        let ws_index = tp
17426            .runtime
17427            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17428        tp.runtime
17429            .decode_v2_rope_fa_rows(
17430                ws_index,
17431                e,
17432                &tp.o,
17433                &session_parts,
17434                &tab_keys,
17435                positions,
17436                stage_pos,
17437                false,
17438                &attention.q_norm,
17439                &attention.k_norm,
17440                &rope_freqs,
17441                t,
17442                head_dim,
17443                geometry.n_rot as usize,
17444                window.unwrap_or(0),
17445                max_ns,
17446                geometry.attention_scale(),
17447                k_tok_bytes,
17448                v_tok_bytes,
17449                self.cfg.rms_eps,
17450                geometry.rope_base,
17451            )
17452            .map(Some)
17453    }
17454
17455    /// Multi-session t-row fa join (batched serving): per-row table entries point at
17456    /// each row's OWN session rings/counters (len_back = 0 — every session appended
17457    /// exactly its one row). Tables stage once per (layer, rank, session-set, t).
17458    #[allow(clippy::too_many_arguments)]
17459    pub(crate) fn step35_batch_fa_rows_join(
17460        &self,
17461        e: &Engine,
17462        il: usize,
17463        caches: &[&mut Cache],
17464        row_to_cache: impl Fn(usize) -> usize,
17465        positions: &[i32],
17466        t: usize,
17467    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17468        use cudarc::driver::DevicePtr;
17469        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17470            return Err("batch fa rows join expects full attention".into());
17471        };
17472        let tp = fa
17473            .step_tp_qkv
17474            .as_ref()
17475            .ok_or("batch fa rows join lost its resident projections")?;
17476        let geometry = self.step35_geom(il);
17477        let heads = geometry.n_head as usize;
17478        let head_dim = geometry.head_dim_k as usize;
17479        let window = geometry.window.map(|w| w as usize);
17480        let ladder = |t_kv: usize| -> usize {
17481            if t_kv <= 2048 {
17482                16
17483            } else if t_kv <= 16384 {
17484                64
17485            } else {
17486                128
17487            }
17488        };
17489        let (mut max_ns, mut k_tok_bytes, mut v_tok_bytes) = (1usize, 0usize, 0usize);
17490        for (r, &pos) in positions.iter().enumerate() {
17491            let cache = &caches[row_to_cache(r)];
17492            let distributed = cache.tp_kv[il]
17493                .as_ref()
17494                .ok_or("batch fa rows join lost a distributed KV cache")?;
17495            (k_tok_bytes, v_tok_bytes) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
17496            let t_kv = window
17497                .map(|w| (pos as usize + 1).min(w))
17498                .unwrap_or(pos as usize + 1);
17499            max_ns = max_ns.max(t_kv.div_ceil(ladder(t_kv)));
17500        }
17501        // Multi-session tables also rebuild from every live K/V/len/base tuple. Keeping a
17502        // process-lifetime raw-pointer cache here omitted V and len identity and had no
17503        // allocation generation, so allocator reuse could bind one request to another.
17504        let ranks = tp.runtime.devices().len();
17505        let mut tables = Vec::with_capacity(ranks);
17506        for rank in 0..ranks {
17507            let engine = tp
17508                .runtime
17509                .rank_engine(rank)
17510                .ok_or("batch fa rows join lost a rank engine")?;
17511            let _main = engine.gpu.enter_main()?;
17512            let s = engine.stream();
17513            let mut host = Vec::with_capacity(t * 6);
17514            for r in 0..t {
17515                let cache = &caches[row_to_cache(r)];
17516                let distributed = cache.tp_kv[il]
17517                    .as_ref()
17518                    .ok_or("batch fa rows join lost a distributed KV cache")?;
17519                let rank_cache = distributed
17520                    .rank(rank)
17521                    .ok_or("batch fa rows join lost a KV cache rank")?;
17522                let (kp, _g0) = rank_cache.k().device_ptr(&s);
17523                let (vp, _g1) = rank_cache.v().device_ptr(&s);
17524                let (lp, _g2) = rank_cache.len_d().device_ptr(&s);
17525                let bp = match rank_cache.base_d() {
17526                    Some(b) => {
17527                        let (p, _g) = b.device_ptr(&s);
17528                        p as u64
17529                    }
17530                    None => 0u64,
17531                };
17532                host.extend_from_slice(&[kp as u64, vp as u64, lp as u64, bp, 0u64, 0u64]);
17533            }
17534            tables.push(engine.stream().clone_htod(&host)?);
17535        }
17536        let tabs: Vec<&CudaSlice<u64>> = tables.iter().collect();
17537        let ws_index = tp
17538            .runtime
17539            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17540        tp.runtime.decode_v2_fa_rows_join(
17541            ws_index,
17542            e,
17543            &tp.o,
17544            &tabs,
17545            t,
17546            head_dim,
17547            window.unwrap_or(0),
17548            max_ns,
17549            geometry.attention_scale(),
17550            k_tok_bytes,
17551            v_tok_bytes,
17552        )
17553    }
17554
17555    /// MEMRA_SPEC_FA2 join for the verify walk: both columns stashed; one shared-KV T=2
17556    /// fa per rank + the weight-amortized o_proj join produce the [2, o_out] `mixed`
17557    /// slab on `e`.
17558    pub(crate) fn step35_verify_spec_fa2_join(
17559        &self,
17560        e: &Engine,
17561        il: usize,
17562        cache: &Cache,
17563        pos0: usize,
17564    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17565        let crate::hybrid::Mixer::Full(fa) = &self.layers[il].mixer else {
17566            return Err("spec fa2 join expects full attention".into());
17567        };
17568        let tp = fa
17569            .step_tp_qkv
17570            .as_ref()
17571            .ok_or("spec fa2 join lost its resident projections")?;
17572        let geometry = self.step35_geom(il);
17573        let heads = geometry.n_head as usize;
17574        let head_dim = geometry.head_dim_k as usize;
17575        let window = geometry.window.map(|w| w as usize);
17576        // POST-append view of the second row (kernel T1 = len - lstart with len =
17577        // pos0 + 2): sp/ns derive from it, and the precheck proved row0 shares them.
17578        let bucket = window.map(|w| (pos0 + 2).min(w)).unwrap_or(pos0 + 2);
17579        let distributed = cache.tp_kv[il]
17580            .as_ref()
17581            .ok_or("spec fa2 join lost its distributed KV cache")?;
17582        let ws_index = tp
17583            .runtime
17584            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
17585        tp.runtime.decode_v2_spec_fa2_join(
17586            ws_index,
17587            e,
17588            &tp.o,
17589            distributed,
17590            head_dim,
17591            window.unwrap_or(0),
17592            bucket,
17593            geometry.attention_scale(),
17594        )
17595    }
17596
17597    /// TWO-COLUMN MoE FFN for the spec verify walk (MEMRA_TCOL_FFN): route both columns
17598    /// with the fixed per-row router program (t=2 grid, per-row bit-equal to t=1), run the
17599    /// two-column device-routed expert sweep, then the t=1 shared-expert program per
17600    /// column. Returns [2, n_embd] on `e`, or None when this layer/config is ineligible
17601    /// (caller falls back to the per-column walk).
17602    pub(crate) fn step35_verify_moe_tn(
17603        &self,
17604        e: &Engine,
17605        il: usize,
17606        z_t: &CudaSlice<f32>,
17607        t: usize,
17608    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17609        let layer = &self.layers[il];
17610        let crate::hybrid::Ffn::Moe(m) = &layer.ffn else {
17611            return Ok(None);
17612        };
17613        let Some(tp) = m.step_tp.as_ref() else {
17614            return Ok(None);
17615        };
17616        let crate::hybrid::StepTpExpertBank::Nvfp4(bank) = &tp.experts else {
17617            return Ok(None);
17618        };
17619        if !crate::tp::step_nvfp4_dev_routes_enabled()?
17620            || !crate::tp::step_tp_dev_router_enabled()?
17621            || !crate::tp::nvfp4_bank_v2_on()
17622            || bank.ep2
17623        {
17624            return Ok(None);
17625        }
17626        let cfg = &self.cfg;
17627        let Some(moe) = cfg.moe.as_ref() else {
17628            return Ok(None);
17629        };
17630        let Some((sf, route_norm)) = cfg.sigmoid_router() else {
17631            return Ok(None);
17632        };
17633        let n_embd = cfg.n_embd as usize;
17634        let n_expert = moe.expert_count as usize;
17635        let n_used = moe.expert_used_count as usize;
17636        if t < 2 || t > 32 || z_t.len() < t * n_embd {
17637            return Err("verify moe t-row geometry".into());
17638        }
17639        let trace = std::env::var("MEMRA_TN_TRACE").as_deref() == Ok("1");
17640        if trace {
17641            eprintln!("[tn-trace] il={il} t={t} logits");
17642        }
17643        let logits = Self::moe_router_logits(e, m, z_t, t, cfg)?;
17644        // Persistent selection rows (host-op diet, same shape law as the t=1 SELW),
17645        // sized for the widest walk (t <= 8).
17646        static SELW2: std::sync::Mutex<Option<(usize, CudaSlice<i32>, CudaSlice<f32>)>> =
17647            std::sync::Mutex::new(None);
17648        let mut selw = SELW2.lock().map_err(|_| "selw2 lock poisoned")?;
17649        if selw.as_ref().is_none_or(|(d, ..)| *d != e.ctx().ordinal()) {
17650            *selw = Some((
17651                e.ctx().ordinal(),
17652                e.htod_i32(&vec![0i32; 32 * n_used])?,
17653                e.htod(&vec![0.0f32; 32 * n_used])?,
17654            ));
17655        }
17656        let (_, sel_d, w_d) = selw.as_mut().expect("armed above");
17657        if trace {
17658            eprintln!("[tn-trace] il={il} topk logits_len={}", logits.len());
17659        }
17660        e.moe_router_sigmoid_topk_into(
17661            &logits,
17662            t,
17663            n_expert,
17664            n_used,
17665            m.active_count(),
17666            &m.exp_probs_b_dev,
17667            &m.active_experts_dev,
17668            sf,
17669            route_norm,
17670            sel_d,
17671            w_d,
17672        )?;
17673        if trace {
17674            eprintln!("[tn-trace] il={il} driver");
17675        }
17676        // MEMRA_MOE_OVERLAP=1: how much would deduping the expert UNION across verify columns
17677        // buy? The sweep runs t*n_used slots (n_sel below), so an expert two columns both select
17678        // is read TWICE — the "weight re-read per (token,slot)" class the NVFP4 CSR twin fixed
17679        // elsewhere (+6.8% at B=8). Whether that is worth building here depends entirely on the
17680        // real overlap, which is a property of the router on real traffic, not of the code. One
17681        // dtoh per layer under the flag; diagnostic only.
17682        if std::env::var("MEMRA_MOE_OVERLAP").as_deref() == Ok("1") {
17683            let sel_host = e.dtoh_i32(sel_d)?;
17684            let slots = t * n_used;
17685            if sel_host.len() >= slots {
17686                let mut uniq = std::collections::HashSet::new();
17687                for &x in &sel_host[..slots] {
17688                    uniq.insert(x);
17689                }
17690                // BUCKET BY t. The same walk serves BOTH the spec verify (t = K+1) and the
17691                // chunked prime (t = MEMRA_PRIME_TROWS_T = 8), and the prime dominates any flat
17692                // average: 511 chunks x 45 layers on a 4k prompt against a handful of verify
17693                // columns. A pooled number reported the PRIME's overlap as the verify's.
17694                type OverlapMap = std::collections::BTreeMap<usize, (u64, u64, u64)>;
17695                static BUCKETS: std::sync::Mutex<Option<OverlapMap>> = std::sync::Mutex::new(None);
17696                if let Ok(mut g) = BUCKETS.lock() {
17697                    let map = g.get_or_insert_with(OverlapMap::new);
17698                    let ent = map.entry(t).or_insert((0, 0, 0));
17699                    ent.0 += slots as u64;
17700                    ent.1 += uniq.len() as u64;
17701                    ent.2 += 1;
17702                    let total: u64 = map.values().map(|v| v.2).sum();
17703                    if total % 2000 == 0 {
17704                        let line = map
17705                            .iter()
17706                            .map(|(tb, (sl, uq, n))| {
17707                                format!(
17708                                    "t={tb}: slots {:.1} distinct {:.1} dup {:.1}% (n={n})",
17709                                    *sl as f64 / *n as f64,
17710                                    *uq as f64 / *n as f64,
17711                                    100.0 * (1.0 - *uq as f64 / *sl as f64)
17712                                )
17713                            })
17714                            .collect::<Vec<_>>()
17715                            .join(" | ");
17716                        eprintln!("[moe-overlap] {line}");
17717                    }
17718                }
17719            }
17720        }
17721        // MEMRA_TN_PREJOIN: ride the routed sweep's prejoin window with the shared expert's
17722        // compute. The verify walk costs ~1.27x the plain decode tick at equal t, and the plain
17723        // tick is the path that already has this window (`_routed_prejoin`); the walk left the
17724        // stream idle across the peer drain and then ran the shexp serially after the join.
17725        // DEFAULT OFF pending its own interleaved cell + the greedy tape: the arm is bit-gateable
17726        // by construction (issue order moves, the float expression does not) but "by construction"
17727        // is an argument, not a receipt, and an unmeasured door does not default on.
17728        let prejoin_shexp = {
17729            static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
17730            crate::step37_door(&ENV, "MEMRA_TN_PREJOIN")
17731        };
17732        let mut staged_gate: Option<CudaSlice<f32>> = None;
17733        let mut out_t = if prejoin_shexp {
17734            let mut staged: Option<CudaSlice<f32>> = None;
17735            let out = tp
17736                .runtime
17737                .run_tensor_parallel_routes_nvfp4_device_routed_tn_prejoin(
17738                    bank,
17739                    e,
17740                    z_t,
17741                    sel_d,
17742                    w_d,
17743                    t,
17744                    n_used,
17745                    tp.activation_limit,
17746                    || {
17747                        staged = Self::step35_shexp_rows_compute(e, m, z_t, t, cfg, il as u16)?;
17748                        Ok(())
17749                    },
17750                )?;
17751            staged_gate = staged;
17752            out
17753        } else {
17754            tp.runtime
17755                .run_tensor_parallel_routes_nvfp4_device_routed_tn(
17756                    bank,
17757                    e,
17758                    z_t,
17759                    sel_d,
17760                    w_d,
17761                    t,
17762                    n_used,
17763                    tp.activation_limit,
17764                )?
17765        };
17766        if trace {
17767            eprintln!("[tn-trace] il={il} shexp out_t={}", out_t.len());
17768        }
17769        // The prejoin arm already issued the shexp compute above; only its accumulate is left.
17770        if let Some(gate) = staged_gate.take() {
17771            Self::step35_shexp_rows_apply(e, m, t, cfg, &gate, &mut out_t)?;
17772            return Ok(Some(out_t));
17773        }
17774        // Shared expert: ONE t-row pass through the per-row-exact twins when the bf16
17775        // dual-silu shape holds (each row's program == the t=1 fused path); otherwise the
17776        // exact t=1 program per column.
17777        if !Self::step35_shexp_rows(e, m, z_t, t, cfg, il as u16, &mut out_t)? {
17778            let mut z_row = e.uninit(n_embd)?;
17779            let mut out_row = e.uninit(n_embd)?;
17780            for c in 0..t {
17781                e.dtod_copy_view(&z_t.slice(c * n_embd..(c + 1) * n_embd), &mut z_row)?;
17782                e.dtod_copy_view(&out_t.slice(c * n_embd..(c + 1) * n_embd), &mut out_row)?;
17783                Self::moe_ffn_grouped_add_shared(e, m, &z_row, 1, cfg, il as u16, &mut out_row)?;
17784                e.dtod_copy_into(&out_row, &mut out_t, c * n_embd)?;
17785            }
17786        }
17787        Ok(Some(out_t))
17788    }
17789
17790    /// T-ROW shared expert (spec verify / batched serving): dual-silu + down + gate +
17791    /// scaled accumulate over all rows in four launches, each the per-row-exact twin of
17792    /// the t=1 fused path. Returns false (untouched `out_t`) when the shape is ineligible.
17793    ///
17794    /// PREJOIN SPLIT (2026-08-26): `_compute` issues the three launches that do not touch `out_t`
17795    /// and `_apply` does the one that does, so the compute can ride the routed sweep's prejoin
17796    /// window while the accumulate stays after the cross-rank join. This whole-function form is
17797    /// kept as the composition of the two halves — same launches, same order, one call.
17798    fn step35_shexp_rows(
17799        e: &Engine,
17800        m: &MoeWeights,
17801        z_t: &CudaSlice<f32>,
17802        t: usize,
17803        cfg: &ModelConfig,
17804        il: u16,
17805        out_t: &mut CudaSlice<f32>,
17806    ) -> Result<bool, Box<dyn std::error::Error>> {
17807        match Self::step35_shexp_rows_compute(e, m, z_t, t, cfg, il)? {
17808            Some(gate) => {
17809                Self::step35_shexp_rows_apply(e, m, t, cfg, &gate, out_t)?;
17810                Ok(true)
17811            }
17812            None => Ok(false),
17813        }
17814    }
17815
17816    /// The shexp launches that do NOT read or write `out_t`: dual-silu, down, and the head gate.
17817    /// `sh_t` is left in the persistent workspace for `step35_shexp_rows_apply` to accumulate.
17818    /// `Ok(None)` = shape ineligible, nothing issued, caller falls back per column.
17819    fn step35_shexp_rows_compute(
17820        e: &Engine,
17821        m: &MoeWeights,
17822        z_t: &CudaSlice<f32>,
17823        t: usize,
17824        cfg: &ModelConfig,
17825        il: u16,
17826    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17827        let n_embd = cfg.n_embd as usize;
17828        let (Some(gate_shexp), Some(up_shexp), Some(down_shexp)) =
17829            (&m.gate_shexp, &m.up_shexp, &m.down_shexp)
17830        else {
17831            return Ok(None);
17832        };
17833        if !crate::Engine::bf16_mmv_on() || n_embd % 8 != 0 || cfg.m3.is_some() {
17834            return Ok(None);
17835        }
17836        let (
17837            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
17838            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
17839            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
17840        ) = (gate_shexp, up_shexp, down_shexp)
17841        else {
17842            return Ok(None);
17843        };
17844        let n_ff_sh = gate_shexp.out_features();
17845        let lim = cfg.clamp_shexp_at(il as u32);
17846        let mut guard = Self::step35_shexp_rows_ws()
17847            .lock()
17848            .map_err(|_| "shexp rows ws lock is poisoned")?;
17849        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
17850        if guard
17851            .as_ref()
17852            .is_none_or(|(d, ne, nf, ..)| (*d, *ne, *nf) != pins)
17853        {
17854            *guard = Some((
17855                pins.0,
17856                pins.1,
17857                pins.2,
17858                e.uninit(32 * n_ff_sh)?,
17859                e.uninit(32 * n_embd)?,
17860            ));
17861        }
17862        let (_, _, _, act_t, sh_t) = guard.as_mut().expect("armed above");
17863        e.matvec_bf16_dual_silu_rows_into(wg, wu, z_t, act_t, n_embd, n_ff_sh, lim, t)?;
17864        e.matvec_bf16_rows_into(wd, act_t, sh_t, n_ff_sh, n_embd, t)?;
17865        // Head gate: sigmoid_dot_rows is the exact t=1 expression per row; gate-less
17866        // shexp accumulates at weight 1 (the fuse_da identity).
17867        let gate = match &m.gate_inp_shexp {
17868            Some(gate_inp_shexp) => {
17869                e.sigmoid_dot_rows(z_t, gate_inp_shexp.float_data(), n_embd, t)?
17870            }
17871            None => e.htod(&vec![1.0f32; t])?,
17872        };
17873        Ok(Some(gate))
17874    }
17875
17876    /// Persistent t-row shexp buffers (widest walk t <= 8), shared by the compute and apply
17877    /// halves. A `static` inside an associated fn is the seam that lets `_apply` reach the `sh_t`
17878    /// that `_compute` filled without threading a device buffer through the prejoin closure
17879    /// (cloning a `CudaSlice` would copy on device, which is the opposite of the point).
17880    #[allow(clippy::type_complexity)]
17881    fn step35_shexp_rows_ws()
17882    -> &'static std::sync::Mutex<Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>>
17883    {
17884        static WS: std::sync::Mutex<Option<(usize, usize, usize, CudaSlice<f32>, CudaSlice<f32>)>> =
17885            std::sync::Mutex::new(None);
17886        &WS
17887    }
17888
17889    /// The one shexp launch that touches `out_t`: `out_t += sh_t * gate` per row. Kept AFTER the
17890    /// routed cross-rank join so the accumulate reads the joined value — same kernel, same operand
17891    /// order, same expression as the unsplit form, which is what makes the prejoin arm bit-gateable.
17892    fn step35_shexp_rows_apply(
17893        e: &Engine,
17894        m: &MoeWeights,
17895        t: usize,
17896        cfg: &ModelConfig,
17897        gate: &CudaSlice<f32>,
17898        out_t: &mut CudaSlice<f32>,
17899    ) -> Result<(), Box<dyn std::error::Error>> {
17900        let n_embd = cfg.n_embd as usize;
17901        let n_ff_sh = m
17902            .gate_shexp
17903            .as_ref()
17904            .ok_or("shexp apply lost its gate projection")?
17905            .out_features();
17906        let mut guard = Self::step35_shexp_rows_ws()
17907            .lock()
17908            .map_err(|_| "shexp rows ws lock is poisoned")?;
17909        let pins = (e.ctx().ordinal(), n_embd, n_ff_sh);
17910        let (_, _, _, _, sh_t) = guard
17911            .as_mut()
17912            .filter(|(d, ne, nf, ..)| (*d, *ne, *nf) == pins)
17913            .ok_or("shexp apply found no compute-side workspace for this shape")?;
17914        e.add_scaled_rows(sh_t, gate, out_t, n_embd, t)?;
17915        Ok(())
17916    }
17917
17918    fn step35_tp_decode_attn_resident_v2(
17919        &self,
17920        e: &Engine,
17921        fa: &FullAttnLayer,
17922        il: usize,
17923        h: &CudaSlice<f32>,
17924        pos_d: &CudaSlice<i32>,
17925        cache: &mut Cache,
17926    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17927        let tp = fa
17928            .step_tp_qkv
17929            .as_ref()
17930            .ok_or("Step TP decode lost its resident projections")?;
17931        let attention = tp
17932            .attention
17933            .as_ref()
17934            .ok_or("Step TP decode lost its resident attention auxiliaries")?;
17935        if !tp.runtime.native_p2p() {
17936            return Err("rank-local Step attention requires native P2P".into());
17937        }
17938        if crate::Engine::kv_fp8_on() {
17939            return Err("rank-local Step attention has not qualified the FP8 KV cache".into());
17940        }
17941
17942        let geometry = self.step35_geom(il);
17943        let window = geometry.window.map(|window| window as usize);
17944        let ranks = tp.runtime.devices().len();
17945        let head_dim = geometry.head_dim_k as usize;
17946        let heads = geometry.n_head as usize;
17947        let kv_heads = geometry.n_head_kv as usize;
17948        if heads % ranks != 0 || kv_heads % ranks != 0 {
17949            return Err(format!(
17950                "Step attention heads q={heads} kv={kv_heads} are not divisible by TP={ranks}"
17951            )
17952            .into());
17953        }
17954        let local_heads = heads / ranks;
17955        let local_kv_heads = kv_heads / ranks;
17956        let max_ctx = cache.max_ctx;
17957
17958        let hydrated = self.ensure_step_tp_kv_cache(e, fa, il, cache)?;
17959
17960        let base_len = cache.kv[il]
17961            .as_ref()
17962            .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?
17963            .len;
17964        {
17965            let distributed = cache.tp_kv[il]
17966                .as_ref()
17967                .ok_or_else(|| format!("Step TP layer {il} lost its distributed KV cache"))?;
17968            if distributed.committed_len() != base_len || distributed.staged_len() != base_len {
17969                return Err(format!(
17970                    "Step TP layer {il} cache lengths diverged before decode: \
17971                     local={base_len} distributed={}/{}",
17972                    distributed.committed_len(),
17973                    distributed.staged_len()
17974                )
17975                .into());
17976            }
17977        }
17978        if pos_d.len() != 1 {
17979            return Err(format!(
17980                "rank-local Step decode requires one position, got {}",
17981                pos_d.len()
17982            )
17983            .into());
17984        }
17985
17986        let decode_input = attention
17987            .decode_input
17988            .as_ref()
17989            .ok_or("Step TP decode v2 requires the replicated decode input")?;
17990        let mut decode_input = decode_input
17991            .lock()
17992            .map_err(|_| "Step TP replicated decode input lock is poisoned")?;
17993
17994        // Gate: with per-rank shards loaded (fused door), the fused QKV+gate kernel computes
17995        // it rank-locally and the model-engine matmul (and its staging copies) disappears.
17996        // Otherwise it queues on e's stream BEFORE decode_v2_input_qkv records the entry
17997        // event, so the rank-stream reads of the staged gate are ordered without a host sync.
17998        let use_gate_shards = (attention.gate_shards.is_some()
17999            || attention.gate_shards_bf16.is_some())
18000            && crate::tp::step_tp_qkv_fused_enabled()?;
18001        let gate_raw = if use_gate_shards {
18002            None
18003        } else {
18004            let gate_weight = fa
18005                .attn_gate
18006                .as_ref()
18007                .ok_or("step35 layer is missing attn_gate.weight")?;
18008            let gate_raw = e.matmul(gate_weight, h, 1)?;
18009            if gate_raw.len() != heads {
18010                return Err(format!(
18011                    "Step TP layer {il} gate output {} != {heads}",
18012                    gate_raw.len()
18013                )
18014                .into());
18015            }
18016            Some(gate_raw)
18017        };
18018
18019        let ws_index = tp
18020            .runtime
18021            .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
18022        let mut ws_guard = tp
18023            .runtime
18024            .decode_v2_workspace()
18025            .lock()
18026            .map_err(|_| "Step TP decode v2 workspace lock is poisoned")?;
18027        let ws = ws_guard
18028            .get_mut(ws_index)
18029            .ok_or("Step TP decode v2 workspace missing after ensure")?;
18030
18031        let mut rope_freqs = Vec::with_capacity(ranks);
18032        for rank in 0..ranks {
18033            let engine = tp
18034                .runtime
18035                .rank_engine(rank)
18036                .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
18037            rope_freqs.push(if geometry.rope_factors {
18038                self.step35_aux
18039                    .as_ref()
18040                    .and_then(|aux| aux.rope_freqs(engine))
18041            } else {
18042                None
18043            });
18044        }
18045        // DCW arm (MEMRA_STEP_TP_DCW=1): device-counter append + counter-derived fa — the
18046        // exact captured-child content, run eagerly. bucket = effective t_kv keeps the fa
18047        // split geometry identical to the kvmod path (one-partition law) -> bit-identical.
18048        // Rebase tokens and sub-vec-floor contexts take the host-row path below.
18049        // (Eligibility computed BEFORE input_qkv so FUSION #1 can defer the norm+rope into
18050        // the fused rope+append+inc launch on dcw tokens.)
18051        let staged_next = base_len + 1;
18052        let t_kv_eff = window
18053            .map(|window| staged_next.min(window))
18054            .unwrap_or(staged_next);
18055        let dcw = crate::tp::step_tp_dcw_enabled()? && use_gate_shards && t_kv_eff >= 96 && {
18056            let (write_row, would_rebase) = cache.tp_kv[il]
18057                .as_ref()
18058                .expect("distributed cache checked above")
18059                .peek_append_ring(1)?;
18060            if !would_rebase {
18061                // Arm the base mirrors on first use: base = logical staged - physical row.
18062                let base = (base_len - write_row) as i32;
18063                let distributed = cache.tp_kv[il]
18064                    .as_mut()
18065                    .expect("distributed cache checked above");
18066                for rank in 0..ranks {
18067                    let engine = tp.runtime.rank_engine(rank).ok_or_else(|| {
18068                        format!("Step TP layer {il} has no engine for rank {rank}")
18069                    })?;
18070                    let _main = engine.gpu.enter_main()?;
18071                    let rank_cache = distributed
18072                        .rank_mut(rank)
18073                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
18074                    if rank_cache.base_d().is_none() {
18075                        rank_cache.arm_base_d(engine.htod_i32(&[base])?);
18076                    }
18077                }
18078            }
18079            !would_rebase
18080        };
18081        let fuse_rope = dcw
18082            && crate::tp::fuse_rope_append_on()
18083            && head_dim == 128
18084            && cache.tp_kv[il]
18085                .as_ref()
18086                .map(|d| d.kv_dim_k() == d.kv_dim_v() && d.kv_dim_k() == local_kv_heads * head_dim)
18087                .unwrap_or(false);
18088
18089        let tcol_col = crate::tp::take_verify_tcol();
18090        // MEMRA_SPEC_FA2 defer: the verify walk armed this column for the shared-KV T=2
18091        // attention. On dcw tokens the per-rank pass still norms/ropes/APPENDS (cache
18092        // state must advance per column) but skips the fa+gate launch; post-rope q and
18093        // gate rows are stashed instead, and ONE fa_decode_dcw2 per rank joins both
18094        // columns after the second append. Non-dcw tokens ignore the defer (the fa runs
18095        // normally and the walk consumes the real output — stash flag stays unset).
18096        let fa2_col = crate::tp::take_spec_fa2_defer();
18097        tp.runtime.decode_v2_input_qkv(
18098            ws,
18099            e,
18100            h,
18101            pos_d,
18102            gate_raw.as_ref(),
18103            if !use_gate_shards {
18104                None
18105            } else if let Some(shards) = attention.gate_shards.as_deref() {
18106                Some(crate::tp::StepTpGateShards::F32(shards))
18107            } else {
18108                attention
18109                    .gate_shards_bf16
18110                    .as_deref()
18111                    .map(crate::tp::StepTpGateShards::Bf16)
18112            },
18113            &mut decode_input,
18114            &tp.q,
18115            &tp.k,
18116            &tp.v,
18117            &attention.q_norm,
18118            &attention.k_norm,
18119            head_dim,
18120            geometry.n_rot as usize,
18121            geometry.rope_base,
18122            &rope_freqs,
18123            self.cfg.rms_eps,
18124            fuse_rope,
18125            tcol_col,
18126        )?;
18127
18128        let transaction = cache.tp_kv[il]
18129            .as_mut()
18130            .expect("distributed cache checked above")
18131            .begin_transaction()?;
18132        let append_result = tp.runtime.append_tp_kv_transaction_inner(
18133            cache.tp_kv[il]
18134                .as_mut()
18135                .expect("distributed cache checked above"),
18136            transaction,
18137            &ws.k,
18138            &ws.v_raw,
18139            1,
18140            dcw,
18141        );
18142        if let Err(error) = append_result {
18143            let _ = tp.runtime.rollback_tp_kv_transaction(
18144                cache.tp_kv[il]
18145                    .as_mut()
18146                    .expect("distributed cache checked above"),
18147                transaction,
18148            );
18149            return Err(error);
18150        }
18151
18152        let staged = (|| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18153            // Copy the view scalars out and DROP the shared borrow — the merged dcw arm
18154            // reborrows the cache mutably per rank.
18155            let (staged_len, physical, k_tok_bytes_c, v_tok_bytes_c, capacity) = {
18156                let distributed = cache.tp_kv[il]
18157                    .as_ref()
18158                    .expect("distributed cache checked above");
18159                let staged_len = distributed.staged_len();
18160                let view_start = window
18161                    .map(|window| staged_len.saturating_sub(window))
18162                    .unwrap_or(0);
18163                (
18164                    staged_len,
18165                    distributed.physical_range(view_start, staged_len)?,
18166                    distributed.k_tok_bytes(),
18167                    distributed.v_tok_bytes(),
18168                    distributed.physical_capacity(),
18169                )
18170            };
18171            let view_start = window
18172                .map(|window| staged_len.saturating_sub(window))
18173                .unwrap_or(0);
18174            let t_kv = staged_len - view_start;
18175            for rank in 0..ranks {
18176                let engine = tp
18177                    .runtime
18178                    .rank_engine(rank)
18179                    .ok_or_else(|| format!("Step TP layer {il} has no engine for rank {rank}"))?;
18180                let _main = engine.gpu.enter_main()?;
18181                if dcw {
18182                    // MERGED per-rank pass (the capture unit): append + inc + fa + gate on ONE
18183                    // stream visit. distributed is borrowed shared here; the planes need mut —
18184                    // reborrow through the cache Option (the closure holds cache mutably).
18185                    {
18186                        let distributed_mut = cache.tp_kv[il]
18187                            .as_mut()
18188                            .expect("distributed cache checked above");
18189                        let (kv_dim_k, kv_dim_v) =
18190                            (distributed_mut.kv_dim_k(), distributed_mut.kv_dim_v());
18191                        let (k_tok_bytes, v_tok_bytes) =
18192                            (distributed_mut.k_tok_bytes(), distributed_mut.v_tok_bytes());
18193                        let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
18194                            format!("Step TP layer {il} has no KV cache rank {rank}")
18195                        })?;
18196                        let (k_plane, v_plane, len_d, base_d) =
18197                            rank_cache.planes_and_counters_mut();
18198                        if fuse_rope {
18199                            // FUSION #1: norms + rope (deferred out of input_qkv) + append
18200                            // + last-block len inc in ONE launch. Bit-identical bodies.
18201                            let same_dev = engine.ctx().ordinal() == e.ctx().ordinal();
18202                            let crate::tp::StepTpDecodeV2Ws {
18203                                q_raw,
18204                                k_raw,
18205                                v_raw,
18206                                q,
18207                                k,
18208                                pos,
18209                                pos_stage,
18210                                fuse_ctr,
18211                                ..
18212                            } = &mut *ws;
18213                            // Same-device rank: the staged-copy elision leaves pos[rank]
18214                            // stale — read the e-context pos stage directly (mirrors the
18215                            // rope elision in input_qkv_rank).
18216                            let pos_ref: &CudaSlice<i32> = if same_dev {
18217                                pos_stage
18218                                    .as_ref()
18219                                    .ok_or("step TP decode v2 pos stage not armed")?
18220                            } else {
18221                                &pos[rank]
18222                            };
18223                            engine.qk_norm_rope_append_inc_dcw(
18224                                &q_raw[rank],
18225                                &k_raw[rank],
18226                                &v_raw[rank],
18227                                &attention.q_norm[rank],
18228                                &attention.k_norm[rank],
18229                                &mut q[rank],
18230                                &mut k[rank],
18231                                pos_ref,
18232                                k_plane,
18233                                v_plane,
18234                                len_d,
18235                                base_d,
18236                                &mut fuse_ctr[rank],
18237                                kv_dim_k,
18238                                kv_dim_v,
18239                                k_tok_bytes,
18240                                v_tok_bytes,
18241                                head_dim,
18242                                geometry.n_rot as usize,
18243                                local_heads,
18244                                local_kv_heads,
18245                                self.cfg.rms_eps,
18246                                geometry.rope_base,
18247                                1.0,
18248                                rope_freqs[rank],
18249                            )?;
18250                        } else {
18251                            engine.append_kv_quantized_dcw(
18252                                &ws.k[rank],
18253                                &ws.v_raw[rank],
18254                                k_plane,
18255                                v_plane,
18256                                len_d,
18257                                base_d,
18258                                kv_dim_k,
18259                                kv_dim_v,
18260                                k_tok_bytes,
18261                                v_tok_bytes,
18262                            )?;
18263                        }
18264                        if !fuse_rope {
18265                            let rank_cache = distributed_mut.rank_mut(rank).ok_or_else(|| {
18266                                format!("Step TP layer {il} has no KV cache rank {rank}")
18267                            })?;
18268                            engine.inc_i32(rank_cache.len_d_mut())?;
18269                        }
18270                    }
18271                    let distributed = cache.tp_kv[il]
18272                        .as_ref()
18273                        .expect("distributed cache checked above");
18274                    let rank_cache = distributed
18275                        .rank(rank)
18276                        .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
18277                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes_c);
18278                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes_c);
18279                    if fa2_col.is_some() {
18280                        // SPEC_FA2 defer: append landed above; the fa for this column
18281                        // runs in the T=2 joined launch after the pair's second append.
18282                        continue;
18283                    }
18284                    {
18285                        // FUSION #2d: combine + head gate in the dcw tail — `gated` receives
18286                        // the gated output directly (bit-identical; one launch saved).
18287                        let crate::tp::StepTpDecodeV2Ws { q, gate, gated, .. } = &mut *ws;
18288                        engine.fa_decode_dcw(
18289                            &q[rank],
18290                            &k_ring,
18291                            &v_ring,
18292                            &mut gated[rank],
18293                            head_dim,
18294                            local_heads,
18295                            local_kv_heads,
18296                            rank_cache.len_d(),
18297                            rank_cache.base_d(),
18298                            window.unwrap_or(0),
18299                            t_kv,
18300                            geometry.attention_scale(),
18301                            k_tok_bytes_c,
18302                            v_tok_bytes_c,
18303                            Some(&gate[rank]),
18304                        )?;
18305                    }
18306                    continue;
18307                }
18308                let distributed = cache.tp_kv[il]
18309                    .as_ref()
18310                    .expect("distributed cache checked above");
18311                let rank_cache = distributed
18312                    .rank(rank)
18313                    .ok_or_else(|| format!("Step TP layer {il} has no KV cache rank {rank}"))?;
18314                let k_view = engine.view_u8_range(
18315                    rank_cache.k(),
18316                    physical.start * k_tok_bytes_c,
18317                    physical.end * k_tok_bytes_c,
18318                );
18319                let v_view = engine.view_u8_range(
18320                    rank_cache.v(),
18321                    physical.start * v_tok_bytes_c,
18322                    physical.end * v_tok_bytes_c,
18323                );
18324                engine.fa_decode_kvmod(
18325                    &ws.q[rank],
18326                    &k_view,
18327                    &v_view,
18328                    &mut ws.attn_out[rank],
18329                    head_dim,
18330                    local_heads,
18331                    local_kv_heads,
18332                    t_kv,
18333                    geometry.attention_scale(),
18334                    k_tok_bytes_c,
18335                    v_tok_bytes_c,
18336                    false,
18337                )?;
18338                engine.attn_head_gate(
18339                    &ws.attn_out[rank],
18340                    &ws.gate[rank],
18341                    &mut ws.gated[rank],
18342                    None,
18343                    head_dim,
18344                    local_heads,
18345                    1,
18346                )?;
18347            }
18348
18349            // MEMRA_TCOL_OPROJ defer: the verify driver armed a column — stash this
18350            // column's `gated` rows and skip the per-column finish choreography entirely
18351            // (the batched b4_tcol + join runs after every column). The returned buffer
18352            // is UNWRITTEN in that mode (oproj-tail precedent); the driver reads the
18353            // stashed flag, never this buffer. Ineligible configs fall back to the
18354            // normal finish and the driver consumes the real `mixed` per column.
18355            let output = if let Some(col) = fa2_col.filter(|_| dcw) {
18356                // SPEC_FA2 stash: q + gate rows to the fa2 slabs; fa, o_proj and the
18357                // finish all run in the joined pass. Returned buffer is UNWRITTEN
18358                // (oproj-defer precedent — the walk reads the stash flag, never this).
18359                tp.runtime.decode_v2_stash_fa2(ws, e, col)?;
18360                crate::tp::set_spec_fa2_stashed();
18361                e.uninit(ws.o_out)?
18362            } else if let Some(col) = crate::tp::take_tcol_oproj_defer() {
18363                if tp.runtime.decode_v2_oproj_tcol_eligible(ws, &tp.o) {
18364                    tp.runtime.decode_v2_stash_gated(ws, e, col)?;
18365                    crate::tp::set_tcol_oproj_stashed();
18366                    e.uninit(ws.o_out)?
18367                } else {
18368                    tp.runtime.decode_v2_finish(ws, e, &tp.o)?
18369                }
18370            } else {
18371                tp.runtime.decode_v2_finish(ws, e, &tp.o)?
18372            };
18373
18374            // Local shadow append: reads ws.k_shadow/ws.v_shadow on e's stream, which
18375            // decode_v2_finish ordered behind the root event. Same math and cache state
18376            // transitions as v1.
18377            let local = cache.kv[il]
18378                .as_mut()
18379                .ok_or_else(|| format!("Step TP layer {il} lost its local KV cache"))?;
18380            if local.len != base_len || base_len + 1 > max_ctx {
18381                return Err(format!(
18382                    "Step TP layer {il} local cache changed during decode: \
18383                     len={} base={base_len} max={max_ctx}",
18384                    local.len
18385                )
18386                .into());
18387            }
18388            if crate::tp::no_local_shadow_on() {
18389                // Lengths advance, contents stay stale (graph-door precedent: decode reads
18390                // only the distributed TP caches; local contents feed spec/MTP scratch).
18391                local.len = base_len + 1;
18392                // MEMRA_LEN_MIRROR_LAZY=1: skip the 4B in-stream htod — nothing reads the
18393                // LOCAL device mirror in TP decode (the dcw fa reads the RANK counters),
18394                // and each tiny copy costs a compute->copy engine turnaround mid-layer.
18395                if !crate::tp::len_mirror_lazy_on() {
18396                    e.set_i32_one(&mut local.len_d, local.len as i32)?;
18397                }
18398            } else {
18399                let retain_from = window
18400                    .map(|window| {
18401                        let staged_retain = (base_len + 1).saturating_sub(window) & !31usize;
18402                        let rollback_retain =
18403                            base_len.saturating_sub(window.saturating_sub(1)) & !31usize;
18404                        staged_retain.min(rollback_retain)
18405                    })
18406                    .unwrap_or(0);
18407                let write_row = e.prepare_kv_append(local, retain_from, 1)?;
18408                e.append_kv_quantized(
18409                    &ws.k_shadow,
18410                    &ws.v_shadow,
18411                    &mut local.k,
18412                    &mut local.v,
18413                    write_row,
18414                    local.kv_dim_k,
18415                    local.kv_dim_v,
18416                    local.k_tok_bytes,
18417                    local.v_tok_bytes,
18418                    false,
18419                )?;
18420                local.len = base_len + 1;
18421                e.set_i32_one(&mut local.len_d, local.len as i32)?;
18422            }
18423            Ok(output)
18424        })();
18425
18426        let output = match staged {
18427            Ok(output) => output,
18428            Err(error) => {
18429                let _ = tp.runtime.rollback_tp_kv_transaction(
18430                    cache.tp_kv[il]
18431                        .as_mut()
18432                        .expect("distributed cache checked above"),
18433                    transaction,
18434                );
18435                if let Some(local) = cache.kv[il].as_mut() {
18436                    local.len = base_len;
18437                    let _ = e.set_i32_one(&mut local.len_d, base_len as i32);
18438                }
18439                return Err(error);
18440            }
18441        };
18442        // MEMRA_LEN_MIRROR_LAZY under FUSE_ROPE_APPEND: the fused append atomicInc owns
18443        // the rank counters (same value as the absolute re-set on full accept), so commit
18444        // host bookkeeping only — kills two 4B in-stream htods per layer. Non-fused dcw
18445        // keeps the absolute set (its appends do NOT inc).
18446        let lazy_commit = fuse_rope && crate::tp::len_mirror_lazy_on();
18447        if lazy_commit {
18448            if let Err(error) = tp.runtime.commit_tp_kv_transaction_external(
18449                cache.tp_kv[il]
18450                    .as_mut()
18451                    .expect("distributed cache checked above"),
18452                transaction,
18453                1,
18454            ) {
18455                let _ = tp.runtime.rollback_tp_kv_transaction(
18456                    cache.tp_kv[il]
18457                        .as_mut()
18458                        .expect("distributed cache checked above"),
18459                    transaction,
18460                );
18461                let local = cache.kv[il].as_mut().expect("local cache checked above");
18462                local.len = base_len;
18463                e.set_i32_one(&mut local.len_d, base_len as i32)?;
18464                return Err(error);
18465            }
18466        } else if let Err(error) = tp.runtime.commit_tp_kv_transaction(
18467            cache.tp_kv[il]
18468                .as_mut()
18469                .expect("distributed cache checked above"),
18470            transaction,
18471            1,
18472        ) {
18473            let _ = tp.runtime.rollback_tp_kv_transaction(
18474                cache.tp_kv[il]
18475                    .as_mut()
18476                    .expect("distributed cache checked above"),
18477                transaction,
18478            );
18479            let local = cache.kv[il].as_mut().expect("local cache checked above");
18480            local.len = base_len;
18481            e.set_i32_one(&mut local.len_d, base_len as i32)?;
18482            return Err(error);
18483        }
18484
18485        let committed = cache.tp_kv[il]
18486            .as_ref()
18487            .expect("distributed cache checked above")
18488            .committed_len();
18489        let local_len = cache.kv[il]
18490            .as_ref()
18491            .expect("local cache checked above")
18492            .len;
18493        if committed != local_len {
18494            return Err(format!(
18495                "Step TP layer {il} committed cache length {committed} != local shadow {local_len}"
18496            )
18497            .into());
18498        }
18499        static V2_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
18500        if !V2_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
18501            eprintln!(
18502                "[step-tp-attn-v2] execute layer={} devices={:?} tokens=1 driver=v2 \
18503                 qkv_tensor_parallel=true qk_norm_rank_local=true rope_rank_local=true \
18504                 kv_cache_distributed=true kv_cache_hydrated={hydrated} \
18505                 attention_tensor_parallel=true attention_scope={} \
18506                 input_path=root-device-replicated gate_tensor_parallel=false \
18507                 gate_shards=device-staged o_tensor_parallel=true o_reduce=root-device \
18508                 local_cache_shadow=true cache_commit=immediate transport={} native_p2p=true \
18509                 bulk_p2p={} workspace=persistent ordering=evented output=e-device \
18510                 performance_claim=false (logged once; every decode layer runs this driver)",
18511                tp.layer,
18512                tp.devices,
18513                if window.is_some() {
18514                    "rank-local-swa-ring"
18515                } else {
18516                    "rank-local-global"
18517                },
18518                tp.runtime.transport_label(),
18519                tp.runtime.bulk_p2p(),
18520            );
18521        }
18522        Ok(output)
18523    }
18524
18525    /// step35 T=1 decode attention (post-`wo`, matching `full_attn_decode_pre`'s contract).
18526    /// `pre_q` = the attn-input norm's q8_1 pair when the caller took the norm-fusion lever
18527    /// (then `h` is a zero-length placeholder and EVERY projection here — including the gate —
18528    /// must be on the q8_1 fast path; `mixer_in_q8_1_fast` enforces that for step35 by also
18529    /// requiring `attn_gate`).
18530    ///
18531    /// SWA decode is a token-aligned VIEW OFFSET into the quantized cache (the gemma4 R6
18532    /// pattern): keys carry absolute rope and the mask is purely positional, so the single
18533    /// query at `len-1` attending the last `win` rows IS the windowed result — no mask kernel.
18534    #[allow(clippy::too_many_arguments)]
18535    pub(crate) fn step35_decode_attn(
18536        &self,
18537        e: &Engine,
18538        fa: &FullAttnLayer,
18539        il: usize,
18540        h: &CudaSlice<f32>,
18541        pre_q: Option<(&CudaSlice<i8>, &CudaSlice<f32>)>,
18542        pos_d: &CudaSlice<i32>,
18543        cache: &mut Cache,
18544    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18545        if fa
18546            .step_tp_qkv
18547            .as_ref()
18548            .is_some_and(|tp| tp.attention.is_some())
18549        {
18550            if pre_q.is_some() {
18551                return Err(
18552                    "rank-local Step attention preserves BF16 activations and refuses the q8_1 \
18553                     pre-quantized decode path"
18554                        .into(),
18555                );
18556            }
18557            return self.step35_tp_decode_attn_resident(e, fa, il, h, pos_d, cache);
18558        }
18559
18560        let geometry = self.step35_geom(il);
18561        let hd = geometry.head_dim_k as usize;
18562        let nkv = geometry.n_head_kv as usize;
18563        let nh = geometry.n_head as usize;
18564        let rbase = geometry.rope_base;
18565        let scale = geometry.attention_scale();
18566        let swa = geometry.window.is_some();
18567        let eps = self.cfg.rms_eps;
18568        let win = geometry.window.unwrap_or(0) as usize;
18569        let n_rot = geometry.n_rot as usize;
18570        let n_embd = self.cfg.n_embd as usize;
18571        let gw = fa
18572            .attn_gate
18573            .as_ref()
18574            .ok_or("step35 layer is missing attn_gate.weight (head-wise attention gate)")?;
18575
18576        let tp_qkv = if fa.step_tp_qkv.is_some() {
18577            if pre_q.is_some() {
18578                return Err(
18579                    "Step Q/K/V TP preserves BF16 activations and refuses the q8_1 \
18580                     pre-quantized decode path"
18581                        .into(),
18582                );
18583            }
18584            self.step35_tp_qkv(e, fa, h, 1)?
18585        } else {
18586            None
18587        };
18588
18589        let (q0, k0, v0, gt) = match tp_qkv {
18590            Some(mut g3) => {
18591                let v = g3.pop().unwrap();
18592                let k = g3.pop().unwrap();
18593                let q = g3.pop().unwrap();
18594                let gt = e.matmul(gw, h, 1)?;
18595                (q, k, v, gt)
18596            }
18597            None => match pre_q {
18598                Some((hq, hdq)) => {
18599                    debug_assert!(
18600                        e.uses_q8_1_fast(gw),
18601                        "step35 pre-quantized decode requires attn_gate on the q8_1 fast path \
18602                         (h is a zero-length placeholder here) — see mixer_in_q8_1_fast"
18603                    );
18604                    let (a, b, c) = match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)? {
18605                        Some(t3) => t3,
18606                        None => (
18607                            e.matmul_pre(&fa.wq, hq, hdq, h, 1)?,
18608                            e.matmul_pre(&fa.wk, hq, hdq, h, 1)?,
18609                            e.matmul_pre(&fa.wv, hq, hdq, h, 1)?,
18610                        ),
18611                    };
18612                    let gt = e.matmul_pre(gw, hq, hdq, h, 1)?;
18613                    (a, b, c, gt)
18614                }
18615                None => {
18616                    if e.uses_q8_1_fast(&fa.wq)
18617                        && e.uses_q8_1_fast(&fa.wk)
18618                        && e.uses_q8_1_fast(&fa.wv)
18619                        && e.uses_q8_1_fast(gw)
18620                    {
18621                        let (hq, hdq) = e.quantize_q8_1(h, 1, n_embd)?;
18622                        let (a, b, c) =
18623                            match e.matmul_q8_fused3(&fa.wq, &fa.wk, &fa.wv, &hq, &hdq)? {
18624                                Some(t3) => t3,
18625                                None => (
18626                                    e.matmul_pre(&fa.wq, &hq, &hdq, h, 1)?,
18627                                    e.matmul_pre(&fa.wk, &hq, &hdq, h, 1)?,
18628                                    e.matmul_pre(&fa.wv, &hq, &hdq, h, 1)?,
18629                                ),
18630                            };
18631                        let gt = e.matmul_pre(gw, &hq, &hdq, h, 1)?;
18632                        (a, b, c, gt)
18633                    } else {
18634                        (
18635                            e.matmul(&fa.wq, h, 1)?,
18636                            e.matmul(&fa.wk, h, 1)?,
18637                            e.matmul(&fa.wv, h, 1)?,
18638                            e.matmul(gw, h, 1)?,
18639                        )
18640                    }
18641                }
18642            },
18643        };
18644
18645        let mut q = e.uninit(nh * hd)?;
18646        e.rms_norm(&q0, fa.q_norm.float_data(), &mut q, hd, nh, eps)?;
18647        let mut k = e.uninit(nkv * hd)?;
18648        e.rms_norm(&k0, fa.k_norm.float_data(), &mut k, hd, nkv, eps)?;
18649        let ff = if swa {
18650            None
18651        } else {
18652            self.step35_aux.as_ref().and_then(|a| a.rope_freqs(e))
18653        };
18654        #[cfg(debug_assertions)]
18655        if let Some(ff) = ff {
18656            crate::debug_assert_tensor_stream_device(
18657                ff,
18658                &e.stream(),
18659                "step35_decode_attn.rope_freqs",
18660            );
18661        }
18662        e.rope_neox2(&mut q, &mut k, pos_d, hd, n_rot, nh, nkv, 1, rbase, 1.0, ff)?;
18663
18664        if std::env::var("MEMRA_NOFA").is_ok() {
18665            return Err(
18666                "MEMRA_NOFA (naive f32 SDPA) is incompatible with the quantized KV \
18667                        cache; unset MEMRA_NOFA to use fa_decode"
18668                    .into(),
18669            );
18670        }
18671        let kvl = cache.kv[il].as_mut().unwrap();
18672        let next_len = kvl.len + 1;
18673        let (off, t_kv) = if swa && next_len > win {
18674            (next_len - win, win)
18675        } else {
18676            (0, next_len)
18677        };
18678        let write_row = e.prepare_kv_append(kvl, off & !31usize, 1)?;
18679        e.append_kv_quantized(
18680            &k,
18681            &v0,
18682            &mut kvl.k,
18683            &mut kvl.v,
18684            write_row,
18685            kvl.kv_dim_k,
18686            kvl.kv_dim_v,
18687            kvl.k_tok_bytes,
18688            kvl.v_tok_bytes,
18689            crate::Engine::kv_fp8_on(),
18690        )?;
18691        kvl.len = next_len;
18692        let physical = kvl.physical_rows(off, off + t_kv)?;
18693        let k_view = e.view_u8_range(
18694            &kvl.k,
18695            physical.start * kvl.k_tok_bytes,
18696            physical.end * kvl.k_tok_bytes,
18697        );
18698        let v_view = e.view_u8_range(
18699            &kvl.v,
18700            physical.start * kvl.v_tok_bytes,
18701            physical.end * kvl.v_tok_bytes,
18702        );
18703        let mut attn = e.uninit(nh * hd)?;
18704        e.fa_decode_kvmod(
18705            &q,
18706            &k_view,
18707            &v_view,
18708            &mut attn,
18709            hd,
18710            nh,
18711            nkv,
18712            t_kv,
18713            scale,
18714            kvl.k_tok_bytes,
18715            kvl.v_tok_bytes,
18716            crate::Engine::kv_fp8_on(),
18717        )?;
18718
18719        let mut ag = e.uninit(nh * hd)?;
18720        e.attn_head_gate(&attn, &gt, &mut ag, None, hd, nh, 1)?;
18721        self.step35_o(e, fa, &ag, 1)
18722    }
18723}
18724
18725// ===================================================================================== //
18726//  gemma-4 E4B (per-layer embeddings + KV-sharing) — FIRST-LIGHT forward.               //
18727//  Dedicated simple path (Stage-B matmuls, per-row causal attention over the quantized  //
18728//  cache) so the tuned 26B/31B paths stay untouched. Arch: research/gemma4-bringup/     //
18729//  e4b-arch-map.md; llama reference: src/models/gemma4.cpp (E4B arms). Wired: forward   //
18730//  (prefill logits), prime (tokenwise-equivalent batched trunk), eager decode_step_h.   //
18731//  NOT wired: dc/graph serving arms, verify/spec, chunked prime (HANDOVER-E4B.md).      //
18732// ===================================================================================== //
18733impl HybridModel {
18734    pub fn is_gemma4_e4b(&self) -> bool {
18735        self.gemma4_aux.as_ref().is_some_and(|a| a.e4b.is_some())
18736    }
18737
18738    /// E4B per-layer geometry: nh/nkv derive from the layer's OWN tensor shapes (the E4B GGUF
18739    /// ships a scalar head_count_kv; swa q 8x256 / kv 2x256, global q 4x512 / kv 1x512).
18740    /// KV-shared layers report the SHARE TARGET's kv count (their wk IS the target's tensor).
18741    fn gemma4_e4b_geom(&self, il: usize) -> (usize, usize, usize, f32, f32, bool) {
18742        let g = self.cfg.gemma4.as_ref().unwrap();
18743        let swa = g.swa_pattern[il];
18744        let hd = if swa {
18745            g.key_length_swa
18746        } else {
18747            g.key_length_global
18748        } as usize;
18749        let Mixer::Full(fa) = &self.layers[il].mixer else {
18750            panic!("e4b layer {il} not full-attn")
18751        };
18752        let nh = fa.wq.out_features() / hd;
18753        let nkv = fa.wk.out_features() / hd;
18754        (
18755            hd,
18756            nkv,
18757            nh,
18758            if swa {
18759                g.rope_base_swa
18760            } else {
18761                g.rope_base_global
18762            },
18763            1.0,
18764            swa,
18765        )
18766    }
18767
18768    /// E4B KV-share target: Some(target) for the trailing shared layers, None = own cache.
18769    fn gemma4_e4b_kv_target(&self, il: usize) -> Option<usize> {
18770        self.layers[il]
18771            .gemma4
18772            .as_ref()
18773            .and_then(|b| b.e4b.as_ref())
18774            .and_then(|e4| e4.kv_share.map(|t| t as usize))
18775    }
18776
18777    /// E4B prologue: inp_pl [t][n_layer][n_epl] =
18778    ///   ( gather(per_layer_tok_embd, tok)*sqrt(n_epl)
18779    ///   + rms_norm(model_proj . x_scaled * 1/sqrt(n_embd), proj_norm) ) * 1/sqrt(2)
18780    /// (llama gemma4.cpp build_inp_per_layer + project_per_layer_inputs, exact order).
18781    fn gemma4_e4b_inp_pl(
18782        &self,
18783        e: &Engine,
18784        tokens: &[u32],
18785        x_scaled: &CudaSlice<f32>,
18786        t: usize,
18787    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18788        let tok_d = e.stream().clone_htod(&tokens.to_vec())?;
18789        self.gemma4_e4b_inp_pl_dev(e, &tok_d, x_scaled, t)
18790    }
18791
18792    /// Device-token prologue core (dc arm shares it: token ids never touch the host).
18793    fn gemma4_e4b_inp_pl_dev(
18794        &self,
18795        e: &Engine,
18796        tok_d: &CudaSlice<u32>,
18797        x_scaled: &CudaSlice<f32>,
18798        t: usize,
18799    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18800        let aux = self.gemma4_aux.as_ref().unwrap();
18801        let m = aux.e4b.as_ref().unwrap();
18802        let n_embd = self.cfg.n_embd as usize;
18803        let n_layer = self.layers.len();
18804        let width = m.n_epl * n_layer;
18805        let tbl = m.tok_tbl_gpu.get_or_init(|| {
18806            e.upload_u8(&m.tok_embd_bytes)
18807                .expect("e4b per-layer token table upload")
18808        });
18809        let mut a =
18810            e.embed_gather_device_td(tbl, tok_d, t, width, m.tok_embd_qt, m.tok_embd_row_bytes)?;
18811        e.scale_inplace(&mut a, (m.n_epl as f32).sqrt(), t * width)?;
18812        let mut p = e.matmul(&m.model_proj, x_scaled, t)?;
18813        e.scale_inplace(&mut p, 1.0 / (n_embd as f32).sqrt(), t * width)?;
18814        let mut pn = e.uninit(t * width)?;
18815        e.rms_norm(
18816            &p,
18817            m.proj_norm.float_data(),
18818            &mut pn,
18819            m.n_epl,
18820            t * n_layer,
18821            self.cfg.rms_eps,
18822        )?;
18823        let mut out = e.uninit(t * width)?;
18824        e.add_scale(&a, &pn, 1.0 / 2f32.sqrt(), &mut out, t * width)?;
18825        Ok(out)
18826    }
18827
18828    /// E4B attention (t-wide, causal, per-row fa over the QUANTIZED cache — first-light
18829    /// correctness path; the fa rows arms come later). Own-KV layers project+norm+rope k/v
18830    /// and append t rows; KV-shared layers are Q-only over the target layer's cache (which
18831    /// already holds this forward's rows — the target runs earlier in the stack).
18832    #[allow(clippy::too_many_arguments)]
18833    fn gemma4_e4b_attn(
18834        &self,
18835        e: &Engine,
18836        il: usize,
18837        hq: &CudaSlice<i8>,
18838        hdq: &CudaSlice<f32>,
18839        pos_d: &CudaSlice<i32>,
18840        t: usize,
18841        cache: &mut Cache,
18842        dc_bucket: Option<usize>,
18843    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18844        let (hd, nkv, nh, base, scale, swa) = self.gemma4_e4b_geom(il);
18845        let eps = self.cfg.rms_eps;
18846        let aux = self.gemma4_aux.as_ref().unwrap();
18847        let ones = aux.ones(e);
18848        #[cfg(debug_assertions)]
18849        crate::debug_assert_tensor_stream_device(ones, &e.stream(), "gemma4_e4b_attn.ones");
18850        let Mixer::Full(fa) = &self.layers[il].mixer else {
18851            unreachable!()
18852        };
18853        // pre-quantized layer input (E4B fusion port, 2026-07-12): ONE norm+quant feeds
18854        // wq/wk/wv via matmul_pre — the first-light path quantized the same h three times
18855        // (the profile's 342 quantize_q8_1/token; glue = 26% of the 6.1ms token).
18856        let h0 = e.zeros(0)?;
18857        let h = &h0;
18858
18859        let ff = if swa {
18860            None
18861        } else {
18862            Some(
18863                aux.rope_freqs(e)
18864                    .expect("e4b global rope needs rope_freqs.weight"),
18865            )
18866        };
18867        #[cfg(debug_assertions)]
18868        if let Some(ff) = ff {
18869            crate::debug_assert_tensor_stream_device(ff, &e.stream(), "gemma4_e4b_attn.rope_freqs");
18870        }
18871        let share = self.gemma4_e4b_kv_target(il);
18872        // Own-KV arms keep the f32 (k, v) alive for the prime fa arm below.
18873        let mut kv_f32: Option<(CudaSlice<f32>, CudaSlice<f32>)> = None;
18874        let mut q;
18875        if let Some(_tgt) = share {
18876            let q0 = e.matmul_pre(&fa.wq, hq, hdq, h, t)?;
18877            q = e.uninit(t * nh * hd)?;
18878            // Q-only through the same fused norm+rope kernel (rk = 0: the k/v segments are
18879            // empty; q0 stands in for the unused k/v pointers).
18880            let mut kdummy = e.uninit(1)?;
18881            let mut vdummy = e.uninit(1)?;
18882            e.rms_norm_qkv_rope(
18883                &q0,
18884                &q0,
18885                &q0,
18886                fa.q_norm.float_data(),
18887                fa.q_norm.float_data(),
18888                ones,
18889                &mut q,
18890                &mut kdummy,
18891                &mut vdummy,
18892                hd,
18893                self.gemma4_rope_dims(il),
18894                nh * t,
18895                0,
18896                pos_d,
18897                nh,
18898                1,
18899                base,
18900                1.0,
18901                ff,
18902                eps,
18903            )?;
18904        } else {
18905            // wave-4b: ONE concat matvec (wq|wk|wv) at t == 1 when the cat tensor exists;
18906            // else the fused3 grid launch; else per-matvec. The cat output is contiguous
18907            // q|k|v rows — the cat norm+rope twin consumes it directly.
18908            let e4bits = self.layers[il].gemma4.as_ref().and_then(|g| g.e4b.as_ref());
18909            let cat = e4bits.and_then(|e4| e4.qkv_cat.as_ref());
18910            q = e.uninit(t * nh * hd)?;
18911            let mut k = e.uninit(t * nkv * hd)?;
18912            let mut v = e.uninit(t * nkv * hd)?;
18913            if t == 1 && cat.is_some() {
18914                let qkv0 = e.matmul_pre(cat.unwrap(), hq, hdq, h, 1)?;
18915                e.rms_norm_qkv_rope_cat(
18916                    &qkv0,
18917                    fa.q_norm.float_data(),
18918                    fa.k_norm.float_data(),
18919                    ones,
18920                    &mut q,
18921                    &mut k,
18922                    &mut v,
18923                    hd,
18924                    self.gemma4_rope_dims(il),
18925                    nh,
18926                    nkv,
18927                    pos_d,
18928                    nh,
18929                    nkv,
18930                    base,
18931                    1.0,
18932                    ff,
18933                    eps,
18934                )?;
18935            } else {
18936                let (q0, k0, v0) = match if t == 1 {
18937                    e.matmul_q4_fused3(&fa.wq, &fa.wk, &fa.wv, hq, hdq)?
18938                } else {
18939                    // E4B verify f3 port (2026-07-14): the 31B segmented-grid batched qkv
18940                    // on E4B's real-V triple — same MEMRA_F2B seam, bit-identical per row.
18941                    static F2B_QKV: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18942                    if *F2B_QKV.get_or_init(|| std::env::var("MEMRA_F2B").as_deref() != Ok("0")) {
18943                        e.matmul_q4_fused3_batched(&fa.wq, &fa.wk, &fa.wv, hq, hdq, t)?
18944                    } else {
18945                        None
18946                    }
18947                } {
18948                    Some(triple) => triple,
18949                    None => (
18950                        e.matmul_pre(&fa.wq, hq, hdq, h, t)?,
18951                        e.matmul_pre(&fa.wk, hq, hdq, h, t)?,
18952                        e.matmul_pre(&fa.wv, hq, hdq, h, t)?,
18953                    ), // E4B: real v (K != V)
18954                };
18955                // wave-3 fold: q/k/v norms + q/k rope in ONE launch (rope math verbatim on
18956                // the normed rows; V ones-rms, never roped).
18957                e.rms_norm_qkv_rope(
18958                    &q0,
18959                    &k0,
18960                    &v0,
18961                    fa.q_norm.float_data(),
18962                    fa.k_norm.float_data(),
18963                    ones,
18964                    &mut q,
18965                    &mut k,
18966                    &mut v,
18967                    hd,
18968                    self.gemma4_rope_dims(il),
18969                    nh * t,
18970                    nkv * t,
18971                    pos_d,
18972                    nh,
18973                    nkv,
18974                    base,
18975                    1.0,
18976                    ff,
18977                    eps,
18978                )?;
18979            }
18980            let kvl = cache.kv[il].as_mut().unwrap();
18981            // class flag must match the cache dims (the g-threading sweep hardcoded `false`
18982            // here and the wkv default corrupted E4B: q8_0 bytes into an e4m3 cache — the
18983            // degenerate tok-0 stream, 2026-07-12).
18984            let cls = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
18985            if dc_bucket.is_some() {
18986                // DC arm (graph serving): append at the len_d slot, advance the counter
18987                // in-stream — replay-correct, no host len in the launch args. Host mirrors
18988                // are NOT touched here (the replay loop owns them; a bump at capture-record
18989                // time would double-count the capture iteration).
18990                debug_assert!(t == 1);
18991                // wave 5c: append + len_d inc fused (one launch; single-block ordering).
18992                e.append_kv_quantized_row_dc_inc(
18993                    &k,
18994                    &v,
18995                    &mut kvl.k,
18996                    &mut kvl.v,
18997                    &mut kvl.len_d,
18998                    kvl.kv_dim_k,
18999                    kvl.kv_dim_v,
19000                    kvl.k_tok_bytes,
19001                    kvl.v_tok_bytes,
19002                    cls,
19003                )?;
19004            } else {
19005                e.append_kv_quantized_rows(
19006                    &k,
19007                    &v,
19008                    &mut kvl.k,
19009                    &mut kvl.v,
19010                    kvl.len,
19011                    t,
19012                    kvl.kv_dim_k,
19013                    kvl.kv_dim_v,
19014                    kvl.k_tok_bytes,
19015                    kvl.v_tok_bytes,
19016                    cls,
19017                )?;
19018                kvl.len += t;
19019            }
19020            kv_f32 = Some((k, v));
19021        }
19022        // attention: per-row causal fa over the (own or target) quantized cache. The cache
19023        // already contains this forward's rows in both arms; row i attends [.., base+i].
19024        let kvl_idx = share.unwrap_or(il);
19025        let kvl = cache.kv[kvl_idx].as_ref().unwrap();
19026        let base_len = kvl.len - t; // pre-append length (target appended this forward too)
19027        let win = self.cfg.gemma4.as_ref().unwrap().sliding_window as usize;
19028        let mut attn = e.uninit(t * nh * hd)?;
19029        // PRIME FA ARMS (perf lane 3, completed 2026-07-31 — the H100 board pinned E4B
19030        // prefill at 482 tok/s: the per-row loop ran the DECODE kernel once per token per
19031        // layer). Fresh-prompt (base_len == 0) batched attention, one launch per layer:
19032        //   - own-KV hd256 under the window: f32 fa_prefill (full causal exact — 26B pattern)
19033        //   - own-KV hd256 above the window (swa): fa_prefill_w windowed twin (12B pattern)
19034        //   - own-KV hd512 globals: fa_prefill_hd512 (12B/31B globals pattern)
19035        //   - KV-shared layers (no f32 k/v): fa_prefill_view over the target's QUANTIZED
19036        //     rows (the T=K verify kernel; the target appended this forward's rows already).
19037        //     swa-shared above the window keeps the per-row loop (no windowed view twin).
19038        // Same numeric class split as the 26B prime (f32/batched prefill vs per-row
19039        // quantized decode); run-gen argmax + chat gates arbitrate. MEMRA_NOFA=1 reverts all.
19040        if t > 1 && base_len == 0 && std::env::var("MEMRA_NOFA").is_err() {
19041            if let Some((kf, vf)) = &kv_f32 {
19042                if hd == 256 && t <= win {
19043                    e.fa_prefill(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
19044                    return Ok(e.matmul(&fa.wo, &attn, t)?);
19045                }
19046                if hd == 256 && swa && t > win {
19047                    e.fa_prefill_w(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
19048                    return Ok(e.matmul(&fa.wo, &attn, t)?);
19049                }
19050                if hd == 512 && !swa {
19051                    e.fa_prefill_hd512(&q, kf, vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
19052                    return Ok(e.matmul(&fa.wo, &attn, t)?);
19053                }
19054            } else if share.is_some() {
19055                let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
19056                let k_view = e.view_u8(&kvl.k, kvl.k.len());
19057                let v_view = e.view_u8(&kvl.v, kvl.v.len());
19058                if hd == 256 && (!swa || t <= win) {
19059                    // inline-dequant quantized-view prefill (fa_prefill_q stamps 256/128)
19060                    e.fa_prefill_view(
19061                        &q,
19062                        &k_view,
19063                        &v_view,
19064                        &mut attn,
19065                        hd,
19066                        nh,
19067                        nkv,
19068                        t,
19069                        t,
19070                        scale,
19071                        true,
19072                        kvl.k_tok_bytes,
19073                        kvl.v_tok_bytes,
19074                        g,
19075                    )?;
19076                    return Ok(e.matmul(&fa.wo, &attn, t)?);
19077                }
19078                // remaining shared classes (swa above the window; hd512 globals): dequant
19079                // the target's t rows ONCE to f32, then the same f32 twins as own-KV.
19080                let kv_dim = nkv * hd;
19081                let mut kf = e.uninit(t * kv_dim)?;
19082                let mut vf = e.uninit(t * kv_dim)?;
19083                e.fa_dequant_kv_view_f32(
19084                    &k_view,
19085                    &v_view,
19086                    &mut kf,
19087                    &mut vf,
19088                    kv_dim,
19089                    kv_dim,
19090                    t,
19091                    kvl.k_tok_bytes,
19092                    kvl.v_tok_bytes,
19093                    g,
19094                )?;
19095                if hd == 512 {
19096                    e.fa_prefill_hd512(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true)?;
19097                } else {
19098                    e.fa_prefill_w(&q, &kf, &vf, &mut attn, hd, nh, nkv, t, t, scale, true, win)?;
19099                }
19100                return Ok(e.matmul(&fa.wo, &attn, t)?);
19101            }
19102        }
19103        if let Some(bucket) = dc_bucket {
19104            // DC arm (t == 1, under-window regime — the generate gate enforces it): ONE
19105            // fa_decode_dc over the live counter. len_d already advanced past this token
19106            // (t_kv = len_d[0], the cache.rs contract). KV-shared layers read the target's
19107            // counter (advanced when the target ran earlier in the stack).
19108            assert!(t == 1);
19109            // hd512 globals: eager (kvmod) picks the SCALAR unified below the fa512 floor,
19110            // and under the window every live t_kv sits below it — cap the capture bucket
19111            // under the floor so fa_decode_dc bakes the same scalar symbol, or the graph
19112            // rides the dpl16 twin and its numeric class diverges from the dc-eager stream
19113            // (E4B-GRAPH-GATE 2/64, 2026-07-12).
19114            let bucket = if hd == 512 && win <= crate::fa512_min_tkv() {
19115                bucket.min(crate::fa512_min_tkv().saturating_sub(1))
19116            } else {
19117                bucket
19118            };
19119            let k_view = e.view_u8(&kvl.k, kvl.k.len());
19120            let v_view = e.view_u8(&kvl.v, kvl.v.len());
19121            let g = (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on());
19122            // Weight prefetch (SOTA item 3, 2026-07-13): wo's decode plane prefetched into
19123            // L2 across the fa window — fa reads KV only, the weight DRAM lanes are idle
19124            // there (E4B valid window +0.65%: 196.8 vs 195.6). Value-free scheduling op,
19125            // captured into the dc graph like any other launch. Extending the cascade to
19126            // the ffn gate/up planes measured NEGATIVE (193.9 vs 195.8 — 29MB/layer floods
19127            // the fill path and evicts still-hot lines); wo-only is the shipped shape.
19128            // MEMRA_WPF=0 rollback seam.
19129            if crate::Engine::wpf_level() >= 1 {
19130                e.prefetch_weight_l2(&fa.wo)?;
19131            }
19132            // wave 5b: the combine emits the wo input q8 pair directly — the standalone
19133            // quantize launch + the f32 attn round-trip fold away (t=1 fast path only).
19134            if e.uses_q8_1_fast(&fa.wo) {
19135                let mut oq = e.alloc_i8_uninit(nh * hd)?;
19136                let mut od = e.zeros(nh * hd / 32)?;
19137                e.fa_decode_dc_q8(
19138                    &q,
19139                    &k_view,
19140                    &v_view,
19141                    &mut attn,
19142                    hd,
19143                    nh,
19144                    nkv,
19145                    &kvl.len_d,
19146                    bucket,
19147                    scale,
19148                    kvl.k_tok_bytes,
19149                    kvl.v_tok_bytes,
19150                    g,
19151                    Some((&mut oq, &mut od)),
19152                )?;
19153                return Ok(e.matmul_pre(&fa.wo, &oq, &od, &attn, t)?);
19154            }
19155            e.fa_decode_dc(
19156                &q,
19157                &k_view,
19158                &v_view,
19159                &mut attn,
19160                hd,
19161                nh,
19162                nkv,
19163                &kvl.len_d,
19164                bucket,
19165                scale,
19166                kvl.k_tok_bytes,
19167                kvl.v_tok_bytes,
19168                g,
19169            )?;
19170            return Ok(e.matmul(&fa.wo, &attn, t)?);
19171        }
19172        for i in 0..t {
19173            let avail = base_len + i + 1;
19174            let (off_tok, t_kv) = if swa && avail > win {
19175                (avail - win, win)
19176            } else {
19177                (0, avail)
19178            };
19179            let k_view = e.view_u8_range(
19180                &kvl.k,
19181                off_tok * kvl.k_tok_bytes,
19182                (off_tok + t_kv) * kvl.k_tok_bytes,
19183            );
19184            let v_view = e.view_u8_range(
19185                &kvl.v,
19186                off_tok * kvl.v_tok_bytes,
19187                (off_tok + t_kv) * kvl.v_tok_bytes,
19188            );
19189            let qv = e.view(&q, t * nh * hd);
19190            let q_row = qv.slice(i * nh * hd..(i + 1) * nh * hd);
19191            let mut q_one = e.uninit(nh * hd)?;
19192            e.copy_view_into(&mut q_one, 0, &q_row, nh * hd)?;
19193            let mut a_one = e.uninit(nh * hd)?;
19194            // read class MUST match the append class (globals are e4m3 under gkv): the
19195            // swa-only flag decoded global-layer e4m3 bytes as q8_0/q5_1 — attention over
19196            // garbage on EVERY E4B global layer, the cross-mode maxdiff-30 root (2026-07-12).
19197            e.fa_decode_kvmod(
19198                &q_one,
19199                &k_view,
19200                &v_view,
19201                &mut a_one,
19202                hd,
19203                nh,
19204                nkv,
19205                t_kv,
19206                scale,
19207                kvl.k_tok_bytes,
19208                kvl.v_tok_bytes,
19209                (!swa && crate::Engine::gkv_on()) || (swa && crate::Engine::wkv_on()),
19210            )?;
19211            e.copy_into(&mut attn, i * nh * hd, &a_one, nh * hd)?;
19212        }
19213        Ok(e.matmul(&fa.wo, &attn, t)?)
19214    }
19215
19216    /// E4B trunk: embed -> prologue -> layers (attn + dense ffn via the 31B tail_core + the
19217    /// per-layer-embedding tail + layer scale) -> output_norm. Returns (softcapped logits
19218    /// device [t, n_vocab], pre-output_norm hidden [t, n_embd]). Appends t rows per own-KV
19219    /// layer; does NOT advance cache.pos (caller owns pos).
19220    fn gemma4_e4b_trunk(
19221        &self,
19222        e: &Engine,
19223        tokens: &[u32],
19224        pos0: usize,
19225        cache: &mut Cache,
19226        head_last: bool,
19227    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19228        let n_embd = self.cfg.n_embd as usize;
19229        let t = tokens.len();
19230        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
19231        let pos_d = e.htod_i32(&pos)?;
19232        let mut x = e.htod(&self.embd.gather(n_embd, tokens))?;
19233        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
19234        let inp_pl = self.gemma4_e4b_inp_pl(e, tokens, &x, t)?;
19235        self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, head_last)
19236    }
19237
19238    /// Layer stack + head over prebuilt (x_scaled, inp_pl, device pos) — everything below
19239    /// here is device-driven, so the dc arm shares it verbatim (stream identity with the
19240    /// eager chain by construction: SAME functions, not twins).
19241    fn gemma4_e4b_trunk_core(
19242        &self,
19243        e: &Engine,
19244        x_in: CudaSlice<f32>,
19245        inp_pl: CudaSlice<f32>,
19246        pos_d: &CudaSlice<i32>,
19247        t: usize,
19248        cache: &mut Cache,
19249        dc_bucket: Option<usize>,
19250        cap_logits: bool,
19251        head_last: bool,
19252    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19253        let n_embd = self.cfg.n_embd as usize;
19254        let eps = self.cfg.rms_eps;
19255        let n_layer = self.layers.len();
19256        let mut x = x_in;
19257        let aux_e4b = self.gemma4_aux.as_ref().unwrap().e4b.as_ref().unwrap();
19258        let n_epl = aux_e4b.n_epl;
19259
19260        // cross-layer fusion (2026-07-12 port of the 26B/31B trunk structure): each layer's
19261        // closing add_scale also EMITS the next layer's attn-normed input pre-quantized
19262        // q8_1 (add_scale_rms_norm_q8_1); the LAST layer emits through output_norm so the
19263        // head rides matmul_pre too. First layer's pair comes from a standalone fused
19264        // norm+quant.
19265        let mut h_carry: Option<(CudaSlice<i8>, CudaSlice<f32>)> = None;
19266        for il in 0..n_layer {
19267            let layer = &self.layers[il];
19268            let (hq, hdq) = match h_carry.take() {
19269                Some(p) => p,
19270                None => e.rms_norm_q8_1(&x, layer.attn_norm.float_data(), n_embd, t, eps)?,
19271            };
19272            let o = self.gemma4_e4b_attn(e, il, &hq, &hdq, pos_d, t, cache, dc_bucket)?;
19273            // dense ffn tail with the post-attn norm FOLDED into its entry (one launch for
19274            // rms(o, post_attn_norm) + residual add + ffn_norm — glue-fusion lane).
19275            let bits = layer.gemma4.as_ref().unwrap();
19276            let e4b = bits.e4b.as_ref().expect("e4b layer bits");
19277            // glue wave 5: the tail DEFERS its post_ffw norm — the FFN exit fuses
19278            // rms(f0, post_ffw) + residual add + q8 emit into ONE launch (rms_pre_add_q8_1),
19279            // killing the rms_norm + add_q8_1 pair per layer. T-GENERIC since 2026-07-13:
19280            // the fused single-phase reduction is NOT FP-order-identical to the unfused
19281            // rms_norm+add pair (the original "bit-identical chain" claim was FALSE — the
19282            // t==1 gate left the batched VERIFY on the unfused chain and split E4B verify
19283            // from decode by logit maxdiff ~0.45, the greedy tie-flip at depth: gate 135/256.
19284            // NOFUSE bisect: disabling ONLY this fusion -> verify maxdiff 0.000e0). With the
19285            // gate dropped, decode AND verify ride the same fused chain — parity by
19286            // construction, VERIFY-GATE 0.000e0.
19287            let fuse_exit = e.uses_q8_1_fast(&e4b.inp_gate);
19288            let (sn, attn_out) = self.gemma4_layer_tail_core_pn(
19289                e,
19290                layer,
19291                &o,
19292                &x,
19293                t,
19294                Some(layer.post_attn_norm.float_data()),
19295                fuse_exit,
19296            )?;
19297            let mut resid = e.uninit(t * n_embd)?;
19298            // per-layer-embedding tail: resid += rms(proj . (gelu(inp_gate . resid) * inp_pl[il]))
19299            // wave-2: the residual add emits q8_1 alongside — inp_gate rides matmul_pre.
19300            // (PLE one-block mega-fusion PROBED NEGATIVE 2026-07-13: argmax-correct but
19301            // 126 vs 189 tok/s — one SM pulling 0.74MB of weights loses to the multi-block
19302            // launch chain it replaced; jsonl row. Kernel deleted per doctrine.)
19303            let g = if fuse_exit {
19304                // sn here = RAW f0 (post_ffw deferred).
19305                let (rq, rd) = e.rms_pre_add_q8_1(
19306                    &sn,
19307                    bits.post_ffw_norm.float_data(),
19308                    &attn_out,
19309                    &mut resid,
19310                    n_embd,
19311                    t,
19312                    self.cfg.rms_eps,
19313                )?;
19314                e.matmul_pre(&e4b.inp_gate, &rq, &rd, &resid, t)?
19315            } else {
19316                e.add(&sn, &attn_out, &mut resid, t * n_embd)?;
19317                e.matmul(&e4b.inp_gate, &resid, t)?
19318            };
19319            let mut act = e.uninit(t * n_epl)?;
19320            let y = if t == 1 && e.uses_q8_1_fast(&e4b.proj) {
19321                let ipv = e.view(&inp_pl, n_epl * n_layer);
19322                let row = ipv.slice(il * n_epl..(il + 1) * n_epl);
19323                let (aq, ad) = e.gelu_tanh_mul_q8_1(&g, &row, &mut act, n_epl, 1)?;
19324                e.matmul_pre(&e4b.proj, &aq, &ad, &act, t)?
19325            } else {
19326                let mut inp_this = e.uninit(t * n_epl)?;
19327                e.copy_rows_strided(
19328                    &inp_pl,
19329                    &mut inp_this,
19330                    n_epl,
19331                    t,
19332                    n_epl * n_layer,
19333                    il * n_epl,
19334                )?;
19335                e.gelu_tanh_mul(&g, &inp_this, &mut act, t * n_epl)?;
19336                e.matmul(&e4b.proj, &act, t)?
19337            };
19338            // rms(y, post_norm) + (yn + resid)*layer_scale + next-layer norm+quant emit,
19339            // ONE launch (glue-fusion lane; last layer emits through output_norm).
19340            let next_norm = if il + 1 < n_layer {
19341                self.layers[il + 1].attn_norm.float_data()
19342            } else {
19343                self.output_norm.float_data()
19344            };
19345            let mut xn = e.uninit(t * n_embd)?;
19346            let pair = e.rms_pre_add_scale_rms_norm_q8_1(
19347                &y,
19348                e4b.post_norm.float_data(),
19349                &resid,
19350                bits.layer_scale,
19351                next_norm,
19352                &mut xn,
19353                n_embd,
19354                t,
19355                eps,
19356            )?;
19357            h_carry = Some(pair);
19358            x = xn;
19359        }
19360        // the head consumes the last layer's fused (output_norm) emit. head_last callers
19361        // (prime, last_only forward) need only the final row's logits — the all-T head is
19362        // t*n_vocab of discarded work (E4B prime: ~134ms GEMM + a 2.26GB dtoh kept 1 row).
19363        let (oq, odq) = h_carry.take().unwrap();
19364        let h0 = e.zeros(0)?;
19365        let hm = if head_last { 1 } else { t };
19366        let (hq, hd) = if head_last && t > 1 {
19367            let mut q1 = e.uninit_i8(n_embd)?;
19368            e.dtod_copy_view_i8(&oq.slice((t - 1) * n_embd..t * n_embd), &mut q1)?;
19369            let nb = n_embd / 32;
19370            let mut d1 = e.uninit(nb)?;
19371            e.dtod_copy_view(&odq.slice((t - 1) * nb..t * nb), &mut d1)?;
19372            (q1, d1)
19373        } else {
19374            (oq, odq)
19375        };
19376        let mut ld = e.matmul_pre(&self.output, &hq, &hd, &h0, hm)?;
19377        // softcap is strictly monotonic — greedy (argmax-only) consumers skip it, matching
19378        // the 26B/31B dc precedent (their dc head goes matmul -> argmax with no cap).
19379        // Logit-returning callers (host logits / spec prime) keep the capped emit.
19380        if cap_logits {
19381            let cap = self.cfg.gemma4.as_ref().unwrap().final_logit_softcapping;
19382            e.softcap(&mut ld, cap, hm * self.output.out_features())?;
19383        }
19384        self.gemma4_suppress(e, &mut ld, hm)?; // mask both capped and argmax-only consumers
19385        Ok((ld, x))
19386    }
19387
19388    /// E4B batched VERIFY (device tokens, the spec round's t=K+1 step): t rows through the
19389    /// e4b trunk (per-row causal attention; own-KV layers append t rows host-len, KV-shared
19390    /// layers ride their targets), per-row device argmax + the POST-output_norm hidden
19391    /// stack (the drafter's h convention). Advances cache.pos/kvl.len by t — the spec
19392    /// round rolls back rejected rows (shared layers have no KvLayer, so the plain rewind
19393    /// covers exactly the layers that appended).
19394    pub fn gemma4_e4b_decode_step_t_am_dev(
19395        &self,
19396        e: &Engine,
19397        tok_d: &CudaSlice<u32>,
19398        t: usize,
19399        pos0: usize,
19400        cache: &mut Cache,
19401    ) -> Result<(CudaSlice<u32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19402        let n_embd = self.cfg.n_embd as usize;
19403        let eps = self.cfg.rms_eps;
19404        let pos: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
19405        let pos_d = e.htod_i32(&pos)?;
19406        let embd_gpu = self
19407            .embd_gpu
19408            .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
19409        let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
19410        let mut x = e.embed_gather_device_td(embd_gpu, tok_d, t, n_embd, qt, rb)?;
19411        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), t * n_embd)?;
19412        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, tok_d, &x, t)?;
19413        let (ld, xp) =
19414            self.gemma4_e4b_trunk_core(e, x, inp_pl, &pos_d, t, cache, None, true, false)?;
19415        // softcap is monotonic — the per-row argmax is invariant to it (the trunk's head
19416        // emit is already capped, matching the eager chain bit-for-bit).
19417        let n_vocab = self.output.out_features();
19418        let mut vam = e.stream().alloc_zeros::<u32>(t)?;
19419        for i in 0..t {
19420            e.argmax_token_device_col(&ld, i, n_vocab, &mut vam, i)?;
19421        }
19422        let mut hn = e.uninit(t * n_embd)?;
19423        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
19424        cache.pos += t;
19425        Ok((vam, hn))
19426    }
19427
19428    /// E4B verify + host logits + POST-output_norm hidden stack (the short-prompt spec
19429    /// prime path — mirror of `gemma4_decode_step_t_h`).
19430    pub(crate) fn gemma4_e4b_decode_step_t_h(
19431        &self,
19432        e: &Engine,
19433        tokens: &[u32],
19434        pos0: usize,
19435        cache: &mut Cache,
19436    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19437        let n_embd = self.cfg.n_embd as usize;
19438        let eps = self.cfg.rms_eps;
19439        let t = tokens.len();
19440        let (ld, xp) = self.gemma4_e4b_trunk(e, tokens, pos0, cache, false)?;
19441        let mut hn = e.uninit(t * n_embd)?;
19442        e.rms_norm(&xp, self.output_norm.float_data(), &mut hn, n_embd, t, eps)?;
19443        cache.pos += t;
19444        Ok((e.dtoh(&ld)?, hn))
19445    }
19446
19447    /// E4B GRAPH-CAPTURABLE dc step: same trunk as the dc step but token_d is updated IN
19448    /// PLACE (self-feeding replay) and every launch arg is a device counter — pos from
19449    /// pos_d (inc'd in-stream), KV slots from len_d (advanced in-stream), attention from
19450    /// fa_decode_dc at `bucket`. Host mirrors (cache.pos / kvl.len) advance in the caller's
19451    /// replay loop. UNDER-WINDOW regime only (the caller gates pos + budget < window).
19452    pub fn gemma4_e4b_decode_step_dcg(
19453        &self,
19454        e: &Engine,
19455        token_d: &mut CudaSlice<u32>,
19456        pos_d: &mut CudaSlice<i32>,
19457        embd_gpu: &CudaSlice<u8>,
19458        embd_qt: i32,
19459        embd_rb: usize,
19460        cache: &mut Cache,
19461        n_vocab: usize,
19462        bucket: usize,
19463    ) -> Result<(), Box<dyn std::error::Error>> {
19464        let n_embd = self.cfg.n_embd as usize;
19465        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
19466        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
19467        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
19468        let (ld, _x) =
19469            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, Some(bucket), false, false)?;
19470        e.argmax_token_device_into(&ld, token_d, n_vocab)?;
19471        e.inc_seqlen(pos_d)?;
19472        Ok(())
19473    }
19474
19475    /// E4B DEVICE-COUNTER decode step (the dc serving arm): token id rides `token_d`, the
19476    /// greedy argmax lands in the returned device buffer — 4B/token host traffic. The layer
19477    /// stack is gemma4_e4b_trunk_core, i.e. the SAME functions the eager chain runs (stream
19478    /// identity by construction, not by twin-kernel parity). Host KV mirrors advance like the
19479    /// 26B dc-eager arm (window views are host math); len_d stays synced by the caller's
19480    /// entry sync + the appends here don't read it. Graph capture is NOT wired (no
19481    /// cap_bucket_max) — the E4B graph arc comes after the perf gates.
19482    #[allow(clippy::too_many_arguments)]
19483    pub fn gemma4_e4b_decode_step_dc(
19484        &self,
19485        e: &Engine,
19486        token_d: &CudaSlice<u32>,
19487        pos_d: &mut CudaSlice<i32>,
19488        embd_gpu: &CudaSlice<u8>,
19489        embd_qt: i32,
19490        embd_rb: usize,
19491        cache: &mut Cache,
19492        n_vocab: usize,
19493    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
19494        let n_embd = self.cfg.n_embd as usize;
19495        let eps = self.cfg.rms_eps;
19496        let mut x = e.embed_gather_device(embd_gpu, token_d, n_embd, embd_qt, embd_rb)?;
19497        e.scale_inplace(&mut x, (n_embd as f32).sqrt(), n_embd)?;
19498        let inp_pl = self.gemma4_e4b_inp_pl_dev(e, token_d, &x, 1)?;
19499        let (ld, _x) =
19500            self.gemma4_e4b_trunk_core(e, x, inp_pl, pos_d, 1, cache, None, false, false)?;
19501        let mut tok_out = e.stream().alloc_zeros::<u32>(1)?;
19502        e.argmax_token_device_into(&ld, &mut tok_out, n_vocab)?;
19503        e.inc_seqlen(pos_d)?;
19504        cache.pos += 1;
19505        let _ = eps;
19506        Ok(tok_out)
19507    }
19508
19509    /// E4B eager T=1 decode step (decode_step_h contract): returns (softcapped logits host,
19510    /// pre-output_norm hidden). Advances cache.pos.
19511    pub(crate) fn gemma4_e4b_decode_step_h(
19512        &self,
19513        e: &Engine,
19514        token: u32,
19515        cache: &mut Cache,
19516    ) -> Result<(Vec<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19517        let (ld, x) = self.gemma4_e4b_trunk(e, &[token], cache.pos, cache, false)?;
19518        let logits = e.dtoh(&ld)?;
19519        cache.pos += 1;
19520        Ok((logits, x))
19521    }
19522
19523    /// E4B batched prime (prime_cache contract: (last-row logits host, h_seed pre-norm last
19524    /// row, hidden stack)). First-light: the t-wide trunk (per-row attention) — correct, not
19525    /// fast; the prefill fa arms come later.
19526    pub(crate) fn gemma4_e4b_prime(
19527        &self,
19528        e: &Engine,
19529        tokens: &[u32],
19530        cache: &mut Cache,
19531    ) -> Result<(Vec<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19532        // Err, not assert (2026-08-07, lane/gemma4-serve-gaps): same served-chunked-prompt
19533        // process-kill as gemma4_prime — refuse per-request.
19534        if cache.pos != 0 {
19535            return Err(
19536                "e4b prime is fresh-prompt only (v0) — prime the full prompt in one \
19537                        call or decode tokenwise"
19538                    .into(),
19539            );
19540        }
19541        let n_embd = self.cfg.n_embd as usize;
19542        let t = tokens.len();
19543        let (ld, x) = self.gemma4_e4b_trunk(e, tokens, 0, cache, true)?;
19544        cache.pos += t;
19545        let last = e.dtoh(&ld)?; // head_last: ld is already the final row only
19546        let xv = e.view(&x, t * n_embd);
19547        let row = xv.slice((t - 1) * n_embd..t * n_embd);
19548        let mut h_seed = e.uninit(n_embd)?;
19549        e.copy_view_into(&mut h_seed, 0, &row, n_embd)?;
19550        Ok((last, h_seed, x))
19551    }
19552
19553    /// E4B prefill logits (forward contract — no persistent cache; scratch cache internally).
19554    pub(crate) fn gemma4_e4b_forward(
19555        &self,
19556        e: &Engine,
19557        tokens: &[u32],
19558        last_only: bool,
19559    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
19560        let mut cache = Cache::new(e, &self.cfg, tokens.len() + 8)?;
19561        let (ld, _x) = self.gemma4_e4b_trunk(e, tokens, 0, &mut cache, last_only)?;
19562        Ok(e.dtoh(&ld)?) // head_last already reduced to the final row when last_only
19563    }
19564}
19565
19566#[cfg(test)]
19567mod prime_chunk_schedule_tests {
19568    use super::{
19569        PRIME_MIN_T, PRIME_PIPE_MIN_CHUNK, active_matrix_values, align_prime_ranges_to_gdn,
19570        dynamic_prime_chunk_ranges, fixed_prime_chunk_ranges, fixed_prime_chunk_ranges_for_ring,
19571        parse_step_ep_grouped_prefill, parse_step_tp_prefill, step_grouped_decode_shape,
19572        step_grouped_prefill_shape, step_tp_prefill_shape, validate_step_prime_batch_modes,
19573    };
19574
19575    fn sizes(ranges: &[(usize, usize)]) -> Vec<usize> {
19576        ranges.iter().map(|(start, end)| end - start).collect()
19577    }
19578
19579    fn auto_chunk(t: usize) -> usize {
19580        t.div_ceil(8).max(PRIME_PIPE_MIN_CHUNK).min(4096)
19581    }
19582
19583    /// TOOTH for the PP-auto-ranges GDN grid law (lane/hermes-perf-fixes, 2026-08-23;
19584    /// primegrid pattern): the AUTO schedules put internal prime-call boundaries OFF the
19585    /// WY-chunk grid — the broken arm must be demonstrably off-grid, and the aligned twin
19586    /// must land every boundary on it without changing coverage.
19587    #[test]
19588    fn auto_prime_ranges_align_to_the_gdn_grid() {
19589        let c = 32usize; // shipped MEMRA_GDN_CHUNK default/clamp floor
19590        let assert_covers = |ranges: &[(usize, usize)], t: usize| {
19591            assert_eq!(ranges.first().map(|&(s, _)| s), Some(0));
19592            assert_eq!(ranges.last().map(|&(_, e)| e), Some(t));
19593            for w in ranges.windows(2) {
19594                assert_eq!(w[0].1, w[1].0, "ranges must stay contiguous");
19595            }
19596            assert!(ranges.iter().all(|&(s, e)| e > s), "no empty range");
19597        };
19598
19599        // The PP-2 auto geometry at a real agentic length: t=9510 -> fill = 1189 (div_ceil
19600        // by 8), every internal boundary off the 32 grid — the falsified-identity arm.
19601        let t = 9510usize;
19602        let fill = auto_chunk(t);
19603        let fixed = fixed_prime_chunk_ranges(t, fill);
19604        assert!(
19605            fixed[..fixed.len() - 1].iter().any(|&(_, e)| e % c != 0),
19606            "broken arm vanished: fixed auto boundaries all landed on-grid"
19607        );
19608        let dynamic = dynamic_prime_chunk_ranges(t, fill, &fixed);
19609        assert!(
19610            dynamic[..dynamic.len() - 1]
19611                .iter()
19612                .any(|&(_, e)| e % c != 0),
19613            "broken arm vanished: dynamic auto boundaries all landed on-grid"
19614        );
19615
19616        for ranges in [&fixed, &dynamic] {
19617            let aligned = align_prime_ranges_to_gdn(ranges, t, c);
19618            assert_covers(&aligned, t);
19619            for &(_, e) in &aligned[..aligned.len() - 1] {
19620                assert_eq!(e % c, 0, "internal boundary {e} off the {c}-grid");
19621            }
19622            // boundaries only move DOWN, at most c-1 tokens.
19623            for (&(_, a), &(_, b)) in aligned.iter().zip(ranges.iter()) {
19624                assert!(a <= b && b - a < c);
19625            }
19626        }
19627
19628        // Collapse/merge: boundaries inside one grid cell fuse instead of emitting an
19629        // empty range; the schedule survives degenerate short fills.
19630        let tight = vec![(0usize, 33usize), (33, 40), (40, 200)];
19631        let aligned = align_prime_ranges_to_gdn(&tight, 200, c);
19632        assert_covers(&aligned, 200);
19633        assert_eq!(aligned, vec![(0, 32), (32, 200)]);
19634
19635        // No-ops: single range, c=0 (grid off), already-aligned schedules.
19636        assert_eq!(align_prime_ranges_to_gdn(&[(0, 200)], 200, c), [(0, 200)]);
19637        assert_eq!(align_prime_ranges_to_gdn(&tight, 200, 0), tight.as_slice());
19638        let on_grid = vec![(0usize, 128usize), (128, 256), (256, 300)];
19639        assert_eq!(
19640            align_prime_ranges_to_gdn(&on_grid, 300, c),
19641            on_grid.as_slice()
19642        );
19643    }
19644
19645    #[test]
19646    fn active_matrix_prefix_scopes_reused_prime_slabs() {
19647        assert_eq!(
19648            active_matrix_values(40 * 4096, 29, 4096, "activation").unwrap(),
19649            29 * 4096
19650        );
19651        assert_eq!(
19652            active_matrix_values(29 * 4096, 29, 4096, "activation").unwrap(),
19653            29 * 4096
19654        );
19655        assert_eq!(
19656            active_matrix_values(29 * 4096, 24, 4096, "activation").unwrap(),
19657            24 * 4096
19658        );
19659        assert!(active_matrix_values(28 * 4096, 29, 4096, "activation").is_err());
19660        assert!(active_matrix_values(usize::MAX, usize::MAX, 2, "activation").is_err());
19661    }
19662
19663    #[test]
19664    fn step_tp_prefill_batch_refuses_before_scheduler_fallback() {
19665        assert!(validate_step_prime_batch_modes(false, false).is_ok());
19666
19667        let grouped_without_tp = validate_step_prime_batch_modes(false, true).unwrap_err();
19668        assert!(grouped_without_tp.contains("requires MEMRA_STEP_TP_PREFILL=1"));
19669
19670        for grouped in [false, true] {
19671            let err = validate_step_prime_batch_modes(true, grouped).unwrap_err();
19672            assert!(err.contains("did not clear the live-server performance gate"));
19673            assert!(err.contains("per-session grouped prefill"));
19674        }
19675    }
19676
19677    #[test]
19678    fn step_grouped_path_is_eager_single_token_only() {
19679        assert!(step_grouped_decode_shape(false, 1));
19680        assert!(!step_grouped_decode_shape(true, 1));
19681        assert!(!step_grouped_decode_shape(false, 2));
19682        assert!(!step_grouped_decode_shape(true, 2));
19683    }
19684
19685    #[test]
19686    fn step_grouped_prefill_door_is_strict_and_capacity_bounded() {
19687        assert!(!parse_step_ep_grouped_prefill(None).unwrap());
19688        assert!(!parse_step_ep_grouped_prefill(Some("")).unwrap());
19689        assert!(!parse_step_ep_grouped_prefill(Some("0")).unwrap());
19690        assert!(parse_step_ep_grouped_prefill(Some("1")).unwrap());
19691        assert!(parse_step_ep_grouped_prefill(Some("true")).is_err());
19692        assert!(parse_step_ep_grouped_prefill(Some("2")).is_err());
19693
19694        assert!(step_grouped_prefill_shape(true, true, PRIME_MIN_T));
19695        assert!(step_grouped_prefill_shape(
19696            true,
19697            true,
19698            crate::cache::PRIME_CHUNK_MAX_TOKENS,
19699        ));
19700        assert!(!step_grouped_prefill_shape(true, true, PRIME_MIN_T - 1,));
19701        assert!(!step_grouped_prefill_shape(
19702            true,
19703            true,
19704            crate::cache::PRIME_CHUNK_MAX_TOKENS + 1,
19705        ));
19706        assert!(!step_grouped_prefill_shape(false, true, PRIME_MIN_T));
19707        assert!(!step_grouped_prefill_shape(true, false, PRIME_MIN_T));
19708    }
19709
19710    #[test]
19711    fn step_tp_prefill_door_is_strict_and_default_off() {
19712        assert!(!parse_step_tp_prefill(None).unwrap());
19713        assert!(!parse_step_tp_prefill(Some("")).unwrap());
19714        assert!(!parse_step_tp_prefill(Some("0")).unwrap());
19715        assert!(parse_step_tp_prefill(Some("1")).unwrap());
19716        assert!(parse_step_tp_prefill(Some("true")).is_err());
19717        assert!(parse_step_tp_prefill(Some("2")).is_err());
19718    }
19719
19720    #[test]
19721    fn step_tp_prefill_requires_a_qualified_even_rank_shape() {
19722        assert!(step_tp_prefill_shape(
19723            true,
19724            PRIME_MIN_T,
19725            4,
19726            true,
19727            true,
19728            false,
19729        ));
19730        assert!(!step_tp_prefill_shape(
19731            false,
19732            PRIME_MIN_T,
19733            4,
19734            true,
19735            true,
19736            false,
19737        ));
19738        assert!(!step_tp_prefill_shape(
19739            true,
19740            PRIME_MIN_T - 1,
19741            4,
19742            true,
19743            true,
19744            false,
19745        ));
19746        // TP2 admits (2026-08-25); odd/1-card placements still refuse.
19747        assert!(step_tp_prefill_shape(
19748            true,
19749            PRIME_MIN_T,
19750            2,
19751            true,
19752            true,
19753            false
19754        ));
19755        assert!(!step_tp_prefill_shape(
19756            true,
19757            PRIME_MIN_T,
19758            1,
19759            true,
19760            true,
19761            false
19762        ));
19763        assert!(!step_tp_prefill_shape(
19764            true,
19765            PRIME_MIN_T,
19766            3,
19767            true,
19768            true,
19769            false
19770        ));
19771        assert!(!step_tp_prefill_shape(
19772            true,
19773            PRIME_MIN_T,
19774            4,
19775            false,
19776            true,
19777            false,
19778        ));
19779        assert!(!step_tp_prefill_shape(
19780            true,
19781            PRIME_MIN_T,
19782            4,
19783            true,
19784            false,
19785            false,
19786        ));
19787        assert!(!step_tp_prefill_shape(
19788            true,
19789            PRIME_MIN_T,
19790            4,
19791            true,
19792            true,
19793            true,
19794        ));
19795    }
19796
19797    #[test]
19798    fn fixed_schedule_retains_measured_geometry() {
19799        assert_eq!(
19800            sizes(&fixed_prime_chunk_ranges(461, 128)),
19801            vec![128, 128, 128, 77]
19802        );
19803        assert_eq!(
19804            sizes(&fixed_prime_chunk_ranges(1833, 230)),
19805            vec![230, 230, 230, 230, 230, 230, 230, 223]
19806        );
19807        assert_eq!(sizes(&fixed_prime_chunk_ranges(4096, 512)), vec![512; 8]);
19808        let capped = sizes(&fixed_prime_chunk_ranges_for_ring(8200, 4096, true));
19809        assert_eq!(capped, vec![4096, 4088, 16]);
19810        assert!(capped.iter().all(|&rows| rows <= 4096));
19811        assert_eq!(
19812            sizes(&fixed_prime_chunk_ranges_for_ring(4100, 4096, false)),
19813            vec![4100],
19814            "flag-off schedule remains byte-for-byte the legacy monolithic tail",
19815        );
19816    }
19817
19818    #[test]
19819    fn dynamic_schedule_matches_registered_shapes() {
19820        let cases = [
19821            (461, vec![64, 141, 132, 124]),
19822            (1833, vec![115, 269, 260, 252, 244, 237, 231, 225]),
19823            (4096, vec![256, 602, 580, 563, 545, 531, 516, 503]),
19824        ];
19825        for (t, expected) in cases {
19826            let chunk = auto_chunk(t);
19827            let fixed = fixed_prime_chunk_ranges(t, chunk);
19828            assert_eq!(
19829                sizes(&dynamic_prime_chunk_ranges(t, chunk, &fixed)),
19830                expected
19831            );
19832        }
19833    }
19834
19835    #[test]
19836    fn dynamic_schedule_covers_exactly_and_shrinks_after_fill() {
19837        for t in 256..=8192 {
19838            let chunk = auto_chunk(t);
19839            let fixed = fixed_prime_chunk_ranges(t, chunk);
19840            let dynamic = dynamic_prime_chunk_ranges(t, chunk, &fixed);
19841            assert_eq!(dynamic.len(), fixed.len(), "T={t}");
19842            assert_eq!(dynamic.first().unwrap().0, 0, "T={t}");
19843            assert_eq!(dynamic.last().unwrap().1, t, "T={t}");
19844            for pair in dynamic.windows(2) {
19845                assert_eq!(pair[0].1, pair[1].0, "T={t}");
19846            }
19847            assert!(
19848                dynamic
19849                    .iter()
19850                    .all(|(start, end)| end - start >= PRIME_MIN_T),
19851                "T={t} sizes={:?}",
19852                sizes(&dynamic)
19853            );
19854            if dynamic.len() >= 3 {
19855                let chunk_sizes = sizes(&dynamic);
19856                assert!(
19857                    chunk_sizes[0] < chunk_sizes[1],
19858                    "T={t} sizes={chunk_sizes:?}"
19859                );
19860                assert!(
19861                    chunk_sizes[1..].windows(2).all(|pair| pair[0] >= pair[1]),
19862                    "T={t} sizes={chunk_sizes:?}"
19863                );
19864            }
19865        }
19866    }
19867}
19868
19869#[cfg(test)]
19870mod page_prefetch_tests {
19871    use super::{
19872        grouped_worker_prefetch_position, page_prefetch_positions,
19873        page_prefetch_window_from_values, worker_prefetch_positions,
19874    };
19875
19876    #[test]
19877    fn page_prefetch_window_keeps_existing_opt_in_default() {
19878        assert_eq!(page_prefetch_window_from_values(false, None), 0);
19879        assert_eq!(page_prefetch_window_from_values(false, Some("8")), 0);
19880        assert_eq!(page_prefetch_window_from_values(true, None), 1);
19881        assert_eq!(page_prefetch_window_from_values(true, Some("bad")), 1);
19882        assert_eq!(page_prefetch_window_from_values(true, Some("0")), 0);
19883        assert_eq!(page_prefetch_window_from_values(true, Some("8")), 8);
19884    }
19885
19886    #[test]
19887    fn rolling_page_prefetch_advises_each_future_expert_once() {
19888        let advised: Vec<_> = (0..7)
19889            .flat_map(|position| page_prefetch_positions(position, 7, 3))
19890            .collect();
19891        assert_eq!(advised, vec![1, 2, 3, 4, 5, 6]);
19892
19893        let one_ahead: Vec<_> = (0..4)
19894            .flat_map(|position| page_prefetch_positions(position, 4, 1))
19895            .collect();
19896        assert_eq!(one_ahead, vec![1, 2, 3]);
19897        assert!(page_prefetch_positions(0, 4, 0).is_empty());
19898    }
19899
19900    #[test]
19901    fn grouped_worker_prefetch_primes_first_then_each_known_next_once() {
19902        assert_eq!(grouped_worker_prefetch_position(0, None), None);
19903        let positions: Vec<_> = std::iter::once(grouped_worker_prefetch_position(4, None).unwrap())
19904            .chain(
19905                (0..4).filter_map(|position| grouped_worker_prefetch_position(4, Some(position))),
19906            )
19907            .collect();
19908        assert_eq!(positions, vec![0, 1, 2, 3]);
19909        assert_eq!(grouped_worker_prefetch_position(1, Some(0)), None);
19910    }
19911
19912    #[test]
19913    fn rolling_worker_prefetch_primes_current_and_each_future_expert_once() {
19914        let queued: Vec<_> = (0..8)
19915            .flat_map(|position| worker_prefetch_positions(position, 8, 5))
19916            .collect();
19917        assert_eq!(queued, (0..8).collect::<Vec<_>>());
19918
19919        let one_at_a_time: Vec<_> = (0..4)
19920            .flat_map(|position| worker_prefetch_positions(position, 4, 1))
19921            .collect();
19922        assert_eq!(one_at_a_time, vec![0, 1, 2, 3]);
19923        assert!(worker_prefetch_positions(0, 4, 0).is_empty());
19924    }
19925}
19926
19927pub struct G4DcSlots {
19928    x: CudaSlice<f32>,
19929    xn: CudaSlice<f32>,
19930    cur: CudaSlice<f32>,
19931    hq: CudaSlice<i8>,
19932    hd_: CudaSlice<f32>,
19933    q0: CudaSlice<f32>,
19934    k0: CudaSlice<f32>,
19935    v0: CudaSlice<f32>,
19936    q: CudaSlice<f32>,
19937    k: CudaSlice<f32>,
19938    v: CudaSlice<f32>,
19939    attn: CudaSlice<f32>,
19940    o: CudaSlice<f32>,
19941    attn_out: CudaSlice<f32>,
19942    zsh: CudaSlice<f32>,
19943    zq: CudaSlice<i8>,
19944    zd: CudaSlice<f32>,
19945    gate: CudaSlice<f32>,
19946    up: CudaSlice<f32>,
19947    act: CudaSlice<f32>,
19948    actq: CudaSlice<i8>,
19949    actd: CudaSlice<f32>,
19950    f0: CudaSlice<f32>,
19951    sn: CudaSlice<f32>,
19952    hn: CudaSlice<f32>,
19953    logits: CudaSlice<f32>,
19954}
19955
19956/// Whole-token decode graph state (step TP graph increment B). One stitched multi-device
19957/// parent per fa bucket, plus the persistent host->graph plumbing: the device token id the
19958/// in-graph embed gathers, the device position the ropes read (advanced in-graph), and the
19959/// fixed logits stage the head writes.
19960pub struct Step35TokenGraphState {
19961    /// (bucket_max, graph) — bucket keyed by the fa split geometry (fa_geom_eager).
19962    pub graphs: Vec<(usize, crate::tp::TokenGraph)>,
19963    pub token_d: cudarc::driver::CudaSlice<u32>,
19964    pub pos_d: cudarc::driver::CudaSlice<i32>,
19965    pub logits_stage: cudarc::driver::CudaSlice<f32>,
19966    /// Cross-child intermediates MUST live at fixed addresses (graph mem nodes remap at
19967    /// launch, so an alloc made inside one captured child is not referable from another):
19968    /// the running residual, the post-attention pair, the shared-expert row, and the
19969    /// e-context mirrors of the root-produced attention output and K/V shadow rows.
19970    pub x: cudarc::driver::CudaSlice<f32>,
19971    pub x1: cudarc::driver::CudaSlice<f32>,
19972    pub mixed_stage: cudarc::driver::CudaSlice<f32>,
19973    pub sh_stage: cudarc::driver::CudaSlice<f32>,
19974    pub k_shadow_stage: cudarc::driver::CudaSlice<f32>,
19975    pub v_shadow_stage: cudarc::driver::CudaSlice<f32>,
19976    /// Alloc-free e-section scratch (child graphs cannot contain mem nodes): router logits,
19977    /// shared-expert gate/up/act rows + sigmoid scalar, dense-FFN z/gate/up/act, head hidden.
19978    pub router_logits: cudarc::driver::CudaSlice<f32>,
19979    pub shexp_gate: cudarc::driver::CudaSlice<f32>,
19980    pub shexp_up: cudarc::driver::CudaSlice<f32>,
19981    pub shexp_act: cudarc::driver::CudaSlice<f32>,
19982    pub gate_sig: cudarc::driver::CudaSlice<f32>,
19983    pub dense_z: cudarc::driver::CudaSlice<f32>,
19984    pub dense_gate: cudarc::driver::CudaSlice<f32>,
19985    pub dense_up: cudarc::driver::CudaSlice<f32>,
19986    pub dense_act: cudarc::driver::CudaSlice<f32>,
19987    pub hn: cudarc::driver::CudaSlice<f32>,
19988    /// MEMRA_TG_PROBE_LAYER diagnostics: capture-time copies of layer K's attention output
19989    /// and post-FFN residual, dumped after replay for graph-vs-eager layer bisection.
19990    pub probe_mixed: cudarc::driver::CudaSlice<f32>,
19991    pub probe_x: cudarc::driver::CudaSlice<f32>,
19992    /// Chunk-loop (F-lite): device token history ring + its device write index, filled by
19993    /// the in-graph tail argmax chain; host reads the ring once per chunk.
19994    pub token_hist: cudarc::driver::CudaSlice<u32>,
19995    pub hist_idx: cudarc::driver::CudaSlice<i32>,
19996}
19997
19998impl HybridModel {
19999    /// Whole-token decode graph (step TP graph increment B): ONE stitched multi-device parent
20000    /// replays the entire 45-layer token — the launch-collapse the per-layer minis could not
20001    /// reach. Returns Some(logits) when the graph handled the token, None for eager fallback
20002    /// (door off, ineligible class, any layer's ring would rebase, or a bucket boundary that
20003    /// needs a rebuild this token).
20004    ///
20005    /// v1 SCOPE (diagnostic door, default OFF): the LOCAL shadow caches advance their lengths
20006    /// but not their contents under this door (the TP rank caches are fully maintained
20007    /// in-graph via the dcw counters). Sessions relying on shadow CONTENT (save/rollback)
20008    /// must not run with the door on until the local-dcw twin lands.
20009    pub(crate) fn step35_token_graph_step(
20010        &self,
20011        e: &Engine,
20012        token: u32,
20013        cache: &mut Cache,
20014    ) -> Result<Option<(Vec<f32>, CudaSlice<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_embd = self.cfg.n_embd as usize;
20025        let n_vocab = self.cfg.n_vocab as usize;
20026        let eps = self.cfg.rms_eps;
20027        let n_layers = self.layers.len();
20028        let pos = cache.pos;
20029        let staged_next = pos + 1;
20030        if staged_next < 96 {
20031            return Ok(None); // sub-vec-floor contexts keep eager (fa kernel-class boundary)
20032        }
20033
20034        // Per-layer eligibility: every TP layer contiguous-appends this token (any rebase ->
20035        // eager fallback for the whole token; the host path also updates base_d there).
20036        for il in 0..n_layers {
20037            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20038                return Ok(None); // caches not hydrated yet — eager warms them
20039            };
20040            if tp_kv.peek_append_ring(1)?.1 {
20041                return Ok(None);
20042            }
20043        }
20044
20045        // Bucket key: the global layers' fa split geometry at this depth (SWA layers cap at
20046        // their window and share one bucket forever after ctx > window).
20047        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20048        if !fa_vec {
20049            return Ok(None);
20050        }
20051        let sp = crate::fa_split_keys(staged_next, 8);
20052        let bucket_max = (n_splits * sp).max(staged_next);
20053
20054        let mut state_guard = self
20055            .step35_token_graph
20056            .lock()
20057            .map_err(|_| "step35 token graph lock is poisoned")?;
20058        if state_guard.is_none() {
20059            let _main = e.gpu.enter_main()?;
20060            let n_expert = self
20061                .cfg
20062                .moe
20063                .as_ref()
20064                .map(|m| m.expert_count as usize)
20065                .unwrap_or(0);
20066            let n_ff_sh = self
20067                .layers
20068                .iter()
20069                .find_map(|l| match &l.ffn {
20070                    crate::hybrid::Ffn::Moe(m) => m.gate_shexp.as_ref().map(|g| g.out_features()),
20071                    _ => None,
20072                })
20073                .unwrap_or(0);
20074            let n_ff_dense = self
20075                .layers
20076                .iter()
20077                .find_map(|l| match &l.ffn {
20078                    crate::hybrid::Ffn::Dense { ffn_gate, .. } => Some(ffn_gate.out_features()),
20079                    _ => None,
20080                })
20081                .unwrap_or(0);
20082            *state_guard = Some(Step35TokenGraphState {
20083                graphs: Vec::new(),
20084                token_d: e.stream().clone_htod(&[0u32])?,
20085                pos_d: e.htod_i32(&[pos as i32])?,
20086                logits_stage: e.htod(&vec![0.0f32; n_vocab])?,
20087                x: e.htod(&vec![0.0f32; n_embd])?,
20088                x1: e.htod(&vec![0.0f32; n_embd])?,
20089                mixed_stage: e.htod(&vec![0.0f32; n_embd])?,
20090                sh_stage: e.htod(&vec![0.0f32; n_embd])?,
20091                k_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
20092                v_shadow_stage: e.htod(&vec![0.0f32; 2048])?,
20093                router_logits: e.htod(&vec![0.0f32; n_expert.max(1)])?,
20094                shexp_gate: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
20095                shexp_up: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
20096                shexp_act: e.htod(&vec![0.0f32; n_ff_sh.max(1)])?,
20097                gate_sig: e.htod(&vec![1.0f32; 1])?,
20098                dense_z: e.htod(&vec![0.0f32; n_embd])?,
20099                dense_gate: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
20100                dense_up: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
20101                dense_act: e.htod(&vec![0.0f32; n_ff_dense.max(1)])?,
20102                hn: e.htod(&vec![0.0f32; n_embd])?,
20103                probe_mixed: e.htod(&vec![0.0f32; n_embd])?,
20104                probe_x: e.htod(&vec![0.0f32; n_embd])?,
20105                token_hist: e.stream().clone_htod(&[0u32; 16])?,
20106                hist_idx: e.htod_i32(&[0])?,
20107            });
20108        }
20109        let state = state_guard.as_mut().expect("state armed above");
20110        // Pre-arm the argmax partials OUTSIDE any capture: the launcher allocates them on
20111        // first use, and an alloc inside a captured section is a mem node (child graphs
20112        // reject those — the tail argmax chain needs them already resident).
20113        {
20114            let _main = e.gpu.enter_main()?;
20115            let Step35TokenGraphState {
20116                logits_stage,
20117                token_d,
20118                ..
20119            } = &mut *state;
20120            e.argmax_token_device_into(logits_stage, token_d, n_vocab)?;
20121        }
20122
20123        // ONE graph, retargeted per bucket (increment C): the per-16-token whole rebuild was
20124        // ~55ms (3.4ms/token persistent); the M1 exec update path moves nsp/ski/gridDimY and
20125        // the partial-pool memset widths in ~1ms. The partial pool is pre-grown to the run
20126        // ceiling at build so the baked pointers never move.
20127        if state.graphs.is_empty() {
20128            // Build the parent at this bucket. Capture executes nothing; correctness is
20129            // pinned at replay by the token-identity gate.
20130            self.step35_token_graph_build(e, cache, state, bucket_max)?;
20131        }
20132        {
20133            let (b, g) = state.graphs.first_mut().expect("graph built above");
20134            if *b != bucket_max {
20135                g.retarget_bucket(bucket_max)?;
20136                *b = bucket_max;
20137            }
20138        }
20139        let graph = state
20140            .graphs
20141            .first()
20142            .map(|(_, g)| g)
20143            .expect("graph built above");
20144
20145        let tg_timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
20146        let t_fence = tg_timing.then(std::time::Instant::now);
20147        // Rank-stream fence: an eager token (warmup, rebase fallback) leaves len-mirror sets
20148        // queued on the rank streams, and graph children carry no ordering edge to those
20149        // streams. Graph tokens themselves enqueue nothing there (external commit), so this
20150        // sync is a no-op between consecutive replays.
20151        {
20152            let fa0 = match &self.layers[0].mixer {
20153                Mixer::Full(fa) => fa,
20154                _ => return Err("step35 token graph expects full-attention layers".into()),
20155            };
20156            let tp0 = fa0
20157                .step_tp_qkv
20158                .as_ref()
20159                .ok_or("step35 token graph lost its TP state")?;
20160            for rank in 0..tp0.runtime.devices().len() {
20161                let engine = tp0
20162                    .runtime
20163                    .rank_engine(rank)
20164                    .ok_or("step35 token graph lost a rank engine")?;
20165                let _main = engine.gpu.enter_main()?;
20166                engine.stream().synchronize()?;
20167            }
20168        }
20169
20170        // Replay: feed the token, launch, read the logits, mirror the host bookkeeping.
20171        {
20172            let _main = e.gpu.enter_main()?;
20173            e.set_u32_one(&mut state.token_d, token)?;
20174            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20175        }
20176        let t_launch = tg_timing.then(std::time::Instant::now);
20177        graph.launch(e)?;
20178        let t_book = tg_timing.then(std::time::Instant::now);
20179        // Host bookkeeping OVERLAPS the replay (nsys 2026-08-21: the 45-layer txn loop was
20180        // 5.4ms/token of inter-token gap when it ran after the sync). Host-only work except
20181        // the local len_d set, which is stream-ordered AFTER the graph on e's stream. On a
20182        // replay error the counters are already advanced — acceptable: the decode aborts.
20183        for il in 0..n_layers {
20184            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20185            let transaction = tp_kv.begin_transaction()?;
20186            let fa = match &self.layers[il].mixer {
20187                Mixer::Full(fa) => fa,
20188                _ => return Err("step35 token graph expects full-attention layers".into()),
20189            };
20190            let tp = fa
20191                .step_tp_qkv
20192                .as_ref()
20193                .ok_or("step35 token graph lost its TP state")?;
20194            // Bookkeeping-only txn (external appends; mirror sets skipped — the in-graph
20195            // incs own the counters). Shards unused.
20196            let empty: [CudaSlice<f32>; 0] = [];
20197            tp.runtime.append_tp_kv_transaction_inner(
20198                tp_kv,
20199                transaction,
20200                &empty,
20201                &empty,
20202                1,
20203                true,
20204            )?;
20205            tp.runtime
20206                .commit_tp_kv_transaction_external(tp_kv, transaction, 1)?;
20207            // Local shadow: lengths advance (v1 keeps contents stale under the door).
20208            if let Some(local) = cache.kv[il].as_mut() {
20209                local.len = pos + 1;
20210                let _main = e.gpu.enter_main()?;
20211                e.set_i32_one(&mut local.len_d, (pos + 1) as i32)?;
20212            }
20213        }
20214        cache.pos = pos + 1;
20215        let t_sync = tg_timing.then(std::time::Instant::now);
20216        let (logits, h_seed) = {
20217            let _main = e.gpu.enter_main()?;
20218            e.stream().synchronize()?;
20219            (e.dtoh(&state.logits_stage)?, e.clone_dtod(&state.x)?)
20220        };
20221        if let (Some(f), Some(l), Some(b), Some(sy)) = (t_fence, t_launch, t_book, t_sync) {
20222            use std::sync::atomic::{AtomicU64, Ordering};
20223            static NS: [AtomicU64; 5] = [
20224                AtomicU64::new(0),
20225                AtomicU64::new(0),
20226                AtomicU64::new(0),
20227                AtomicU64::new(0),
20228                AtomicU64::new(0),
20229            ];
20230            static CALLS: AtomicU64 = AtomicU64::new(0);
20231            let now = std::time::Instant::now();
20232            NS[0].fetch_add((l - f).as_nanos() as u64, Ordering::Relaxed); // fence+set
20233            NS[1].fetch_add((b - l).as_nanos() as u64, Ordering::Relaxed); // launch call
20234            NS[2].fetch_add((sy - b).as_nanos() as u64, Ordering::Relaxed); // bookkeeping
20235            NS[3].fetch_add((now - sy).as_nanos() as u64, Ordering::Relaxed); // sync+dtoh
20236            NS[4].fetch_add((now - f).as_nanos() as u64, Ordering::Relaxed); // total
20237            let calls = CALLS.fetch_add(1, Ordering::Relaxed) + 1;
20238            if calls % 100 == 0 {
20239                let avg = |i: usize| NS[i].load(Ordering::Relaxed) as f64 / calls as f64 / 1e3;
20240                eprintln!(
20241                    "[tg-timing] calls={calls} fence_us={:.0} launch_us={:.0} book_us={:.0} \
20242                     syncdtoh_us={:.0} total_us={:.0}",
20243                    avg(0),
20244                    avg(1),
20245                    avg(2),
20246                    avg(3),
20247                    avg(4)
20248                );
20249            }
20250        }
20251        // MEMRA_TG_PROBE_LAYER diagnostics: append the captured layer-K probes.
20252        if std::env::var("MEMRA_TG_PROBE_LAYER").is_ok() {
20253            use std::io::Write;
20254            let (pm, px) = {
20255                let _main = e.gpu.enter_main()?;
20256                (e.dtoh(&state.probe_mixed)?, e.dtoh(&state.probe_x)?)
20257            };
20258            for (path, data) in [
20259                ("/root/tg-probe-mixed.bin", &pm),
20260                ("/root/tg-probe-x.bin", &px),
20261            ] {
20262                let mut fo = std::fs::OpenOptions::new()
20263                    .create(true)
20264                    .append(true)
20265                    .open(path)?;
20266                for v in data {
20267                    fo.write_all(&v.to_le_bytes())?;
20268                }
20269            }
20270        }
20271        // MEMRA_DUMP_HN twin of the eager tail's dump (same format: appended raw LE f32 rows)
20272        // so a graph arm and an eager arm produce position-aligned pre-head hidden streams.
20273        if let Ok(path) = std::env::var("MEMRA_DUMP_HN") {
20274            let hh = {
20275                let _main = e.gpu.enter_main()?;
20276                e.dtoh(&state.hn)?
20277            };
20278            use std::io::Write;
20279            let mut fo = std::fs::OpenOptions::new()
20280                .create(true)
20281                .append(true)
20282                .open(path)?;
20283            for v in &hh {
20284                fo.write_all(&v.to_le_bytes())?;
20285            }
20286        }
20287        // MEMRA_STEP_TP_GRAPH_DEBUG=1: per-token device-counter dump (drift hunts). One dtoh
20288        // per rank per token; diagnostics only.
20289        if std::env::var("MEMRA_STEP_TP_GRAPH_DEBUG").as_deref() == Ok("1") {
20290            for il in [0usize, 1, 44] {
20291                let tp_kv = cache.tp_kv[il].as_ref().expect("eligibility checked above");
20292                let host_len = tp_kv.staged_len();
20293                let fa = match &self.layers[il].mixer {
20294                    Mixer::Full(fa) => fa,
20295                    _ => continue,
20296                };
20297                let tp = fa
20298                    .step_tp_qkv
20299                    .as_ref()
20300                    .ok_or("step35 token graph lost its TP state")?;
20301                for rank in 0..tp.runtime.devices().len() {
20302                    let engine = tp
20303                        .runtime
20304                        .rank_engine(rank)
20305                        .ok_or("step35 token graph lost a rank engine")?;
20306                    let rank_cache = tp_kv.rank(rank).ok_or("debug rank cache missing")?;
20307                    let _main = engine.gpu.enter_main()?;
20308                    engine.stream().synchronize()?;
20309                    let len_d = engine.dtoh_i32_one(rank_cache.len_d())?;
20310                    let base_d = match rank_cache.base_d() {
20311                        Some(b) => engine.dtoh_i32_one(b)?,
20312                        None => -1,
20313                    };
20314                    eprintln!(
20315                        "[graph-debug] pos={pos} il={il} rank={rank} host_len={host_len} \
20316                         len_d={len_d} base_d={base_d}"
20317                    );
20318                }
20319            }
20320        }
20321        Ok(Some((logits, h_seed)))
20322    }
20323
20324    /// MEMRA_HEAD_SPLIT worker: dev0 computes lm-head rows [0, half), rank1 computes
20325    /// [half, n_vocab) concurrently on its otherwise-idle tail, and the halves concatenate
20326    /// on e — bit-identical per logit to the single-device matvec. Process-static workspace
20327    /// (SHEXP_WS pattern) pinned by the head tensor pointer; rank1 holds a one-time 0.5GB
20328    /// replica of its row half. Returns None when ineligible (no bf16 head / no rank1).
20329    pub(crate) fn head_split_matvec(
20330        &self,
20331        e: &Engine,
20332        hn: &CudaSlice<f32>,
20333    ) -> Result<Option<Vec<f32>>, Box<dyn std::error::Error>> {
20334        if self.head_split_fill_device(e, hn)?.is_none() {
20335            return Ok(None);
20336        }
20337        let guard = HEAD_SPLIT_WS
20338            .lock()
20339            .map_err(|_| "head split lock is poisoned")?;
20340        let ws = guard.as_ref().expect("filled above");
20341        let _main = e.gpu.enter_main()?;
20342        Ok(Some(e.dtoh(&ws.logits_e)?))
20343    }
20344
20345    /// Compute body of the split head: arms the replica + staging on first use, then fills
20346    /// the persistent full-logits row (e's half by view matvec, rank1's half by raw P2P
20347    /// push) and orders e's stream behind it. None = ineligible.
20348    fn head_split_fill_device(
20349        &self,
20350        e: &Engine,
20351        hn: &CudaSlice<f32>,
20352    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
20353        use cudarc::driver::DevicePtr;
20354        let crate::model::GpuTensor::FloatBf16 { data: head, .. } = &self.output else {
20355            return Ok(None);
20356        };
20357        let Some(rank1) = self.layers.first().and_then(|l| match &l.mixer {
20358            Mixer::Full(fa) => fa
20359                .step_tp_qkv
20360                .as_ref()
20361                .and_then(|tp| tp.runtime.rank_engine(1)),
20362            _ => None,
20363        }) else {
20364            return Ok(None);
20365        };
20366        let n_embd = self.cfg.n_embd as usize;
20367        let n_vocab = self.cfg.n_vocab as usize;
20368        let half = n_vocab / 2;
20369        let mut guard = HEAD_SPLIT_WS
20370            .lock()
20371            .map_err(|_| "head split lock is poisoned")?;
20372        let pin = {
20373            let _main = e.gpu.enter_main()?;
20374            let stream = e.stream();
20375            let (ptr, _g) = head.device_ptr(&stream);
20376            ptr as u64
20377        };
20378        if guard.as_ref().is_none_or(|ws| ws.pin != pin) {
20379            // One-time: upload rank1's row half + persistent staging.
20380            let hi_rows = n_vocab - half;
20381            let (w1, hn1, y1, ev_done) = {
20382                let _r1 = rank1.gpu.enter_main()?;
20383                (
20384                    rank1.alloc_u8_uninit(hi_rows * n_embd * 2)?,
20385                    rank1.htod(&vec![0.0f32; n_embd])?,
20386                    rank1.htod(&vec![0.0f32; hi_rows])?,
20387                    rank1.ctx().new_event(None)?,
20388                )
20389            };
20390            {
20391                use cudarc::driver::sys;
20392                let src = pin + (half * n_embd * 2) as u64;
20393                let dst = {
20394                    let _r1 = rank1.gpu.enter_main()?;
20395                    let rstream = rank1.stream();
20396                    let (d, _g) = w1.device_ptr(&rstream);
20397                    d as u64
20398                };
20399                let _r1 = rank1.gpu.enter_main()?;
20400                let r = unsafe {
20401                    sys::cuMemcpyAsync(
20402                        dst as sys::CUdeviceptr,
20403                        src as sys::CUdeviceptr,
20404                        hi_rows * n_embd * 2,
20405                        rank1.stream().cu_stream() as sys::CUstream,
20406                    )
20407                };
20408                if r != sys::CUresult::CUDA_SUCCESS {
20409                    return Err(format!("head split replica upload: {r:?}").into());
20410                }
20411                rank1.stream().synchronize()?;
20412            }
20413            let (logits_e, ev_hn) = {
20414                let _main = e.gpu.enter_main()?;
20415                (e.htod(&vec![0.0f32; n_vocab])?, e.ctx().new_event(None)?)
20416            };
20417            let (raw_hn1, raw_y1) = {
20418                let _r1 = rank1.gpu.enter_main()?;
20419                let rstream = rank1.stream();
20420                let (a, _g0) = hn1.device_ptr(&rstream);
20421                let (b, _g1) = y1.device_ptr(&rstream);
20422                (a as u64, b as u64)
20423            };
20424            let raw_logits_hi = {
20425                let _main = e.gpu.enter_main()?;
20426                let stream = e.stream();
20427                let (l, _g) = logits_e.device_ptr(&stream);
20428                l as u64 + (half * 4) as u64
20429            };
20430            *guard = Some(HeadSplit {
20431                pin,
20432                w1,
20433                hn1,
20434                y1,
20435                logits_e,
20436                ev_hn,
20437                ev_done,
20438                raw_hn1,
20439                raw_y1,
20440                raw_logits_hi,
20441                samp: None,
20442            });
20443        }
20444        let ws = guard.as_mut().expect("armed above");
20445        let hi_rows = n_vocab - half;
20446        // e: signal hn ready; rank1: pull hn, matvec its half, push the logits half back.
20447        let raw_hn = {
20448            let _main = e.gpu.enter_main()?;
20449            let stream = e.stream();
20450            let (h, _g) = hn.device_ptr(&stream);
20451            ws.ev_hn.record(&stream)?;
20452            h as u64
20453        };
20454        {
20455            let _r1 = rank1.gpu.enter_main()?;
20456            rank1.stream().wait(&ws.ev_hn)?;
20457            crate::tp::raw_copy_bytes(ws.raw_hn1, raw_hn, n_embd * 4, rank1)?;
20458            let HeadSplit { w1, hn1, y1, .. } = &mut *ws;
20459            rank1.matvec_bf16_into(w1, hn1, y1, n_embd, hi_rows)?;
20460            crate::tp::raw_copy_bytes(ws.raw_logits_hi, ws.raw_y1, hi_rows * 4, rank1)?;
20461            ws.ev_done.record(&rank1.stream())?;
20462        }
20463        {
20464            let _main = e.gpu.enter_main()?;
20465            let head_lo = head.slice(0..half * n_embd * 2);
20466            let HeadSplit { logits_e, .. } = &mut *ws;
20467            // Writes rows [0, half) of logits_e; rank1's raw push fills [half, n_vocab).
20468            e.matvec_bf16_view_into(&head_lo, hn, logits_e, n_embd, half)?;
20469            e.stream().wait(&ws.ev_done)?;
20470            Ok(Some(()))
20471        }
20472    }
20473
20474    /// Device twin of `head_split_matvec` for the chain: fills the persistent full-logits
20475    /// row exactly like the host variant (identical halves, identical concat) and runs the
20476    /// device argmax into `token_d` — NO host readback. Returns false when the split is
20477    /// ineligible (caller falls back to the plain matmul head).
20478    pub(crate) fn head_split_argmax_device(
20479        &self,
20480        e: &Engine,
20481        hn: &CudaSlice<f32>,
20482        token_d: &mut CudaSlice<u32>,
20483    ) -> Result<bool, Box<dyn std::error::Error>> {
20484        if self.head_split_fill_device(e, hn)?.is_none() {
20485            return Ok(false);
20486        }
20487        let n_vocab = self.cfg.n_vocab as usize;
20488        let guard = HEAD_SPLIT_WS
20489            .lock()
20490            .map_err(|_| "head split lock is poisoned")?;
20491        let ws = guard.as_ref().expect("filled above");
20492        let _main = e.gpu.enter_main()?;
20493        e.argmax_token_device_into(&ws.logits_e, token_d, n_vocab)?;
20494        Ok(true)
20495    }
20496
20497    /// SAMPLED twin of `head_split_argmax_device`. The split head already materializes the
20498    /// full concatenated row in `ws.logits_e`, so sampling does NOT have to give up HEAD_SPLIT
20499    /// — it draws from that row on device (filter thresholds, Gumbel perturbation, argmax)
20500    /// exactly as the serve tick does. Worth ~0.2 ms/token: the post-W8 census had the
20501    /// unsplit q8 head at ~364 us against ~82 us per half.
20502    pub(crate) fn head_split_sample_device(
20503        &self,
20504        e: &Engine,
20505        hn: &CudaSlice<f32>,
20506        token_d: &mut CudaSlice<u32>,
20507        samp: &crate::decode_batch::DevSamp,
20508        ctr: u32,
20509    ) -> Result<bool, Box<dyn std::error::Error>> {
20510        if self.head_split_fill_device(e, hn)?.is_none() {
20511            return Ok(false);
20512        }
20513        let n_vocab = self.cfg.n_vocab as usize;
20514        let guard = HEAD_SPLIT_WS
20515            .lock()
20516            .map_err(|_| "head split lock is poisoned")?;
20517        let mut guard = guard;
20518        let ws = guard.as_mut().expect("filled above");
20519        let _main = e.gpu.enter_main()?;
20520        if ws.samp.is_none() {
20521            ws.samp = Some(SampScratch {
20522                pb: e.zeros(n_vocab)?,
20523                th: e.zeros(1)?,
20524                z: e.zeros(1)?,
20525                mx: e.zeros(1)?,
20526                rows: e.htod_i32(&[0i32])?,
20527            });
20528        }
20529        let filtered = samp.top_k > 0 || samp.top_p < 1.0 || samp.min_p > 0.0;
20530        let HeadSplit {
20531            logits_e,
20532            samp: scratch,
20533            ..
20534        } = &mut *ws;
20535        let sc = scratch.as_mut().expect("armed above");
20536        if filtered {
20537            e.filter_stats(
20538                logits_e, n_vocab, &sc.rows, &mut sc.th, &mut sc.z, &mut sc.mx, n_vocab, 1,
20539                samp.temp, samp.top_k, samp.top_p, samp.min_p,
20540            )?;
20541            let SampScratch { pb, th, mx, .. } = sc;
20542            e.gumbel_perturb_filtered_col(
20543                logits_e, 0, pb, n_vocab, samp.seed, ctr, samp.temp, mx, th, 0,
20544            )?;
20545        } else {
20546            e.gumbel_perturb_col(logits_e, 0, &mut sc.pb, n_vocab, samp.seed, ctr, samp.temp)?;
20547        }
20548        e.argmax_token_device_col(&sc.pb, 0, n_vocab, token_d, 0)?;
20549        Ok(true)
20550    }
20551
20552    /// One-per-chunk host readback of the persistent full-logits row (the LAST chain
20553    /// token's row).
20554    pub(crate) fn head_split_logits_dtoh(
20555        &self,
20556        e: &Engine,
20557    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
20558        let guard = HEAD_SPLIT_WS
20559            .lock()
20560            .map_err(|_| "head split lock is poisoned")?;
20561        let ws = guard.as_ref().ok_or("head split logits not armed")?;
20562        let _main = e.gpu.enter_main()?;
20563        Ok(e.dtoh(&ws.logits_e)?)
20564    }
20565
20566    /// Chunk-loop replay (F-lite, MEMRA_STEP_TP_GRAPH_LOOP): run up to `k_target` greedy
20567    /// tokens as back-to-back graph launches chained through the in-graph tail argmax —
20568    /// ONE host sync, ONE history readback, and ONE bulk KV transaction per layer per
20569    /// chunk. Returns None when ineligible (caller falls back to the per-token path).
20570    /// The chunk consumes `token` (already emitted by the caller) as launch 0's input and
20571    /// returns the ids the chain argmax'd (hist[0..k]) plus the LAST launch's logits row —
20572    /// hist[k-1] is exactly argmax(logits), so the caller emits hist[..k-1] and lets its
20573    /// own loop re-derive hist[k-1] from the returned row.
20574    pub fn step35_token_graph_chunk(
20575        &self,
20576        e: &Engine,
20577        token: u32,
20578        k_target: usize,
20579        cache: &mut Cache,
20580    ) -> Result<Option<(Vec<u32>, Vec<f32>)>, Box<dyn std::error::Error>> {
20581        if !self.uses_sliding_gated_moe_program()
20582            || !crate::tp::step_tp_graph_enabled()?
20583            || !crate::tp::step_tp_dcw_enabled()?
20584            || !crate::tp::step_tp_qkv_fused_enabled()?
20585            || !crate::tp::step_tp_dev_router_enabled()?
20586            || !crate::tp::step_nvfp4_dev_routes_enabled()?
20587        {
20588            return Ok(None);
20589        }
20590        let n_layers = self.layers.len();
20591        let pos = cache.pos;
20592        let staged_next = pos + 1;
20593        if staged_next < 96 {
20594            return Ok(None);
20595        }
20596        // Bucket for the FIRST token; the chunk must not cross the bucket boundary (the
20597        // exec's n_splits ladder must match eager per depth).
20598        let (fa_vec, n_splits) = e.fa_geom_eager(staged_next, 128, 8, false);
20599        if !fa_vec {
20600            return Ok(None);
20601        }
20602        let sp = crate::fa_split_keys(staged_next, 8);
20603        let bucket_max = (n_splits * sp).max(staged_next);
20604        let to_boundary = bucket_max.saturating_sub(staged_next) + 1;
20605        let mut k = k_target.min(to_boundary).min(16);
20606        if k < 2 {
20607            return Ok(None);
20608        }
20609        // Every layer must contiguous-append all k rows (no rebase inside the chunk).
20610        for il in 0..n_layers {
20611            let Some(tp_kv) = cache.tp_kv[il].as_ref() else {
20612                return Ok(None);
20613            };
20614            while k >= 2 && tp_kv.peek_append_ring(k)?.1 {
20615                k -= 1;
20616            }
20617            if k < 2 {
20618                return Ok(None);
20619            }
20620        }
20621
20622        let mut state_guard = self
20623            .step35_token_graph
20624            .lock()
20625            .map_err(|_| "step35 token graph lock is poisoned")?;
20626        let Some(state) = state_guard.as_mut() else {
20627            return Ok(None); // per-token path arms the state + stages first
20628        };
20629        if state.graphs.is_empty() {
20630            return Ok(None);
20631        }
20632        {
20633            let (b, g) = state.graphs.first_mut().expect("checked above");
20634            if *b != bucket_max {
20635                g.retarget_bucket(bucket_max)?;
20636                *b = bucket_max;
20637            }
20638        }
20639        let graph = state.graphs.first().map(|(_, g)| g).expect("checked above");
20640
20641        // Rank-stream fence (eager stragglers; see the per-token path).
20642        {
20643            let fa0 = match &self.layers[0].mixer {
20644                Mixer::Full(fa) => fa,
20645                _ => return Err("step35 token graph expects full-attention layers".into()),
20646            };
20647            let tp0 = fa0
20648                .step_tp_qkv
20649                .as_ref()
20650                .ok_or("step35 token graph lost its TP state")?;
20651            for rank in 0..tp0.runtime.devices().len() {
20652                let engine = tp0
20653                    .runtime
20654                    .rank_engine(rank)
20655                    .ok_or("step35 token graph lost a rank engine")?;
20656                let _main = engine.gpu.enter_main()?;
20657                engine.stream().synchronize()?;
20658            }
20659        }
20660
20661        // Seed the chain and fire k launches back-to-back: launch i embeds the token the
20662        // PREVIOUS launch's tail argmax wrote (launch 0 embeds the host-seeded `token`).
20663        {
20664            let _main = e.gpu.enter_main()?;
20665            e.set_u32_one(&mut state.token_d, token)?;
20666            e.set_i32_one(&mut state.pos_d, pos as i32)?;
20667            e.set_i32_one(&mut state.hist_idx, 0)?;
20668        }
20669        for _ in 0..k {
20670            graph.launch(e)?;
20671        }
20672        // Bulk host bookkeeping overlaps the replays: one k-row txn per layer.
20673        for il in 0..n_layers {
20674            let tp_kv = cache.tp_kv[il].as_mut().expect("eligibility checked above");
20675            let transaction = tp_kv.begin_transaction()?;
20676            let fa = match &self.layers[il].mixer {
20677                Mixer::Full(fa) => fa,
20678                _ => return Err("step35 token graph expects full-attention layers".into()),
20679            };
20680            let tp = fa
20681                .step_tp_qkv
20682                .as_ref()
20683                .ok_or("step35 token graph lost its TP state")?;
20684            let empty: [CudaSlice<f32>; 0] = [];
20685            tp.runtime.append_tp_kv_transaction_inner(
20686                tp_kv,
20687                transaction,
20688                &empty,
20689                &empty,
20690                k,
20691                true,
20692            )?;
20693            tp.runtime
20694                .commit_tp_kv_transaction_external(tp_kv, transaction, k)?;
20695            if let Some(local) = cache.kv[il].as_mut() {
20696                local.len = pos + k;
20697                let _main = e.gpu.enter_main()?;
20698                e.set_i32_one(&mut local.len_d, (pos + k) as i32)?;
20699            }
20700        }
20701        cache.pos = pos + k;
20702        let (hist, logits) = {
20703            let _main = e.gpu.enter_main()?;
20704            e.stream().synchronize()?;
20705            (e.dtoh_u32(&state.token_hist)?, e.dtoh(&state.logits_stage)?)
20706        };
20707        Ok(Some((hist[..k].to_vec(), logits)))
20708    }
20709}
20710
20711impl HybridModel {
20712    /// Capture the whole-token parent for one fa bucket. Capture executes nothing; the
20713    /// section closures issue the SAME calls the eager dcw path runs (bit-proven), with the
20714    /// per-token operands living at the fixed stage addresses. Groups: the two rank sections
20715    /// of each phase fork in parallel and merge into the following root section.
20716    #[allow(clippy::too_many_arguments)]
20717    fn step35_token_graph_build(
20718        &self,
20719        e: &Engine,
20720        cache: &mut Cache,
20721        state: &mut Step35TokenGraphState,
20722        bucket_max: usize,
20723    ) -> Result<(), Box<dyn std::error::Error>> {
20724        use cudarc::driver::DevicePtr;
20725        let n_embd = self.cfg.n_embd as usize;
20726        let eps = self.cfg.rms_eps;
20727        let n_layers = self.layers.len();
20728        let started = std::time::Instant::now();
20729        if !crate::router_kernel_on() {
20730            return Err(
20731                "step35 token graph requires the router kernel (MEMRA_ROUTER_KERNEL=0)".into(),
20732            );
20733        }
20734        if !Engine::bf16_mmv_on() || n_embd % 8 != 0 {
20735            return Err("step35 token graph requires MEMRA_BF16_MMV bf16-resident matvecs".into());
20736        }
20737
20738        // Device embed table (the spec/graph lanes' lazily-uploaded copy).
20739        let embd_gpu = self
20740            .embd_gpu_try(e)
20741            .ok_or("step35 token graph could not upload the device embed table")?;
20742        let embd_qtype = match self.embd.ggml_type {
20743            memra_gguf::GgmlType::BF16 => crate::QT_BF16,
20744            memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
20745            other => return Err(format!("token graph embed dtype {other:?} unhandled").into()),
20746        };
20747        let embd_row_bytes = self.embd.raw.len() / self.cfg.n_vocab as usize;
20748
20749        // Fixed-stage pointers the sections reference.
20750        let (p_mixed, p_kshadow, p_vshadow) = {
20751            let _main = e.gpu.enter_main()?;
20752            let stream = e.stream();
20753            let (a, _g) = state.mixed_stage.device_ptr(&stream);
20754            let (b, _g) = state.k_shadow_stage.device_ptr(&stream);
20755            let (c, _g) = state.v_shadow_stage.device_ptr(&stream);
20756            (a as u64, b as u64, c as u64)
20757        };
20758
20759        crate::tp::token_graph_build_begin()?;
20760        let mut group_id: u32 = 0;
20761        for il in 0..n_layers {
20762            let layer = &self.layers[il];
20763            let fa = match &layer.mixer {
20764                Mixer::Full(fa) => fa,
20765                _ => return Err("step35 token graph expects full-attention layers".into()),
20766            };
20767            let tp = fa
20768                .step_tp_qkv
20769                .as_ref()
20770                .ok_or("step35 token graph lost its TP state")?;
20771            let attention = tp
20772                .attention
20773                .as_ref()
20774                .ok_or("step35 token graph lost its attention aux")?;
20775            let geometry = self.step35_geom(il);
20776            let window = geometry.window.map(|w| w as usize);
20777            let head_dim = geometry.head_dim_k as usize;
20778            let heads = geometry.n_head as usize;
20779            let kv_heads = geometry.n_head_kv as usize;
20780            let ranks = tp.runtime.devices().len();
20781            let local_heads = heads / ranks;
20782            let local_kv_heads = kv_heads / ranks;
20783            let layer_bucket = window.map(|w| bucket_max.min(w)).unwrap_or(bucket_max);
20784            let use_gate_shards =
20785                attention.gate_shards.is_some() || attention.gate_shards_bf16.is_some();
20786            if !use_gate_shards {
20787                return Err("step35 token graph requires the fused gate shards".into());
20788            }
20789
20790            let ws_index = tp
20791                .runtime
20792                .decode_v2_ensure(e, &tp.q, &tp.k, &tp.v, &tp.o, heads)?;
20793            let ws_mutex = tp.runtime.decode_v2_workspace();
20794            let mut ws_guard = ws_mutex
20795                .lock()
20796                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
20797            let ws = ws_guard
20798                .get_mut(ws_index)
20799                .ok_or("step TP decode v2 workspace missing after ensure")?;
20800            tp.runtime
20801                .decode_v2_arm_token_mirrors(ws, p_mixed, (p_kshadow, p_vshadow))?;
20802            let mut rope_freqs = Vec::with_capacity(ranks);
20803            for rank in 0..ranks {
20804                let engine = tp
20805                    .runtime
20806                    .rank_engine(rank)
20807                    .ok_or("step35 token graph lost a rank engine")?;
20808                rope_freqs.push(if geometry.rope_factors {
20809                    self.step35_aux
20810                        .as_ref()
20811                        .and_then(|aux| aux.rope_freqs(engine))
20812                } else {
20813                    None
20814                });
20815            }
20816            let gate_shards_arg = if let Some(shards) = attention.gate_shards.as_deref() {
20817                Some(crate::tp::StepTpGateShards::F32(shards))
20818            } else {
20819                attention
20820                    .gate_shards_bf16
20821                    .as_deref()
20822                    .map(crate::tp::StepTpGateShards::Bf16)
20823            };
20824
20825            // ---- E1: embed (layer 0) / attn norm into h_stage + pos copy ----
20826            let decode_input = attention
20827                .decode_input
20828                .as_ref()
20829                .ok_or("step35 token graph requires the replicated decode input")?;
20830            let mut decode_input = decode_input
20831                .lock()
20832                .map_err(|_| "replicated decode input lock is poisoned")?;
20833            // Stage arming happens through the eager stage flow once; require it here.
20834            if ws.h_stage.is_none() {
20835                return Err(
20836                    "step35 token graph requires the stage flow armed (run eager dcw first)".into(),
20837                );
20838            }
20839            {
20840                let state_x = &mut state.x;
20841                let token_d = &state.token_d;
20842                let pos_d = &state.pos_d;
20843                crate::tp::graph_section(e, None, || {
20844                    let _main = e.gpu.enter_main()?;
20845                    if il == 0 {
20846                        e.embed_gather_device_into(
20847                            embd_gpu,
20848                            token_d,
20849                            state_x,
20850                            n_embd,
20851                            embd_qtype,
20852                            embd_row_bytes,
20853                        )?;
20854                    }
20855                    {
20856                        let h_stage = ws.h_stage.as_mut().expect("stage armed checked above");
20857                        e.rms_norm(
20858                            state_x,
20859                            layer.attn_norm.float_data(),
20860                            h_stage,
20861                            n_embd,
20862                            1,
20863                            eps,
20864                        )?;
20865                    }
20866                    {
20867                        let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
20868                        let mut dst = pos_stage.slice_mut(0..1);
20869                        e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
20870                    }
20871                    Ok(())
20872                })?;
20873            }
20874
20875            // ---- R0/R1 (parallel): projections + dcw attention interior ----
20876            group_id += 1;
20877            for rank in 0..ranks {
20878                let engine = tp
20879                    .runtime
20880                    .rank_engine(rank)
20881                    .ok_or("step35 token graph lost a rank engine")?;
20882                {
20883                    // fa partial pool must reach the RUN CEILING before capture — an
20884                    // in-capture grow is a mem node (child graphs reject those), and the
20885                    // retarget path (increment C) widens the baked memsets up to the ceiling
20886                    // without moving the pool pointers. Two ensures cover both sp rungs.
20887                    let ceiling = window
20888                        .map(|w| cache.max_ctx.min(w))
20889                        .unwrap_or(cache.max_ctx);
20890                    let _main = engine.gpu.enter_main()?;
20891                    engine.fa_dcw_pool_ensure(
20892                        head_dim,
20893                        local_heads,
20894                        local_kv_heads,
20895                        ceiling.min(2048),
20896                    )?;
20897                    engine.fa_dcw_pool_ensure(head_dim, local_heads, local_kv_heads, ceiling)?;
20898                    engine.fa_dcw_pool_ensure(
20899                        head_dim,
20900                        local_heads,
20901                        local_kv_heads,
20902                        layer_bucket,
20903                    )?;
20904                }
20905                let runtime = &tp.runtime;
20906                let q_norm = &attention.q_norm;
20907                let k_norm = &attention.k_norm;
20908                let gate_ref = gate_shards_arg.as_ref();
20909                crate::tp::graph_section(engine, Some(group_id), || {
20910                    runtime.decode_v2_input_qkv_rank(
20911                        ws,
20912                        &state.pos_d,
20913                        &mut decode_input,
20914                        &tp.q,
20915                        &tp.k,
20916                        &tp.v,
20917                        q_norm,
20918                        k_norm,
20919                        head_dim,
20920                        geometry.n_rot as usize,
20921                        geometry.rope_base,
20922                        &rope_freqs,
20923                        eps,
20924                        gate_ref,
20925                        true,
20926                        false,
20927                        rank,
20928                        None,
20929                    )?;
20930                    // Merged dcw interior at the BUCKET geometry (one-partition law makes the
20931                    // replayed values track the live counters).
20932                    let distributed = cache.tp_kv[il]
20933                        .as_mut()
20934                        .ok_or("step35 token graph lost a TP cache")?;
20935                    let (kv_dim_k, kv_dim_v) = (distributed.kv_dim_k(), distributed.kv_dim_v());
20936                    let (ktb, vtb) = (distributed.k_tok_bytes(), distributed.v_tok_bytes());
20937                    let capacity = distributed.physical_capacity();
20938                    {
20939                        let rank_cache = distributed
20940                            .rank_mut(rank)
20941                            .ok_or("step35 token graph lost a rank cache")?;
20942                        let (k_plane, v_plane, len_d, base_d) =
20943                            rank_cache.planes_and_counters_mut();
20944                        engine.append_kv_quantized_dcw(
20945                            &ws.k[rank],
20946                            &ws.v_raw[rank],
20947                            k_plane,
20948                            v_plane,
20949                            len_d,
20950                            base_d,
20951                            kv_dim_k,
20952                            kv_dim_v,
20953                            ktb,
20954                            vtb,
20955                        )?;
20956                    }
20957                    {
20958                        let rank_cache = distributed
20959                            .rank_mut(rank)
20960                            .ok_or("step35 token graph lost a rank cache")?;
20961                        engine.inc_i32(rank_cache.len_d_mut())?;
20962                    }
20963                    let rank_cache = distributed
20964                        .rank(rank)
20965                        .ok_or("step35 token graph lost a rank cache")?;
20966                    let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * ktb);
20967                    let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * vtb);
20968                    // Graph build keeps the UNFUSED combine + gate pair: the bucket
20969                    // retarget addresses combine's nsp at arg slot 6, and the fused
20970                    // combine_gate kernel shifts it to 7 — bit-identical either way, so
20971                    // only the eager arm takes FUSION #2d.
20972                    engine.fa_decode_dcw(
20973                        &ws.q[rank],
20974                        &k_ring,
20975                        &v_ring,
20976                        &mut ws.attn_out[rank],
20977                        head_dim,
20978                        local_heads,
20979                        local_kv_heads,
20980                        rank_cache.len_d(),
20981                        rank_cache.base_d(),
20982                        window.unwrap_or(0),
20983                        layer_bucket,
20984                        geometry.attention_scale(),
20985                        ktb,
20986                        vtb,
20987                        None,
20988                    )?;
20989                    engine.attn_head_gate(
20990                        &ws.attn_out[rank],
20991                        &ws.gate[rank],
20992                        &mut ws.gated[rank],
20993                        None,
20994                        head_dim,
20995                        local_heads,
20996                        1,
20997                    )?;
20998                    runtime.decode_v2_finish_rank_partial(ws, &tp.o, true, rank)?;
20999                    Ok(())
21000                })?;
21001            }
21002
21003            // ---- ROOT: combine + shadows + e-mirrors ----
21004            {
21005                let root = tp
21006                    .runtime
21007                    .rank_engine(0)
21008                    .ok_or("step35 token graph lost the root engine")?;
21009                let runtime = &tp.runtime;
21010                crate::tp::graph_section(root, None, || runtime.decode_v2_finish_root_fused(ws))?;
21011            }
21012            drop(ws_guard);
21013            drop(decode_input);
21014
21015            let probe_layer: Option<usize> = std::env::var("MEMRA_TG_PROBE_LAYER")
21016                .ok()
21017                .and_then(|v| v.parse().ok());
21018            if probe_layer == Some(il) {
21019                let Step35TokenGraphState {
21020                    mixed_stage,
21021                    probe_mixed,
21022                    ..
21023                } = &mut *state;
21024                crate::tp::graph_section(e, None, || {
21025                    let _main = e.gpu.enter_main()?;
21026                    let mut dst = probe_mixed.slice_mut(0..n_embd);
21027                    e.stream()
21028                        .memcpy_dtod(&mixed_stage.slice(0..n_embd), &mut dst)?;
21029                    Ok(())
21030                })?;
21031            }
21032
21033            // ---- FFN half ----
21034            match &layer.ffn {
21035                crate::hybrid::Ffn::Dense {
21036                    ffn_gate,
21037                    ffn_up,
21038                    ffn_down,
21039                } => {
21040                    let n_ff = ffn_gate.out_features();
21041                    let lim = self.cfg.clamp_shexp_at(il as u32);
21042                    // Alloc-free inline of ffn_swiglu_decode's bf16 tail: dual gate/up matvec is
21043                    // bit-identical per row to the two matmul-dispatched matvec_bf16 launches.
21044                    // A clamped dense layer would take eager's q8_1 branch instead -- refuse.
21045                    if lim.is_some() {
21046                        return Err("step35 token graph dense FFN with clamp unsupported".into());
21047                    }
21048                    let (wg_d, wu_d, wd_d) = match (ffn_gate, ffn_up, ffn_down) {
21049                        (
21050                            crate::model::GpuTensor::FloatBf16 { data: wg, .. },
21051                            crate::model::GpuTensor::FloatBf16 { data: wu, .. },
21052                            crate::model::GpuTensor::FloatBf16 { data: wd, .. },
21053                        ) => (wg, wu, wd),
21054                        _ => {
21055                            return Err(
21056                                "step35 token graph dense FFN requires bf16-resident weights"
21057                                    .into(),
21058                            );
21059                        }
21060                    };
21061                    crate::tp::graph_section(e, None, || {
21062                        let _main = e.gpu.enter_main()?;
21063                        let Step35TokenGraphState {
21064                            x,
21065                            x1,
21066                            mixed_stage,
21067                            dense_z,
21068                            dense_gate,
21069                            dense_up,
21070                            dense_act,
21071                            sh_stage,
21072                            ..
21073                        } = &mut *state;
21074                        e.add_rms_norm(
21075                            x,
21076                            mixed_stage,
21077                            layer.post_attn_norm.float_data(),
21078                            x1,
21079                            dense_z,
21080                            n_embd,
21081                            1,
21082                            eps,
21083                        )?;
21084                        // TWO SINGLE matvecs, not the dual: eager dense rides two
21085                        // matmul-dispatched matvec_bf16 launches; the dual twin measured a
21086                        // ~2e-9 residual difference here (token-graph bisection, 2026-08-21).
21087                        e.matvec_bf16_into(wg_d, dense_z, dense_gate, n_embd, n_ff)?;
21088                        e.matvec_bf16_into(wu_d, dense_z, dense_up, n_embd, n_ff)?;
21089                        Self::ffn_act_lim(
21090                            e, &self.cfg, dense_gate, dense_up, 1.0, 1.0, lim, dense_act, n_ff,
21091                        )?;
21092                        e.matvec_bf16_into(wd_d, dense_act, sh_stage, n_ff, n_embd)?;
21093                        e.add(x1, sh_stage, x, n_embd)?;
21094                        Ok(())
21095                    })?;
21096                }
21097                crate::hybrid::Ffn::Moe(m) => {
21098                    let moe = self
21099                        .cfg
21100                        .moe
21101                        .as_ref()
21102                        .ok_or("step35 token graph needs moe cfg")?;
21103                    let n_expert = moe.expert_count as usize;
21104                    let n_used = moe.expert_used_count as usize;
21105                    let sigmoid = self
21106                        .cfg
21107                        .sigmoid_router()
21108                        .ok_or("step35 token graph needs the sigmoid router")?;
21109                    let step_tp = m
21110                        .step_tp
21111                        .as_ref()
21112                        .ok_or("step35 token graph needs TP experts")?;
21113                    let bank = match &step_tp.experts {
21114                        crate::hybrid::StepTpExpertBank::Nvfp4(bank) => bank,
21115                        _ => return Err("step35 token graph needs the NVFP4 bank".into()),
21116                    };
21117                    let routes_ws_mutex = bank.device_workspace_handle();
21118                    let mut routes_guard = routes_ws_mutex
21119                        .lock()
21120                        .map_err(|_| "routes workspace lock is poisoned")?;
21121                    let routes_ws = routes_guard
21122                        .as_mut()
21123                        .ok_or("step35 token graph requires the routes workspace warmed")?;
21124                    routes_ws.arm_stages(e, bank.input_width, n_used)?;
21125                    step_tp.runtime.routes_arm_raw(bank, routes_ws)?;
21126                    let p_z = {
21127                        let root = step_tp
21128                            .runtime
21129                            .rank_engine(0)
21130                            .ok_or("routes root engine missing")?;
21131                        let _main = root.gpu.enter_main()?;
21132                        let stream = root.stream();
21133                        let in_stage = routes_ws
21134                            .in_stage_handle()
21135                            .ok_or("routes in stage not armed")?;
21136                        let (a, _g) = in_stage.device_ptr(&stream);
21137                        a as u64
21138                    };
21139                    let local_out = bank.expert_width / ranks;
21140
21141                    // ---- E2: post-attn norm into the routes in-stage + router + staging ----
21142                    crate::tp::graph_section(e, None, || {
21143                        let _main = e.gpu.enter_main()?;
21144                        {
21145                            let in_stage = routes_ws
21146                                .in_stage_mut()
21147                                .ok_or("routes in stage not armed")?;
21148                            let Step35TokenGraphState {
21149                                x, x1, mixed_stage, ..
21150                            } = &mut *state;
21151                            e.add_rms_norm(
21152                                x,
21153                                mixed_stage,
21154                                layer.post_attn_norm.float_data(),
21155                                x1,
21156                                in_stage,
21157                                n_embd,
21158                                1,
21159                                eps,
21160                            )?;
21161                        }
21162                        {
21163                            let z_ref = routes_ws
21164                                .in_stage_handle()
21165                                .ok_or("routes in stage not armed")?;
21166                            e.router_gemv_into(
21167                                m.gate_inp.float_data(),
21168                                z_ref,
21169                                &mut state.router_logits,
21170                                n_embd,
21171                                n_expert,
21172                                1,
21173                            )?;
21174                        }
21175                        let (sel_e, w_e) = routes_ws
21176                            .dev_route_e_mut()
21177                            .ok_or("routes staging not armed")?;
21178                        e.moe_router_sigmoid_topk_into(
21179                            &state.router_logits,
21180                            1,
21181                            n_expert,
21182                            n_used,
21183                            m.active_count(),
21184                            &m.exp_probs_b_dev,
21185                            &m.active_experts_dev,
21186                            sigmoid.0,
21187                            sigmoid.1,
21188                            sel_e,
21189                            w_e,
21190                        )?;
21191                        Ok(())
21192                    })?;
21193
21194                    // ---- R0r/R1r (parallel): routes sweeps ----
21195                    group_id += 1;
21196                    for rank in 0..ranks {
21197                        let engine = step_tp
21198                            .runtime
21199                            .rank_engine(rank)
21200                            .ok_or("routes rank engine missing")?;
21201                        let runtime = &step_tp.runtime;
21202                        crate::tp::graph_section(engine, Some(group_id), || {
21203                            runtime.routes_rank_section(
21204                                bank,
21205                                routes_ws,
21206                                p_z,
21207                                local_out,
21208                                n_used,
21209                                step_tp.activation_limit,
21210                                rank,
21211                            )
21212                        })?;
21213                    }
21214
21215                    // ---- ROOTr: combine into the out stage ----
21216                    {
21217                        let root = step_tp
21218                            .runtime
21219                            .rank_engine(0)
21220                            .ok_or("routes root engine missing")?;
21221                        let runtime = &step_tp.runtime;
21222                        crate::tp::graph_section(root, None, || {
21223                            runtime.routes_root_section(bank, routes_ws)
21224                        })?;
21225                    }
21226
21227                    // ---- E3: shexp + add_shared onto the out stage + residual ----
21228                    // Alloc-free inline of moe_ffn_grouped_add_shared's bf16_dual arm (the arm
21229                    // eager takes under MEMRA_BF16_MMV, guarded at fn entry).
21230                    let lim_sh = self.cfg.clamp_shexp_at(il as u32);
21231                    let (wg_sh, wu_sh, wd_sh) = match (&m.gate_shexp, &m.up_shexp, &m.down_shexp) {
21232                        (
21233                            Some(crate::model::GpuTensor::FloatBf16 { data: wg, .. }),
21234                            Some(crate::model::GpuTensor::FloatBf16 { data: wu, .. }),
21235                            Some(crate::model::GpuTensor::FloatBf16 { data: wd, .. }),
21236                        ) => (wg, wu, wd),
21237                        _ => {
21238                            return Err(
21239                                "step35 token graph shexp requires bf16-resident weights".into()
21240                            );
21241                        }
21242                    };
21243                    let n_ff_sh = m
21244                        .gate_shexp
21245                        .as_ref()
21246                        .expect("matched Some above")
21247                        .out_features();
21248                    // No sigmoid gate on this model's shexp -> gate_sig stays at its 1.0
21249                    // init, reproducing eager's ones vector without a launch.
21250                    let gate_inp_shexp = m.gate_inp_shexp.as_ref();
21251                    crate::tp::graph_section(e, None, || {
21252                        let _main = e.gpu.enter_main()?;
21253                        let (z_ref, out_stage) = routes_ws
21254                            .in_and_out_stages_mut()
21255                            .ok_or("routes stages not armed")?;
21256                        let Step35TokenGraphState {
21257                            x,
21258                            x1,
21259                            sh_stage,
21260                            shexp_gate,
21261                            shexp_up,
21262                            shexp_act,
21263                            gate_sig,
21264                            ..
21265                        } = &mut *state;
21266                        e.matvec_bf16_dual_into(
21267                            wg_sh, wu_sh, z_ref, shexp_gate, shexp_up, n_embd, n_ff_sh,
21268                        )?;
21269                        Self::ffn_act_lim(
21270                            e, &self.cfg, shexp_gate, shexp_up, 1.0, 1.0, lim_sh, shexp_act,
21271                            n_ff_sh,
21272                        )?;
21273                        e.matvec_bf16_into(wd_sh, shexp_act, sh_stage, n_ff_sh, n_embd)?;
21274                        if let Some(gate_w) = gate_inp_shexp {
21275                            e.sigmoid_dot_rows_into(
21276                                z_ref,
21277                                gate_w.float_data(),
21278                                gate_sig,
21279                                n_embd,
21280                                1,
21281                            )?;
21282                        }
21283                        e.add_scaled_rows(sh_stage, gate_sig, out_stage, n_embd, 1)?;
21284                        e.add(x1, out_stage, x, n_embd)?;
21285                        Ok(())
21286                    })?;
21287                }
21288            }
21289            if probe_layer == Some(il) {
21290                let Step35TokenGraphState { x, probe_x, .. } = &mut *state;
21291                crate::tp::graph_section(e, None, || {
21292                    let _main = e.gpu.enter_main()?;
21293                    let mut dst = probe_x.slice_mut(0..n_embd);
21294                    e.stream().memcpy_dtod(&x.slice(0..n_embd), &mut dst)?;
21295                    Ok(())
21296                })?;
21297            }
21298        }
21299
21300        // ---- Tail: output norm + head into the logits stage ----
21301        let head = match &self.output {
21302            crate::model::GpuTensor::FloatBf16 { data, .. } => data,
21303            _ => return Err("step35 token graph head requires the bf16-resident output".into()),
21304        };
21305        crate::tp::graph_section(e, None, || {
21306            let _main = e.gpu.enter_main()?;
21307            let Step35TokenGraphState {
21308                x,
21309                hn,
21310                logits_stage,
21311                token_d,
21312                pos_d,
21313                token_hist,
21314                hist_idx,
21315                ..
21316            } = &mut *state;
21317            e.rms_norm(x, self.output_norm.float_data(), hn, n_embd, 1, eps)?;
21318            e.matvec_bf16_into(head, hn, logits_stage, n_embd, self.cfg.n_vocab as usize)?;
21319            // Chunk-loop tail: greedy argmax feeds token_d (host-identical tie-break,
21320            // argmax_gate-validated), the id lands in the history ring, and pos advances on
21321            // device — consecutive launches chain with NO host sync. Single-token mode
21322            // overwrites token_d/pos_d from the host before each launch, so these nodes are
21323            // harmless there.
21324            e.argmax_token_device_into(logits_stage, token_d, self.cfg.n_vocab as usize)?;
21325            e.u32_hist_append(token_d, token_hist, hist_idx)?;
21326            e.inc_i32(pos_d)?;
21327            Ok(())
21328        })?;
21329
21330        let graph = crate::tp::token_graph_build_finish()?;
21331        state.graphs.push((bucket_max, graph));
21332        eprintln!(
21333            "[step35-token-graph] built bucket={bucket_max} layers={n_layers} \
21334             build_ms={:.0} performance_claim=false",
21335            started.elapsed().as_secs_f64() * 1e3
21336        );
21337        Ok(())
21338    }
21339}