Skip to main content

memra_engine/
tp_transport.rs

1//! TP-N TRANSPORT — `MEMRA_TP_TRANSPORT` (born lane/glm5-tp-transport 2026-09-01 as
2//! `MEMRA_GLM5_TP_TRANSPORT`, rank-widened by lane/glm5-composition 2026-09-01, generalized
3//! lane/glm5-extract2; the family alias stays honored).
4//!
5//! ENGINE-GENERIC BY CONSTRUCTION, and this is a claim about content rather than a hope: not
6//! one function below knows a layer type, a mixer, an expert or a model family. The whole
7//! module is rank indices, publication events, and named point-to-point hop shapes over dense
8//! blocks. The glm5 TP-N shard walk is today's ONLY CONSUMER (it owns its rank geometry, its
9//! owner-first law and its shard maps); an hy3/step TP walk will arm the same transport once
10//! it exists, and `pp.rs`'s `BoundarySlot` is where the shape came from.
11//!
12//! WHAT THIS IS. The data-movement layer under a TP execution program (`MEMRA_GLM5_TP` is the
13//! first). The TP
14//! seam's arithmetic is column-parallel-over-gather: every cross-rank hop is PURE MOVEMENT
15//! (the one arithmetic site is the MoE slot-ordered combine, and it stays on root). So the
16//! transport is swappable WITHOUT changing a single bit, and this module is the seam where
17//! the swap happens — one named function per hop shape, two arms behind each:
18//!
19//!   * `host-canonical` (v1, the DEFAULT): every hop is `dtoh` -> host buffer -> `htod`.
20//!     `Engine::dtoh` ends in `stream().synchronize()`, so each leg is a full stream drain
21//!     plus a PCIe host round trip. This is what every banked glm5 TP number was measured
22//!     on (`research/glm53-flash-bringup-20260827/tp2-battery-20260831/RESULTS.md` cell 2:
23//!     "v1 transport = host-canonical (every boot announces `transport=host-canonical` on
24//!     all four seams)").
25//!   * `peer-pull`: the hop is a DEVICE copy issued on the CONSUMER's stream, reading the
26//!     producer's buffer, ordered by a publication event. Zero host legs, zero stream
27//!     drains. PULL and not push by design (see the transport laws below).
28//!
29//! RANK WIDENING (lane/glm5-composition). The 2026-09-01 original of this module hardcoded
30//! two ranks (`Rank::Root`/`Rank::Peer`). Ranks are now plain indices (`0` = root, the
31//! owner-first law unchanged) and every hop shape takes per-rank parts; at two ranks each
32//! arm reproduces the v1 walk HOP FOR HOP (same primitive count, same order, same census),
33//! which is what keeps the banked TP-2 numbers comparable across this widening. The
34//! publication link carries one pub event and one release event PER RANK — `cuEventRecord`
35//! overwrites and `cuStreamWaitEvent` captures state at call time, so the record-then-wait
36//! pairs a single host thread issues in program order stay correct at any rank count.
37//!
38//! WHY PULL. On the RTX PRO 6000 PCIe fabric a peer transport's direction is not symmetric
39//! and the consumer-issued shape wins twice:
40//!
41//!   1. `research/pro6000-multicard-research-20260901/RESEARCH.md` §2.2b measures
42//!      "approximately 52 GB/s for peer reads and 2.6 GB/s for peer writes, so all three
43//!      communication phases use pull transfers" (b12x#139, 16x PRO 6000) — 110 us push vs
44//!      26 us pull for the same collective. That asymmetry is scoped to SM-ISSUED
45//!      (kernel load/store) traffic; a copy-engine `cuMemcpyDtoDAsync` measures ~54-56 GB/s
46//!      in BOTH directions (§2.2, §3.2 `read_ce`/`write_ce`). We use the copy engine, so the
47//!      20x is NOT our headline reason — it is the reason a future fused collective must be
48//!      pull-shaped, and the reason we never build the push twin.
49//!   2. The ORDERING reason, which IS our headline reason and is specific to our tax. When
50//!      the consumer issues the copy, the consuming kernel on that same stream is ordered
51//!      after it for free. A push (producer-issued, the `pp.rs` `BoundaryTransport::Peer`
52//!      shape) needs a second event + wait to publish into the consumer's stream. Our
53//!      measured tax is a ROUND-TRIP and LAUNCH count tax, not a byte tax
54//!      (RESEARCH.md §2.6, §6.3c; three upstream maintainers agree, §1.5g), so removing
55//!      launches per hop is the lever, and pull removes one ordering primitive per hop.
56//!
57//! NO ATOMICS, ANYWHERE. Every SM120 PCIe pair reports `NativeAtomicSupported=0`
58//! (RESEARCH.md §2.1): "CAS on peer memory silently loses barrier tokens under PCIe load"
59//! and it fails LOAD-DEPENDENTLY, so microbenchmarks pass. This transport uses only
60//! copy-engine copies and CUDA events — no peer flag polling, no compare-and-swap, no
61//! payload-value sentinels (RESEARCH.md §6.13: a payload-sentinel prototype caused a
62//! launch-day production stall). If a fused device-resident collective is ever built here,
63//! §6.13's protocol is pre-specified and its bug list is pre-registered.
64//!
65//! WHAT THIS IS NOT. This is not a collective. There is no reduce anywhere in the glm5 TP
66//! program (that is what makes decode BYTE identity the gate bar instead of a tolerance
67//! band), so there is no all-reduce to fuse, no ring, no barrier. Everything here is a
68//! point-to-point copy of a dense block plus a byte-preserving strided placement.
69//!
70//! FAIL-CLOSED. Arming `peer-pull` runs a byte-integrity pull ladder through the EXACT
71//! primitive the walk uses, in EVERY ordered rank pair, before any layer is sharded; a
72//! single differing byte refuses the load by name. That is a TRANSFER-tier check by
73//! RESEARCH.md §2.4's ladder and it is honest about it: it proves our copy path moves the
74//! right bytes, and it does NOT prove a kernel peer-dereference works (only a
75//! `simpleP2P`-class KERNEL peer read does — banked separately as the lane's
76//! `peer-read-probe.cu`, run by `HEALTH.sh` at every box window).
77//!
78//! Engagement markers: the TAG IS THE CALLER'S (the phase-1 `spec_phase` pattern) — glm5
79//! passes `"glm5-tp-transport"`, so every banked receipt line from lane/glm5-tp-transport and
80//! lane/glm5-composition keeps its exact bytes while a second family gets its own marker.
81//! Counters below are the per-token instrument and are the TRANSPORT's, not the family's.
82
83// lane/clippy-zero-restore-20260901: this transport's exact code shape is receipt-bound
84// (lane/glm5-composition keeps its exact bytes, header above); index loops stay as gated.
85#![allow(clippy::needless_range_loop)]
86
87use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
88
89use cudarc::driver::{CudaEvent, CudaSlice};
90
91use crate::Engine;
92
93/// Rank index. `0` is root (the model's own engine — the owner-first rank law); `1..ranks`
94/// are the peer engines in `MEMRA_GLM5_TP` device order.
95pub const ROOT: usize = 0;
96
97// ------------------------------------------------------------------------------------------
98// Flag
99// ------------------------------------------------------------------------------------------
100
101/// Which transport the armed `MEMRA_GLM5_TP` seam moves its bytes with.
102#[derive(Clone, Copy, PartialEq, Eq, Debug)]
103pub enum TpTransport {
104    /// v1: `dtoh` -> host -> `htod` per hop. Every banked glm5 TP number.
105    HostCanonical,
106    /// Consumer-issued device peer copy per hop, ordered by a publication event.
107    PeerPull,
108}
109
110impl TpTransport {
111    /// The receipt spelling that goes in every announce line and every gate log.
112    pub fn name(self) -> &'static str {
113        match self {
114            Self::HostCanonical => "host-canonical",
115            Self::PeerPull => "peer-pull",
116        }
117    }
118}
119
120/// The general fleet flag.
121pub const TRANSPORT_ENV: &str = "MEMRA_TP_TRANSPORT";
122/// The family alias lane/glm5-tp-transport shipped. Still honored — its gate arms, its box
123/// scripts and every banked transport receipt set it. Never silently dead.
124pub const TRANSPORT_ENV_GLM5: &str = "MEMRA_GLM5_TP_TRANSPORT";
125
126/// Parse a transport value. Pure so the law is unit-testable without touching the process
127/// environment. `flag` is the name the operator actually set, so a refusal names it.
128///
129/// DEFAULT IS `host-canonical` (OFF), by decision, not by accident: on the day this flag
130/// landed the peer-pull arm had zero receipts on real peer hardware (the rig is a single
131/// card — `LAW:rig-exactness-only` — so its gate arms run the code path over N contexts on
132/// ONE device and can only prove bit-preservation, never fabric engagement). Unmeasured
133/// behaviour does not default ON. The default flips in the same commit as the box window's
134/// interleaved re-price receipt, and the FLAGS.md row carries both arms plus the rollback
135/// seam (`=0`).
136pub fn parse_transport(flag: &str, value: Option<&str>) -> Result<TpTransport, String> {
137    match value {
138        None | Some("") | Some("0") | Some("host-canonical") => Ok(TpTransport::HostCanonical),
139        Some("1") | Some("peer-pull") => Ok(TpTransport::PeerPull),
140        Some(other) => Err(format!(
141            "{flag}={other:?} is not a known transport \
142             (host-canonical | 0 = the v1 host-staged arm, the default; peer-pull | 1 = the \
143             consumer-issued device peer copy arm)"
144        )),
145    }
146}
147
148/// Resolve the general name and the family alias to ONE armed `(name, value)`, then parse.
149///
150/// This is a VALUED flag resolved ONCE at arm time, not a per-call boolean door, so a
151/// disagreeing pair REFUSES the load naming both names — the `ep_map::resolve_ep_map_env`
152/// stance. (Falling closed, which `lib.rs::alias_door_from` does for per-call doors, is only
153/// correct where an abort would kill live sessions; arming happens before any session exists,
154/// and a load that silently picked a transport the operator did not choose is worse than a
155/// refused load.)
156pub fn resolve_transport(
157    general: Option<&str>,
158    alias: Option<&str>,
159) -> Result<(TpTransport, &'static str), String> {
160    let (armed, value) = match (general, alias) {
161        (Some(g), Some(a)) if g != a => {
162            return Err(format!(
163                "{TRANSPORT_ENV}={g:?} and {TRANSPORT_ENV_GLM5}={a:?} disagree — the alias and \
164                 the general flag name ONE transport (unset one); refused rather than silently \
165                 picking a precedence winner"
166            ));
167        }
168        (Some(g), _) => (TRANSPORT_ENV, Some(g)),
169        (None, Some(a)) => (TRANSPORT_ENV_GLM5, Some(a)),
170        (None, None) => (TRANSPORT_ENV, None),
171    };
172    Ok((parse_transport(armed, value)?, armed))
173}
174
175/// Every transport name currently SET, for ROLLBACK ADVICE only — never for policy.
176///
177/// Why advice needs this and the armed name is not enough: a disagreeing pair is already
178/// refused at resolve time, so by the time anything can fail we are in one of two states —
179/// exactly one name set, or BOTH set to the same value. In the second state the armed name is
180/// the general one, so telling the operator to zero only that leaves the alias still asking
181/// for `peer-pull`, and THAT disagreement refuses the load. Advice that creates a second
182/// failure is worse than no advice.
183pub fn set_transport_names() -> Vec<&'static str> {
184    [TRANSPORT_ENV, TRANSPORT_ENV_GLM5]
185        .into_iter()
186        .filter(|k| std::env::var_os(k).is_some_and(|v| !v.is_empty()))
187        .collect()
188}
189
190/// Render [`set_transport_names`] as `NAME=0 [NAME=0]` — what to actually type.
191fn rollback_advice() -> String {
192    let names = set_transport_names();
193    if names.is_empty() {
194        return format!("{TRANSPORT_ENV}=0");
195    }
196    names
197        .iter()
198        .map(|n| format!("{n}=0"))
199        .collect::<Vec<_>>()
200        .join(" ")
201}
202
203/// Live read of [`resolve_transport`]. Returns the transport and the ARMED FLAG NAME, so
204/// downstream refusals and peer-access grants cite the flag the operator typed.
205pub fn transport_env() -> Result<(TpTransport, &'static str), String> {
206    resolve_transport(
207        std::env::var(TRANSPORT_ENV).ok().as_deref(),
208        std::env::var(TRANSPORT_ENV_GLM5).ok().as_deref(),
209    )
210}
211
212// ------------------------------------------------------------------------------------------
213// Instrument — the per-token movement census (lane stage 1b)
214// ------------------------------------------------------------------------------------------
215//
216// RESEARCH.md's shortlist item 2 asks for "our per-token collective COUNT and total BYTES on
217// the TP arm" and warns that bytes alone cannot explain the tax. These counters answer both
218// halves from the live walk instead of from arithmetic, in BOTH arms, so an A/B reads the
219// movement change directly rather than inferring it from tok/s.
220
221/// Host legs: one per `dtoh` or `htod` a TP hop performed. Pinned 0 on the peer-pull arm.
222pub static TP_HOST_LEGS: AtomicU64 = AtomicU64::new(0);
223
224/// Host legs that ended in a full stream drain (`Engine::dtoh` synchronizes). This is the
225/// count the 13-18 ms/token v1 join tax reconstructs from. Pinned 0 on the peer-pull arm.
226pub static TP_HOST_SYNCS: AtomicU64 = AtomicU64::new(0);
227
228/// Consumer-issued cross-rank device copies. Pinned 0 on the host-canonical arm.
229pub static TP_PEER_PULLS: AtomicU64 = AtomicU64::new(0);
230
231/// Publication events recorded/awaited to order a cross-rank copy. Pinned 0 on the
232/// host-canonical arm (whose ordering is the host sync itself).
233pub static TP_PUB_EVENTS: AtomicU64 = AtomicU64::new(0);
234
235/// Same-rank device copies a hop issued (own-part placements and dense block moves). Present
236/// in both arms; the host-canonical arm reaches its own part through the host instead.
237pub static TP_LOCAL_COPIES: AtomicU64 = AtomicU64::new(0);
238
239/// Total bytes a TP hop moved across a rank boundary, both arms, counted once per crossing
240/// (a host-canonical hop crosses PCIe twice and is charged twice — that IS its cost).
241pub static TP_XFER_BYTES: AtomicU64 = AtomicU64::new(0);
242
243macro_rules! snapshot_fns {
244    ($($name:ident => $counter:ident),* $(,)?) => {
245        $(
246            /// Snapshot for a gate's before/after delta.
247            pub fn $name() -> u64 { $counter.load(Ordering::Relaxed) }
248        )*
249    };
250}
251
252snapshot_fns! {
253    tp_host_legs => TP_HOST_LEGS,
254    tp_host_syncs => TP_HOST_SYNCS,
255    tp_peer_pulls => TP_PEER_PULLS,
256    tp_pub_events => TP_PUB_EVENTS,
257    tp_local_copies => TP_LOCAL_COPIES,
258    tp_xfer_bytes => TP_XFER_BYTES,
259}
260
261/// One line carrying every movement counter, for a gate log or a box-window receipt.
262pub fn transport_census_line(tag: &str, transport: TpTransport) -> String {
263    format!(
264        "[{tag}] census transport={} host_legs={} host_syncs={} peer_pulls={} \
265         pub_events={} local_copies={} xfer_bytes={}",
266        transport.name(),
267        tp_host_legs(),
268        tp_host_syncs(),
269        tp_peer_pulls(),
270        tp_pub_events(),
271        tp_local_copies(),
272        tp_xfer_bytes(),
273    )
274}
275
276fn charge_host_leg(bytes: usize, sync: bool) {
277    TP_HOST_LEGS.fetch_add(1, Ordering::Relaxed);
278    if sync {
279        TP_HOST_SYNCS.fetch_add(1, Ordering::Relaxed);
280    }
281    TP_XFER_BYTES.fetch_add(bytes as u64, Ordering::Relaxed);
282}
283
284fn charge_peer_pull(bytes: usize) {
285    TP_PEER_PULLS.fetch_add(1, Ordering::Relaxed);
286    TP_XFER_BYTES.fetch_add(bytes as u64, Ordering::Relaxed);
287}
288
289fn charge_local_copy() {
290    TP_LOCAL_COPIES.fetch_add(1, Ordering::Relaxed);
291}
292
293// ------------------------------------------------------------------------------------------
294// The publication link
295// ------------------------------------------------------------------------------------------
296
297/// The peer-pull ordering primitives, built once per TP runtime and REUSED per hop.
298///
299/// One publication event and one release event PER RANK, not per hop or per pair:
300/// `cuEventRecord` overwrites, and `cuStreamWaitEvent` captures the event's state at call
301/// time, so a single host thread issuing record-then-wait in program order gets correct
302/// ordering with `2 * ranks` events total. Creating an event per hop would put a driver
303/// allocation on the per-token path — the exact class of cost this lane exists to remove.
304///
305/// The RELEASE (write-after-read) half is NOT optional. Building this transport with only
306/// the publication half is exactly the defect the rig gate caught on its FIRST run: arm X1
307/// (peer-pull, sequential EP walk) failed DECODE SELF-CONSISTENCY at step 17 — two
308/// repetitions of the same greedy walk diverged — because every per-slot expert row is a
309/// fresh stream-ordered allocation on the peer whose async free was enqueued on the PEER
310/// stream while the ROOT stream's pull was still reading it. The step seam banks the same
311/// hazard as a keepalive comment (`tp.rs:7831-7834`). And note the more valuable half of
312/// that gate run: arm X2 (peer-pull composed with the EP dispatch diet) PASSED EVERY BAR on
313/// the same broken build — a VACUOUS GREEN; the undieted arm is the one that can see it,
314/// which is why both arms are gated and why the sequential walk is not retired.
315pub struct PeerPullLink {
316    /// `pub_ev[r]` is recorded on rank r's stream; awaited by a consumer before it reads
317    /// rank r's memory.
318    pub_ev: Vec<CudaEvent>,
319    /// `rel_ev[r]` is recorded on rank r's stream AFTER it has finished READING another
320    /// rank's buffer; the PRODUCING rank waits on it before proceeding, which is what stops
321    /// its stream-ordered allocator from recycling the buffer the reader's copy engine is
322    /// still reading.
323    rel_ev: Vec<CudaEvent>,
324}
325
326impl PeerPullLink {
327    pub fn new(engines: &[&Engine]) -> Result<Self, Box<dyn std::error::Error>> {
328        let mut pub_ev = Vec::with_capacity(engines.len());
329        let mut rel_ev = Vec::with_capacity(engines.len());
330        for e in engines {
331            pub_ev.push(e.ctx().new_event(None)?);
332            rel_ev.push(e.ctx().new_event(None)?);
333        }
334        Ok(Self { pub_ev, rel_ev })
335    }
336}
337
338/// Everything a hop needs: the rank engines (index = rank, `[0]` = root), the transport arm,
339/// and (on the peer-pull arm) the publication link. Built by the caller per hop from the TP
340/// runtime, so this module never has to own the runtime type and the borrow shapes at the
341/// call sites stay exactly as they are today.
342pub struct Hop<'a> {
343    pub engines: Vec<&'a Engine>,
344    pub transport: TpTransport,
345    pub link: Option<&'a PeerPullLink>,
346}
347
348impl<'a> Hop<'a> {
349    pub fn ranks(&self) -> usize {
350        self.engines.len()
351    }
352
353    pub fn engine(&self, r: usize) -> &'a Engine {
354        self.engines[r]
355    }
356
357    fn link(&self) -> Result<&'a PeerPullLink, Box<dyn std::error::Error>> {
358        self.link.ok_or_else(|| {
359            "glm5-tp transport: the peer-pull arm is armed without a publication link \
360             (runtime construction bug — refused rather than issuing an unordered peer read)"
361                .into()
362        })
363    }
364
365    /// Order `consumer`'s stream after everything `producer` has enqueued. One event record
366    /// plus one cross-context wait; NO host boundary.
367    ///
368    /// THE DESIGN DELTA vs the step seam, stated because it is the whole point of this lane.
369    /// `tp.rs`'s native-P2P pull sites fence the producer with a host
370    /// `engine.stream().synchronize()` — see the "PRODUCER FENCE (2026-08-20 flake fix)"
371    /// comments at `tp.rs:3543` and `tp.rs:2636`. That is correct and it is also a full
372    /// stream drain, i.e. it keeps the exact cost class this lane exists to delete: a
373    /// native-P2P arm built that way still pays one host sync per hop and would read as
374    /// "native P2P did not help". This link instead uses the `pp.rs` `BoundarySlot` ordering
375    /// contract (`ev_tx.record(s_tx)` then `s_rx.wait(&ev_tx)`, `pp.rs:2647`/`pp.rs:2678`),
376    /// which the hy3 PP-4 qualification closed at 50/50 fresh processes and 200/200 runtime
377    /// probes with zero byte mismatch on this exact card class — and which the tp2 lane
378    /// already cherry-picked onto this line (`tp2-20260831/LANE.md` stage 1). So: PULL
379    /// direction from `tp.rs`, EVENT ordering from `pp.rs`, host syncs from neither.
380    fn publish(&self, producer: usize, consumer: usize) -> Result<(), Box<dyn std::error::Error>> {
381        let link = self.link()?;
382        let ev = &link.pub_ev[producer];
383        {
384            let prod = self.engine(producer);
385            let _main = prod.gpu.enter_main()?;
386            ev.record(&prod.stream())?;
387        }
388        {
389            let cons = self.engine(consumer);
390            let _main = cons.gpu.enter_main()?;
391            cons.stream().wait(ev)?;
392        }
393        TP_PUB_EVENTS.fetch_add(2, Ordering::Relaxed);
394        Ok(())
395    }
396
397    /// The write-after-read half: order `producer`'s stream after `consumer` has finished
398    /// reading producer-owned memory, so the producer cannot recycle it under the reader.
399    /// Costs one event record plus one cross-context wait; no host boundary.
400    fn release(&self, producer: usize, consumer: usize) -> Result<(), Box<dyn std::error::Error>> {
401        let link = self.link()?;
402        let ev = &link.rel_ev[consumer];
403        {
404            let cons = self.engine(consumer);
405            let _main = cons.gpu.enter_main()?;
406            ev.record(&cons.stream())?;
407        }
408        {
409            let prod = self.engine(producer);
410            let _main = prod.gpu.enter_main()?;
411            prod.stream().wait(ev)?;
412        }
413        TP_PUB_EVENTS.fetch_add(2, Ordering::Relaxed);
414        Ok(())
415    }
416
417    /// The one cross-rank primitive: `consumer`'s stream copies `n` f32 from the producer's
418    /// `src[src_off..]` into its own `dst[dst_off..]`. A PEER READ by construction — the
419    /// copy is enqueued on the reading side.
420    #[allow(clippy::too_many_arguments)] // allow: (producer, src, src_off) x (consumer, dst, dst_off) x n IS the shape of a cross-rank ranged copy; collapsing it into a struct would hide which side owns which pointer, and that is the one thing a peer copy must never be vague about
421    fn pull_f32(
422        &self,
423        producer: usize,
424        src: &CudaSlice<f32>,
425        src_off: usize,
426        consumer: usize,
427        dst: &mut CudaSlice<f32>,
428        dst_off: usize,
429        n: usize,
430    ) -> Result<(), Box<dyn std::error::Error>> {
431        if src.len() < src_off + n || dst.len() < dst_off + n {
432            return Err(format!(
433                "glm5-tp peer pull geometry: src {}..{} of {} -> dst {}..{} of {}",
434                src_off,
435                src_off + n,
436                src.len(),
437                dst_off,
438                dst_off + n,
439                dst.len(),
440            )
441            .into());
442        }
443        {
444            let consumer_engine = self.engine(consumer);
445            // Rank-local CUDA scope: the consuming rank's context must be current for the copy
446            // to be enqueued on its stream and for the peer source pointer to resolve through
447            // that context's UVA mapping (the `Gpu::enter_main` contract in memra-runtime, and
448            // the discipline every `tp.rs` cross-context copy site follows). The scope CLOSES
449            // before the release fence below, which needs the producer's context current.
450            let _main = consumer_engine.gpu.enter_main()?;
451            let mut view = dst.slice_mut(dst_off..dst_off + n);
452            consumer_engine
453                .stream()
454                .memcpy_dtod(&src.slice(src_off..src_off + n), &mut view)?;
455        }
456        // WRITE-AFTER-READ FENCE. The read above runs on the CONSUMER's stream against memory
457        // the PRODUCER owns. Without this, the producer's stream is free to enqueue that
458        // buffer's async free (or any reuse of the recycled block) while the copy engine is
459        // still reading it — a silent cross-rank corruption that presents as run-to-run
460        // NON-DETERMINISM, which is how the rig gate found it (arm X1, decode
461        // self-consistency, step 17). See [`PeerPullLink`].
462        self.release(producer, consumer)?;
463        charge_peer_pull(n * std::mem::size_of::<f32>());
464        Ok(())
465    }
466}
467
468// ------------------------------------------------------------------------------------------
469// Hop shapes
470// ------------------------------------------------------------------------------------------
471//
472// Four shapes cover every cross-rank movement in the glm5 TP program. The call sites name
473// the shape; the arm lives here. Both arms move the SAME bytes by the same layout rules, so
474// swapping the arm cannot change a bit — which is why the gate bar stays decode BYTE
475// identity across the swap rather than a band.
476
477/// FAN-OUT: replicate root's `src` into a fresh buffer on EVERY peer rank. Returns the peer
478/// buffers indexed `[rank - 1]` (the `rt.peers` convention).
479///
480/// The mixer input `x`/`h` and the MoE activation `z` take this shape. Host-canonical pays
481/// ONE draining `dtoh` plus one `htod` per peer (identical to v1 at two ranks); peer-pull
482/// pays one publication chain and one peer read per peer.
483pub fn fanout_f32(
484    hop: &Hop<'_>,
485    src: &CudaSlice<f32>,
486    n: usize,
487) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
488    let bytes = n * std::mem::size_of::<f32>();
489    let mut out = Vec::with_capacity(hop.ranks() - 1);
490    match hop.transport {
491        TpTransport::HostCanonical => {
492            let host = hop.engine(ROOT).dtoh_view(&src.slice(0..n))?;
493            charge_host_leg(bytes, true);
494            for to in 1..hop.ranks() {
495                out.push(hop.engine(to).htod(&host)?);
496                charge_host_leg(bytes, false);
497            }
498        }
499        TpTransport::PeerPull => {
500            for to in 1..hop.ranks() {
501                let mut buf = hop.engine(to).uninit(n)?;
502                hop.publish(ROOT, to)?;
503                hop.pull_f32(ROOT, src, 0, to, &mut buf, 0, n)?;
504                out.push(buf);
505            }
506        }
507    }
508    Ok(out)
509}
510
511/// FAN-OUT to ONE rank: replicate root's `src` into a fresh buffer on rank `to`. The EP
512/// diet's shape (a rank whose routing owns zero pairs receives zero activation bytes — the
513/// placement-map multiplier); at two ranks it is exactly the whole-group fan-out.
514pub fn fanout_f32_to(
515    hop: &Hop<'_>,
516    to: usize,
517    src: &CudaSlice<f32>,
518    n: usize,
519) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
520    let bytes = n * std::mem::size_of::<f32>();
521    match hop.transport {
522        TpTransport::HostCanonical => {
523            let host = hop.engine(ROOT).dtoh_view(&src.slice(0..n))?;
524            charge_host_leg(bytes, true);
525            let out = hop.engine(to).htod(&host)?;
526            charge_host_leg(bytes, false);
527            Ok(out)
528        }
529        TpTransport::PeerPull => {
530            let mut buf = hop.engine(to).uninit(n)?;
531            hop.publish(ROOT, to)?;
532            hop.pull_f32(ROOT, src, 0, to, &mut buf, 0, n)?;
533            Ok(buf)
534        }
535    }
536}
537
538/// FAN-OUT, i32 twin (the MLA position vector). Same shape, same accounting.
539pub fn fanout_i32(
540    hop: &Hop<'_>,
541    src: &CudaSlice<i32>,
542    n: usize,
543) -> Result<Vec<CudaSlice<i32>>, Box<dyn std::error::Error>> {
544    let bytes = n * std::mem::size_of::<i32>();
545    let mut out = Vec::with_capacity(hop.ranks() - 1);
546    match hop.transport {
547        TpTransport::HostCanonical => {
548            let host = hop.engine(ROOT).dtoh_i32(src)?;
549            charge_host_leg(bytes, true);
550            for to in 1..hop.ranks() {
551                out.push(hop.engine(to).htod_i32(&host)?);
552                charge_host_leg(bytes, false);
553            }
554        }
555        TpTransport::PeerPull => {
556            for to in 1..hop.ranks() {
557                let mut buf = hop.engine(to).uninit_i32(n)?;
558                hop.publish(ROOT, to)?;
559                {
560                    let consumer = hop.engine(to);
561                    let _main = consumer.gpu.enter_main()?;
562                    let mut view = buf.slice_mut(0..n);
563                    consumer.stream().memcpy_dtod(&src.slice(0..n), &mut view)?;
564                }
565                // The same write-after-read fence `Hop::pull_f32` applies; the i32 twin is
566                // not exempt, and an exemption here would be a hazard that only shows up
567                // under load.
568                hop.release(ROOT, to)?;
569                charge_peer_pull(bytes);
570                out.push(buf);
571            }
572        }
573    }
574    Ok(out)
575}
576
577/// GATHER: reconstruct the FULL token-major `[t, ranks * part]` tensor on EVERY rank from
578/// each rank's dense `[t, part]` shard (`parts[r]` resident on rank r, laid at columns
579/// `r*part..(r+1)*part`). Returns the full tensors indexed by rank.
580///
581/// This is the column-parallel-over-gather join: after it, each rank's `wo` column slice is
582/// a full-K matvec over identical bytes, which is what makes the join pure movement.
583///
584/// The peer-pull arm moves ONE dense block per (producer, consumer) direction and
585/// reconstructs the interleave with `place_rows_strided` — the kernel whose own doc says it
586/// "exists so multi-GPU collectives can move one dense shard per rank and reconstruct the
587/// canonical token-major matrix without issuing one peer copy per token". At `t == 1` the
588/// placement degenerates to a contiguous range and is skipped entirely: the pull lands
589/// straight at its column offset.
590pub fn gather_parts(
591    hop: &Hop<'_>,
592    parts: &[&CudaSlice<f32>],
593    t: usize,
594    part: usize,
595) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
596    let ranks = hop.ranks();
597    if t == 0 || part == 0 {
598        return Err("glm5-tp gather: zero geometry".into());
599    }
600    if parts.len() != ranks {
601        return Err(format!("glm5-tp gather: {} parts for {ranks} ranks", parts.len()).into());
602    }
603    let full = ranks * part;
604    let span = t * part;
605    for (r, p) in parts.iter().enumerate() {
606        if p.len() < span {
607            return Err(format!(
608                "glm5-tp gather geometry: part[{r}] {} for t={t} part={part}",
609                p.len()
610            )
611            .into());
612        }
613    }
614    let bytes = span * std::mem::size_of::<f32>();
615    match hop.transport {
616        TpTransport::HostCanonical => {
617            // Drain every rank's part (peers first, root last — v1's order at two ranks),
618            // assemble the token-major full matrix once on host, upload to every rank.
619            let mut hosts: Vec<Option<Vec<f32>>> = (0..ranks).map(|_| None).collect();
620            for r in (0..ranks).rev() {
621                hosts[r] = Some(hop.engine(r).dtoh_view(&parts[r].slice(0..span))?);
622                charge_host_leg(bytes, true);
623            }
624            let mut full_host = vec![0f32; t * full];
625            for (r, h) in hosts.iter().enumerate() {
626                let h = h.as_ref().expect("drained above");
627                for tok in 0..t {
628                    full_host[tok * full + r * part..tok * full + (r + 1) * part]
629                        .copy_from_slice(&h[tok * part..(tok + 1) * part]);
630                }
631            }
632            let mut out = Vec::with_capacity(ranks);
633            for r in 0..ranks {
634                out.push(hop.engine(r).htod(&full_host)?);
635                charge_host_leg(t * full * std::mem::size_of::<f32>(), false);
636            }
637            Ok(out)
638        }
639        TpTransport::PeerPull => {
640            // Every producer publishes to every consumer (record-then-wait per ordered
641            // pair — the same primitive count as v1's two publish calls at two ranks),
642            // then every consumer pulls each foreign part.
643            for s in 0..ranks {
644                for d in 0..ranks {
645                    if s != d {
646                        hop.publish(s, d)?;
647                    }
648                }
649            }
650            let mut out = Vec::with_capacity(ranks);
651            for d in 0..ranks {
652                let mut full_d = hop.engine(d).uninit(t * full)?;
653                if t == 1 {
654                    // Decode: the strided placement IS a contiguous range, so land every
655                    // part directly at its column offset and skip the placement kernels.
656                    for s in 0..ranks {
657                        if s == d {
658                            hop.engine(d).copy_range_into(
659                                &mut full_d,
660                                s * part,
661                                parts[s],
662                                0,
663                                part,
664                            )?;
665                            charge_local_copy();
666                        } else {
667                            hop.pull_f32(s, parts[s], 0, d, &mut full_d, s * part, part)?;
668                        }
669                    }
670                } else {
671                    // Prime: one dense block per foreign direction, then byte-preserving
672                    // placements.
673                    for s in 0..ranks {
674                        if s == d {
675                            hop.engine(d).place_rows_strided(
676                                parts[s],
677                                &mut full_d,
678                                part,
679                                t,
680                                full,
681                                s * part,
682                            )?;
683                            charge_local_copy();
684                        } else {
685                            let mut foreign = hop.engine(d).uninit(span)?;
686                            hop.pull_f32(s, parts[s], 0, d, &mut foreign, 0, span)?;
687                            hop.engine(d).place_rows_strided(
688                                &foreign,
689                                &mut full_d,
690                                part,
691                                t,
692                                full,
693                                s * part,
694                            )?;
695                            charge_local_copy();
696                        }
697                    }
698                }
699                out.push(full_d);
700            }
701            Ok(out)
702        }
703    }
704}
705
706/// CONCAT-ON-ROOT: assemble the mixer output `[t, ranks * part]` on ROOT from the per-rank
707/// column-`wo` slices, rank r's rows at columns `r*part..(r+1)*part`.
708///
709/// Root is the only consumer (the residual stream and mHC are root-owned), so only the
710/// foreign directions cross. Same `t == 1` fast path as [`gather_parts`].
711pub fn concat_parts_on_root(
712    hop: &Hop<'_>,
713    parts: &[&CudaSlice<f32>],
714    t: usize,
715    part: usize,
716) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
717    let ranks = hop.ranks();
718    if t == 0 || part == 0 {
719        return Err("glm5-tp concat: zero geometry".into());
720    }
721    if parts.len() != ranks {
722        return Err(format!("glm5-tp concat: {} parts for {ranks} ranks", parts.len()).into());
723    }
724    let full = ranks * part;
725    let span = t * part;
726    for (r, p) in parts.iter().enumerate() {
727        if p.len() < span {
728            return Err(format!(
729                "glm5-tp concat geometry: part[{r}] {} for t={t} part={part}",
730                p.len()
731            )
732            .into());
733        }
734    }
735    let bytes = span * std::mem::size_of::<f32>();
736    match hop.transport {
737        TpTransport::HostCanonical => {
738            let mut hosts: Vec<Option<Vec<f32>>> = (0..ranks).map(|_| None).collect();
739            for r in (0..ranks).rev() {
740                hosts[r] = Some(hop.engine(r).dtoh_view(&parts[r].slice(0..span))?);
741                charge_host_leg(bytes, true);
742            }
743            let mut out_host = vec![0f32; t * full];
744            for (r, h) in hosts.iter().enumerate() {
745                let h = h.as_ref().expect("drained above");
746                for tok in 0..t {
747                    out_host[tok * full + r * part..tok * full + (r + 1) * part]
748                        .copy_from_slice(&h[tok * part..(tok + 1) * part]);
749                }
750            }
751            let out = hop.engine(ROOT).htod(&out_host)?;
752            charge_host_leg(t * full * std::mem::size_of::<f32>(), false);
753            Ok(out)
754        }
755        TpTransport::PeerPull => {
756            let mut out = hop.engine(ROOT).uninit(t * full)?;
757            for s in 1..ranks {
758                hop.publish(s, ROOT)?;
759            }
760            if t == 1 {
761                for s in 0..ranks {
762                    if s == ROOT {
763                        hop.engine(ROOT)
764                            .copy_range_into(&mut out, s * part, parts[s], 0, part)?;
765                        charge_local_copy();
766                    } else {
767                        hop.pull_f32(s, parts[s], 0, ROOT, &mut out, s * part, part)?;
768                    }
769                }
770            } else {
771                for s in 0..ranks {
772                    if s == ROOT {
773                        hop.engine(ROOT).place_rows_strided(
774                            parts[s],
775                            &mut out,
776                            part,
777                            t,
778                            full,
779                            s * part,
780                        )?;
781                        charge_local_copy();
782                    } else {
783                        let mut foreign = hop.engine(ROOT).uninit(span)?;
784                        hop.pull_f32(s, parts[s], 0, ROOT, &mut foreign, 0, span)?;
785                        hop.engine(ROOT).place_rows_strided(
786                            &foreign,
787                            &mut out,
788                            part,
789                            t,
790                            full,
791                            s * part,
792                        )?;
793                        charge_local_copy();
794                    }
795                }
796            }
797            Ok(out)
798        }
799    }
800}
801
802/// v1-SHAPE FAN-OUT, part 1: stage the producer's whole block to HOST in one draining leg.
803///
804/// Exists only so the `host-canonical` arm of the SEQUENTIAL EP walk keeps its v1 hop pattern
805/// exactly — one `dtoh` of `z` per layer-call, then one row `htod` per token per consuming
806/// rank. That pattern is what the banked 22.65 tok/s engine-twin number was measured on, and
807/// `MEMRA_GLM5_TP_TRANSPORT=0` has to reproduce it hop-for-hop, not just byte-for-byte, or the
808/// rollback arm silently becomes a different (faster) program and the A/B measures two changes.
809pub fn host_stage_block(
810    hop: &Hop<'_>,
811    from: usize,
812    src: &CudaSlice<f32>,
813    n: usize,
814) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
815    let host = hop.engine(from).dtoh_view(&src.slice(0..n))?;
816    charge_host_leg(n * std::mem::size_of::<f32>(), true);
817    Ok(host)
818}
819
820/// v1-SHAPE FAN-OUT, part 2: upload one staged host row to rank `to` (one non-draining leg).
821pub fn host_row_to(
822    hop: &Hop<'_>,
823    to: usize,
824    row: &[f32],
825) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
826    let out = hop.engine(to).htod(row)?;
827    charge_host_leg(std::mem::size_of_val(row), false);
828    Ok(out)
829}
830
831/// BLOCK-RETURN: land rank `from`'s dense `[rows, width]` block into `dst[dst_off..]` on
832/// root. The EP walk's shape: the dieted arm returns one compact block per (layer-call,
833/// rank); the v1 sequential arm returns one expert row per foreign-owned slot. `n` is
834/// `rows * width`.
835pub fn return_block_to_root(
836    hop: &Hop<'_>,
837    from: usize,
838    blk: &CudaSlice<f32>,
839    dst: &mut CudaSlice<f32>,
840    dst_off: usize,
841    n: usize,
842) -> Result<(), Box<dyn std::error::Error>> {
843    let bytes = n * std::mem::size_of::<f32>();
844    match hop.transport {
845        TpTransport::HostCanonical => {
846            let host = hop.engine(from).dtoh_view(&blk.slice(0..n))?;
847            charge_host_leg(bytes, true);
848            hop.engine(ROOT).htod_f32_into_at(&host, dst, dst_off)?;
849            charge_host_leg(bytes, false);
850            Ok(())
851        }
852        TpTransport::PeerPull => {
853            hop.publish(from, ROOT)?;
854            hop.pull_f32(from, blk, 0, ROOT, dst, dst_off, n)
855        }
856    }
857}
858
859/// BLOCK-RETURN into a FRESH root buffer (the v1 per-slot EP shape, which hands the returned
860/// row straight to `axpy_into`).
861pub fn return_row_to_root(
862    hop: &Hop<'_>,
863    from: usize,
864    y: &CudaSlice<f32>,
865    n: usize,
866) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
867    match hop.transport {
868        TpTransport::HostCanonical => {
869            let host = hop.engine(from).dtoh_view(&y.slice(0..n))?;
870            charge_host_leg(n * std::mem::size_of::<f32>(), true);
871            let out = hop.engine(ROOT).htod(&host)?;
872            charge_host_leg(n * std::mem::size_of::<f32>(), false);
873            Ok(out)
874        }
875        TpTransport::PeerPull => {
876            let mut out = hop.engine(ROOT).uninit(n)?;
877            hop.publish(from, ROOT)?;
878            hop.pull_f32(from, y, 0, ROOT, &mut out, 0, n)?;
879            Ok(out)
880        }
881    }
882}
883
884// ------------------------------------------------------------------------------------------
885// Arming: the byte-integrity pull ladder
886// ------------------------------------------------------------------------------------------
887
888/// Payload sizes the arm-time ladder validates, mirroring the step seam's
889/// `NATIVE_P2P_PROBE_WORDS` (16 KiB .. 64 MiB) so a glm5 refusal and a step refusal name the
890/// same fabric with the same numbers. The bottom rung is deliberately BELOW our decode hop
891/// sizes (a decode gather part is <= 16 KiB at hidden 4096 f32) and the top rung above any
892/// prime chunk block, because RESEARCH.md §2.4 records that LL and Simple protocol paths
893/// "can fail differently" by size and §2.10 records phantom readings that only a byte check
894/// catches.
895const PULL_PROBE_WORDS: &[usize] = &[4_096, 16_384, 262_144, 16_777_216];
896
897static TRANSPORT_MARKED: AtomicBool = AtomicBool::new(false);
898
899/// Arm the transport for a TP load. On the peer-pull arm this validates the EXACT primitive
900/// the walk uses, in EVERY ordered rank pair, at every rung of [`PULL_PROBE_WORDS`], against
901/// a poisoned destination — and refuses the load on a single differing word.
902///
903/// `same_device_gate` is the one-card rig emulation (N contexts on one device): the pull
904/// primitive is exercised unchanged, the ladder still runs, and the announce says so, but no
905/// claim about a real fabric is made or implied.
906///
907/// What this DOES prove: our copy path moves the right bytes in every direction at four
908/// sizes. What it does NOT prove (RESEARCH.md §2.4's ladder, stated so nobody reads it
909/// wider): that a KERNEL dereference of a peer pointer works. On direct-attach (`NODE`)
910/// hosts the driver stages SM-issued peer access through system memory by default (§2.3b)
911/// and "`nvidia-smi topo -p2p r` returns OK while `cudaMemcpy` looks healthy, so neither
912/// detects it". Only a `simpleP2P`-class kernel peer read does; that lives in the lane's
913/// `peer-read-probe.cu` and is a HEALTH.sh item, not a serving-path dependency — this
914/// transport never dereferences a peer pointer from a kernel.
915pub fn arm_transport(
916    transport: TpTransport,
917    armed_flag: &str,
918    tag: &str,
919    engines: &[&Engine],
920    same_device_gate: bool,
921) -> Result<Option<PeerPullLink>, Box<dyn std::error::Error>> {
922    if transport == TpTransport::HostCanonical {
923        announce(
924            tag,
925            transport,
926            same_device_gate,
927            "host staging, no peer mapping",
928        );
929        return Ok(None);
930    }
931    if same_device_gate {
932        // N contexts on ONE device: there is no peer to grant (`cuDeviceCanAccessPeer`
933        // of a device with itself is not a peer relation) and no fabric to cross. The pull
934        // primitive still runs, unchanged, over the contexts' UVA mappings — which is
935        // what makes the rig gate a real test of the CODE and an explicit non-test of the
936        // FABRIC. Skipping the grant here is why the gate arm cannot silently pass on a box
937        // where the grant would have failed: on a real group the grants run and refuse.
938        eprintln!(
939            "[{tag}] same-device gate: peer-access grant SKIPPED (one device, {} \
940             contexts); the ladder below proves bit-preservation only, never fabric engagement",
941            engines.len(),
942        );
943    } else {
944        for (i, a) in engines.iter().enumerate() {
945            for (j, b) in engines.iter().enumerate() {
946                if i != j {
947                    crate::tp::grant_peer_access(a, b, &format!("{armed_flag}=peer-pull"))?;
948                }
949            }
950        }
951    }
952    let link = PeerPullLink::new(engines)?;
953    let hop = Hop {
954        engines: engines.to_vec(),
955        transport,
956        link: Some(&link),
957    };
958    // MEASUREMENT HYGIENE. The ladder below drives the SAME instrumented primitive the walk
959    // does, so it charges the movement census — and it charges it HARD: 4 rungs x every
960    // ordered pair is >100 MiB of arm-time traffic against ~15 MiB per DECODE TOKEN of real
961    // walk traffic. The first gate run made this visible and unmissable: `xfer_bytes` on the
962    // peer-pull arm read 25x the host-canonical arm's, which is the opposite of the truth
963    // (peer-pull crosses PCIe HALF as much, because a host bounce crosses it twice). A box
964    // window reading `xfer_bytes` deltas would have derived a bytes/token figure that was
965    // ~97% arming noise. So: snapshot before, restore after. Arm-time validation traffic is
966    // not walk traffic and must not appear in a per-token census. The ladder's own receipt
967    // is its PASS line.
968    let census_before = (
969        TP_PEER_PULLS.load(Ordering::Relaxed),
970        TP_PUB_EVENTS.load(Ordering::Relaxed),
971        TP_XFER_BYTES.load(Ordering::Relaxed),
972        TP_HOST_LEGS.load(Ordering::Relaxed),
973        TP_HOST_SYNCS.load(Ordering::Relaxed),
974        TP_LOCAL_COPIES.load(Ordering::Relaxed),
975    );
976    let ranks = engines.len();
977    for &words in PULL_PROBE_WORDS {
978        for producer in 0..ranks {
979            for consumer in 0..ranks {
980                if consumer == producer {
981                    continue;
982                }
983                let expected: Vec<f32> = (0..words)
984                    .map(|i| {
985                        f32::from_bits(
986                            (i as u32)
987                                .wrapping_mul(0x9e37_79b9)
988                                .wrapping_add(((producer as u32) + 1) << 16)
989                                // Keep every probe word a finite, non-NaN pattern so a
990                                // comparison failure is a TRANSPORT fact and never a float
991                                // identity artifact.
992                                & 0x7f7f_ffff,
993                        )
994                    })
995                    .collect();
996                let poison: Vec<f32> = expected.iter().map(|v| -*v - 1.0).collect();
997                let src = hop.engine(producer).htod(&expected)?;
998                let mut dst = hop.engine(consumer).htod(&poison)?;
999                hop.publish(producer, consumer)?;
1000                hop.pull_f32(producer, &src, 0, consumer, &mut dst, 0, words)?;
1001                let actual = hop.engine(consumer).dtoh(&dst)?;
1002                let mismatches = actual
1003                    .iter()
1004                    .zip(&expected)
1005                    .filter(|(a, b)| a.to_bits() != b.to_bits())
1006                    .count();
1007                if mismatches != 0 {
1008                    return Err(format!(
1009                        "{armed_flag}=peer-pull byte-integrity ladder FAILED: \
1010                         rank{producer}->rank{consumer} at {} bytes, {mismatches}/{words} \
1011                         words differ (refused before any layer was sharded; roll back \
1012                         with: {})",
1013                        words * std::mem::size_of::<f32>(),
1014                        rollback_advice(),
1015                    )
1016                    .into());
1017                }
1018            }
1019        }
1020    }
1021    // Restore the census to its pre-ladder state (see the hygiene note above).
1022    let restore = |counter: &AtomicU64, before: u64| {
1023        let now = counter.load(Ordering::Relaxed);
1024        counter.fetch_sub(now.saturating_sub(before), Ordering::Relaxed);
1025    };
1026    restore(&TP_PEER_PULLS, census_before.0);
1027    restore(&TP_PUB_EVENTS, census_before.1);
1028    restore(&TP_XFER_BYTES, census_before.2);
1029    restore(&TP_HOST_LEGS, census_before.3);
1030    restore(&TP_HOST_SYNCS, census_before.4);
1031    restore(&TP_LOCAL_COPIES, census_before.5);
1032    eprintln!(
1033        "[{tag}] peer-pull byte-integrity ladder PASS: directions={} \
1034         byte_ladder={:?} mismatches=0 same_device_gate={same_device_gate} \
1035         census_excluded=arm-time-ladder-traffic",
1036        ranks * (ranks - 1),
1037        PULL_PROBE_WORDS
1038            .iter()
1039            .map(|w| w * std::mem::size_of::<f32>())
1040            .collect::<Vec<_>>(),
1041    );
1042    announce(
1043        tag,
1044        transport,
1045        same_device_gate,
1046        "consumer-issued cuMemcpyDtoDAsync, event-published, atomics-free",
1047    );
1048    Ok(Some(link))
1049}
1050
1051fn announce(tag: &str, transport: TpTransport, same_device_gate: bool, how: &str) {
1052    if TRANSPORT_MARKED.swap(true, Ordering::Relaxed) {
1053        return;
1054    }
1055    eprintln!(
1056        "[{tag}] armed transport={} shape={how} same_device_gate={same_device_gate} \
1057         performance_claim=false",
1058        transport.name(),
1059    );
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064    use super::*;
1065
1066    #[test]
1067    fn transport_parse_is_literal_and_fail_closed() {
1068        for off in [None, Some(""), Some("0"), Some("host-canonical")] {
1069            assert_eq!(
1070                parse_transport(TRANSPORT_ENV, off).unwrap(),
1071                TpTransport::HostCanonical
1072            );
1073        }
1074        for on in [Some("1"), Some("peer-pull")] {
1075            assert_eq!(
1076                parse_transport(TRANSPORT_ENV, on).unwrap(),
1077                TpTransport::PeerPull
1078            );
1079        }
1080        // Every other spelling refuses BY NAME rather than silently picking an arm — a
1081        // typo'd transport must never serve the other one. Asserted under BOTH names, so a
1082        // banked script setting the alias reads its own name back.
1083        for bad in ["peer_pull", "peerpull", "p2p", "native", "2", "on", "true"] {
1084            for flag in [TRANSPORT_ENV, TRANSPORT_ENV_GLM5] {
1085                let err =
1086                    parse_transport(flag, Some(bad)).expect_err("unknown transport must refuse");
1087                assert!(err.contains(flag), "{err}");
1088                assert!(err.contains(bad), "{err}");
1089                assert!(err.contains("host-canonical"), "{err}");
1090                assert!(err.contains("peer-pull"), "{err}");
1091            }
1092        }
1093    }
1094
1095    #[test]
1096    fn alias_resolution_honors_both_names_and_refuses_a_disagreeing_pair() {
1097        // unset/unset -> the default, cited under the general name
1098        assert_eq!(
1099            resolve_transport(None, None).unwrap(),
1100            (TpTransport::HostCanonical, TRANSPORT_ENV)
1101        );
1102        // either name alone arms, and the ARMED NAME comes back with it
1103        assert_eq!(
1104            resolve_transport(Some("peer-pull"), None).unwrap(),
1105            (TpTransport::PeerPull, TRANSPORT_ENV)
1106        );
1107        assert_eq!(
1108            resolve_transport(None, Some("peer-pull")).unwrap(),
1109            (TpTransport::PeerPull, TRANSPORT_ENV_GLM5)
1110        );
1111        // an agreeing pair resolves to the general name
1112        assert_eq!(
1113            resolve_transport(Some("1"), Some("1")).unwrap(),
1114            (TpTransport::PeerPull, TRANSPORT_ENV)
1115        );
1116        // a DISAGREEING pair refuses, naming both — arming happens before any session exists,
1117        // so refusing is safe and a silently-chosen transport is not
1118        for (g, a) in [("peer-pull", "0"), ("0", "1"), ("1", "host-canonical")] {
1119            let err = resolve_transport(Some(g), Some(a))
1120                .expect_err("a disagreeing pair must refuse the load");
1121            assert!(err.contains(TRANSPORT_ENV), "{err}");
1122            assert!(err.contains(TRANSPORT_ENV_GLM5), "{err}");
1123            assert!(err.contains("disagree"), "{err}");
1124        }
1125        // NOT a disagreement by meaning, but IS one by string: two spellings of the same arm.
1126        // Deliberately literal rather than normalize-then-compare, so an operator who typed
1127        // two different things is told instead of having one silently win.
1128        assert!(resolve_transport(Some("0"), Some("host-canonical")).is_err());
1129    }
1130
1131    #[test]
1132    fn default_is_the_v1_arm() {
1133        // The written default (docs/FLAGS.md): unmeasured behaviour does not default ON.
1134        assert_eq!(
1135            parse_transport(TRANSPORT_ENV, None).unwrap(),
1136            TpTransport::HostCanonical
1137        );
1138        assert_eq!(TpTransport::HostCanonical.name(), "host-canonical");
1139        assert_eq!(TpTransport::PeerPull.name(), "peer-pull");
1140    }
1141
1142    /// The census line is a receipt: every counter must be named in it, because a box window
1143    /// greps this line and a missing field reads as a zero.
1144    #[test]
1145    fn census_line_names_every_counter() {
1146        let line = transport_census_line("glm5-tp-transport", TpTransport::PeerPull);
1147        for field in [
1148            "transport=peer-pull",
1149            "host_legs=",
1150            "host_syncs=",
1151            "peer_pulls=",
1152            "pub_events=",
1153            "local_copies=",
1154            "xfer_bytes=",
1155        ] {
1156            assert!(line.contains(field), "{line} is missing {field}");
1157        }
1158    }
1159}