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