Skip to main content

memra_engine/
glm5_tp.rs

1//! glm5_next (GLM-5.3-Flash) TP-N seam — `MEMRA_GLM5_TP` (lane/glm5-tp2, 2026-08-31;
2//! rank-widened to TP-4 by lane/glm5-composition, 2026-09-01).
3//!
4//! WHAT THIS IS. A correctness-first tensor-parallel execution program for the glm5_next
5//! hybrid trunk, per the lane's shard map (`research/glm53-flash-bringup-20260827/
6//! tp2-20260831/SHARD-MAP.md`). Per layer class:
7//!
8//!   * KDA (34 layers): head-sharded, `heads / ranks` per rank. Each rank runs the UNCHANGED
9//!     `kda_core_gated` program on its shard (per-head kernels: conv, L2 norm, gate, scan,
10//!     gated rmsnorm are all head-independent), the gated `[t, qkv/ranks]` parts are
11//!     gathered, and each rank's COLUMN-parallel `wo` slice (out rows over the FULL gathered
12//!     input) computes its slice of the output with the same plain matvec kernel — joins are
13//!     pure data movement, never a partial-sum reduction, which is what makes model-level
14//!     TP-vs-plain BYTE identity the bar instead of a tolerance band.
15//!   * MLA/DSA (11 layers): head-sharded per-head operands (`wq_b`, `wk_b`, `wv_b`);
16//!     REPLICATED per-token shared work (`wq_a`/`q_a_norm`, `wkv_a`/`kv_a_norm`, the whole
17//!     indexer + k-pool selection) — every rank computes identical bytes from identical
18//!     inputs, so the latent + indexer planes are replicated per rank and no per-token
19//!     cross-rank hop exists in the latent chain. `wo` is column-parallel over the gathered
20//!     attention parts, exactly like KDA.
21//!   * MoE (sparse-FFN layers): EP-N, whole experts, contiguous slices (even split: rank =
22//!     expert / (n_expert/ranks)). The router stays root-computed (host sigmoid top-k,
23//!     unchanged); each owner extracts its slots' UNWEIGHTED down rows with the same
24//!     fused-epilogue kernels at n_used=1, and root re-applies the slot-ordered fmaf
25//!     accumulation chain — the same rounded-operation sequence as the plain
26//!     `moe_down8_fma_q8` walk. Shared expert, dense MLPs, router, mHC, norms, embed and
27//!     lm_head stay ROOT-OWNED (the `MEMRA_STEP_TP` owner-stage precedent).
28//!
29//! TRANSPORT is a SEPARATE, SWAPPABLE AXIS (`MEMRA_GLM5_TP_TRANSPORT`,
30//! lane/glm5-tp-transport 2026-09-01). Because every cross-rank hop above is pure movement,
31//! the transport arm cannot change a bit — so this module names the hop SHAPES and
32//! `tp_transport` owns the bytes. `host-canonical` (the default, and what every banked
33//! glm5 TP number was measured on) bounces each hop through host with a full stream drain
34//! per leg; `peer-pull` issues a consumer-side device peer copy per hop with event ordering
35//! and no host boundary. The join-diet doors are an orthogonal axis (they cut hop COUNT; the
36//! transport cuts hop COST) and compose.
37//!
38//! FAIL-CLOSED SURFACE. The preflight refuses before any TP CUDA state exists: non-glm5
39//! plans, rank counts outside the qualified set (2 and 4 — see [`GLM5_TP_ALLOWED_RANKS`]),
40//! head/expert counts that do not divide, duplicate devices (serving parse), co-armed
41//! `MEMRA_PP_STAGES>1`, `MEMRA_STEP_TP`/`MEMRA_STEP_EP`. A sharded layer POISONS every plain
42//! path: `kda_core`, `mla_attn_cached` and the batched walks refuse a TP-armed layer by
43//! name. The memra-server worker refuses the flag outright (serving wiring is the named
44//! box-lane increment, not v1).
45//!
46//! Engagement markers: `[glm5-tp-preflight]`, `[glm5-tp-kda]`, `[glm5-tp-mla]`,
47//! `[glm5-tp-ep]`, `[glm5-tp-transport]` — every marker carries `performance_claim=false`,
48//! and the first four name the LIVE transport rather than a hardcoded string (the
49//! tp2-battery greps `transport=` on all four seams, and a hardcoded value would have made a
50//! transport A/B unreadable from the boot log).
51
52// lane/clippy-zero-restore-20260901: perf-gated TP2 host code (fresh lane receipts);
53// index loops stay in their gated shape — iterator reshapes are not bit-neutral by inspection.
54#![allow(clippy::needless_range_loop)]
55
56use std::ops::Range;
57use std::sync::Arc;
58use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
59
60use cudarc::driver::CudaSlice;
61
62use crate::Engine;
63use crate::kda::{ConvArm, KdaAttnLayer};
64use crate::model::GpuTensor;
65use memra_kv::{Cache, LatentKvLayer, RecurLayer};
66
67/// The qualified rank envelope: TP-2 (the v1 seam, box-battery-gated) and TP-4.
68///
69/// GEOMETRY (lane/glm5-tp-transport, 2026-09-01). The DSA indexer is REPLICATED per rank by
70/// this seam's own shard map (`shard_mla_layer`'s `replicate_indexer`), so its 32 heads
71/// impose no divisibility constraint at any rank count. TP-4 needs NO padding on the
72/// glm5_next geometry: 64/4 = 16 KDA heads, 64/4 = 16 MLA heads, 288/4 = 72 experts,
73/// 4096/4 = 1024 `wo` out rows, KDA `head_dim` 128 is rank-count independent. TP-3 remains
74/// refused: the only real obstruction is the 64 attention/KDA heads (a HEAD-PADDING
75/// question, 64 -> 66, RESEARCH.md §1.5d — not built). See `tp-transport-20260901/LANE.md`
76/// "TP-4 divisibility".
77pub const GLM5_TP_ALLOWED_RANKS: [usize; 2] = [2, 4];
78
79/// This family's receipt marker for the GENERAL transport seam (`tp_transport`, generalized
80/// lane/glm5-extract2). The tag is the caller's so lane/glm5-tp-transport's and
81/// lane/glm5-composition's banked gate and box receipts keep their exact bytes while a second
82/// family gets its own marker — the same rule phase 1 set for `[glm5-phase]` on the shared
83/// spec timers.
84pub const GLM5_TP_TRANSPORT_TAG: &str = "glm5-tp-transport";
85
86pub type Glm5TpLayerSpec = crate::tp::StepEpLayerSpec;
87
88// ------------------------------------------------------------------------------------------
89// Flag
90// ------------------------------------------------------------------------------------------
91
92/// Raw `MEMRA_GLM5_TP` value. Empty / unset / `"0"` = seam off.
93pub fn glm5_tp_env_raw() -> Option<String> {
94    std::env::var("MEMRA_GLM5_TP").ok()
95}
96
97/// Cheap armed check for co-refusal sites (server boot, spec doors). Parse errors count as
98/// ARMED so a misspelled spec still refuses the co-armed program instead of racing the
99/// loader's own refusal.
100pub fn glm5_tp_armed() -> bool {
101    matches!(glm5_tp_env_raw().as_deref(), Some(v) if !v.is_empty() && v != "0")
102}
103
104/// Parse the shared `LAYER[-LAYER]@DEVICE,DEVICE[,...][;...]` grammar for the glm5 door.
105/// `trunk_layers` is the loaded model's trunk length (the `all` shorthand expands against
106/// it — the model contract owns that number, never a constant in the parser).
107pub fn parse_glm5_tp_layer_specs(
108    value: Option<&str>,
109    trunk_layers: usize,
110) -> Result<Vec<Glm5TpLayerSpec>, String> {
111    crate::tp::parse_layer_specs_for_trunk("MEMRA_GLM5_TP", value, Some(trunk_layers))
112}
113
114/// Gate-harness knob, never a serving flag: `MEMRA_GLM5_TP_GATE_SAME_DEV=1` builds every
115/// peer rank as an ADDITIONAL CUDA CONTEXT ON THE ROOT DEVICE (the one-card rig gate's
116/// emulation; the ppN same-device-stages precedent). The spec's non-root device ids become
117/// logical rank ids. The serving worker refuses `MEMRA_GLM5_TP` outright, so this can never
118/// leak into serving.
119pub fn gate_same_device() -> bool {
120    std::env::var("MEMRA_GLM5_TP_GATE_SAME_DEV").as_deref() == Ok("1")
121}
122
123/// Gate-harness RED-arm knob, never a serving flag (`MEMRA_GLM5_TP_GATE_RED`):
124///   * `swap-wo` — each rank's column `wo` slice takes the NEXT rank's out rows (a broken
125///     shard map); the gate run MUST diverge from plain.
126///   * `swap-ep-gateup` — the root EP slab's gate and up projections swap (wrong expert
127///     weights); MUST diverge.
128///   * `skip-peer-combine` — the EP combine drops every peer-owned slot; MUST diverge,
129///     which is also the non-vacuity proof that the peer ranks contribute real work.
130///   * `corrupt-ep-map` — the placement's local-slot table for rank 0 is reversed after
131///     the slabs are built (owner table and slab bytes disagree — a corrupted map row);
132///     MUST diverge. This is the red that proves the MEASURED-placement indirection is
133///     load-bearing, not decorative.
134///
135/// Unknown values refuse at load.
136#[derive(Clone, Copy, PartialEq, Eq, Debug)]
137pub enum GateRed {
138    SwapWo,
139    SwapEpGateUp,
140    SkipPeerCombine,
141    CorruptEpMap,
142}
143
144pub fn gate_red() -> Result<Option<GateRed>, String> {
145    match std::env::var("MEMRA_GLM5_TP_GATE_RED").ok().as_deref() {
146        None | Some("") => Ok(None),
147        Some("swap-wo") => Ok(Some(GateRed::SwapWo)),
148        Some("swap-ep-gateup") => Ok(Some(GateRed::SwapEpGateUp)),
149        Some("skip-peer-combine") => Ok(Some(GateRed::SkipPeerCombine)),
150        Some("corrupt-ep-map") => Ok(Some(GateRed::CorruptEpMap)),
151        Some(other) => Err(format!(
152            "MEMRA_GLM5_TP_GATE_RED={other:?} is not a known red arm \
153             (swap-wo | swap-ep-gateup | skip-peer-combine | corrupt-ep-map)"
154        )),
155    }
156}
157
158// ------------------------------------------------------------------------------------------
159// Runtime
160// ------------------------------------------------------------------------------------------
161
162/// The TP-N rank runtime. Rank 0 (root) executes on the model's own engine — the PP-owner
163/// context, exactly like the step seam's owner-first rank law. Ranks `1..ranks` each own a
164/// full peer Engine, in `MEMRA_GLM5_TP` device order (`peers[i]` = rank `i + 1`).
165pub struct Glm5TpRt {
166    pub peers: Vec<Engine>,
167    pub root_dev: usize,
168    pub peer_devs: Vec<usize>,
169    /// True only when built through [`Glm5TpRt::new_gate_same_device`] — the one-card rig
170    /// gate's multi-context emulation (the ppN same-device gate precedent). The env-driven
171    /// serving parse can never reach this: the grammar refuses duplicate devices.
172    pub same_device_gate: bool,
173    /// Which transport every cross-rank hop of this runtime moves its bytes with
174    /// (`MEMRA_GLM5_TP_TRANSPORT`, default `host-canonical`). Frozen at
175    /// [`Glm5TpRt::arm_transport`] time, announced once, and named in every gate log.
176    pub transport: crate::tp_transport::TpTransport,
177    /// The peer-pull ordering primitives — `Some` only on the peer-pull arm, and only after
178    /// its byte-integrity ladder passed.
179    link: Option<crate::tp_transport::PeerPullLink>,
180}
181
182impl Glm5TpRt {
183    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
184        let root_dev = devices[0];
185        let peer_devs: Vec<usize> = devices[1..].to_vec();
186        for &d in &peer_devs {
187            if d == root_dev || peer_devs.iter().filter(|&&x| x == d).count() > 1 {
188                return Err(format!(
189                    "MEMRA_GLM5_TP rank devices must be distinct in serving; got {devices:?} \
190                     (the same-device form exists only for the rig gate binary)"
191                )
192                .into());
193            }
194        }
195        let mut peers = Vec::with_capacity(peer_devs.len());
196        for &d in &peer_devs {
197            peers.push(Engine::new(d)?);
198        }
199        Ok(Self {
200            peers,
201            root_dev,
202            peer_devs,
203            same_device_gate: false,
204            transport: crate::tp_transport::TpTransport::HostCanonical,
205            link: None,
206        })
207    }
208
209    /// Same-device multi-context runtime for the ONE-CARD rig gate (exactness only). Every
210    /// peer rank is an additional CUDA context on the root device: the whole shard/join
211    /// walk — shard loads, replicated compute, gathers, canonical combines — executes
212    /// exactly as on N cards, minus real peer transport (which the pro6000 batteries
213    /// qualify on the box card class separately).
214    pub fn new_gate_same_device(
215        root_dev: usize,
216        ranks: usize,
217    ) -> Result<Self, Box<dyn std::error::Error>> {
218        let mut peers = Vec::with_capacity(ranks - 1);
219        for _ in 1..ranks {
220            peers.push(Engine::new(root_dev)?);
221        }
222        Ok(Self {
223            peers,
224            root_dev,
225            peer_devs: vec![root_dev; ranks - 1],
226            same_device_gate: true,
227            transport: crate::tp_transport::TpTransport::HostCanonical,
228            link: None,
229        })
230    }
231
232    /// Rank count of this runtime (root + peers).
233    pub fn ranks(&self) -> usize {
234        self.peers.len() + 1
235    }
236
237    /// Distinct device ordinals participating in this runtime.
238    pub fn devices(&self) -> Vec<usize> {
239        let mut devs = Vec::with_capacity(1 + self.peer_devs.len());
240        devs.push(self.root_dev);
241        for &d in &self.peer_devs {
242            if !devs.contains(&d) {
243                devs.push(d);
244            }
245        }
246        devs
247    }
248
249    /// Freeze the transport for this runtime: read the flag, grant peer access (real groups
250    /// only), and run the byte-integrity pull ladder over every ordered rank pair. Called
251    /// from the preflight BEFORE any layer is sharded, so a bad fabric refuses the load
252    /// rather than corrupting a shard.
253    pub fn arm_transport(&mut self, root: &Engine) -> Result<(), Box<dyn std::error::Error>> {
254        // The seam is general; the TAG and the flag name are the FAMILY's (lane/glm5-extract2,
255        // the phase-1 caller-owned-tag pattern) — `[glm5-tp-transport]` bytes stay exactly as
256        // lane/glm5-tp-transport and lane/glm5-composition banked them, and `armed_flag` is
257        // whichever of `MEMRA_TP_TRANSPORT` / `MEMRA_GLM5_TP_TRANSPORT` the operator actually
258        // set, so a peer-access or ladder refusal names their flag.
259        let (transport, armed_flag) = crate::tp_transport::transport_env()?;
260        let engines: Vec<&Engine> = std::iter::once(root).chain(self.peers.iter()).collect();
261        let link = crate::tp_transport::arm_transport(
262            transport,
263            armed_flag,
264            GLM5_TP_TRANSPORT_TAG,
265            &engines,
266            self.same_device_gate,
267        )?;
268        self.transport = transport;
269        self.link = link;
270        Ok(())
271    }
272
273    /// Build the per-hop transport handle. Every cross-rank movement in the glm5 TP walk
274    /// goes through one of `tp_transport`'s named hop shapes with this handle, which is
275    /// what makes the arm swap a ONE-PLACE change and the movement census automatic.
276    pub fn hop<'a>(&'a self, root: &'a Engine) -> crate::tp_transport::Hop<'a> {
277        crate::tp_transport::Hop {
278            engines: std::iter::once(root).chain(self.peers.iter()).collect(),
279            transport: self.transport,
280            link: self.link.as_ref(),
281        }
282    }
283}
284
285// ------------------------------------------------------------------------------------------
286// Preflight
287// ------------------------------------------------------------------------------------------
288
289/// What the loader tells the preflight about the model, extracted from the plan/config
290/// BEFORE any TP CUDA state exists. Structural laws are dimension-derived (they hold for
291/// the mini fixture and the real artifact alike): the laws ARE the geometry checks.
292pub struct Glm5TpModelView {
293    pub trunk_layers: usize,
294    /// Per-layer mixer class, `trunk_layers` entries.
295    pub layer_class: Vec<Glm5LayerClass>,
296    /// Per-layer "has routed-expert FFN" flag (dense-prefix layers are false).
297    pub layer_is_moe: Vec<bool>,
298    pub kda_heads: usize,
299    pub kda_head_dim: usize,
300    pub mla_heads: usize,
301    pub n_routed_experts: usize,
302    pub top_k: usize,
303}
304
305#[derive(Clone, Copy, PartialEq, Eq, Debug)]
306pub enum Glm5LayerClass {
307    Kda,
308    Mla,
309}
310
311/// The armed load plan: the runtime plus the layer set the spec selected, plus the
312/// measured expert-placement map when `MEMRA_EP_MAP` (or its glm5 alias) is armed (validated at
313/// preflight, before any TP CUDA state — absent flag = the even split, byte-unchanged).
314pub struct Glm5TpLoadPlan {
315    pub rt: Arc<Glm5TpRt>,
316    pub layers: std::collections::BTreeSet<usize>,
317    pub ep_map: Option<crate::ep_map::EpMap>,
318}
319
320/// Load + validate the placement map against the model view and the armed layer set.
321/// The env seam is the general `ep_map::ep_map_env()` (`MEMRA_EP_MAP`, glm5 alias
322/// honored); every refusal names the flag that ARMED the load. `Some("")` REFUSES (a
323/// set-but-empty flag is an operator error, never a silent even split). Fail-closed on
324/// every axis: unreadable file, malformed text, rank/expert-count mismatch, layer-cover
325/// mismatch. Returns `None` only when both names are UNSET.
326fn load_glm5_ep_map(
327    view: &Glm5TpModelView,
328    layers: &std::collections::BTreeSet<usize>,
329    ranks: usize,
330) -> Result<Option<crate::ep_map::EpMap>, Box<dyn std::error::Error>> {
331    let Some((flag, path)) = crate::ep_map::ep_map_env()? else {
332        return Ok(None);
333    };
334    if path.is_empty() {
335        return Err(format!(
336            "{flag} is set but empty (fail-closed: unset the flag for \
337                    the even split; an empty value never silently means default)"
338        )
339        .into());
340    }
341    let text = std::fs::read_to_string(&path)
342        .map_err(|e| format!("{flag}={path}: cannot read the map file ({e}) — refused by name"))?;
343    let map = crate::ep_map::EpMap::parse(&text).map_err(|e| format!("{flag}={path}: {e}"))?;
344    if map.ranks != ranks {
345        return Err(format!(
346            "{flag}={path}: map declares ranks={}, this load is TP-{ranks} \
347             (re-mint the map for the armed rank count)",
348            map.ranks
349        )
350        .into());
351    }
352    if map.n_experts != view.n_routed_experts {
353        return Err(format!(
354            "{flag}={path}: map declares expert_count={}, the model routes {}",
355            map.n_experts, view.n_routed_experts
356        )
357        .into());
358    }
359    if map.entry_rank != 0 {
360        return Err(format!(
361            "{flag}={path}: entry_rank={} but the glm5 TP first-hop card is \
362             rank 0 (root: router + combine + shared expert) — re-mint with \
363             --entry-rank 0 (refused rather than silently remapping ranks)",
364            map.entry_rank
365        )
366        .into());
367    }
368    let ep_layers: Vec<usize> = layers
369        .iter()
370        .copied()
371        .filter(|&il| view.layer_is_moe[il])
372        .collect();
373    map.validate_layer_cover(&ep_layers)
374        .map_err(|e| format!("{flag}={path}: {e}"))?;
375    // Receipt anchor: the map bytes that armed this load, named by digest.
376    let digest = {
377        use sha2::{Digest, Sha256};
378        let mut h = Sha256::new();
379        h.update(text.as_bytes());
380        let out = h.finalize();
381        out.iter().map(|b| format!("{b:02x}")).collect::<String>()
382    };
383    eprintln!(
384        "[glm5-tp-preflight] ep-map armed path={path} sha256={digest} layers={} \
385         experts={} ranks={} entry_rank={} performance_claim=false",
386        map.layers.len(),
387        map.n_experts,
388        map.ranks,
389        map.entry_rank,
390    );
391    Ok(Some(map))
392}
393
394/// Decode-diet doors that never co-arm with `MEMRA_GLM5_TP` in v1 (merge-forward
395/// 2026-08-31): every TP-x-door pair is UNPROVEN. The TP byte/band gates ran with every
396/// door cold, and each door's own gate ran on the unsharded walk, so v1 refuses by name
397/// rather than silently picking an arm; a pair unlocks only with its own composition gate
398/// (the `MEMRA_GLM5_TP` row in docs/FLAGS.md carries the matrix). `MEMRA_GLM5_VERIFY_BATCH`
399/// is absent DELIBERATELY: its walk exists only inside glm5 spec sessions — co-refused on
400/// a sharded model unless `MEMRA_GLM5_SPEC_TP=1` arms the GATED composition
401/// (lane/glm5-composition; the spec x TP pair HAS its composition gate, `glm5-tp-gate`
402/// arms S2/Q-S4), whose admission REQUIRES the batched walk by name.
403pub const GLM5_TP_REFUSED_DOOR_FLAGS: [(&str, &str); 4] = [
404    (
405        "MEMRA_HC_FUSED_PRE",
406        "the fused mHC pre-chain is gated on the unsharded walk only",
407    ),
408    (
409        "MEMRA_HC_DECODE_WS",
410        "the workspace decode walk carries no TP mixer branches",
411    ),
412    (
413        "MEMRA_KDA_FUSED_PROJ",
414        "the fused six-projection door (either operand arm) is gated on full-width \
415         projections, never head shards",
416    ),
417    (
418        "MEMRA_MLA_DECODE_SPLIT",
419        "the absorb/decompress split is gated on the full-head geometry",
420    ),
421];
422
423/// The pure composition law over [`GLM5_TP_REFUSED_DOOR_FLAGS`]: the first armed door
424/// refuses by name, before any TP CUDA state exists. Delegates to the general
425/// [`crate::tp::refuse_door_composition`] pattern (lane/glm5-extract-general) with this
426/// door's own table — error bytes unchanged. `armed` reports whether a flag is set to
427/// `"1"` (env in production; a plain set in the unit test — the module keeps its tests
428/// env-mutation-free).
429pub fn refuse_glm5_tp_door_composition(armed: impl Fn(&str) -> bool) -> Result<(), String> {
430    crate::tp::refuse_door_composition("MEMRA_GLM5_TP", &GLM5_TP_REFUSED_DOOR_FLAGS, armed)
431}
432
433/// Fail-closed preflight + runtime construction. Returns `None` when the seam is off.
434/// Every illegal geometry refuses HERE, before any rank engine or shard exists.
435pub fn prepare_glm5_tp_load(
436    e: &Engine,
437    view: &Glm5TpModelView,
438) -> Result<Option<Glm5TpLoadPlan>, Box<dyn std::error::Error>> {
439    let raw = glm5_tp_env_raw();
440    let specs = parse_glm5_tp_layer_specs(raw.as_deref(), view.trunk_layers)?;
441    if specs.is_empty() {
442        return Ok(None);
443    }
444
445    // Co-armed programs refuse by name: two parallel/spec programs on one model never
446    // silently coexist (the MEMRA_DSPARK precedent).
447    if crate::pp::pp_cuts(view.trunk_layers).is_some() {
448        return Err(
449            "MEMRA_GLM5_TP + MEMRA_PP_STAGES>1: the TP x PP composition is unwired and \
450             refuses until its own gate exists (stage 5 of the tp2 lane names it)"
451                .into(),
452        );
453    }
454    if !crate::tp::step_tp_layer_specs()?.is_empty()
455        || !crate::tp::step_ep_layer_specs()?.is_empty()
456    {
457        return Err(
458            "MEMRA_GLM5_TP + MEMRA_STEP_TP/MEMRA_STEP_EP: the step and glm5 parallel \
459             contracts never co-arm"
460                .into(),
461        );
462    }
463    refuse_glm5_tp_door_composition(|flag| std::env::var(flag).as_deref() == Ok("1"))?;
464
465    // One device group across the whole spec (one runtime group), root-first; the rank
466    // count comes from the device list and must be in the qualified envelope.
467    let devices = specs[0].devices.clone();
468    let ranks = devices.len();
469    if !GLM5_TP_ALLOWED_RANKS.contains(&ranks) {
470        return Err(format!(
471            "MEMRA_GLM5_TP names {ranks} devices per layer; the qualified rank envelope is \
472             {GLM5_TP_ALLOWED_RANKS:?} (TP-3 is a head-padding question, not built — see the \
473             module doc)"
474        )
475        .into());
476    }
477
478    // Structural geometry laws, all dimension-derived.
479    if view.layer_class.len() != view.trunk_layers || view.layer_is_moe.len() != view.trunk_layers {
480        return Err(format!(
481            "glm5-tp preflight: layer class map ({}/{}) does not cover the {}-layer trunk",
482            view.layer_class.len(),
483            view.layer_is_moe.len(),
484            view.trunk_layers
485        )
486        .into());
487    }
488    if !view.kda_heads.is_multiple_of(ranks) || view.kda_heads == 0 {
489        return Err(format!(
490            "glm5-tp: {} KDA heads do not shard across {ranks} ranks",
491            view.kda_heads
492        )
493        .into());
494    }
495    if view.kda_head_dim != crate::kda::KDA_HEAD_DIM {
496        return Err(format!(
497            "glm5-tp: KDA head_dim {} is not the {} the scan kernel is instantiated for",
498            view.kda_head_dim,
499            crate::kda::KDA_HEAD_DIM
500        )
501        .into());
502    }
503    if !view.mla_heads.is_multiple_of(ranks) || view.mla_heads == 0 {
504        return Err(format!(
505            "glm5-tp: {} MLA heads do not shard across {ranks} ranks",
506            view.mla_heads
507        )
508        .into());
509    }
510    if !view.n_routed_experts.is_multiple_of(ranks) || view.n_routed_experts == 0 {
511        return Err(format!(
512            "glm5-tp: {} routed experts do not partition across {ranks} ranks",
513            view.n_routed_experts
514        )
515        .into());
516    }
517    if view.top_k > view.n_routed_experts {
518        return Err("glm5-tp: top_k exceeds the routed expert count".into());
519    }
520
521    for s in &specs {
522        if s.devices != devices {
523            return Err(format!(
524                "MEMRA_GLM5_TP carries ONE runtime group: layer {} names devices {:?}, \
525                 the first spec names {:?}",
526                s.layer, s.devices, devices
527            )
528            .into());
529        }
530        if s.layer >= view.trunk_layers {
531            return Err(format!(
532                "MEMRA_GLM5_TP layer {} outside the {}-layer trunk",
533                s.layer, view.trunk_layers
534            )
535            .into());
536        }
537    }
538    let root_dev = e.ctx().ordinal();
539    if devices[0] != root_dev {
540        return Err(format!(
541            "MEMRA_GLM5_TP rank list {:?} must start with the owning device {root_dev} \
542             (the owner-first rank law)",
543            devices
544        )
545        .into());
546    }
547
548    // Validate the gate red-arm spelling at load (fail-closed), and pick the transport.
549    let red = gate_red()?;
550    let same_dev = gate_same_device();
551    if let Some(red) = red {
552        eprintln!("[glm5-tp-preflight] GATE RED ARM armed: {red:?} — outputs MUST diverge");
553    }
554    let mut rt = if same_dev {
555        eprintln!(
556            "[glm5-tp-preflight] GATE same-device emulation: {} peer ranks are additional \
557             contexts on device {root_dev} (spec devices {:?} are logical rank ids)",
558            ranks - 1,
559            &devices[1..],
560        );
561        Glm5TpRt::new_gate_same_device(root_dev, ranks)?
562    } else {
563        Glm5TpRt::new(&devices)?
564    };
565    // Transport arms HERE — after the rank engines exist, BEFORE any layer is sharded. A
566    // peer-pull ladder failure refuses the load with zero TP shards built (lane/glm5-tp-transport).
567    rt.arm_transport(e)?;
568    let rt = Arc::new(rt);
569    let layers: std::collections::BTreeSet<usize> = specs.iter().map(|s| s.layer).collect();
570    let ep_map = load_glm5_ep_map(view, &layers, ranks)?;
571    let (mut kda_n, mut mla_n, mut moe_n) = (0usize, 0usize, 0usize);
572    for &il in &layers {
573        match view.layer_class[il] {
574            Glm5LayerClass::Kda => kda_n += 1,
575            Glm5LayerClass::Mla => mla_n += 1,
576        }
577        if view.layer_is_moe[il] {
578            moe_n += 1;
579        }
580    }
581    eprintln!(
582        "[glm5-tp-preflight] armed ranks={ranks} devices={devices:?} layers={} \
583         kda_shard={kda_n} mla_shard={mla_n} moe_ep={moe_n} kda_heads_per_rank={} \
584         mla_heads_per_rank={} experts_per_rank={} transport={} \
585         weights_loaded=false performance_claim=false",
586        layers.len(),
587        view.kda_heads / ranks,
588        view.mla_heads / ranks,
589        view.n_routed_experts / ranks,
590        rt.transport.name(),
591    );
592    Ok(Some(Glm5TpLoadPlan { rt, layers, ep_map }))
593}
594
595// ------------------------------------------------------------------------------------------
596// Shard mechanics
597// ------------------------------------------------------------------------------------------
598
599fn outer_rows(ne: &[u64]) -> (usize, usize) {
600    // GGML axis order: ne[0] is the fastest (innermost). The shardable axis is the LAST
601    // (outermost) — out rows on a 2D projection, the head axis on a 3D per-head slab.
602    let outer = *ne.last().expect("tensor has at least one axis") as usize;
603    let inner: usize = ne[..ne.len() - 1].iter().map(|&d| d as usize).product();
604    (outer, inner.max(1))
605}
606
607/// Copy `rows` of `t`'s outermost axis onto `dst` (host bounce; load-time only). Mirror
608/// planes (`rp`/`rp4`/`f16`/`fp8`/`blk`) REFUSE by name: v1 shards carry the raw layout —
609/// a pure byte-permutation difference, bit-identical by the mirrors' own contracts.
610fn shard_rows(
611    src_engine: &Engine,
612    dst: &Engine,
613    t: &GpuTensor,
614    rows: Range<usize>,
615) -> Result<GpuTensor, Box<dyn std::error::Error>> {
616    match t {
617        GpuTensor::Float { data, ne } => {
618            let (outer, inner) = outer_rows(ne);
619            if rows.end > outer {
620                return Err(format!("shard rows {rows:?} exceed outer axis {outer}").into());
621            }
622            let host = src_engine.dtoh(data)?;
623            let piece = &host[rows.start * inner..rows.end * inner];
624            let mut ne2 = ne.clone();
625            *ne2.last_mut().unwrap() = (rows.end - rows.start) as u64;
626            Ok(GpuTensor::Float {
627                data: dst.htod(piece)?,
628                ne: ne2,
629            })
630        }
631        GpuTensor::FloatBf16 { data, ne } => {
632            let (outer, inner) = outer_rows(ne);
633            if rows.end > outer {
634                return Err(format!("shard rows {rows:?} exceed outer axis {outer}").into());
635            }
636            let host = src_engine.dtoh_u8(data)?;
637            let piece = &host[rows.start * inner * 2..rows.end * inner * 2];
638            let mut ne2 = ne.clone();
639            *ne2.last_mut().unwrap() = (rows.end - rows.start) as u64;
640            Ok(GpuTensor::FloatBf16 {
641                data: dst.htod_bytes(piece)?,
642                ne: ne2,
643            })
644        }
645        GpuTensor::Quant {
646            bytes,
647            qtype,
648            row_bytes,
649            ne,
650            scale,
651            rp,
652            fp8,
653            rp4,
654            blk,
655            f16,
656            #[cfg(memra_cutlass)]
657            cutlass,
658        } => {
659            if *rp {
660                return Err(
661                    "glm5-tp shard: rp split-plane mirror layout is unwired — load \
662                            the TP-armed tensor with MEMRA_RP=0 (raw layout is bit-identical \
663                            by the mirror's own contract)"
664                        .into(),
665                );
666            }
667            if fp8.is_some() || rp4.is_some() || blk.is_some() || f16.is_some() {
668                return Err(
669                    "glm5-tp shard: a decode/prefill mirror (fp8/rp4/blk/f16) is present on a \
670                     TP-armed tensor — mirrors are unwired for shards in v1; disable the \
671                     mirror door for this load"
672                        .into(),
673                );
674            }
675            #[cfg(memra_cutlass)]
676            if cutlass.is_some() {
677                return Err("glm5-tp shard: cutlass prefill operand unwired for shards".into());
678            }
679            let (outer, inner) = outer_rows(ne);
680            if ne.len() != 2 {
681                return Err("glm5-tp shard: quantized shards are 2D-only in v1".into());
682            }
683            let _ = inner;
684            if rows.end > outer {
685                return Err(format!("shard rows {rows:?} exceed outer axis {outer}").into());
686            }
687            let host = src_engine.dtoh_u8(bytes)?;
688            let piece = &host[rows.start * row_bytes..rows.end * row_bytes];
689            let mut ne2 = ne.clone();
690            *ne2.last_mut().unwrap() = (rows.end - rows.start) as u64;
691            Ok(GpuTensor::Quant {
692                bytes: dst.htod_bytes(piece)?,
693                qtype: *qtype,
694                row_bytes: *row_bytes,
695                ne: ne2,
696                scale: *scale,
697                rp: false,
698                fp8: None,
699                rp4: None,
700                blk: None,
701                f16: None,
702                #[cfg(memra_cutlass)]
703                cutlass: None,
704            })
705        }
706    }
707}
708
709/// Full replica of `t` on `dst` (host bounce). Same mirror refusals as [`shard_rows`].
710fn replicate(
711    src_engine: &Engine,
712    dst: &Engine,
713    t: &GpuTensor,
714) -> Result<GpuTensor, Box<dyn std::error::Error>> {
715    let (outer, _) = outer_rows(t.ne());
716    shard_rows(src_engine, dst, t, 0..outer)
717}
718
719/// Rank r's engine within a runtime, given the root engine (rank 0 has no owned Engine in
720/// the runtime — it IS the model's engine).
721pub(crate) fn rank_engine<'a>(e: &'a Engine, rt: &'a Glm5TpRt, r: usize) -> &'a Engine {
722    if r == 0 { e } else { &rt.peers[r - 1] }
723}
724
725// ------------------------------------------------------------------------------------------
726// KDA sidecar
727// ------------------------------------------------------------------------------------------
728
729/// The KDA TP sidecar: the peer ranks' head shards plus the runtime handle. The OUTER
730/// `KdaAttnLayer` that carries this in its `tp` field is the root shard; every shard's
731/// `wo` field holds that rank's COLUMN slice (out rows over the full `qkv` input).
732pub struct Glm5TpKda {
733    pub rt: Arc<Glm5TpRt>,
734    /// `peers[i]` is rank `i + 1`'s shard, resident on `rt.peers[i]`.
735    pub peers: Vec<KdaAttnLayer>,
736    /// Full-width qkv of the UNSHARDED layer (`ranks * shard qkv`) — the gather width.
737    pub full_qkv: usize,
738    /// Full hidden width (`wo` out rows across all ranks).
739    pub n_embd: usize,
740}
741
742impl Glm5TpKda {
743    pub fn ranks(&self) -> usize {
744        self.peers.len() + 1
745    }
746}
747
748static KDA_MARKED: AtomicBool = AtomicBool::new(false);
749
750/// Shard one loaded KDA layer: returns the ROOT shard (heads/ranks, wo out-rows
751/// `0..H/ranks`) with the peer shards in its `tp` sidecar. The full layer's tensors are
752/// consumed and dropped — per-layer transient VRAM is one layer, never the model.
753pub(crate) fn shard_kda_layer(
754    e: &Engine,
755    rt: &Arc<Glm5TpRt>,
756    la: KdaAttnLayer,
757) -> Result<KdaAttnLayer, Box<dyn std::error::Error>> {
758    if la.tp.is_some() {
759        return Err("shard_kda_layer: layer is already sharded".into());
760    }
761    let ranks = rt.ranks();
762    let heads = la.heads();
763    let head_dim = la.head_dim();
764    let qkv = la.qkv();
765    let kernel = la.conv_kernel();
766    if !heads.is_multiple_of(ranks) {
767        return Err(format!("KDA heads {heads} do not shard across {ranks} ranks").into());
768    }
769    let hl = heads / ranks; // heads per rank
770    let ql = qkv / ranks; // channels per rank
771    let n_embd = la.wo.out_features();
772    if !n_embd.is_multiple_of(ranks) {
773        return Err(format!("KDA wo out {n_embd} does not split across ranks").into());
774    }
775    let hh = n_embd / ranks;
776
777    let mut shard_plan = la.plan;
778    shard_plan.num_heads = hl as u32;
779
780    // Per-rank fused conv slice: plane p occupies rows [p*qkv, (p+1)*qkv) of the fused
781    // [3*qkv, kernel] buffer; rank r takes channel rows [r*ql, (r+1)*ql) of each plane.
782    let conv_host = e.dtoh(&la.conv)?;
783    let conv_rank =
784        |dst: &Engine, r: usize| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
785            let mut piece = Vec::with_capacity(3 * ql * kernel);
786            for p in 0..3 {
787                let a = (p * qkv + r * ql) * kernel;
788                piece.extend_from_slice(&conv_host[a..a + ql * kernel]);
789            }
790            dst.htod(&piece)
791        };
792
793    // Gate red arm: a broken shard map hands each rank the NEXT rank's wo out rows
794    // (the two-rank swap generalized to a rotation — still guaranteed wrong on every rank).
795    let wo_rank = |r: usize| -> usize {
796        match gate_red() {
797            Ok(Some(GateRed::SwapWo)) => (r + 1) % ranks,
798            _ => r,
799        }
800    };
801
802    let rank_shard = |dst: &Engine, r: usize| -> Result<KdaAttnLayer, Box<dyn std::error::Error>> {
803        let wr = wo_rank(r);
804        Ok(KdaAttnLayer {
805            plan: shard_plan,
806            wq: shard_rows(e, dst, &la.wq, r * ql..(r + 1) * ql)?,
807            wk: shard_rows(e, dst, &la.wk, r * ql..(r + 1) * ql)?,
808            wv: shard_rows(e, dst, &la.wv, r * ql..(r + 1) * ql)?,
809            f_a: replicate(e, dst, &la.f_a)?,
810            f_b: shard_rows(e, dst, &la.f_b, r * ql..(r + 1) * ql)?,
811            g_a: replicate(e, dst, &la.g_a)?,
812            g_b: shard_rows(e, dst, &la.g_b, r * ql..(r + 1) * ql)?,
813            b_proj: shard_rows(e, dst, &la.b_proj, r * hl..(r + 1) * hl)?,
814            // COLUMN-parallel wo: rank r owns OUT rows [r*hh, (r+1)*hh) over the FULL qkv
815            // input — consumed by the join over the gathered gated tensor, never by
816            // kda_core_gated itself.
817            wo: shard_rows(e, dst, &la.wo, wr * hh..(wr + 1) * hh)?,
818            conv: conv_rank(dst, r)?,
819            a_log: shard_rows(e, dst, &la.a_log, r * hl..(r + 1) * hl)?,
820            dt_bias: shard_rows(e, dst, &la.dt_bias, r * ql..(r + 1) * ql)?,
821            o_norm: replicate(e, dst, &la.o_norm)?,
822            tp: None,
823        })
824    };
825
826    let mut root = rank_shard(e, 0)?;
827    let mut peers = Vec::with_capacity(ranks - 1);
828    for r in 1..ranks {
829        peers.push(rank_shard(&rt.peers[r - 1], r)?);
830    }
831    if !KDA_MARKED.swap(true, Ordering::Relaxed) {
832        eprintln!(
833            "[glm5-tp-kda] head shard armed: ranks={ranks} heads_per_rank={hl} \
834             head_dim={head_dim} wo=column-over-gather transport={} performance_claim=false",
835            rt.transport.name(),
836        );
837    }
838    root.tp = Some(Box::new(Glm5TpKda {
839        rt: Arc::clone(rt),
840        peers,
841        full_qkv: qkv,
842        n_embd,
843    }));
844    Ok(root)
845}
846
847/// Ensure layer `il`'s per-rank KDA state planes exist (lazily, sized for the SHARD
848/// geometry — the canonical `cache.recur[il]` planes are full-width and stay untouched
849/// as allocated; the TP walk never reads them). Index 0 = root's plane on `e`, index r =
850/// rank r's plane on its peer engine.
851fn ensure_kda_tp_state<'c>(
852    e: &Engine,
853    rt: &Glm5TpRt,
854    la_root: &KdaAttnLayer,
855    cache: &'c mut Cache,
856    il: usize,
857) -> Result<&'c mut Vec<RecurLayer>, Box<dyn std::error::Error>> {
858    if cache.glm5_tp_recur.len() <= il {
859        return Err(format!("glm5-tp: cache carries no TP recur slot for layer {il}").into());
860    }
861    if cache.glm5_tp_recur[il].is_none() {
862        let conv_pad = la_root.conv_width() * (la_root.conv_kernel() - 1);
863        let state = la_root.state_width();
864        let mk = |dev: &Engine| -> Result<RecurLayer, Box<dyn std::error::Error>> {
865            Ok(RecurLayer {
866                conv_state: dev.zeros(conv_pad)?,
867                ssm_state: dev.zeros(state)?,
868                ssm_state_alt: dev.zeros(state)?,
869            })
870        };
871        let mut planes = Vec::with_capacity(rt.ranks());
872        planes.push(mk(e)?);
873        for p in &rt.peers {
874            planes.push(mk(p)?);
875        }
876        cache.glm5_tp_recur[il] = Some(planes);
877    }
878    Ok(cache.glm5_tp_recur[il].as_mut().unwrap())
879}
880
881/// The KDA TP walk, ONE body for both consumers (the #80 review's dedup finding — the
882/// forked verify twin had already drifted to root-first issue order):
883///   * prime/decode ([`kda_tp_cached`]): `verify_stash = None`, plain `wo` matmul —
884///     byte-for-byte the pre-composition walk.
885///   * spec x TP verify rows ([`kda_tp_verify_rows`]): `verify_stash = Some`, per-rank
886///     pre-round ssm snapshot + batched `KdaStash::Rows` capture, `wo` on the ROWS-EXACT
887///     class (the unsharded verify walk's own routing), per-rank scan-ns accumulated into
888///     `scan_clock` so the `[glm5-phase-v]` receipt keeps its sequential-floor share on
889///     the composed shape.
890///
891/// Issue order is PEERS FIRST, ROOT LAST on both arms (v1's order; the twins document it).
892/// THREE cross-rank hop shapes, each a named `tp_transport` shape: fan-out of `x`,
893/// gather of the gated parts, concat of the `wo` parts. On `host-canonical` at two ranks
894/// that is 5 draining `dtoh` + 4 `htod` per layer-call, exactly as v1; on `peer-pull` it is
895/// device peer copies, local copies and 0 host boundaries.
896#[allow(clippy::too_many_arguments)] // mirrors the kda entry contract shape
897fn kda_tp_core(
898    e: &Engine,
899    la_root: &KdaAttnLayer,
900    x: &CudaSlice<f32>,
901    t: usize,
902    eps: f32,
903    cache: &mut Cache,
904    il: usize,
905    arm: ConvArm,
906    verify_stash: Option<&mut Glm5TpKdaVerifyStash>,
907    mut scan_clock: Option<&mut u64>,
908) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
909    let tp = la_root
910        .tp
911        .as_ref()
912        .ok_or("kda_tp_core called on an unsharded layer")?;
913    let rt = &tp.rt;
914    let ranks = rt.ranks();
915    let ql = la_root.qkv(); // per-rank channels
916    let full = tp.full_qkv;
917    let n_embd = tp.n_embd;
918    let hh = n_embd / ranks;
919    let rows_exact = verify_stash.is_some();
920    // Per-rank verify capture, RANK-indexed regardless of issue order; assembled into the
921    // caller's stash after the loop.
922    let mut captured: Vec<Option<(CudaSlice<f32>, crate::kda::KdaRowsStash)>> =
923        (0..ranks).map(|_| None).collect();
924
925    let hop = rt.hop(e);
926    // HOP 1 — fan-out of the mixer input to every peer rank. `x.len()` and not `t * n_embd`:
927    // the v1 arm moved the WHOLE buffer, and the arms must move identical byte ranges or
928    // the transport A/B stops being a transport A/B.
929    let x_peers = crate::tp_transport::fanout_f32(&hop, x, x.len())?;
930    let states = ensure_kda_tp_state(e, rt, la_root, cache, il)?;
931
932    // Peer shards first (host-canonical serial walk; overlap is the box arc), root last —
933    // v1's issue order at two ranks, both arms.
934    let mut gated: Vec<Option<CudaSlice<f32>>> = (0..ranks).map(|_| None).collect();
935    for r in (1..ranks).chain(std::iter::once(0)) {
936        let dev = if r == 0 { e } else { &rt.peers[r - 1] };
937        let la = if r == 0 { la_root } else { &tp.peers[r - 1] };
938        let xin = if r == 0 { x } else { &x_peers[r - 1] };
939        // Verify arm: the pre-round snapshot on the rank's engine, BEFORE the batched
940        // call advances the resident state (the ckpt contract's per-rank twin).
941        let snap = if rows_exact {
942            Some(dev.clone_dtod(&states[r].ssm_state)?)
943        } else {
944            None
945        };
946        let mut rank_stash: Option<crate::kda::KdaRowsStash> = None;
947        let mut rank_scan_ns = 0u64;
948        let out = {
949            let RecurLayer {
950                conv_state,
951                ssm_state,
952                ssm_state_alt,
953            } = &mut states[r];
954            let out = crate::kda::kda_core_gated(
955                dev,
956                la,
957                xin,
958                t,
959                eps,
960                conv_state,
961                ssm_state,
962                ssm_state_alt,
963                arm,
964                if rows_exact {
965                    crate::kda::KdaStash::Rows(&mut rank_stash)
966                } else {
967                    crate::kda::KdaStash::None
968                },
969                scan_clock.as_deref_mut().map(|_| &mut rank_scan_ns),
970                None,
971            )?;
972            std::mem::swap(ssm_state, ssm_state_alt);
973            out
974        };
975        if let Some(clock) = scan_clock.as_deref_mut() {
976            *clock += rank_scan_ns;
977        }
978        if rows_exact {
979            let snap = snap.expect("verify arm cloned the snapshot above");
980            let rank_stash = rank_stash
981                .ok_or("kda_core_gated returned without filling the requested rows stash")?;
982            captured[r] = Some((snap, rank_stash));
983        }
984        gated[r] = Some(out);
985    }
986    if let Some(stash_vec) = verify_stash {
987        stash_vec.clear();
988        for c in captured {
989            stash_vec.push(c.expect("every rank captured on the verify arm"));
990        }
991    }
992
993    // HOP 2 — gather the gated parts into the FULL [t, qkv] layout on EVERY rank
994    // (column-parallel wo needs the whole input on each rank). Token-major interleave: row
995    // tok is [rank0 ql | rank1 ql | ...]. `full == ranks * ql` by the shard map.
996    debug_assert_eq!(full, ranks * ql);
997    let gated_refs: Vec<&CudaSlice<f32>> = gated
998        .iter()
999        .map(|g| g.as_ref().expect("filled above"))
1000        .collect();
1001    let fulls = crate::tp_transport::gather_parts(&hop, &gated_refs, t, ql)?;
1002
1003    // Per-rank column wo slices: each output element is one full-K dot by the SAME kernel
1004    // class the consumer's unsharded walk uses — no cross-rank arithmetic in this join.
1005    let mut ys = Vec::with_capacity(ranks);
1006    if rows_exact {
1007        ys.push(e.matmul_rows_exact(&la_root.wo, &fulls[0], t)?);
1008        for r in 1..ranks {
1009            ys.push(rt.peers[r - 1].matmul_rows_exact(&tp.peers[r - 1].wo, &fulls[r], t)?);
1010        }
1011    } else {
1012        ys.push(e.matmul(&la_root.wo, &fulls[0], t)?);
1013        for r in 1..ranks {
1014            ys.push(rt.peers[r - 1].matmul(&tp.peers[r - 1].wo, &fulls[r], t)?);
1015        }
1016    }
1017
1018    // HOP 3 — concat the column parts into the mixer output on ROOT.
1019    let y_refs: Vec<&CudaSlice<f32>> = ys.iter().collect();
1020    crate::tp_transport::concat_parts_on_root(&hop, &y_refs, t, hh)
1021}
1022
1023/// The KDA TP walk for one prime/decode call — [`kda_tp_core`] with no verify capture.
1024#[allow(clippy::too_many_arguments)] // mirrors the kda entry contract shape
1025pub(crate) fn kda_tp_cached(
1026    e: &Engine,
1027    la_root: &KdaAttnLayer,
1028    x: &CudaSlice<f32>,
1029    t: usize,
1030    eps: f32,
1031    cache: &mut Cache,
1032    il: usize,
1033    arm: ConvArm,
1034) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1035    kda_tp_core(e, la_root, x, t, eps, cache, il, arm, None, None)
1036}
1037
1038/// Per-rank rollback material of ONE sharded-KDA verify round (lane/glm5-composition, the
1039/// spec x TP composition): index = rank; each entry is that rank's pre-round ssm snapshot
1040/// (cloned on the rank's engine BEFORE its batched call advanced the resident state) plus
1041/// the batched [`crate::kda::KdaRowsStash`] its `KdaStash::Rows` call filled. Rollback to
1042/// `keep` rows restores every rank through `kda_verify_rollback_rows_on` with the rank's
1043/// own engine/shard/plane tuple — the same two-plane contract as the unsharded stash,
1044/// per rank.
1045pub type Glm5TpKdaVerifyStash = Vec<(CudaSlice<f32>, crate::kda::KdaRowsStash)>;
1046
1047/// The sharded-KDA VERIFY walk (spec x TP composition) — [`kda_tp_core`] with the verify
1048/// capture armed: batched `KdaStash::Rows` per rank, ROWS-EXACT `wo` (the unsharded verify
1049/// walk's own routing), per-rank scan-ns accumulated into `scan_clock`. Returns the mixer
1050/// output plus the rank-indexed rollback stash the ckpt banks.
1051#[allow(clippy::too_many_arguments)] // mirrors the kda verify entry contract shape
1052pub(crate) fn kda_tp_verify_rows(
1053    e: &Engine,
1054    la_root: &KdaAttnLayer,
1055    x: &CudaSlice<f32>,
1056    t: usize,
1057    eps: f32,
1058    cache: &mut Cache,
1059    il: usize,
1060    scan_clock: Option<&mut u64>,
1061) -> Result<(CudaSlice<f32>, Glm5TpKdaVerifyStash), Box<dyn std::error::Error>> {
1062    let mut stash: Glm5TpKdaVerifyStash = Vec::new();
1063    let out = kda_tp_core(
1064        e,
1065        la_root,
1066        x,
1067        t,
1068        eps,
1069        cache,
1070        il,
1071        ConvArm::Prefill,
1072        Some(&mut stash),
1073        scan_clock,
1074    )?;
1075    Ok((out, stash))
1076}
1077
1078/// Roll every rank's sharded-KDA state back to "after row `keep-1`" from a spec x TP verify
1079/// round (the [`Glm5TpKdaVerifyStash`] contract). Full accept never calls this — the
1080/// resident per-rank states ARE the state after the last kept row.
1081pub(crate) fn kda_tp_verify_rollback(
1082    e: &Engine,
1083    la_root: &KdaAttnLayer,
1084    stash: &Glm5TpKdaVerifyStash,
1085    keep: usize,
1086    cache: &mut Cache,
1087    il: usize,
1088) -> Result<(), Box<dyn std::error::Error>> {
1089    let tp = la_root
1090        .tp
1091        .as_ref()
1092        .ok_or("kda_tp_verify_rollback called on an unsharded layer")?;
1093    let rt = &tp.rt;
1094    let ranks = rt.ranks();
1095    if stash.len() != ranks {
1096        return Err(format!(
1097            "glm5-tp verify rollback: stash carries {} ranks, the runtime has {ranks}",
1098            stash.len()
1099        )
1100        .into());
1101    }
1102    let states = cache.glm5_tp_recur[il]
1103        .as_mut()
1104        .ok_or_else(|| format!("glm5-tp verify rollback: layer {il} has no per-rank state"))?;
1105    for r in 0..ranks {
1106        let dev = if r == 0 { e } else { &rt.peers[r - 1] };
1107        let la = if r == 0 { la_root } else { &tp.peers[r - 1] };
1108        let (snap, rows) = &stash[r];
1109        crate::kda::kda_verify_rollback_rows_on(dev, la, snap, rows, keep, &mut states[r], il)?;
1110    }
1111    Ok(())
1112}
1113
1114// ------------------------------------------------------------------------------------------
1115// MLA sidecar
1116// ------------------------------------------------------------------------------------------
1117
1118/// The MLA TP sidecar: the peer ranks' head shards (with replicated `wq_a`/`wkv_a`/norms
1119/// and full indexer replicas) plus the runtime handle.
1120pub struct Glm5TpMla {
1121    pub rt: Arc<Glm5TpRt>,
1122    /// `peers[i]` is rank `i + 1`'s shard, resident on `rt.peers[i]`.
1123    pub peers: Vec<crate::hybrid::MlaAttnLayer>,
1124    /// Full head count of the unsharded layer.
1125    pub full_heads: usize,
1126    /// Full hidden width (`wo` out rows across all ranks).
1127    pub n_embd: usize,
1128}
1129
1130impl Glm5TpMla {
1131    pub fn ranks(&self) -> usize {
1132        self.peers.len() + 1
1133    }
1134}
1135
1136static MLA_MARKED: AtomicBool = AtomicBool::new(false);
1137
1138pub(crate) fn shard_mla_layer(
1139    e: &Engine,
1140    rt: &Arc<Glm5TpRt>,
1141    la: crate::hybrid::MlaAttnLayer,
1142) -> Result<crate::hybrid::MlaAttnLayer, Box<dyn std::error::Error>> {
1143    use crate::hybrid::{MlaAttnLayer, MlaIndexer};
1144    if la.tp.is_some() {
1145        return Err("shard_mla_layer: layer is already sharded".into());
1146    }
1147    let ranks = rt.ranks();
1148    let g = la.geom;
1149    let nh = g.n_head;
1150    if !nh.is_multiple_of(ranks) {
1151        return Err(format!("MLA heads {nh} do not shard across {ranks} ranks").into());
1152    }
1153    let hl = nh / ranks;
1154    let head_q = g.d_nope + g.d_rope; // per-head wq_b out rows
1155    let n_embd = la.wo.out_features();
1156    if !n_embd.is_multiple_of(ranks) {
1157        return Err(format!("MLA wo out {n_embd} does not split across ranks").into());
1158    }
1159    let hh = n_embd / ranks;
1160
1161    let mut shard_geom = g;
1162    shard_geom.n_head = hl;
1163
1164    let replicate_indexer =
1165        |dst: &Engine, ix: &MlaIndexer| -> Result<MlaIndexer, Box<dyn std::error::Error>> {
1166            Ok(MlaIndexer {
1167                wq_b: replicate(e, dst, &ix.wq_b)?,
1168                wk: replicate(e, dst, &ix.wk)?,
1169                k_norm_w: replicate(e, dst, &ix.k_norm_w)?,
1170                k_norm_b: replicate(e, dst, &ix.k_norm_b)?,
1171                weights_proj: replicate(e, dst, &ix.weights_proj)?,
1172                kpool_gate: replicate(e, dst, &ix.kpool_gate)?,
1173                kpool_ape: replicate(e, dst, &ix.kpool_ape)?,
1174                geom: ix.geom,
1175            })
1176        };
1177
1178    // Gate red arm: a broken shard map hands each rank the NEXT rank's wo out rows.
1179    let wo_rank = |r: usize| -> usize {
1180        match gate_red() {
1181            Ok(Some(GateRed::SwapWo)) => (r + 1) % ranks,
1182            _ => r,
1183        }
1184    };
1185
1186    let rank_shard = |dst: &Engine, r: usize| -> Result<MlaAttnLayer, Box<dyn std::error::Error>> {
1187        let wr = wo_rank(r);
1188        Ok(MlaAttnLayer {
1189            wq_a: replicate(e, dst, &la.wq_a)?,
1190            q_a_norm: replicate(e, dst, &la.q_a_norm)?,
1191            wq_b: shard_rows(e, dst, &la.wq_b, r * hl * head_q..(r + 1) * hl * head_q)?,
1192            wkv_a: replicate(e, dst, &la.wkv_a)?,
1193            kv_a_norm: replicate(e, dst, &la.kv_a_norm)?,
1194            // 3D per-head slabs: the head axis is outermost.
1195            wk_b: shard_rows(e, dst, &la.wk_b, r * hl..(r + 1) * hl)?,
1196            wv_b: shard_rows(e, dst, &la.wv_b, r * hl..(r + 1) * hl)?,
1197            // COLUMN-parallel wo: rank r owns OUT rows over the full N*V input.
1198            wo: shard_rows(e, dst, &la.wo, wr * hh..(wr + 1) * hh)?,
1199            geom: shard_geom,
1200            index: match &la.index {
1201                Some(ix) => Some(replicate_indexer(dst, ix)?),
1202                None => None,
1203            },
1204            tp: None,
1205            tp_shard: true,
1206        })
1207    };
1208
1209    let mut root = rank_shard(e, 0)?;
1210    let mut peers = Vec::with_capacity(ranks - 1);
1211    for r in 1..ranks {
1212        peers.push(rank_shard(&rt.peers[r - 1], r)?);
1213    }
1214    if !MLA_MARKED.swap(true, Ordering::Relaxed) {
1215        eprintln!(
1216            "[glm5-tp-mla] head shard armed: ranks={ranks} heads_per_rank={hl} kv_rank={} \
1217             latent=replicated indexer=replicated wo=column-over-gather transport={} \
1218             performance_claim=false",
1219            g.kv_rank,
1220            rt.transport.name(),
1221        );
1222    }
1223    root.tp = Some(Box::new(Glm5TpMla {
1224        rt: Arc::clone(rt),
1225        peers,
1226        full_heads: nh,
1227        n_embd,
1228    }));
1229    Ok(root)
1230}
1231
1232/// Ensure the PEER ranks' replicated latent planes for layer `il` exist, geometry-cloned
1233/// from the canonical (root) plane. The canonical plane IS the root replica — the root path
1234/// is unchanged. `cache_slot` holds one plane per peer rank (`[i]` = rank `i + 1`).
1235pub(crate) fn ensure_mla_peer_latent(
1236    rt: &Glm5TpRt,
1237    canonical: &LatentKvLayer,
1238    cache_slot: &mut Option<Vec<LatentKvLayer>>,
1239) -> Result<(), Box<dyn std::error::Error>> {
1240    if cache_slot.is_some() {
1241        return Ok(());
1242    }
1243    let mut planes = Vec::with_capacity(rt.peers.len());
1244    for dev in &rt.peers {
1245        let rows = dev.zeros(canonical.rows.len())?;
1246        // Fresh replica starts at len 0 like a fresh canonical plane; the walk appends to
1247        // every replica in the same calls, so the lengths stay in lock-step by construction.
1248        let len_d = dev.htod_i32(&[0])?;
1249        let index_rows = match &canonical.index_rows {
1250            Some(p) => Some(dev.zeros(p.len())?),
1251            None => None,
1252        };
1253        planes.push(LatentKvLayer {
1254            rows,
1255            width: canonical.width,
1256            index_width: canonical.index_width,
1257            len: 0,
1258            len_d,
1259            index_rows,
1260            index_ring_rows: canonical.index_ring_rows,
1261            index_pool_keys: None, // lazily allocated by the core, exactly like the canonical plane
1262            index_pools_ready: 0,
1263            index_pool: canonical.index_pool,
1264        });
1265    }
1266    *cache_slot = Some(planes);
1267    Ok(())
1268}
1269
1270// ------------------------------------------------------------------------------------------
1271// MoE EP sidecar
1272// ------------------------------------------------------------------------------------------
1273
1274/// One rank's expert slab: the rank's owned experts packed in ASCENDING expert-id order
1275/// for every projection, device-resident on that rank. For the even split the packing is
1276/// the contiguous slice — byte-for-byte the pre-map layout.
1277pub struct EpRankSlab {
1278    pub gate: CudaSlice<u8>,
1279    pub up: CudaSlice<u8>,
1280    pub down: CudaSlice<u8>,
1281    pub n_experts: usize,
1282}
1283
1284/// The MoE EP sidecar on `MoeWeights`: per-rank expert slabs, the placement tables,
1285/// and the runtime handle. Router, shared expert, macros and all host metadata stay on
1286/// the unchanged `MoeWeights`.
1287///
1288/// PLACEMENT INDEPENDENCE (the contract the gate's skewed-map arm proves): `owner_of`
1289/// only selects WHICH rank runs the identical per-expert program over identical
1290/// host-canonical input bytes; `local_of` indexes the same expert bytes wherever they
1291/// were packed; the combine stays slot-ordered on root. The map moves bytes, never
1292/// changes arithmetic.
1293pub struct Glm5EpExps {
1294    pub rt: Arc<Glm5TpRt>,
1295    /// `slabs[r]` is rank r's expert slab (`[0]` = root's, on the model's engine).
1296    pub slabs: Vec<EpRankSlab>,
1297    /// `owner_of[expert]` = owning rank (0 = root).
1298    pub owner_of: Vec<u8>,
1299    /// `local_of[expert]` = slot inside the owner's slab (ascending-id packing order).
1300    pub local_of: Vec<u32>,
1301    /// Per-rank grouped-dispatch pointer tables, `[rank]`, each the `DevExps::ptr_row`
1302    /// shape ([3 * n_expert] u64 device pointers: gate | up | down planes, indexed by GLOBAL
1303    /// expert id, resident on the owning rank's device). Owned experts point at
1304    /// `slab_base + local * stride`; non-owned entries are 0 and never dereferenced — the EP
1305    /// grouped-prime CSR is built per rank from `owner_of`, so a foreign id cannot reach the
1306    /// wrong rank's table. Built AFTER the gate-red slab mutations, from the FINAL slab
1307    /// buffers and the FINAL `local_of`, so `swap-ep-gateup` and `corrupt-ep-map` bite the
1308    /// grouped walk exactly as they bite the sequential one.
1309    pub ptr_rows: Vec<CudaSlice<u64>>,
1310}
1311
1312impl Glm5EpExps {
1313    /// Owner rank of `expert` under the armed placement (even split when no map).
1314    pub fn owner(&self, expert: usize) -> usize {
1315        self.owner_of[expert] as usize
1316    }
1317
1318    pub fn ranks(&self) -> usize {
1319        self.slabs.len()
1320    }
1321}
1322
1323static EP_MARKED: AtomicBool = AtomicBool::new(false);
1324
1325/// Engagement counter: PEER-owned expert slots dispatched by the EP walk (counted before
1326/// any gate-red skip, so a red arm can still assert a peer was ROUTED). Gates read it to
1327/// prove the peer ranks contribute real expert work — a token stream that never routes a
1328/// peer-owned expert makes every EP identity arm vacuous.
1329pub static GLM5_EP_PEER_SLOT_DISPATCHES: AtomicU64 = AtomicU64::new(0);
1330
1331pub fn glm5_ep_peer_slot_dispatches() -> u64 {
1332    GLM5_EP_PEER_SLOT_DISPATCHES.load(Ordering::Relaxed)
1333}
1334
1335// ---- EP dispatch-diet engagement counters (lane/glm5-ep-diet, 2026-08-31) ----------------
1336// The box A/B greps announces and reads these deltas; the rig gate asserts them non-vacuous
1337// on the ON arms and FLAT on the pinned-`=0` arms.
1338
1339/// Layer-calls that took the dieted EP walk (`MEMRA_GLM5_EP_DIET`) instead of the v1
1340/// per-slot host-canonical walk.
1341pub static GLM5_EP_DIET_DISPATCHES: AtomicU64 = AtomicU64::new(0);
1342
1343/// Snapshot of [`GLM5_EP_DIET_DISPATCHES`] — gates take a before/after delta.
1344pub fn glm5_ep_diet_dispatches() -> u64 {
1345    GLM5_EP_DIET_DISPATCHES.load(Ordering::Relaxed)
1346}
1347
1348/// Bulk peer-row block returns performed by the dieted walk (one per (layer-call, peer
1349/// rank) that routed at least one slot owned by that rank; each replaces that call's ENTIRE
1350/// per-slot return dribble for that rank).
1351pub static GLM5_EP_DIET_BULK_RETURNS: AtomicU64 = AtomicU64::new(0);
1352
1353/// Snapshot of [`GLM5_EP_DIET_BULK_RETURNS`].
1354pub fn glm5_ep_diet_bulk_returns() -> u64 {
1355    GLM5_EP_DIET_BULK_RETURNS.load(Ordering::Relaxed)
1356}
1357
1358/// Per-slot synchronous peer round-trips (one peer DtoH + one root pageable HtoD each, the
1359/// v1 walk's dominant hop class) that the dieted walk folded into its bulk returns — one
1360/// count per peer-owned slot bulked.
1361pub static GLM5_EP_DIET_PEER_ROUNDTRIPS_AVOIDED: AtomicU64 = AtomicU64::new(0);
1362
1363/// Snapshot of [`GLM5_EP_DIET_PEER_ROUNDTRIPS_AVOIDED`].
1364pub fn glm5_ep_diet_peer_roundtrips_avoided() -> u64 {
1365    GLM5_EP_DIET_PEER_ROUNDTRIPS_AVOIDED.load(Ordering::Relaxed)
1366}
1367
1368/// Per-token peer z uploads the dieted walk avoided: `t-1` per (fanned layer-call, peer
1369/// rank) (one bulk [t, n_embd] upload replaces t per-token uploads) plus `t` per (layer-call,
1370/// rank) whose routing never touched that rank's experts (the fan-out is skipped entirely —
1371/// the placement-map multiplier: single-rank layer-calls move ZERO activation bytes off
1372/// root).
1373pub static GLM5_EP_DIET_FANOUT_UPLOADS_AVOIDED: AtomicU64 = AtomicU64::new(0);
1374
1375/// Snapshot of [`GLM5_EP_DIET_FANOUT_UPLOADS_AVOIDED`].
1376pub fn glm5_ep_diet_fanout_uploads_avoided() -> u64 {
1377    GLM5_EP_DIET_FANOUT_UPLOADS_AVOIDED.load(Ordering::Relaxed)
1378}
1379
1380/// Layer-calls that took the per-rank grouped-GEMM EP prime (`MEMRA_GLM5_EP_GROUPED_PRIME`).
1381/// Stays 0 whenever the plain grouped-prefill conjuncts do not hold (e.g. non-f16g-eligible
1382/// expert qtypes — the rig fixture's Q8_0 bank always falls closed to the sequential walk).
1383pub static GLM5_EP_GROUPED_PRIME_DISPATCHES: AtomicU64 = AtomicU64::new(0);
1384
1385/// Snapshot of [`GLM5_EP_GROUPED_PRIME_DISPATCHES`].
1386pub fn glm5_ep_grouped_prime_dispatches() -> u64 {
1387    GLM5_EP_GROUPED_PRIME_DISPATCHES.load(Ordering::Relaxed)
1388}
1389
1390/// Arm one MoE layer for EP. `placement` is the layer's validated map row
1391/// (`owners[expert] = rank`) when `MEMRA_EP_MAP` (or its glm5 alias) is armed; `None` = the
1392/// even split, whose ascending-id packing is byte-for-byte the pre-map contiguous slices.
1393pub(crate) fn arm_moe_ep(
1394    e: &Engine,
1395    rt: &Arc<Glm5TpRt>,
1396    m: &mut crate::hybrid::MoeWeights,
1397    placement: Option<&[u8]>,
1398) -> Result<(), Box<dyn std::error::Error>> {
1399    if m.glm5_ep.is_some() {
1400        return Err("arm_moe_ep: layer is already EP-armed".into());
1401    }
1402    let ranks = rt.ranks();
1403    let n_expert = m.gate_exps.n_expert;
1404    if !n_expert.is_multiple_of(ranks) {
1405        return Err(format!(
1406            "glm5-tp EP: {n_expert} experts do not partition across {ranks} ranks"
1407        )
1408        .into());
1409    }
1410    if m.gate_exps.layouts.is_some() || m.up_exps.layouts.is_some() || m.down_exps.layouts.is_some()
1411    {
1412        return Err("glm5-tp EP: per-expert mixed layouts are unwired for EP shards".into());
1413    }
1414    let owner_of: Vec<u8> = match placement {
1415        Some(owners) => {
1416            // The preflight validated the map; re-assert the two structural laws at the
1417            // consumption site so a wiring bug can never hand a foreign row to a layer.
1418            if owners.len() != n_expert {
1419                return Err(format!(
1420                    "glm5-tp EP: placement row carries {} owners for a {n_expert}-expert bank",
1421                    owners.len()
1422                )
1423                .into());
1424            }
1425            if owners.iter().any(|&r| (r as usize) >= ranks) {
1426                return Err(
1427                    format!("glm5-tp EP: placement row names a rank outside TP-{ranks}").into(),
1428                );
1429            }
1430            owners.to_vec()
1431        }
1432        None => crate::ep_map::EpMap::even_owners(n_expert, ranks),
1433    };
1434    // Ascending-id packing per rank + the local-slot table.
1435    let mut local_of = vec![0u32; n_expert];
1436    let mut owned: Vec<Vec<usize>> = vec![Vec::new(); ranks];
1437    for ex in 0..n_expert {
1438        let r = owner_of[ex] as usize;
1439        local_of[ex] = owned[r].len() as u32;
1440        owned[r].push(ex);
1441    }
1442    if owned.iter().any(|o| o.is_empty()) {
1443        return Err("glm5-tp EP: placement leaves a rank with zero experts (refused)".into());
1444    }
1445    let slab =
1446        |dev: &Engine, experts: &[usize]| -> Result<EpRankSlab, Box<dyn std::error::Error>> {
1447            // Tail-slack pads mirror the resident-slab builder (`build_dev_exps`): 8 B
1448            // alignment slack on gate/up and 144 B on down — the ragged-k grouped GEMM
1449            // walks whole superblocks and may overread past the LAST row (harmless bytes,
1450            // the zero-padded k-range multiplies them away; the slack only prevents the
1451            // OOB fault). Bytes at every in-slab offset are unchanged, so the sequential
1452            // per-slot views read exactly what they read before.
1453            let cut = |h: &crate::model::HostExps,
1454                       pad: usize|
1455             -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1456                let stride = h.expert_stride;
1457                let bytes = h.bytes.as_bytes();
1458                // Contiguous ascending run (the even split, and any contiguous map row):
1459                // one direct upload of the existing byte range — no host copy.
1460                let contiguous = experts.windows(2).all(|w| w[1] == w[0] + 1);
1461                if contiguous {
1462                    let a = experts[0] * stride;
1463                    let b = (experts[experts.len() - 1] + 1) * stride;
1464                    return dev.htod_bytes_padded(&bytes[a..b], pad);
1465                }
1466                // General map row: pack the owned experts ascending into one staging
1467                // buffer (load-time only; per-layer transient host = one rank's slab).
1468                let mut staged = Vec::with_capacity(experts.len() * stride);
1469                for &ex in experts {
1470                    staged.extend_from_slice(&bytes[ex * stride..(ex + 1) * stride]);
1471                }
1472                dev.htod_bytes_padded(&staged, pad)
1473            };
1474            Ok(EpRankSlab {
1475                gate: cut(&m.gate_exps, 8)?,
1476                up: cut(&m.up_exps, 8)?,
1477                down: cut(&m.down_exps, 144)?,
1478                n_experts: experts.len(),
1479            })
1480        };
1481    let mut slabs = Vec::with_capacity(ranks);
1482    for r in 0..ranks {
1483        slabs.push(slab(rank_engine(e, rt, r), &owned[r])?);
1484    }
1485    // Gate red arm: wrong expert weights on the root rank (gate/up swapped).
1486    if matches!(gate_red(), Ok(Some(GateRed::SwapEpGateUp))) {
1487        let root = &mut slabs[0];
1488        std::mem::swap(&mut root.gate, &mut root.up);
1489    }
1490    // Gate red arm: a corrupted map row — the local-slot table for rank 0 is reversed
1491    // AFTER the slabs were packed, so the owner table and the slab bytes disagree.
1492    if matches!(gate_red(), Ok(Some(GateRed::CorruptEpMap))) {
1493        let n0 = owned[0].len() as u32;
1494        for &ex in &owned[0] {
1495            local_of[ex] = n0 - 1 - local_of[ex];
1496        }
1497    }
1498    // Per-rank grouped-dispatch pointer tables (lane/glm5-ep-diet): the `DevExps::ptr_row`
1499    // shape over each rank's OWN slab, built from the FINAL slab buffers and the FINAL
1500    // `local_of` so both gate reds above flow into the grouped walk too. ~3*n_expert*8 B per
1501    // rank per layer — negligible next to the slabs they index.
1502    let ptr_table = |dev: &Engine,
1503                     slab: &EpRankSlab,
1504                     rank: u8|
1505     -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
1506        use cudarc::driver::DevicePtr;
1507        let (pg, pu, pd) = {
1508            let s = dev.stream();
1509            let (pg, _g0) = slab.gate.device_ptr(&s);
1510            let (pu, _g1) = slab.up.device_ptr(&s);
1511            let (pd, _g2) = slab.down.device_ptr(&s);
1512            (pg, pu, pd)
1513        };
1514        let mut host = vec![0u64; 3 * n_expert];
1515        for ex in 0..n_expert {
1516            if owner_of[ex] != rank {
1517                continue; // non-owned: 0, never dereferenced (rank CSRs filter by owner)
1518            }
1519            let local = local_of[ex] as usize;
1520            host[ex] = pg + (local * m.gate_exps.expert_stride) as u64;
1521            host[n_expert + ex] = pu + (local * m.up_exps.expert_stride) as u64;
1522            host[2 * n_expert + ex] = pd + (local * m.down_exps.expert_stride) as u64;
1523        }
1524        dev.htod_u64(&host)
1525    };
1526    let mut ptr_rows = Vec::with_capacity(ranks);
1527    for r in 0..ranks {
1528        ptr_rows.push(ptr_table(rank_engine(e, rt, r), &slabs[r], r as u8)?);
1529    }
1530    if !EP_MARKED.swap(true, Ordering::Relaxed) {
1531        eprintln!(
1532            "[glm5-tp-ep] expert-parallel armed: experts_per_rank={:?} ownership={} \
1533             router=root combine=slot-ordered-fmaf transport={} \
1534             performance_claim=false",
1535            owned.iter().map(Vec::len).collect::<Vec<_>>(),
1536            // ORDER MATTERS and it was wrong once: the ownership string must land on
1537            // `ownership={}` and the transport on `transport={}`. The first gate run's
1538            // receipt-extract printed `[glm5-tp-ep] transport=even-split`, which is how the
1539            // swap was caught — a receipt line is only worth what its argument order is.
1540            if placement.is_some() {
1541                "measured-map"
1542            } else {
1543                "even-split"
1544            },
1545            rt.transport.name(),
1546        );
1547    }
1548    // The root-resident full slab (if the loader built one) is superseded by the EP slices;
1549    // dropping it returns its VRAM and removes the arm that would silently bypass EP.
1550    m.dev_exps = None;
1551    m.glm5_ep = Some(Glm5EpExps {
1552        rt: Arc::clone(rt),
1553        slabs,
1554        owner_of,
1555        local_of,
1556        ptr_rows,
1557    });
1558    Ok(())
1559}
1560
1561#[cfg(test)]
1562mod tests {
1563    use super::*;
1564
1565    #[test]
1566    fn parse_is_literal_and_fail_closed() {
1567        // Off spellings.
1568        assert!(parse_glm5_tp_layer_specs(None, 45).unwrap().is_empty());
1569        assert!(parse_glm5_tp_layer_specs(Some(""), 45).unwrap().is_empty());
1570        assert!(parse_glm5_tp_layer_specs(Some("0"), 45).unwrap().is_empty());
1571        // The full-model shorthand expands against the CALLER's trunk, not a constant.
1572        let all = parse_glm5_tp_layer_specs(Some("all@0,1"), 45).unwrap();
1573        assert_eq!(all.len(), 45);
1574        assert_eq!(all[0].devices, vec![0, 1]);
1575        let all4 = parse_glm5_tp_layer_specs(Some("all@0,1"), 4).unwrap();
1576        assert_eq!(all4.len(), 4);
1577        // The TP-4 device list parses through the same grammar.
1578        let quad = parse_glm5_tp_layer_specs(Some("all@0,1,2,3"), 45).unwrap();
1579        assert_eq!(quad.len(), 45);
1580        assert_eq!(quad[0].devices, vec![0, 1, 2, 3]);
1581        // Explicit ranges.
1582        let r = parse_glm5_tp_layer_specs(Some("0-2@0,1;4@0,1"), 45).unwrap();
1583        assert_eq!(
1584            r.iter().map(|s| s.layer).collect::<Vec<_>>(),
1585            vec![0, 1, 2, 4]
1586        );
1587        // Refusals: duplicate devices, duplicate layers, garbage.
1588        assert!(parse_glm5_tp_layer_specs(Some("0@0,0"), 45).is_err());
1589        assert!(parse_glm5_tp_layer_specs(Some("0@0,1;0@0,1"), 45).is_err());
1590        assert!(parse_glm5_tp_layer_specs(Some("banana"), 45).is_err());
1591    }
1592
1593    fn fixture_view() -> Glm5TpModelView {
1594        Glm5TpModelView {
1595            trunk_layers: 4,
1596            layer_class: vec![
1597                Glm5LayerClass::Kda,
1598                Glm5LayerClass::Mla,
1599                Glm5LayerClass::Kda,
1600                Glm5LayerClass::Mla,
1601            ],
1602            layer_is_moe: vec![false, true, true, true],
1603            kda_heads: 4,
1604            kda_head_dim: 128,
1605            mla_heads: 4,
1606            n_routed_experts: 4,
1607            top_k: 2,
1608        }
1609    }
1610
1611    /// Structural preflight refusals, exercised WITHOUT constructing any CUDA state: every
1612    /// geometry law here fires before `prepare_glm5_tp_load` reaches the runtime build.
1613    /// (The armed happy path needs an Engine and lives in the gate binary.)
1614    #[test]
1615    fn preflight_geometry_laws_are_dimension_derived() {
1616        // The checks below mirror prepare_glm5_tp_load's law order on the view alone, at
1617        // BOTH qualified rank counts.
1618        let v = fixture_view();
1619        for ranks in GLM5_TP_ALLOWED_RANKS {
1620            assert_eq!(v.kda_heads % ranks, 0);
1621            assert_eq!(v.mla_heads % ranks, 0);
1622            assert_eq!(v.n_routed_experts % ranks, 0);
1623        }
1624        let odd = Glm5TpModelView {
1625            kda_heads: 3,
1626            ..fixture_view()
1627        };
1628        assert_ne!(odd.kda_heads % 2, 0);
1629        let bad_dim = Glm5TpModelView {
1630            kda_head_dim: 64,
1631            ..fixture_view()
1632        };
1633        assert_ne!(bad_dim.kda_head_dim, crate::kda::KDA_HEAD_DIM);
1634        let odd_experts = Glm5TpModelView {
1635            n_routed_experts: 5,
1636            ..fixture_view()
1637        };
1638        assert_ne!(odd_experts.n_routed_experts % 2, 0);
1639        // TP-3 stays outside the qualified envelope (head padding not built).
1640        assert!(!GLM5_TP_ALLOWED_RANKS.contains(&3));
1641    }
1642
1643    #[test]
1644    fn armed_check_counts_parse_errors_as_armed() {
1645        // glm5_tp_armed is a cheap co-refusal predicate: any nonempty non-"0" value counts,
1646        // including a spec the parser would refuse — the co-armed program must not race the
1647        // loader's own refusal.
1648        // (Env-mutation-free: the predicate's contract is pure string classification.)
1649        for (v, armed) in [
1650            ("", false),
1651            ("0", false),
1652            ("all@0,1", true),
1653            ("all@0,1,2,3", true),
1654            ("junk", true),
1655        ] {
1656            let is_armed = !v.is_empty() && v != "0";
1657            assert_eq!(is_armed, armed);
1658        }
1659    }
1660
1661    #[test]
1662    fn every_refused_door_composition_bites_by_name() {
1663        // The merge-forward composition matrix (2026-08-31): each decode-diet door armed
1664        // alone must refuse, naming BOTH flags — a silent pick is the failure mode this
1665        // guards. (Env-mutation-free: the law is pure over the armed predicate; the live
1666        // env read is one closure at the prepare_glm5_tp_load call site, and the tp-gate
1667        // red receipt exercises it end to end.)
1668        for (flag, _) in GLM5_TP_REFUSED_DOOR_FLAGS {
1669            let err = refuse_glm5_tp_door_composition(|f| f == flag)
1670                .expect_err("an armed door must refuse");
1671            assert!(err.contains("MEMRA_GLM5_TP"), "{err}");
1672            assert!(err.contains(flag), "{err}");
1673            assert!(err.contains("unproven composition"), "{err}");
1674        }
1675        // All doors cold = no refusal.
1676        refuse_glm5_tp_door_composition(|_| false).expect("cold doors must pass");
1677        // The verify-batch flag is DELIBERATELY not in the matrix (the gated spec x TP
1678        // composition owns that pair — its admission REQUIRES the batched walk); arming
1679        // it alone must not trip this law.
1680        refuse_glm5_tp_door_composition(|f| f == "MEMRA_GLM5_VERIFY_BATCH")
1681            .expect("verify-batch is refused via the spec co-refusal, not here");
1682    }
1683}