Skip to main content

memra_engine/
pp.rs

1//! M2 pipeline-parallel N-stage runtime (generalizes the M1 2-stage seam).
2//!
3//! Door: `MEMRA_PP_STAGES=N` (default OFF — unset/0/1 = no behavior change anywhere).
4//! Stage map: N stages over the trunk layers with N-1 cuts. `MEMRA_PP_SPLITS=c1,..,cN-1`
5//! sets the cuts explicitly (strictly increasing, in (0, n_layers)); `MEMRA_PP_SPLIT=<i>`
6//! is the N=2 back-compat spelling; default = even split (cut s = s*n_layers/N).
7//! Placement: `MEMRA_PP_DEVICES=d0,..,dN-1` maps stage s to device ds (default: all on
8//! the primary engine's device).
9//!
10//! M1 history (increments 1-2, merged + hardened on the 8x box 2026-08-02): seam + gate
11//! single-device; then real transport — per-stage streams/events, device placement,
12//! peer-copy boundary (M0: cudaMemcpyPeerAsync beats NCCL 2.8x at PP activation sizes),
13//! per-context PDL module caches, default-mempool peer grants. All five r3 gates PASS
14//! bit-identical (receipts ~/receipts/m1-pp2/ on darklanes-bench).
15//!
16//! M2 increment 1 (this file): N-STAGE GENERALIZATION — `Pp2Rt` becomes `PpNRt`:
17//!   - `stages`: Vec of per-stage execution homes (device, context, stream, remote Engine);
18//!   - `boundaries`: N-1 boundary runtimes, each with TWO persistent double-buffered slots
19//!     (ev_tx/ev_rx per slot) and its own overlap step counter; transport is selected PER
20//!     BOUNDARY (dtod same-device / cudaMemcpyPeerAsync cross-device by default; opt-in
21//!     `MEMRA_PP_HOST_BOUNCE=1` uses pinned D2H + H2D instead);
22//!   - the default peer transport grants peer + default-mempool access between EVERY distinct
23//!     pair of devices in use. Host bounce skips serving-time grants; its boot diagnostics
24//!     transiently enable peer + pool access, then revoke the pool grants and disable peer access
25//!     before proceeding. Sharded weights plus stage-local auxiliary buffers ensure that no peer
26//!     read can bypass the bounced boundary.
27//!
28//! M2 increment 2 (weight sharding): the loader uploads each stage's layer range THROUGH
29//! that stage's engine (`layer_engine`), so weights land on the device that runs them —
30//! the bring-up peer-read placement dies. `output_norm` + lm head load through the LAST
31//! stage's engine; the embed table stays host-side with stage 0. Split-plane/f16 decode
32//! mirrors are built per layer through the owning stage's engine too (the rp4 mirrors ARE
33//! the decode weights on the q8 path — leaving them on dev0 would fake the kill).
34//! Rollback seam: `MEMRA_PP_SHARD=0` = M1 bring-up placement (all weights on primary,
35//! remote stages peer-read).
36//!
37//! M2 increment 3 (deferred readback — the pipelining seed): `PendingLogits` — the eager
38//! decode arm can END a step without the logits D2H (`decode_step_h_ppn_deferred`): the
39//! logits stay device-resident with a completion event; `wait()` drains them through a
40//! DEDICATED readback stream (waits the event, copies, syncs) so tokens t+1.. keep
41//! enqueuing on the stage streams while token t drains. Per-token math is fully
42//! event-ordered (same slots, same ev_tx/ev_rx chain) — scheduling changes, math does
43//! not; the pipelined replay arm of `ppn-gate` proves bit-identity per step.
44//!
45//! Ownership across a boundary (unchanged from M1):
46//!   - hidden state [n_embd] f32 is the ONLY tensor that crosses;
47//!   - KV/linear-attn cache entries are per-layer: stage s exclusively owns cache state
48//!     for its layer range (and, under MEMRA_PP_DEVICES, allocates it on its device);
49//!   - position/rope state is the scalar `cache.pos` snapshot taken once per step; every stage
50//!     uploads its own position buffer on its own stream (no cross-device position pointer);
51//!   - the embed table lives with stage 0, output_norm + lm head with the last stage.
52//!
53//! THE MULTI-STREAM LAW (why this is safe with cudarc event tracking disabled): all
54//! cross-stage bytes flow through the persistent boundary slots, ordered by ev_tx/ev_rx;
55//! per-stage scratch is allocated AND freed on that stage's stream (stream-ordered); the
56//! async mem pool runs with opportunistic reuse OFF + internal dependencies ON
57//! (memra-runtime), so a block freed on stream A and reused on stream B carries a
58//! driver-inserted dependency. Weights are load-time state no stage stream can precede,
59//! and the step's terminal logits readback (sync D2H, or PendingLogits' event-ordered
60//! readback stream) drains the last stage, whose TX-wait chain transitively drains all.
61//!
62//! Scope: plain eager decode only (generic arm N-stage; gemma4 arm 2-stage). NOT wired:
63//! batch/dc/graph/spec loops and the gemma4-E4B eager arm.
64//!
65//! CORRECTION (pp2-hardening 2026-08-06): this header used to add "(`warn_unwired_once`
66//! fires)" to that list, which was wrong. `warn_unwired_once` has exactly two call sites
67//! and BOTH are gemma4-specific (decode.rs, hybrid_forward.rs) — the batch/dc/graph/spec
68//! loops never warned. Worse, the batched loop did not merely run unsplit: it walked the
69//! whole trunk on the primary stream and, under a sharded cross-device placement,
70//! peer-read every remote stage's weights each step — 28x slower at B=1 with all three
71//! `decode-batch-gate` gates PASSING (peer reads are byte-exact, so only perf broke).
72//! `decode_step_batch` now FAILS CLOSED in that regime via `pp_sharded_cross_device()`
73//! (`MEMRA_PP_ALLOW_UNSPLIT_BATCH=1` = measurement override). "Unwired" for dc/graph/spec
74//! still means "runs unsplit, silently" — audit each before trusting it on a pair.
75
76use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
77use std::sync::{Arc, Mutex, OnceLock};
78
79use cudarc::driver::{CudaContext, CudaEvent, CudaSlice, CudaStream};
80
81use crate::Engine;
82
83/// Returns the stage fence iff the ppN door is open: `MEMRA_PP_STAGES=N` (N >= 2) with a
84/// valid cut list. The fence has N+1 entries: `[0, c1, .., cN-1, n_layers]`; stage s runs
85/// layers `[fence[s], fence[s+1])`. Reads the environment on every call (gates toggle the
86/// door in-process); the cost is a few getenv per decode step, eager-loop noise.
87pub fn pp_cuts(n_layers: usize) -> Option<Vec<usize>> {
88    let n_st: usize = match std::env::var("MEMRA_PP_STAGES") {
89        Ok(v) if v.is_empty() || v == "0" || v == "1" => return None,
90        Ok(v) => match v.parse::<usize>() {
91            Ok(n) => n,
92            Err(_) => {
93                warn_bad_once(&format!("MEMRA_PP_STAGES={v} unparseable; door stays OFF"));
94                return None;
95            }
96        },
97        Err(_) => return None,
98    };
99    if n_st < 2 || n_st > n_layers {
100        warn_bad_once(&format!(
101            "MEMRA_PP_STAGES={n_st} outside [2, n_layers={n_layers}]; door stays OFF"
102        ));
103        return None;
104    }
105    let mut fence = Vec::with_capacity(n_st + 1);
106    fence.push(0usize);
107    if let Ok(s) = std::env::var("MEMRA_PP_SPLITS") {
108        let parts: Result<Vec<usize>, _> =
109            s.split(',').map(|p| p.trim().parse::<usize>()).collect();
110        match parts {
111            Ok(cuts) if cuts.len() == n_st - 1 => fence.extend(cuts),
112            _ => {
113                warn_bad_once(&format!(
114                    "MEMRA_PP_SPLITS={s} invalid (want {} comma-separated cuts); door stays OFF",
115                    n_st - 1
116                ));
117                return None;
118            }
119        }
120    } else if let Ok(v) = std::env::var("MEMRA_PP_SPLIT") {
121        // N=2 back-compat spelling. With N>2 a single split is ambiguous — fail the door
122        // loudly rather than guess (a silent even-split would fake a gate config).
123        if n_st != 2 {
124            warn_bad_once(&format!(
125                "MEMRA_PP_SPLIT={v} set with MEMRA_PP_STAGES={n_st}; use MEMRA_PP_SPLITS \
126                 for N>2 — door stays OFF"
127            ));
128            return None;
129        }
130        match v.parse::<usize>() {
131            Ok(c) => fence.push(c),
132            Err(_) => {
133                warn_bad_once(&format!("MEMRA_PP_SPLIT={v} unparseable; door stays OFF"));
134                return None;
135            }
136        }
137    } else {
138        for s in 1..n_st {
139            fence.push(s * n_layers / n_st);
140        }
141    }
142    fence.push(n_layers);
143    for w in fence.windows(2) {
144        if w[0] >= w[1] {
145            warn_bad_once(&format!(
146                "pp stage fence {fence:?} not strictly increasing over [0, {n_layers}]; \
147                 door stays OFF"
148            ));
149            return None;
150        }
151    }
152    Some(fence)
153}
154
155/// N=2 back-compat view of the door (the gemma4 arm and `pp2-gate` are 2-stage): `Some(cut)`
156/// iff the door is open with EXACTLY two stages.
157pub fn pp2_split(n_layers: usize) -> Option<usize> {
158    pp_cuts(n_layers).filter(|f| f.len() == 3).map(|f| f[1])
159}
160
161/// The stage that owns layer `il` under `fence` (see `pp_cuts`).
162pub fn stage_of(fence: &[usize], il: usize) -> usize {
163    debug_assert!(fence.len() >= 2);
164    match fence[1..fence.len() - 1].binary_search(&il) {
165        // fence[1..][k] == il means il is the FIRST layer of stage k+1
166        Ok(k) => k + 1,
167        Err(k) => k,
168    }
169}
170
171/// MEMRA_PP_STREAMS=0: rollback to the increment-1 same-stream seam (boundary = two plain
172/// dtod copies on the ambient compute stream, no per-stage streams/events/devices).
173pub fn pp2_streams_off() -> bool {
174    matches!(std::env::var("MEMRA_PP_STREAMS").as_deref(), Ok("0"))
175}
176
177/// True iff the ppN door would put TWO OR MORE stage streams on ONE device (devices
178/// unset = all stages on the primary; or an explicit placement with a repeated device).
179/// The deferred-readback (pipelined) arm is REFUSED in this regime: the 2026-08-02 x20
180/// soak record — singledev pipelined 13/20 PASS default, 7 failures each diverging at a
181/// different step (timing-race signature); MEMRA_PDL=0 went 20/20 on one soak but a
182/// second same-config soak on the auto-gated build failed 2/20 (n2) and battery-4 failed
183/// n4 — so PDL narrows the window without closing it, and the true root cause (same
184/// Engine kernels concurrent on two streams of one device) is NOT fixed by any flag yet.
185/// Cross-device pipelined (one stage stream per device) is 23/23 clean post-fix. Refuse
186/// loudly rather than return silently-wrong logits. Env-only read (callable pre-runtime).
187pub fn pp_multi_stream_same_device() -> bool {
188    let stages_open = std::env::var("MEMRA_PP_STAGES")
189        .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
190        .unwrap_or(false);
191    let devices = std::env::var("MEMRA_PP_DEVICES").ok().filter(|v| !v.is_empty());
192    if (!stages_open && devices.is_none()) || pp2_streams_off() {
193        return false;
194    }
195    match devices {
196        None => true, // door open, no placement: every stage stream lands on the primary
197        Some(s) => {
198            let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
199            let n = v.len();
200            v.sort_unstable();
201            v.dedup();
202            v.len() < n // repeated device = shared-device streams
203        }
204    }
205}
206
207/// True iff the ppN door is open AND the placement spans 2+ DISTINCT devices AND the
208/// per-stage sharded loader is on — i.e. some layers' weights live on a device other than
209/// the primary. Any path that walks the WHOLE trunk on one stream in this regime reads
210/// those weights over PCIe every step. Env-only read (callable pre-runtime).
211///
212/// Measured cost of doing that (pp2-hardening 2026-08-06, 2x RTX PRO 6000, PCIe Gen5 x16
213/// P2P, decode-batch-bench q9, N=5 interleaved, `research/pp2-hardening-20260806`):
214/// **B=1 7.4 vs 208.9 tok/s (28x), B=4 29.8 vs 491.3 (16.5x), B=8 47.4 vs 657.0 (13.9x)**.
215/// The same sweep with `MEMRA_PP_SHARD=0` (weights all home) returns 178.5/491.1/656.6 —
216/// identical to the single-device door-open arm — so the entire cliff is the peer read,
217/// not the door and not the placement plumbing. Exactness is NOT the issue: peer reads
218/// return identical bytes and every `decode-batch-gate` gate PASSED on this config, which
219/// is precisely why it needs a refusal rather than a gate.
220pub fn pp_sharded_cross_device() -> bool {
221    let stages_open = std::env::var("MEMRA_PP_STAGES")
222        .map(|v| v.parse::<usize>().map(|n| n >= 2).unwrap_or(false))
223        .unwrap_or(false);
224    // MEMRA_PP_STREAMS=0 (2026-08-06, pp2-batch): the same-stream rollback seam ALSO turns
225    // the sharded loader off — `layer_engine` returns the primary engine whenever
226    // `pp2_streams_off()`, and `new_cache` skips `Cache::new_ppn` on the same condition. So
227    // in that regime every weight and every cache is home on the primary and an unsplit walk
228    // peer-reads NOTHING. Without this term the guard refused that config too: a spurious
229    // refusal of a placement that is sound and full-speed. Found wiring the batched pp arm.
230    if !stages_open || pp_shard_off() || pp2_streams_off() {
231        return false;
232    }
233    match pp2_devices_env() {
234        None => false, // no placement: every stage is the primary device, nothing remote
235        Some(s) => {
236            let mut v: Vec<&str> = s.split(',').map(|p| p.trim()).collect();
237            v.sort_unstable();
238            v.dedup();
239            v.len() >= 2
240        }
241    }
242}
243
244/// The shared fail-closed guard for EVERY decode path that has no pp stage split.
245/// Returns `Err` iff `pp_sharded_cross_device()` — i.e. the caller would walk the whole
246/// trunk on one stream while some layers' weights live on another device, peer-reading
247/// them every step. `path` names the refusing function so the operator knows which loop
248/// they hit; `alt` names the working alternative for that loop.
249///
250/// One helper rather than four copies because the audit found FOUR paths with the same
251/// hole (`decode_step_batch`, `decode_step_dc`, the graph capture that wraps dc, and
252/// `decode_step_t*` verify), and a per-path copy is how one gets missed on the next
253/// addition. Override: `MEMRA_PP_ALLOW_UNSPLIT_BATCH=1` (one door for all of them —
254/// they are the same measurement question).
255pub fn refuse_unsplit_if_remote(path: &str, alt: &str) -> Result<(), Box<dyn std::error::Error>> {
256    if pp_host_bounce_active() {
257        return Err(format!(
258            "{path}: refused with MEMRA_PP_HOST_BOUNCE=1 on sharded cross-device PP — \
259             this unsplit path peer-reads remote weights, while host bounce covers only \
260             explicit stage-boundary transfers. Use {alt}; the \
261             MEMRA_PP_ALLOW_UNSPLIT_BATCH override is unavailable on a broken-peer host."
262        )
263        .into());
264    }
265    if pp_sharded_cross_device()
266        && std::env::var("MEMRA_PP_ALLOW_UNSPLIT_BATCH").as_deref() != Ok("1")
267    {
268        return Err(format!(
269            "{path}: refused with the ppN door open across 2+ devices — this path has no pp \
270             stage split, so it would walk ALL layers on one stream and peer-read every \
271             remote stage's weights each step (measured 28x slower at B=1, 13.9x at B=8 on \
272             a PRO 6000 pair over PCIe Gen5 x16 P2P; research/pp2-hardening-20260806). \
273             Exactness is unaffected — peer reads return identical bytes and the exactness \
274             gates PASS on this config — which is exactly why it must refuse instead of \
275             being caught by a gate. Fixes, in order: {alt}; or MEMRA_PP_SHARD=0 (all \
276             weights home on the primary — full speed, forfeits the capacity PP-2 exists \
277             for); or close the pp door. MEMRA_PP_ALLOW_UNSPLIT_BATCH=1 overrides for \
278             measurement."
279        )
280        .into());
281    }
282    Ok(())
283}
284
285/// MEMRA_BATCH_PP=0: rollback/A-B seam for the BATCHED stage split (pp2-batch 2026-08-06).
286/// Default ON — with the ppN door open the batched decode step takes its own stage split
287/// (`decode_step_batch_ppn`) exactly as the eager step does. Setting 0 sends the batched
288/// path back through the unsplit body, which under a sharded cross-device placement is
289/// then caught by `refuse_unsplit_if_remote` (the 28x peer-read regime) rather than run
290/// silently. Exists so the bit-identity gate can A/B split vs unsplit IN ONE PROCESS
291/// against the same loaded weights — read per step, never memoized, for that reason.
292pub fn batch_pp_on() -> bool {
293    std::env::var("MEMRA_BATCH_PP").as_deref() != Ok("0")
294}
295
296/// MEMRA_DUAL_PP three-state mode for the dual-active PP-2 batched decode path.
297/// Default ON (owner flip 2026-08-11) after the box1 PRO-pair re-gate: correctness
298/// bit-identity B=1..5, servestress no-thrash, 10-boot soak 929/929 golden matches with
299/// 0 slot collisions across 9123 pairs (research/dualpp2-20260811/RESULTS-regate.md), plus
300/// the dualpp1 c>=8 interleaved perf floor (+20.753% minimum,
301/// research/dualpp1-20260811/RESULTS.md).
302///
303/// The three states carry different failure semantics on purpose:
304/// - `Off` (`MEMRA_DUAL_PP=0`): the serial rollback seam. Overlap also follows OFF unless
305///   `MEMRA_PP_OVERLAP` is set explicitly, so one flag restores the exact pre-flip naked path.
306/// - `Forced` (`MEMRA_DUAL_PP=1`): the pre-flip explicit request. A placement that cannot
307///   run dual (single-slot boundary, host bounce, non-PP-2 fence) REFUSES with the binding
308///   quoted reason before any token or cache advance — the gate negative cells pin this.
309/// - `Auto` (unset): the flipped default. Dual runs where the re-gate validated it
310///   (PP-2 fence, double-slot, peer transport, B>=2) and silently degrades to the serial
311///   PP-N walker everywhere else — naked PP-3 serving and the MEMRA_PP_HOST_BOUNCE=1
312///   broken-peer escape hatch must keep decoding, not refuse.
313#[derive(Clone, Copy, PartialEq, Eq, Debug)]
314pub enum DualPpMode {
315    Off,
316    Forced,
317    Auto,
318}
319
320/// Pure resolution for MEMRA_DUAL_PP, split from the env read so the flip regression tests
321/// cannot race parallel test threads on process env.
322pub fn dual_pp_mode_resolve(v: Option<&str>) -> DualPpMode {
323    match v {
324        Some("0") => DualPpMode::Off,
325        Some("1") => DualPpMode::Forced,
326        _ => DualPpMode::Auto,
327    }
328}
329
330pub fn dual_pp_mode() -> DualPpMode {
331    dual_pp_mode_resolve(std::env::var("MEMRA_DUAL_PP").ok().as_deref())
332}
333
334/// True when the dual-active door is open (Forced or Auto). Read per step so the
335/// model-level gate can replay serial and waved arms against one loaded checkpoint.
336pub fn dual_pp_on() -> bool {
337    dual_pp_mode() != DualPpMode::Off
338}
339
340/// Engine-entry routing for the dual-active path, kept pure for the flip regression
341/// tests. `Forced` routes every B>=2 PP-2 call into `decode_step_batch_dual` even when
342/// the placement cannot run it, so the binding refusals stay reachable and loud.
343/// `Auto` routes only the exact re-gated regime and leaves everything else on the serial
344/// PP-N walker. `dual_pp_eligibility` remains behind this as defense in depth.
345pub fn dual_pp_route(
346    mode: DualPpMode,
347    batch: usize,
348    stages: usize,
349    double_slot: bool,
350    host_bounce: bool,
351) -> bool {
352    if batch < 2 {
353        return false;
354    }
355    match mode {
356        DualPpMode::Off => false,
357        DualPpMode::Forced => true,
358        DualPpMode::Auto => stages == 2 && double_slot && !host_bounce,
359    }
360}
361
362/// Binding-amendment refusal text. The negative gate quotes this exact line and requires the
363/// decode call to return before producing a token or advancing a cache.
364pub const DUAL_PP_SINGLE_SLOT_REFUSAL: &str =
365    "decode_step_batch_dual: refused: PP boundary is single-slot; set MEMRA_PP_OVERLAP=1 so both alternating boundary slots are prepared before dual-active decode";
366pub const DUAL_PP_HOST_BOUNCE_REFUSAL: &str =
367    "decode_step_batch_dual: refused: MEMRA_PP_HOST_BOUNCE=1 is unvalidated for dual-active decode; disable MEMRA_DUAL_PP or use peer transport";
368
369/// Pure schedule policy shared by the runtime and kernel-check manifest cells. A single row
370/// has no second wave and must stay on the serial PP-N walker.
371pub fn dual_pp_wave_mid(batch: usize) -> Option<usize> {
372    (batch >= 2).then_some((batch + 1) / 2)
373}
374
375/// Fail-closed eligibility check kept pure so the negative manifest cell cannot accidentally
376/// initialize CUDA state. Slot preparation itself remains `PpNRt::prepare_overlap_slots`.
377pub fn dual_pp_eligibility(
378    stages: usize,
379    double_slot: bool,
380    host_bounce: bool,
381) -> Result<(), &'static str> {
382    if stages != 2 {
383        return Err("decode_step_batch_dual: refused: dual-active decode requires exactly two PP stages");
384    }
385    if !double_slot {
386        return Err(DUAL_PP_SINGLE_SLOT_REFUSAL);
387    }
388    if host_bounce {
389        return Err(DUAL_PP_HOST_BOUNCE_REFUSAL);
390    }
391    Ok(())
392}
393
394/// Liveness is counted only while the two host-driven decode layer walkers are both active.
395/// Enqueue order is not proof for Step: its router readback synchronizes the issuing thread.
396static DUAL_PP_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
397static DUAL_PP_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
398static DUAL_PP_STAGE_NS: [AtomicU64; 4] = [
399    AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0),
400];
401static DUAL_PP_STAGE_SAMPLES: [AtomicUsize; 4] = [
402    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
403];
404static DUAL_PP_TIMING_DROPPED: AtomicUsize = AtomicUsize::new(0);
405static DUAL_PP_SLOT_PAIRS: AtomicUsize = AtomicUsize::new(0);
406static DUAL_PP_SLOT_USES: [AtomicUsize; 2] = [AtomicUsize::new(0), AtomicUsize::new(0)];
407static DUAL_PP_SLOT_COLLISIONS: AtomicUsize = AtomicUsize::new(0);
408
409pub const DUAL_PP_STAGE_NAMES: [&str; 4] = [
410    "wave_a_stage0", "wave_a_stage1", "wave_b_stage0", "wave_b_stage1",
411];
412
413pub fn dual_pp_overlaps() -> usize {
414    DUAL_PP_OVERLAPS.load(Ordering::Relaxed)
415}
416
417/// Record the two boundary slots selected for one dual-active wave pair. A same-slot pair is
418/// rejected by the caller before wave B can consume a residual; the collision counter makes that
419/// fail-closed path observable to the detached soak instead of relying only on log scanning.
420pub(crate) fn record_dual_pp_slot_pair(slot_a: usize, slot_b: usize) -> bool {
421    debug_assert!(slot_a < DUAL_PP_SLOT_USES.len());
422    debug_assert!(slot_b < DUAL_PP_SLOT_USES.len());
423    if slot_a == slot_b {
424        DUAL_PP_SLOT_COLLISIONS.fetch_add(1, Ordering::Relaxed);
425        return false;
426    }
427    DUAL_PP_SLOT_USES[slot_a].fetch_add(1, Ordering::Relaxed);
428    DUAL_PP_SLOT_USES[slot_b].fetch_add(1, Ordering::Relaxed);
429    DUAL_PP_SLOT_PAIRS.fetch_add(1, Ordering::Relaxed);
430    true
431}
432
433/// `(completed wave pairs, [slot 0 uses, slot 1 uses], rejected same-slot pairs)`.
434pub fn dual_pp_slot_snapshot() -> (usize, [usize; 2], usize) {
435    (
436        DUAL_PP_SLOT_PAIRS.load(Ordering::Relaxed),
437        std::array::from_fn(|i| DUAL_PP_SLOT_USES[i].load(Ordering::Relaxed)),
438        DUAL_PP_SLOT_COLLISIONS.load(Ordering::Relaxed),
439    )
440}
441
442/// CUDA-event timing is a diagnostic-only process door. The scored N=5 block runs without
443/// it; the companion box1 diagnostic process enables it and exports cumulative per-wave
444/// stage spans through `/metrics`.
445pub fn dual_pp_timing_on() -> bool {
446    static ON: OnceLock<bool> = OnceLock::new();
447    *ON.get_or_init(|| std::env::var("MEMRA_DUAL_PP_TIMING").as_deref() == Ok("1"))
448}
449
450pub(crate) fn record_dual_pp_stage_ms(stage: usize, ms: f32) {
451    assert!(stage < DUAL_PP_STAGE_NS.len(), "dual PP timing stage out of range");
452    let ns = (f64::from(ms) * 1_000_000.0).round() as u64;
453    DUAL_PP_STAGE_NS[stage].fetch_add(ns, Ordering::Relaxed);
454    DUAL_PP_STAGE_SAMPLES[stage].fetch_add(1, Ordering::Relaxed);
455}
456
457/// Timing is diagnostic only: a CUDA event that is not ready (or otherwise fails) must not
458/// change decode control flow. Count and warn once, then leave the scored-path result intact.
459pub(crate) fn record_dual_pp_timing_drop(
460    context: &str,
461    err: &dyn std::fmt::Display,
462) {
463    let previous = DUAL_PP_TIMING_DROPPED.fetch_add(1, Ordering::Relaxed);
464    if previous == 0 {
465        eprintln!(
466            "[dual-pp] WARN: skipped diagnostic timing sample at {context}: {err}; decode continues"
467        );
468    }
469}
470
471pub(crate) fn record_dual_pp_stage_result<E: std::fmt::Display>(
472    stage: usize,
473    elapsed: Result<f32, E>,
474) {
475    match elapsed {
476        Ok(ms) => record_dual_pp_stage_ms(stage, ms),
477        Err(err) => record_dual_pp_timing_drop(DUAL_PP_STAGE_NAMES[stage], &err),
478    }
479}
480
481pub fn dual_pp_timing_dropped() -> usize {
482    DUAL_PP_TIMING_DROPPED.load(Ordering::Relaxed)
483}
484
485/// `(total_nanoseconds, samples)` for wave-A stage0/stage1 then wave-B stage0/stage1.
486pub fn dual_pp_timing_snapshot() -> ([u64; 4], [usize; 4]) {
487    (
488        std::array::from_fn(|i| DUAL_PP_STAGE_NS[i].load(Ordering::Relaxed)),
489        std::array::from_fn(|i| DUAL_PP_STAGE_SAMPLES[i].load(Ordering::Relaxed)),
490    )
491}
492
493pub(crate) struct DualPpStageGuard;
494
495pub(crate) fn enter_dual_pp_stage() -> DualPpStageGuard {
496    let active = DUAL_PP_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
497    if active > 0 {
498        DUAL_PP_OVERLAPS.fetch_add(1, Ordering::Relaxed);
499    }
500    DualPpStageGuard
501}
502
503impl Drop for DualPpStageGuard {
504    fn drop(&mut self) {
505        let active = DUAL_PP_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
506        debug_assert!(active > 0, "dual PP active-stage counter underflow");
507    }
508}
509
510/// MEMRA_PRIME_PP=0: rollback/A-B seam for the PRIME (chunked prefill) stage split
511/// (lane/pp-leverb 2026-08-08). Default ON — with the ppN door open the chunked prime takes
512/// its own per-stage range walk exactly as the eager/batched/verify steps do. Setting 0 sends
513/// prime back through the unsplit whole-trunk walk. NOTE: unlike batch/dc/graph/spec, prime
514/// keeps NO `refuse_unsplit_if_remote` — its unsplit walk over a sharded placement is the
515/// measured 22% amortized peer-read tax (research/pp-prefill-20260807 anatomy: m=4096
516/// amortizes the weight reads), not the decode 28x cliff, and the unsplit walk IS the
517/// split-vs-unsplit gate's reference arm (`prime-split-gate`), so it must stay callable.
518/// Read per call, never memoized (the gate A/Bs both arms in one process).
519pub fn prime_pp_on() -> bool {
520    std::env::var("MEMRA_PRIME_PP").as_deref() != Ok("0")
521}
522
523/// MEMRA_PRIME_PIPE=0: rollback/A-B seam for the PP-2 PRIME CHUNK PIPELINE
524/// (lane/cx-pipeline-prime 2026-08-08). Default ON when the prime stage split is live;
525/// setting 0 keeps the serial per-chunk stage walk. Read per prime call so the exactness
526/// gate can replay both schedules against one loaded model.
527pub fn prime_pipe_on() -> bool {
528    std::env::var("MEMRA_PRIME_PIPE").as_deref() != Ok("0")
529}
530
531/// SPLIT-LIVENESS COUNTER for the prime stage split: bumped ONCE per prime chunk that
532/// actually executed the per-stage walk. The `prime-split-gate` requires this to ADVANCE
533/// during its split arm — bit-identity of two identical UNSPLIT walks is vacuous, so a gate
534/// that only compared bits would go green while the walker doesn't exist. With the counter,
535/// the gate is RED until the walker lands (the tickinv35 pattern: the gate exists and fails
536/// before the mechanism does). Relaxed ordering: single-threaded host issue, count-only.
537pub static PRIME_SPLIT_CHUNKS: AtomicUsize = AtomicUsize::new(0);
538
539/// Read the split-liveness counter (gate-side).
540pub fn prime_split_chunks() -> usize {
541    PRIME_SPLIT_CHUNKS.load(Ordering::Relaxed)
542}
543
544/// PIPELINE-LIVENESS COUNTER: bumped only when a second PP-2 prime stage enters its layer
545/// walker while the other stage's walker is still active. Step's per-layer router readback
546/// synchronizes the host, so enqueue order alone is not liveness: a single host thread can
547/// call stage 0(N+1) before the stage-1 epilogue and still serialize all trunk computation.
548pub static PRIME_PIPE_OVERLAPS: AtomicUsize = AtomicUsize::new(0);
549
550/// Read the prime-pipeline overlap counter (gate-side).
551pub fn prime_pipe_overlaps() -> usize {
552    PRIME_PIPE_OVERLAPS.load(Ordering::Relaxed)
553}
554
555static PRIME_PIPE_ACTIVE_STAGES: AtomicUsize = AtomicUsize::new(0);
556
557pub(crate) struct PrimePipeStageGuard;
558
559/// Mark one host-driven stage walker active. With PP-2, a transition 1 -> 2 proves the
560/// two device walkers overlap in wall time; exactly one transition is counted per pair.
561pub(crate) fn enter_prime_pipe_stage() -> PrimePipeStageGuard {
562    let active = PRIME_PIPE_ACTIVE_STAGES.fetch_add(1, Ordering::AcqRel);
563    if active > 0 {
564        PRIME_PIPE_OVERLAPS.fetch_add(1, Ordering::Relaxed);
565    }
566    PrimePipeStageGuard
567}
568
569impl Drop for PrimePipeStageGuard {
570    fn drop(&mut self) {
571        let active = PRIME_PIPE_ACTIVE_STAGES.fetch_sub(1, Ordering::AcqRel);
572        debug_assert!(active > 0, "prime pipeline active-stage counter underflow");
573    }
574}
575
576/// Step35 cross-request prime liveness counters (lane/cx-prime-batch, 2026-08-08).
577/// The exactness gate requires BOTH to advance: a successful step35 batch alone is not
578/// sufficient under PP-N if it walked the whole sharded trunk on one stream.
579pub static STEP35_PRIME_BATCHES: AtomicUsize = AtomicUsize::new(0);
580pub static STEP35_PRIME_BATCH_SPLITS: AtomicUsize = AtomicUsize::new(0);
581
582pub fn step35_prime_batches() -> usize {
583    STEP35_PRIME_BATCHES.load(Ordering::Relaxed)
584}
585
586pub fn step35_prime_batch_splits() -> usize {
587    STEP35_PRIME_BATCH_SPLITS.load(Ordering::Relaxed)
588}
589
590/// MEMRA_SPEC_PP=0: rollback/A-B seam for the SPEC VERIFY stage split (pp2-spec 2026-08-06).
591/// Default ON — with the ppN door open the verify forward (`decode_step_t_core_ppn`) takes its
592/// own stage split exactly as the eager and batched steps do. Setting 0 sends verify back through
593/// the unsplit trunk walk, which under a sharded cross-device placement is then caught by
594/// `refuse_unsplit_if_remote` (the 28x peer-read regime) rather than running silently. Exists so
595/// the bit-identity gate can A/B split vs unsplit IN ONE PROCESS against the same loaded weights
596/// — read per verify call, never memoized, for that reason.
597pub fn spec_pp_on() -> bool {
598    std::env::var("MEMRA_SPEC_PP").as_deref() != Ok("0")
599}
600
601/// MEMRA_PP_OVERLAP: alternate the double-buffered boundary slots per step (the
602/// pipelining seed). Scheduling structure only, never math. Read per step so gates can
603/// A/B in-process.
604///
605/// Unset follows the dual-PP mode (owner flip 2026-08-11): `Auto` resolves ON — the naked
606/// serve path is the box1 re-gate's dual arm (MEMRA_DUAL_PP=1 MEMRA_PP_OVERLAP=1,
607/// 929/929 golden, 0/9123 slot collisions). `Off` resolves OFF so MEMRA_DUAL_PP=0 alone
608/// restores the exact pre-flip serial naked path. `Forced` resolves OFF so the binding
609/// single-slot refusal of the explicit pre-flip request stays reachable — the
610/// decode-batch-gate negative cell pins and asserts precisely that combination.
611pub fn pp2_overlap() -> bool {
612    pp2_overlap_resolve(std::env::var("MEMRA_PP_OVERLAP").ok().as_deref(), dual_pp_mode())
613}
614
615/// Pure resolution for MEMRA_PP_OVERLAP, split from the env read for the flip
616/// regression tests.
617pub fn pp2_overlap_resolve(v: Option<&str>, mode: DualPpMode) -> bool {
618    match v {
619        Some("1") => true,
620        Some(_) => false,
621        None => mode == DualPpMode::Auto,
622    }
623}
624
625/// Broken-peer escape hatch: stage-boundary activations travel through page-locked host
626/// memory instead of `cudaMemcpyPeerAsync`. Default OFF; captured when `PpNRt` is built.
627pub fn pp_host_bounce_on() -> bool {
628    matches!(std::env::var("MEMRA_PP_HOST_BOUNCE").as_deref(), Ok("1"))
629}
630
631/// True when host bounce is the live transport for a sharded cross-device placement.
632/// Callers use this to close paths that still peer-read non-boundary state.
633pub fn pp_host_bounce_active() -> bool {
634    pp_host_bounce_on() && pp_sharded_cross_device()
635}
636
637/// M2 increment 2 rollback seam: MEMRA_PP_SHARD=0 = the M1 bring-up placement (all
638/// weights upload through the primary engine; remote stages peer-read). Default ON —
639/// under MEMRA_PP_DEVICES each stage's layer range uploads through its own engine.
640pub fn pp_shard_off() -> bool {
641    matches!(std::env::var("MEMRA_PP_SHARD").as_deref(), Ok("0"))
642}
643
644/// Raw `MEMRA_PP_DEVICES` (parsed/validated at PpNRt build — a bad string must fail the
645/// decode step loudly, never silently fall back to same-device and fake a gate PASS).
646fn pp2_devices_env() -> Option<String> {
647    std::env::var("MEMRA_PP_DEVICES").ok().filter(|v| !v.is_empty())
648}
649
650static WARNED_BAD: AtomicBool = AtomicBool::new(false);
651fn warn_bad_once(msg: &str) {
652    if !WARNED_BAD.swap(true, Ordering::Relaxed) {
653        eprintln!("[pp] {msg}");
654    }
655}
656
657static WARNED_UNWIRED: AtomicBool = AtomicBool::new(false);
658/// One-time notice when the door is set but the executing path has no pp arm
659/// (M2 wires the generic eager decode at any N and the gemma4 eager arm at N=2).
660pub fn warn_unwired_once(path: &str) {
661    let open = std::env::var("MEMRA_PP_STAGES")
662        .map(|v| !v.is_empty() && v != "0" && v != "1")
663        .unwrap_or(false);
664    if open && !WARNED_UNWIRED.swap(true, Ordering::Relaxed) {
665        eprintln!(
666            "[pp] MEMRA_PP_STAGES set but `{path}` has no pp arm at this N; running unsplit"
667        );
668    }
669}
670
671// ======================================================================================
672//  PpNRt: the M2 transport runtime (per-stage streams, per-boundary events + slots)
673// ======================================================================================
674
675/// One pipeline stage's execution home: device, context, launch stream, and (for a stage
676/// remote to the primary engine's device) a dedicated Engine in that device's primary
677/// context (CUmodules are per-context).
678pub struct StageRt {
679    pub dev: usize,
680    pub ctx: Arc<CudaContext>,
681    pub stream: Arc<CudaStream>,
682    /// `Some` only when `dev` differs from the primary engine's device.
683    engine: Option<Engine>,
684}
685
686/// One boundary slot: a persistent RX-side buffer + its TX/RX completion events.
687/// PERSISTENT because the buffer is written by the TX stage's stream and read by the RX
688/// stage's: a per-step alloc/free would enqueue the free on ONE stream while the other
689/// might still be reading (the cross-stream free hazard) — a never-freed slot cannot race.
690struct BoundarySlot {
691    buf: Mutex<Option<CudaSlice<f32>>>,
692    /// Recorded on the TX stage's stream after the TX copy; RX waits on it. Created in
693    /// the TX stage's context (cuEventRecord requires event ctx == stream ctx).
694    ev_tx: CudaEvent,
695    /// Recorded on the RX stage's stream after the RX copy; the NEXT TX into this slot
696    /// waits on it (write-after-read guard). Created in the RX stage's context. Waiting
697    /// on a never-recorded event is a defined no-op, so step 0 needs no special case.
698    ev_rx: CudaEvent,
699}
700
701/// Boundary b sits between stage b (TX) and stage b+1 (RX). Two slots, alternating per
702/// step under MEMRA_PP_OVERLAP=1 (each boundary counts its own steps — a decode step
703/// crosses every boundary exactly once, so the counters stay in lockstep).
704struct BoundaryRt {
705    slots: [BoundarySlot; 2],
706    step: AtomicUsize,
707    /// true iff stage b and stage b+1 live on different devices (peer transport).
708    cross: bool,
709}
710
711#[derive(Clone, Copy, Debug, PartialEq, Eq)]
712enum BoundaryTransport {
713    Local,
714    Peer,
715    HostBounce,
716}
717
718#[derive(Clone, Copy)]
719struct BoundaryPath {
720    boundary: usize,
721    src_stage: usize,
722    dst_stage: usize,
723    transport: BoundaryTransport,
724}
725
726fn boundary_transport(cross: bool, host_bounce: bool) -> BoundaryTransport {
727    match (cross, host_bounce) {
728        (false, _) => BoundaryTransport::Local,
729        (true, false) => BoundaryTransport::Peer,
730        (true, true) => BoundaryTransport::HostBounce,
731    }
732}
733
734const PEER_PROBE_FIXED_BYTES: usize = 16 * 1024;
735const PEER_PROBE_TOKEN_WIDTHS: [usize; 4] = [
736    1,
737    8,
738    16,
739    crate::cache::PRIME_CHUNK_MAX_TOKENS,
740];
741
742/// Native cross-device boundary copies between low-frequency runtime integrity probes.
743/// Fixed rather than operator-tunable: this is a safety gate, not a performance experiment.
744pub const PEER_RUNTIME_PROBE_INTERVAL_COPIES: u64 = 8 * 1024;
745/// One complete runtime width rotation. The maximum-chunk rung runs once per cycle.
746pub const PEER_RUNTIME_PROBE_CYCLE_COPIES: u64 =
747    PEER_RUNTIME_PROBE_INTERVAL_COPIES * PEER_PROBE_TOKEN_WIDTHS.len() as u64;
748
749pub const PEER_PROBE_REQUIRED_REFUSAL: &str =
750    "PP bring-up refused: MEMRA_PEER_PROBE=0 cannot authorize native peer transport for a \
751     sharded cross-device placement while MEMRA_PP_HOST_BOUNCE!=1; leave MEMRA_PEER_PROBE \
752     enabled or set MEMRA_PP_HOST_BOUNCE=1";
753
754#[derive(Clone, Copy, Debug, PartialEq, Eq)]
755pub enum PeerProbeStartupPolicy {
756    Allowed,
757    BypassedWithHostBounce,
758}
759
760/// Pure startup policy so unit tests and kernel-check pin the entire refusal matrix without
761/// mutating process-global environment variables.
762pub fn peer_probe_startup_policy(
763    probe_on: bool,
764    sharded_cross_device: bool,
765    host_bounce: bool,
766) -> Result<PeerProbeStartupPolicy, &'static str> {
767    match (probe_on, sharded_cross_device, host_bounce) {
768        (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
769        (false, true, true) => Ok(PeerProbeStartupPolicy::BypassedWithHostBounce),
770        _ => Ok(PeerProbeStartupPolicy::Allowed),
771    }
772}
773
774static PEER_PROBE_BYPASSED: AtomicU64 = AtomicU64::new(0);
775static PEER_BOUNDARY_COPIES: AtomicU64 = AtomicU64::new(0);
776static PEER_RUNTIME_PROBES: AtomicU64 = AtomicU64::new(0);
777static PEER_RUNTIME_PROBE_FAILURES: AtomicU64 = AtomicU64::new(0);
778static PEER_RUNTIME_LAST_PROBE_COPY: AtomicU64 = AtomicU64::new(0);
779static PEER_RUNTIME_PROBE_FAILED: AtomicBool = AtomicBool::new(false);
780
781#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
782pub struct PeerProbeMetrics {
783    pub bypassed: u64,
784    pub boundary_copies: u64,
785    pub runtime_probes: u64,
786    pub runtime_failures: u64,
787}
788
789pub fn peer_probe_metrics() -> PeerProbeMetrics {
790    PeerProbeMetrics {
791        bypassed: PEER_PROBE_BYPASSED.load(Ordering::Relaxed),
792        boundary_copies: PEER_BOUNDARY_COPIES.load(Ordering::Relaxed),
793        runtime_probes: PEER_RUNTIME_PROBES.load(Ordering::Relaxed),
794        runtime_failures: PEER_RUNTIME_PROBE_FAILURES.load(Ordering::Relaxed),
795    }
796}
797
798fn runtime_peer_probe_due(copies: u64, last_probe_copy: u64) -> bool {
799    copies.saturating_sub(last_probe_copy) >= PEER_RUNTIME_PROBE_INTERVAL_COPIES
800}
801
802/// Select one boot-probe width per zero-based runtime probe attempt. The maximum-chunk rung is
803/// last, so it runs once every four cadence ticks rather than on every runtime re-probe.
804fn runtime_peer_probe_width(probe_index: u64) -> (usize, usize) {
805    let width_index = (probe_index % PEER_PROBE_TOKEN_WIDTHS.len() as u64) as usize;
806    (width_index, PEER_PROBE_TOKEN_WIDTHS[width_index])
807}
808
809fn peer_probe_on() -> bool {
810    std::env::var("MEMRA_PEER_PROBE").as_deref() != Ok("0")
811}
812
813#[derive(Clone, Copy, Debug, PartialEq, Eq)]
814enum PeerProbeDecision {
815    Clean,
816    ProceedWithHostBounce { mismatches: usize },
817}
818
819fn peer_probe_mismatch_count(expected: &[u8], readback: &[u8]) -> usize {
820    expected
821        .iter()
822        .zip(readback)
823        .filter(|(a, b)| a != b)
824        .count()
825        + expected.len().abs_diff(readback.len())
826}
827
828fn peer_probe_decision(
829    expected: &[u8],
830    readback: &[u8],
831    host_bounce: bool,
832) -> Result<PeerProbeDecision, String> {
833    let mismatches = peer_probe_mismatch_count(expected, readback);
834    if mismatches == 0 {
835        Ok(PeerProbeDecision::Clean)
836    } else if host_bounce {
837        Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches })
838    } else {
839        Err(format!("{mismatches} mismatched byte(s)"))
840    }
841}
842
843fn peer_probe_pattern(bytes: usize, boundary: usize, src_dev: usize, dst_dev: usize) -> Vec<u8> {
844    let mut state = 0xD1B5_4A32_D192_ED03u64
845        ^ (bytes as u64).rotate_left(7)
846        ^ (boundary as u64).rotate_left(19)
847        ^ (src_dev as u64).rotate_left(31)
848        ^ (dst_dev as u64).rotate_left(43);
849    (0..bytes)
850        .map(|_| {
851            state ^= state << 13;
852            state ^= state >> 7;
853            state ^= state << 17;
854            state as u8
855        })
856        .collect()
857}
858
859fn peer_probe_bytes_to_f32(bytes: &[u8]) -> Vec<f32> {
860    assert_eq!(bytes.len() % std::mem::size_of::<f32>(), 0);
861    bytes
862        .chunks_exact(std::mem::size_of::<f32>())
863        .map(|chunk| f32::from_bits(u32::from_ne_bytes(chunk.try_into().unwrap())))
864        .collect()
865}
866
867fn peer_probe_f32_to_bytes(values: &[f32]) -> Vec<u8> {
868    values
869        .iter()
870        .flat_map(|value| value.to_bits().to_ne_bytes())
871        .collect()
872}
873
874/// A legacy `cuMemAlloc` buffer used only by the boot probe. Unlike memra's normal
875/// stream-ordered allocations, it becomes peer-visible through `cuCtxEnablePeerAccess`
876/// without requiring the default-pool grants that deliberately happen after the probe.
877struct PeerProbeBuffer {
878    ctx: Arc<CudaContext>,
879    ptr: cudarc::driver::sys::CUdeviceptr,
880}
881
882impl PeerProbeBuffer {
883    fn new(ctx: &Arc<CudaContext>, bytes: usize) -> Result<Self, Box<dyn std::error::Error>> {
884        ctx.bind_to_thread()?;
885        let ptr = unsafe { cudarc::driver::result::malloc_sync(bytes)? };
886        Ok(Self { ctx: ctx.clone(), ptr })
887    }
888}
889
890impl Drop for PeerProbeBuffer {
891    fn drop(&mut self) {
892        if self.ctx.bind_to_thread().is_ok() {
893            let _ = unsafe { cudarc::driver::result::free_sync(self.ptr) };
894        }
895    }
896}
897
898fn peer_probe_copy(
899    src: &StageRt,
900    dst: &StageRt,
901    expected: &[u8],
902) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
903    let bytes = expected.len();
904    let src_buf = PeerProbeBuffer::new(&src.ctx, bytes)?;
905    unsafe {
906        cudarc::driver::result::memcpy_htod_sync(src_buf.ptr, expected)?;
907    }
908
909    let dst_buf = PeerProbeBuffer::new(&dst.ctx, bytes)?;
910    let poison: Vec<u8> = expected.iter().map(|b| !b).collect();
911    unsafe {
912        cudarc::driver::result::memcpy_htod_sync(dst_buf.ptr, &poison)?;
913    }
914
915    src.ctx.bind_to_thread()?;
916    unsafe {
917        cudarc::driver::result::memcpy_peer_async(
918            dst.ctx.cu_ctx(),
919            dst_buf.ptr,
920            src.ctx.cu_ctx(),
921            src_buf.ptr,
922            bytes,
923            src.stream.cu_stream(),
924        )?;
925    }
926    src.stream.synchronize()?;
927
928    dst.ctx.bind_to_thread()?;
929    let mut readback = vec![0u8; bytes];
930    unsafe {
931        cudarc::driver::result::memcpy_dtoh_sync(&mut readback, dst_buf.ptr)?;
932    }
933    Ok(readback)
934}
935
936fn run_peer_probe_pass(
937    stages: &[StageRt],
938    peer_capable: &[(usize, usize)],
939    host_bounce: bool,
940    label: &str,
941    bytes: usize,
942) -> Result<(), Box<dyn std::error::Error>> {
943    if bytes == 0 {
944        return Err(format!("PP peer byte-integrity probe {label} size is zero").into());
945    }
946    let started = std::time::Instant::now();
947    let mut copies = 0usize;
948    let mut skipped = 0usize;
949    let mut total_mismatches = 0usize;
950
951    for boundary in 0..stages.len() - 1 {
952        if stages[boundary].dev == stages[boundary + 1].dev {
953            continue;
954        }
955        for (src_idx, dst_idx) in [(boundary, boundary + 1), (boundary + 1, boundary)] {
956            let src = &stages[src_idx];
957            let dst = &stages[dst_idx];
958            if !peer_capable.contains(&(src.dev, dst.dev)) {
959                if host_bounce {
960                    skipped += 1;
961                    eprintln!(
962                        "[pp] peer byte-integrity probe SKIP: boundary={boundary} \
963                         dev{}->dev{} label={label} bytes={bytes} (peer capability unavailable; \
964                         MEMRA_PP_HOST_BOUNCE=1 remains fail-safe)",
965                        src.dev, dst.dev,
966                    );
967                    continue;
968                }
969                return Err(format!(
970                    "PP peer byte-integrity probe cannot run boundary={boundary} \
971                     dev{}->dev{}: peer access was not enabled",
972                    src.dev, dst.dev,
973                )
974                .into());
975            }
976
977            let expected = peer_probe_pattern(bytes, boundary, src.dev, dst.dev);
978            let readback = match peer_probe_copy(src, dst, &expected) {
979                Ok(readback) => readback,
980                Err(err) if host_bounce => {
981                    skipped += 1;
982                    eprintln!(
983                        "[pp] peer byte-integrity probe ERROR: boundary={boundary} \
984                         dev{}->dev{} label={label} bytes={bytes}: {err}; \
985                         MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
986                        src.dev, dst.dev,
987                    );
988                    continue;
989                }
990                Err(err) => {
991                    return Err(format!(
992                        "PP peer byte-integrity probe FAILED: boundary={boundary} \
993                         dev{}->dev{} label={label} bytes={bytes}: {err}; refusing native P2P \
994                         (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
995                         MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
996                        src.dev, dst.dev,
997                    )
998                    .into());
999                }
1000            };
1001            copies += 1;
1002            match peer_probe_decision(&expected, &readback, host_bounce) {
1003                Ok(PeerProbeDecision::Clean) => {}
1004                Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches }) => {
1005                    total_mismatches += mismatches;
1006                    eprintln!(
1007                        "[pp] peer byte-integrity probe CORRUPTION: boundary={boundary} \
1008                         dev{}->dev{} label={label} bytes={bytes} mismatches={mismatches}; \
1009                         MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path",
1010                        src.dev, dst.dev,
1011                    );
1012                }
1013                Err(mismatch) => {
1014                    return Err(format!(
1015                        "PP peer byte-integrity probe FAILED: boundary={boundary} \
1016                         dev{}->dev{} label={label} bytes={bytes}: {mismatch}; refusing native \
1017                         P2P (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1018                         MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)",
1019                        src.dev, dst.dev,
1020                    )
1021                    .into());
1022                }
1023            }
1024        }
1025    }
1026
1027    let status = if total_mismatches > 0 {
1028        "BOUNCE"
1029    } else if skipped > 0 && copies > 0 {
1030        "PARTIAL"
1031    } else if skipped > 0 {
1032        "SKIP"
1033    } else {
1034        "PASS"
1035    };
1036    eprintln!(
1037        "[pp] peer byte-integrity probe {}: label={label} bytes={bytes} copies={copies} \
1038         skipped={skipped} mismatches={total_mismatches} elapsed_ms={:.3}",
1039        status,
1040        started.elapsed().as_secs_f64() * 1e3,
1041    );
1042    Ok(())
1043}
1044
1045fn host_bounce_capacity(n_embd: usize) -> Result<(usize, usize), String> {
1046    if n_embd == 0 {
1047        return Err("MEMRA_PP_HOST_BOUNCE needs non-zero model n_embd".into());
1048    }
1049    let elems = n_embd
1050        .checked_mul(crate::cache::PRIME_CHUNK_MAX_TOKENS)
1051        .ok_or_else(|| format!("host-bounce element count overflows for n_embd={n_embd}"))?;
1052    let bytes = elems
1053        .checked_mul(std::mem::size_of::<f32>())
1054        .ok_or_else(|| format!("host-bounce byte count overflows for n_embd={n_embd}"))?;
1055    Ok((elems, bytes))
1056}
1057
1058/// One bidirectional-DMA staging allocation. `CU_MEMHOSTALLOC_PORTABLE` matters here: the
1059/// D2H producer and H2D consumer are in distinct CUDA primary contexts. Cacheable memory is
1060/// intentional (rather than cudarc's write-combined pinned slice) because this allocation is
1061/// the destination of D2H as well as the source of H2D.
1062struct PinnedHostBounce {
1063    ptr: *mut f32,
1064    len: usize,
1065}
1066
1067unsafe impl Send for PinnedHostBounce {}
1068unsafe impl Sync for PinnedHostBounce {}
1069
1070impl PinnedHostBounce {
1071    fn new(len: usize) -> Result<Self, Box<dyn std::error::Error>> {
1072        let bytes = len
1073            .checked_mul(std::mem::size_of::<f32>())
1074            .ok_or("host-bounce pinned allocation size overflow")?;
1075        let ptr = unsafe {
1076            cudarc::driver::result::malloc_host(
1077                bytes,
1078                cudarc::driver::sys::CU_MEMHOSTALLOC_PORTABLE,
1079            )?
1080        } as *mut f32;
1081        if ptr.is_null() {
1082            return Err("cuMemHostAlloc returned a null host-bounce pointer".into());
1083        }
1084        Ok(Self { ptr, len })
1085    }
1086
1087    fn prefix(&self, n: usize) -> &[f32] {
1088        assert!(n <= self.len, "host-bounce source {n} > capacity {}", self.len);
1089        unsafe { std::slice::from_raw_parts(self.ptr, n) }
1090    }
1091
1092    fn prefix_mut(&mut self, n: usize) -> &mut [f32] {
1093        assert!(n <= self.len, "host-bounce destination {n} > capacity {}", self.len);
1094        unsafe { std::slice::from_raw_parts_mut(self.ptr, n) }
1095    }
1096}
1097
1098impl Drop for PinnedHostBounce {
1099    fn drop(&mut self) {
1100        let _ = unsafe { cudarc::driver::result::free_host(self.ptr.cast()) };
1101    }
1102}
1103
1104struct HostBounceRt {
1105    n_embd: usize,
1106    capacity: usize,
1107    slots: Vec<Option<[Mutex<PinnedHostBounce>; 2]>>,
1108}
1109
1110impl HostBounceRt {
1111    fn new(n_embd: usize, boundaries: &[BoundaryRt]) -> Result<Self, Box<dyn std::error::Error>> {
1112        let (capacity, _) = host_bounce_capacity(n_embd)?;
1113        let mut slots = Vec::with_capacity(boundaries.len());
1114        for boundary in boundaries {
1115            slots.push(if boundary.cross {
1116                Some([
1117                    Mutex::new(PinnedHostBounce::new(capacity)?),
1118                    Mutex::new(PinnedHostBounce::new(capacity)?),
1119                ])
1120            } else {
1121                None
1122            });
1123        }
1124        Ok(Self { n_embd, capacity, slots })
1125    }
1126
1127    fn slot(
1128        &self,
1129        boundary: usize,
1130        slot: usize,
1131    ) -> Result<&Mutex<PinnedHostBounce>, Box<dyn std::error::Error>> {
1132        self.slots
1133            .get(boundary)
1134            .and_then(Option::as_ref)
1135            .and_then(|slots| slots.get(slot))
1136            .ok_or_else(|| format!("host-bounce slot {boundary}:{slot} is not initialized").into())
1137    }
1138}
1139
1140pub struct PpNRt {
1141    stages: Vec<StageRt>,
1142    boundaries: Vec<BoundaryRt>,
1143    /// true iff ANY boundary crosses devices.
1144    cross_any: bool,
1145    /// Captured once at runtime construction; default false preserves the peer transport.
1146    host_bounce: bool,
1147    /// Boot-time peer validation is default-on; `MEMRA_PEER_PROBE=0` is diagnostics-only.
1148    peer_probe: bool,
1149    /// Directed device pairs for which `cuDeviceCanAccessPeer` succeeded.
1150    peer_capable: Vec<(usize, usize)>,
1151    /// Sticky one-time model-width probe result. The value is the one-row geometry byte count.
1152    peer_probe_geometry: OnceLock<Result<usize, String>>,
1153    /// Lazily allocated after the authoritative model width is known at cache creation.
1154    bounce: OnceLock<Result<HostBounceRt, String>>,
1155    /// Dedicated readback stream in the LAST stage's context (deferred logits D2H —
1156    /// waiting there instead of on the compute stream keeps later tokens enqueuable).
1157    readback: Arc<CudaStream>,
1158}
1159
1160/// M1 name kept alive for external callers (`pp-transport-smoke`, receipts, docs).
1161pub type Pp2Rt = PpNRt;
1162
1163static RTN: OnceLock<Result<PpNRt, String>> = OnceLock::new();
1164
1165impl PpNRt {
1166    /// The process-wide transport runtime, built on first use against the primary engine.
1167    /// The stage count + device map freeze at first build (one config per process — gates
1168    /// run one placement per invocation). Build errors are sticky and loud.
1169    pub fn get(e: &Engine) -> Result<&'static PpNRt, Box<dyn std::error::Error>> {
1170        RTN.get_or_init(|| Self::build(e).map_err(|err| err.to_string()))
1171            .as_ref()
1172            .map_err(|s| -> Box<dyn std::error::Error> { s.clone().into() })
1173    }
1174
1175    fn build(e: &Engine) -> Result<PpNRt, Box<dyn std::error::Error>> {
1176        let primary_dev = e.ctx().ordinal();
1177        // Stage count: MEMRA_PP_DEVICES length wins when set (it IS the placement);
1178        // else MEMRA_PP_STAGES; else 2 (the M1 default — pp-transport-smoke runs doorless).
1179        let devices: Vec<usize> = match pp2_devices_env() {
1180            Some(s) => {
1181                let parts: Result<Vec<usize>, _> =
1182                    s.split(',').map(|p| p.trim().parse::<usize>()).collect();
1183                match parts {
1184                    Ok(v) if v.len() >= 2 => v,
1185                    _ => {
1186                        return Err(format!(
1187                            "MEMRA_PP_DEVICES={s} unparseable (want <d0>,..,<dN-1> e.g. 0,1,2,3)"
1188                        )
1189                        .into())
1190                    }
1191                }
1192            }
1193            None => {
1194                let n_st = std::env::var("MEMRA_PP_STAGES")
1195                    .ok()
1196                    .and_then(|v| v.parse::<usize>().ok())
1197                    .filter(|&n| n >= 2)
1198                    .unwrap_or(2);
1199                vec![primary_dev; n_st]
1200            }
1201        };
1202        if let Ok(v) = std::env::var("MEMRA_PP_STAGES") {
1203            if let Ok(n) = v.parse::<usize>() {
1204                if n >= 2 && n != devices.len() {
1205                    return Err(format!(
1206                        "MEMRA_PP_DEVICES lists {} devices but MEMRA_PP_STAGES={n} — \
1207                         refusing an ambiguous placement",
1208                        devices.len()
1209                    )
1210                    .into());
1211                }
1212            }
1213        }
1214        let n_st = devices.len();
1215        let cross_any = devices.iter().any(|&d| d != devices[0]);
1216        let host_bounce = pp_host_bounce_on();
1217        let peer_probe = peer_probe_on();
1218        let sharded_cross_device = cross_any && !pp_shard_off();
1219        if host_bounce && cross_any {
1220            if pp_shard_off() {
1221                return Err(
1222                    "MEMRA_PP_HOST_BOUNCE=1 refuses MEMRA_PP_SHARD=0: the boundary can bounce, \
1223                     but remote stages would still peer-read primary-device weights"
1224                        .into(),
1225                );
1226            }
1227            if devices.last().copied() != Some(primary_dev) {
1228                return Err(format!(
1229                    "MEMRA_PP_HOST_BOUNCE=1 requires the primary engine on the last/head stage \
1230                     (primary dev{primary_dev}, placement {devices:?}); otherwise returned \
1231                     logits/hidden state remain peer reads"
1232                )
1233                .into());
1234            }
1235        }
1236        let peer_probe_policy =
1237            peer_probe_startup_policy(peer_probe, sharded_cross_device, host_bounce)?;
1238        if peer_probe_policy == PeerProbeStartupPolicy::BypassedWithHostBounce {
1239            PEER_PROBE_BYPASSED.fetch_add(1, Ordering::Relaxed);
1240            eprintln!(
1241                "[pp] SECURITY RED: peer_probe_bypassed: MEMRA_PEER_PROBE=0 on a sharded \
1242                 cross-device placement; MEMRA_PP_HOST_BOUNCE=1 is the only enabled transport"
1243            );
1244        }
1245
1246        // Validate every placement ordinal in both transports. Native peer transport requires
1247        // access both ways. Host bounce remains usable without it, but records any capable pairs
1248        // so the byte probe can still diagnose a lying peer path before selecting the fallback.
1249        let mut used: Vec<usize> = devices.clone();
1250        used.push(primary_dev);
1251        used.sort_unstable();
1252        used.dedup();
1253        let mut peer_capable = Vec::new();
1254        if used.len() > 1 {
1255            let n = cudarc::driver::result::device::get_count()? as usize;
1256            for &d in &used {
1257                if d >= n {
1258                    return Err(format!(
1259                        "MEMRA_PP_DEVICES={devices:?} but only {n} CUDA device(s) present"
1260                    )
1261                    .into());
1262                }
1263            }
1264            if !host_bounce || peer_probe {
1265                for &a in &used {
1266                    for &b in &used {
1267                        if a == b {
1268                            continue;
1269                        }
1270                        let da = cudarc::driver::result::device::get(a as i32)?;
1271                        let db = cudarc::driver::result::device::get(b as i32)?;
1272                        let mut can: i32 = 0;
1273                        let capability = unsafe {
1274                            cudarc::driver::sys::cuDeviceCanAccessPeer(&mut can, da, db).result()
1275                        };
1276                        if let Err(err) = capability {
1277                            if host_bounce {
1278                                eprintln!(
1279                                    "[pp] peer byte-integrity probe capability query failed for \
1280                                     dev{a}->dev{b}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1281                                );
1282                                continue;
1283                            }
1284                            return Err(err.into());
1285                        }
1286                        if can == 0 {
1287                            if !host_bounce {
1288                                return Err(format!(
1289                                    "device {a} cannot peer-access device {b} \
1290                                     (cuDeviceCanAccessPeer=0); ppN cross-device needs P2P — \
1291                                     refusing a silently-staged path"
1292                                )
1293                                .into());
1294                            }
1295                        } else {
1296                            peer_capable.push((a, b));
1297                        }
1298                    }
1299                }
1300            }
1301        }
1302
1303        // PER-STAGE ENGINE ISOLATION (2026-08-02 singledev pipelined find): Engine owns
1304        // lazily-grown SHARED scratch pools (fa_part_pool, fa_vf16_scratch, argmax
1305        // partials, ...) that are stable-pointer by design — safe on one stream, a data
1306        // race the moment two stage streams run concurrently through the SAME Engine
1307        // (deferred readback, >=2 tokens in flight: token t+1's stage-0 fa memsets the
1308        // partials while token t's stage-s fa still reads them — the nondeterministic
1309        // all-logits divergence; cross-device arms were immune because remote stages
1310        // already got their own Engine). Every stage s>0 gets its OWN Engine even on the
1311        // primary device: same CUcontext (primary retain), so the per-context CUmodule
1312        // cache makes it cheap; scratch pools are per-Engine, so stages never share.
1313        // Stage 0 keeps the primary engine (single-threaded host issue: the only
1314        // concurrent user of `e` during a pp walk is stage 0 itself).
1315        let mk_stage = |dev: usize, s: usize| -> Result<StageRt, Box<dyn std::error::Error>> {
1316            if dev == primary_dev && s == 0 {
1317                let ctx = e.ctx().clone();
1318                let stream = ctx.new_stream()?;
1319                Ok(StageRt { dev, ctx, stream, engine: None })
1320            } else {
1321                let eng = Engine::new(dev)?;
1322                let ctx = eng.ctx().clone();
1323                let stream = ctx.new_stream()?;
1324                Ok(StageRt { dev, ctx, stream, engine: Some(eng) })
1325            }
1326        };
1327        let mut stages = Vec::with_capacity(n_st);
1328        for (s, &d) in devices.iter().enumerate() {
1329            stages.push(mk_stage(d, s)?);
1330        }
1331
1332        if cross_any
1333            && !peer_probe
1334            && peer_probe_policy != PeerProbeStartupPolicy::BypassedWithHostBounce
1335        {
1336            eprintln!(
1337                "[pp] WARNING: MEMRA_PEER_PROBE=0 skips the boot-time peer byte-integrity \
1338                 gate; diagnostics escape hatch active"
1339            );
1340        }
1341
1342        if used.len() > 1 {
1343            if !host_bounce {
1344            // A context per distinct device (first stage that lives there; the primary's
1345            // context for the primary device).
1346            let ctx_of = |d: usize| -> &Arc<CudaContext> {
1347                if d == primary_dev {
1348                    e.ctx()
1349                } else {
1350                    &stages.iter().find(|s| s.dev == d).unwrap().ctx
1351                }
1352            };
1353            // Enable peer access BOTH ways for every distinct pair (idempotent;
1354            // ALREADY_ENABLED is success).
1355            for &a in &used {
1356                for &b in &used {
1357                    if a == b {
1358                        continue;
1359                    }
1360                    ctx_of(a).bind_to_thread()?;
1361                    let rc = unsafe {
1362                        cudarc::driver::sys::cuCtxEnablePeerAccess(ctx_of(b).cu_ctx(), 0)
1363                    };
1364                    use cudarc::driver::sys::cudaError_enum as E;
1365                    if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
1366                        return Err(format!(
1367                            "cuCtxEnablePeerAccess(dev{a} -> dev{b}) failed: {rc:?}"
1368                        )
1369                        .into());
1370                    }
1371                }
1372            }
1373            // The fixed-size byte gate runs immediately after peer enable and before pool
1374            // grants. Legacy allocations make it exercise the exact `cuMemcpyPeerAsync` API
1375            // without depending on the pool setup that follows.
1376            if peer_probe && cross_any {
1377                let probe = run_peer_probe_pass(
1378                    &stages,
1379                    &peer_capable,
1380                    host_bounce,
1381                    "fixed-16KiB",
1382                    PEER_PROBE_FIXED_BYTES,
1383                );
1384                e.ctx().bind_to_thread()?;
1385                probe?;
1386            }
1387            // MEM-POOL access grant (8x box 2026-08-02, M1 cross-device fix #2):
1388            // cuCtxEnablePeerAccess does NOT map STREAM-ORDERED POOL allocations, and every
1389            // engine buffer/weight goes through the device default pool (cuMemAllocAsync via
1390            // cudarc; memra-runtime configures that pool). A stage kernel dereferencing
1391            // another device's weights — or a boundary peer TX writing the RX slot — needs
1392            // cuMemPoolSetAccess on the OWNING device's default pool for the ACCESSING
1393            // device; without it the first remote dereference is CUDA_ERROR_ILLEGAL_ADDRESS
1394            // (reported at the next API call in the poisoned context). Grant all pairs.
1395            for &owner in &used {
1396                for &accessor in &used {
1397                    if owner == accessor {
1398                        continue;
1399                    }
1400                    let dev = cudarc::driver::result::device::get(owner as i32)?;
1401                    let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1402                    unsafe {
1403                        cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1404                    }
1405                    let desc = cudarc::driver::sys::CUmemAccessDesc {
1406                        location: cudarc::driver::sys::CUmemLocation {
1407                            type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1408                            id: accessor as i32,
1409                        },
1410                        flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1411                    };
1412                    let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1413                    if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1414                        return Err(format!(
1415                            "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1416                        )
1417                        .into());
1418                    }
1419                }
1420            }
1421            // MEM-POOL access grant (8x box 2026-08-02, cross-device fix #2):
1422            // cuCtxEnablePeerAccess does NOT map STREAM-ORDERED POOL allocations, and every
1423            // engine buffer/weight goes through the device default pool (cuMemAllocAsync via
1424            // cudarc; memra-runtime configures that pool). A stage-1 kernel dereferencing
1425            // dev0 weights — or the stage-0 peer TX writing dev1's RX slot — needs
1426            // cuMemPoolSetAccess on the OWNING device's default pool for the ACCESSING
1427            // device; without it the first remote dereference is CUDA_ERROR_ILLEGAL_ADDRESS
1428            // (reported at the next API call in the poisoned context). Grant both ways.
1429            for (owner, accessor) in [(stages[0].dev, stages[1].dev), (stages[1].dev, stages[0].dev)] {
1430                let dev = cudarc::driver::result::device::get(owner as i32)?;
1431                let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1432                unsafe {
1433                    cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1434                }
1435                let desc = cudarc::driver::sys::CUmemAccessDesc {
1436                    location: cudarc::driver::sys::CUmemLocation {
1437                        type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1438                        id: accessor as i32,
1439                    },
1440                    flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1441                };
1442                let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1443                if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1444                    return Err(format!(
1445                        "cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: {rc:?}"
1446                    )
1447                    .into());
1448                }
1449            }
1450            // restore the primary context for the caller's subsequent work
1451            e.ctx().bind_to_thread()?;
1452            eprintln!(
1453                "[pp] cross-device transport: {} (cudaMemcpyPeerAsync per cross boundary; \
1454                 peer + default-pool access granted all pairs over {used:?}; weight home: {})",
1455                devices
1456                    .iter()
1457                    .enumerate()
1458                    .map(|(s, d)| format!("stage{s}=dev{d}"))
1459                    .collect::<Vec<_>>()
1460                    .join(" "),
1461                if pp_shard_off() {
1462                    format!("dev{primary_dev} (MEMRA_PP_SHARD=0 bring-up placement)")
1463                } else {
1464                    "per-stage (sharded loader)".to_string()
1465                }
1466            );
1467            } else {
1468                e.ctx().bind_to_thread()?;
1469                eprintln!(
1470                    "[pp] cross-device transport: {} (HOST-STAGED pinned D2H -> H2D per cross \
1471                     boundary; MEMRA_PP_HOST_BOUNCE=1; peer-pool grants bypassed; \
1472                     diagnostic peer access is removed before host-staged serving; \
1473                     weight home: per-stage (sharded loader))",
1474                    devices
1475                        .iter()
1476                        .enumerate()
1477                        .map(|(s, d)| format!("stage{s}=dev{d}"))
1478                        .collect::<Vec<_>>()
1479                        .join(" "),
1480                );
1481            }
1482        }
1483
1484        let mk_slot = |tx: &StageRt, rx: &StageRt| -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1485            Ok(BoundarySlot {
1486                buf: Mutex::new(None),
1487                ev_tx: tx.ctx.new_event(None)?,
1488                ev_rx: rx.ctx.new_event(None)?,
1489            })
1490        };
1491        let mut boundaries = Vec::with_capacity(n_st - 1);
1492        for b in 0..n_st - 1 {
1493            let (tx, rx) = (&stages[b], &stages[b + 1]);
1494            boundaries.push(BoundaryRt {
1495                slots: [mk_slot(tx, rx)?, mk_slot(tx, rx)?],
1496                step: AtomicUsize::new(0),
1497                cross: tx.dev != rx.dev,
1498            });
1499        }
1500        let readback = stages[n_st - 1].ctx.new_stream()?;
1501        let rt = PpNRt {
1502            stages,
1503            boundaries,
1504            cross_any,
1505            host_bounce,
1506            peer_probe,
1507            peer_capable,
1508            peer_probe_geometry: OnceLock::new(),
1509            bounce: OnceLock::new(),
1510            readback,
1511        };
1512        if rt.peer_probe && rt.cross_any && rt.host_bounce {
1513            rt.run_host_bounce_legacy_probe(e)?;
1514        }
1515        Ok(rt)
1516    }
1517
1518    pub fn n_stages(&self) -> usize {
1519        self.stages.len()
1520    }
1521
1522    /// True iff any boundary crosses devices.
1523    pub fn cross_device(&self) -> bool {
1524        self.cross_any
1525    }
1526
1527    fn context_for_dev<'a>(
1528        &'a self,
1529        e: &'a Engine,
1530        dev: usize,
1531    ) -> Result<&'a Arc<CudaContext>, Box<dyn std::error::Error>> {
1532        if dev == e.ctx().ordinal() {
1533            return Ok(e.ctx());
1534        }
1535        self.stages
1536            .iter()
1537            .find(|stage| stage.dev == dev)
1538            .map(|stage| &stage.ctx)
1539            .ok_or_else(|| format!("PP peer probe has no CUDA context for dev{dev}").into())
1540    }
1541
1542    fn enable_probe_peer_access(
1543        &self,
1544        e: &Engine,
1545        pairs: &[(usize, usize)],
1546    ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1547        let mut enabled = Vec::new();
1548        for &(src_dev, dst_dev) in pairs {
1549            let enable = (|| -> Result<(), Box<dyn std::error::Error>> {
1550                let src_ctx = self.context_for_dev(e, src_dev)?;
1551                let dst_ctx = self.context_for_dev(e, dst_dev)?;
1552                src_ctx.bind_to_thread()?;
1553                let rc = unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(dst_ctx.cu_ctx(), 0) };
1554                use cudarc::driver::sys::cudaError_enum as E;
1555                if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
1556                    Ok(())
1557                } else {
1558                    Err(format!("{rc:?}").into())
1559                }
1560            })();
1561            if let Err(err) = enable {
1562                eprintln!(
1563                    "[pp] peer byte-integrity probe could not enable \
1564                     dev{src_dev}->dev{dst_dev}: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1565                );
1566            } else {
1567                enabled.push((src_dev, dst_dev));
1568            }
1569        }
1570        Ok(enabled)
1571    }
1572
1573    fn disable_probe_peer_access(
1574        &self,
1575        e: &Engine,
1576        pairs: &[(usize, usize)],
1577    ) -> Result<(), Box<dyn std::error::Error>> {
1578        let mut failures = Vec::new();
1579        for &(src_dev, dst_dev) in pairs {
1580            let disable = (|| -> Result<(), Box<dyn std::error::Error>> {
1581                let src_ctx = self.context_for_dev(e, src_dev)?;
1582                let dst_ctx = self.context_for_dev(e, dst_dev)?;
1583                src_ctx.bind_to_thread()?;
1584                let rc = unsafe { cudarc::driver::sys::cuCtxDisablePeerAccess(dst_ctx.cu_ctx()) };
1585                use cudarc::driver::sys::cudaError_enum as E;
1586                if rc == E::CUDA_SUCCESS || rc == E::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED {
1587                    Ok(())
1588                } else {
1589                    Err(format!("{rc:?}").into())
1590                }
1591            })();
1592            if let Err(err) = disable {
1593                failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1594            }
1595        }
1596        e.ctx().bind_to_thread()?;
1597        if failures.is_empty() {
1598            eprintln!(
1599                "[pp] peer byte-integrity probe teardown: disabled {} diagnostic pair(s); \
1600                 host-bounce serving has no probe-enabled peer access",
1601                pairs.len(),
1602            );
1603            Ok(())
1604        } else {
1605            Err(format!(
1606                "PP peer probe could not disable diagnostic peer access ({}); \
1607                 refusing host-bounce serving",
1608                failures.join(", "),
1609            )
1610            .into())
1611        }
1612    }
1613
1614    fn grant_probe_pool_access(
1615        &self,
1616        e: &Engine,
1617        pairs: &[(usize, usize)],
1618    ) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
1619        let mut granted = Vec::new();
1620        for &(src_dev, dst_dev) in pairs {
1621            let grant = (|| -> Result<(), Box<dyn std::error::Error>> {
1622                self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1623                let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1624                let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1625                unsafe {
1626                    cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1627                }
1628                let desc = cudarc::driver::sys::CUmemAccessDesc {
1629                    location: cudarc::driver::sys::CUmemLocation {
1630                        type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1631                        id: src_dev as i32,
1632                    },
1633                    flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1634                };
1635                let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1636                if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1637                    Ok(())
1638                } else {
1639                    Err(format!("{rc:?}").into())
1640                }
1641            })();
1642            if let Err(err) = grant {
1643                eprintln!(
1644                    "[pp] production-slot probe could not grant dev{src_dev} access to \
1645                     dev{dst_dev}'s default pool: {err}; MEMRA_PP_HOST_BOUNCE=1 remains active"
1646                );
1647            } else {
1648                granted.push((src_dev, dst_dev));
1649            }
1650        }
1651        Ok(granted)
1652    }
1653
1654    fn revoke_probe_pool_access(
1655        &self,
1656        e: &Engine,
1657        pairs: &[(usize, usize)],
1658    ) -> Result<(), Box<dyn std::error::Error>> {
1659        let mut failures = Vec::new();
1660        for &(src_dev, dst_dev) in pairs {
1661            let revoke = (|| -> Result<(), Box<dyn std::error::Error>> {
1662                self.context_for_dev(e, dst_dev)?.bind_to_thread()?;
1663                let dev = cudarc::driver::result::device::get(dst_dev as i32)?;
1664                let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
1665                unsafe {
1666                    cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, dev).result()?;
1667                }
1668                let desc = cudarc::driver::sys::CUmemAccessDesc {
1669                    location: cudarc::driver::sys::CUmemLocation {
1670                        type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1671                        id: src_dev as i32,
1672                    },
1673                    flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_NONE,
1674                };
1675                let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
1676                if rc == cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
1677                    Ok(())
1678                } else {
1679                    Err(format!("{rc:?}").into())
1680                }
1681            })();
1682            if let Err(err) = revoke {
1683                failures.push(format!("dev{src_dev}->dev{dst_dev}: {err}"));
1684            }
1685        }
1686        e.ctx().bind_to_thread()?;
1687        if failures.is_empty() {
1688            Ok(())
1689        } else {
1690            Err(format!(
1691                "PP peer probe could not revoke diagnostic pool access ({}); \
1692                 refusing host-bounce serving",
1693                failures.join(", "),
1694            )
1695            .into())
1696        }
1697    }
1698
1699    fn run_host_bounce_legacy_probe(
1700        &self,
1701        e: &Engine,
1702    ) -> Result<(), Box<dyn std::error::Error>> {
1703        let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
1704        let probe = run_peer_probe_pass(
1705            &self.stages,
1706            &enabled,
1707            true,
1708            "fixed-16KiB-legacy-preflight",
1709            PEER_PROBE_FIXED_BYTES,
1710        );
1711        let disable = self.disable_probe_peer_access(e, &enabled);
1712        disable?;
1713        probe
1714    }
1715
1716    fn new_peer_probe_boundary(
1717        &self,
1718        src_stage: usize,
1719        dst_stage: usize,
1720    ) -> Result<BoundaryRt, Box<dyn std::error::Error>> {
1721        let tx = &self.stages[src_stage];
1722        let rx = &self.stages[dst_stage];
1723        let mk_slot = || -> Result<BoundarySlot, Box<dyn std::error::Error>> {
1724            Ok(BoundarySlot {
1725                buf: Mutex::new(None),
1726                ev_tx: tx.ctx.new_event(None)?,
1727                ev_rx: rx.ctx.new_event(None)?,
1728            })
1729        };
1730        Ok(BoundaryRt {
1731            slots: [mk_slot()?, mk_slot()?],
1732            step: AtomicUsize::new(0),
1733            cross: tx.dev != rx.dev,
1734        })
1735    }
1736
1737    fn production_probe_readback(
1738        &self,
1739        path: BoundaryPath,
1740        boundary: &BoundaryRt,
1741        expected: &[u8],
1742        n: usize,
1743        slot_idx: usize,
1744    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1745        debug_assert_eq!(expected.len(), n * std::mem::size_of::<f32>());
1746        let host = peer_probe_bytes_to_f32(expected);
1747        let poison_bytes: Vec<u8> = expected.iter().map(|byte| !byte).collect();
1748        let poison = peer_probe_bytes_to_f32(&poison_bytes);
1749        let src = &self.stages[path.src_stage];
1750        let dst = &self.stages[path.dst_stage];
1751
1752        // Pre-poison the exact stream-ordered BoundarySlot allocation so a missing or partial
1753        // peer write cannot accidentally agree where the deterministic source contains zeroes.
1754        dst.ctx.bind_to_thread()?;
1755        let poison_buf = dst.stream.clone_htod(&poison)?;
1756        dst.stream.synchronize()?;
1757        let replaced = boundary.slots[slot_idx].buf.lock().unwrap().replace(poison_buf);
1758        drop(replaced);
1759        dst.stream.synchronize()?;
1760
1761        src.ctx.bind_to_thread()?;
1762        let x = src.stream.clone_htod(&host)?;
1763        self.tx_slot_path(path, boundary, &x, n, slot_idx)?;
1764
1765        dst.ctx.bind_to_thread()?;
1766        let work = self.rx_slot_path(path, boundary, slot_idx, n)?;
1767        let back = dst.stream.clone_dtoh(&work)?;
1768        dst.stream.synchronize()?;
1769        Ok(peer_probe_f32_to_bytes(&back))
1770    }
1771
1772    fn clear_peer_probe_boundary(
1773        &self,
1774        boundary: &BoundaryRt,
1775        src_stage: usize,
1776        dst_stage: usize,
1777    ) -> Result<(), Box<dyn std::error::Error>> {
1778        self.stages[dst_stage].ctx.bind_to_thread()?;
1779        for slot in &boundary.slots {
1780            let buffer = slot.buf.lock().unwrap().take();
1781            drop(buffer);
1782        }
1783        self.stages[src_stage].stream.synchronize()?;
1784        self.stages[dst_stage].stream.synchronize()?;
1785        Ok(())
1786    }
1787
1788    fn run_production_peer_probe(
1789        &self,
1790        enabled_pairs: &[(usize, usize)],
1791        host_bounce: bool,
1792        n_embd: usize,
1793    ) -> Result<(), Box<dyn std::error::Error>> {
1794        let started = std::time::Instant::now();
1795        let mut copies = 0usize;
1796        let mut skipped = 0usize;
1797        let mut total_mismatches = 0usize;
1798        let mut largest_clean_payload = 0usize;
1799
1800        for boundary_idx in 0..self.stages.len() - 1 {
1801            if self.stages[boundary_idx].dev == self.stages[boundary_idx + 1].dev {
1802                continue;
1803            }
1804            for (src_stage, dst_stage) in [
1805                (boundary_idx, boundary_idx + 1),
1806                (boundary_idx + 1, boundary_idx),
1807            ] {
1808                let src_dev = self.stages[src_stage].dev;
1809                let dst_dev = self.stages[dst_stage].dev;
1810                if !enabled_pairs.contains(&(src_dev, dst_dev)) {
1811                    if host_bounce {
1812                        skipped += PEER_PROBE_TOKEN_WIDTHS.len();
1813                        eprintln!(
1814                            "[pp] production-slot peer probe SKIP: boundary={boundary_idx} \
1815                             dev{src_dev}->dev{dst_dev} widths_tokens={:?} \
1816                             (peer or pool access unavailable; MEMRA_PP_HOST_BOUNCE=1 remains \
1817                             fail-safe)",
1818                            PEER_PROBE_TOKEN_WIDTHS,
1819                        );
1820                        continue;
1821                    }
1822                    return Err(format!(
1823                        "PP production-slot peer probe cannot run boundary={boundary_idx} \
1824                         dev{src_dev}->dev{dst_dev}: peer/pool access is not enabled"
1825                    )
1826                    .into());
1827                }
1828
1829                let probe_boundary = self.new_peer_probe_boundary(src_stage, dst_stage)?;
1830                let path = BoundaryPath {
1831                    boundary: boundary_idx,
1832                    src_stage,
1833                    dst_stage,
1834                    transport: BoundaryTransport::Peer,
1835                };
1836                let mut direction_copies = 0usize;
1837                let mut direction_skipped = 0usize;
1838                let mut direction_mismatches = 0usize;
1839                let mut direction_largest_clean = 0usize;
1840                let mut failure = None;
1841
1842                for (width_idx, tokens) in PEER_PROBE_TOKEN_WIDTHS.into_iter().enumerate() {
1843                    let n = n_embd.checked_mul(tokens).ok_or_else(|| {
1844                        format!(
1845                            "PP production-slot probe element count overflows for \
1846                             n_embd={n_embd} tokens={tokens}"
1847                        )
1848                    })?;
1849                    let bytes = n.checked_mul(std::mem::size_of::<f32>()).ok_or_else(|| {
1850                        format!(
1851                            "PP production-slot probe byte count overflows for \
1852                             n_embd={n_embd} tokens={tokens}"
1853                        )
1854                    })?;
1855                    let expected = peer_probe_pattern(
1856                        bytes,
1857                        boundary_idx,
1858                        src_dev,
1859                        dst_dev,
1860                    );
1861                    let readback = match self.production_probe_readback(
1862                        path,
1863                        &probe_boundary,
1864                        &expected,
1865                        n,
1866                        width_idx % 2,
1867                    ) {
1868                        Ok(readback) => readback,
1869                        Err(err) if host_bounce => {
1870                            skipped += 1;
1871                            direction_skipped += 1;
1872                            eprintln!(
1873                                "[pp] production-slot peer probe ERROR: \
1874                                 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
1875                                 tokens={tokens} bytes={bytes}: {err}; \
1876                                 MEMRA_PP_HOST_BOUNCE=1, proceeding on the host-staged path"
1877                            );
1878                            continue;
1879                        }
1880                        Err(err) => {
1881                            failure = Some(format!(
1882                                "PP production-slot peer probe FAILED: \
1883                                 boundary={boundary_idx} dev{src_dev}->dev{dst_dev} \
1884                                 tokens={tokens} bytes={bytes}: {err}; refusing native P2P \
1885                                 (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1886                                 MEMRA_PEER_PROBE=0 cannot authorize sharded native peer \
1887                                 transport)"
1888                            ));
1889                            break;
1890                        }
1891                    };
1892                    copies += 1;
1893                    direction_copies += 1;
1894                    let mismatches = peer_probe_mismatch_count(&expected, &readback);
1895                    if mismatches == 0 {
1896                        largest_clean_payload = largest_clean_payload.max(bytes);
1897                        direction_largest_clean = direction_largest_clean.max(bytes);
1898                    } else if host_bounce {
1899                        total_mismatches += mismatches;
1900                        direction_mismatches += mismatches;
1901                        eprintln!(
1902                            "[pp] production-slot peer probe CORRUPTION: \
1903                             boundary={boundary_idx} dev{src_dev}->dev{dst_dev} tokens={tokens} \
1904                             bytes={bytes} mismatches={mismatches}; MEMRA_PP_HOST_BOUNCE=1, \
1905                             proceeding on the host-staged path"
1906                        );
1907                    } else {
1908                        failure = Some(format!(
1909                            "PP production-slot peer probe FAILED: boundary={boundary_idx} \
1910                             dev{src_dev}->dev{dst_dev} tokens={tokens} bytes={bytes}: \
1911                             {mismatches} mismatched byte(s); refusing native P2P \
1912                             (set MEMRA_PP_HOST_BOUNCE=1 to use the host-staged path; \
1913                             MEMRA_PEER_PROBE=0 cannot authorize sharded native peer transport)"
1914                        ));
1915                        break;
1916                    }
1917                }
1918
1919                self.clear_peer_probe_boundary(&probe_boundary, src_stage, dst_stage)?;
1920                if let Some(err) = failure {
1921                    return Err(err.into());
1922                }
1923                eprintln!(
1924                    "[pp] production-slot peer probe direction: boundary={boundary_idx} \
1925                     dev{src_dev}->dev{dst_dev} copies={direction_copies} \
1926                     skipped={direction_skipped} mismatches={direction_mismatches} \
1927                     largest_clean_payload_bytes={direction_largest_clean}"
1928                );
1929            }
1930        }
1931
1932        let status = if total_mismatches > 0 {
1933            "BOUNCE"
1934        } else if skipped > 0 && copies > 0 {
1935            "PARTIAL"
1936        } else if skipped > 0 {
1937            "SKIP"
1938        } else {
1939            "PASS"
1940        };
1941        eprintln!(
1942            "[pp] production-slot peer probe {status}: widths_tokens={:?} copies={copies} \
1943             skipped={skipped} mismatches={total_mismatches} \
1944             largest_clean_payload_bytes={largest_clean_payload} elapsed_ms={:.3}",
1945            PEER_PROBE_TOKEN_WIDTHS,
1946            started.elapsed().as_secs_f64() * 1e3,
1947        );
1948        Ok(())
1949    }
1950
1951    fn run_host_bounce_production_probe(
1952        &self,
1953        e: &Engine,
1954        n_embd: usize,
1955    ) -> Result<(), Box<dyn std::error::Error>> {
1956        let enabled = self.enable_probe_peer_access(e, &self.peer_capable)?;
1957        let granted = self.grant_probe_pool_access(e, &enabled)?;
1958        let probe = self.run_production_peer_probe(&granted, true, n_embd);
1959        // Teardown always runs, but the probe verdict wins: a CORRUPTION verdict (probe is
1960        // Err) must never be masked by a teardown failure. `revoke?; disable?; probe`
1961        // short-circuited teardown errors BEFORE probe was inspected, discarding the byte-
1962        // integrity signal on any teardown hiccup (hermes 9d6ae8d3). Surface teardown errors
1963        // only when the probe itself succeeded.
1964        let revoke = self.revoke_probe_pool_access(e, &granted);
1965        let disable = self.disable_probe_peer_access(e, &enabled);
1966        probe?;
1967        revoke?;
1968        disable?;
1969        Ok(())
1970    }
1971
1972    fn init_peer_probe_geometry(
1973        &self,
1974        e: &Engine,
1975        n_embd: usize,
1976    ) -> Result<(), Box<dyn std::error::Error>> {
1977        if !self.peer_probe || !self.cross_any {
1978            return Ok(());
1979        }
1980        let bytes = n_embd
1981            .checked_mul(std::mem::size_of::<f32>())
1982            .ok_or_else(|| format!("PP boundary-slot byte count overflows for n_embd={n_embd}"))?;
1983        let result = self.peer_probe_geometry.get_or_init(|| {
1984            let probe = if self.host_bounce {
1985                self.run_host_bounce_production_probe(e, n_embd)
1986            } else {
1987                self.run_production_peer_probe(&self.peer_capable, false, n_embd)
1988            };
1989            let restore = e.ctx().bind_to_thread();
1990            match (probe, restore) {
1991                (Ok(()), Ok(())) => Ok(bytes),
1992                (Err(err), _) => Err(err.to_string()),
1993                (_, Err(err)) => Err(err.to_string()),
1994            }
1995        });
1996        let probed = result
1997            .as_ref()
1998            .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
1999        if *probed != bytes {
2000            return Err(format!(
2001                "peer probe initialized for boundary-slot bytes={probed} but model requests \
2002                 bytes={bytes}; one PP runtime supports one model geometry per process"
2003            )
2004            .into());
2005        }
2006        Ok(())
2007    }
2008
2009    /// Finish boot-time transport setup from the authoritative model width. This runs the
2010    /// production `BoundarySlot` ladder at 1/8/16/`PRIME_CHUNK_MAX_TOKENS` `[n_embd] f32` rows
2011    /// once, then allocates host-bounce slots when selected. The loader calls it before uploading
2012    /// the first model weight; `new_cache` repeats the call as an idempotent guard before the first
2013    /// forward.
2014    pub fn init_boundary_transport(
2015        &self,
2016        e: &Engine,
2017        n_embd: usize,
2018    ) -> Result<(), Box<dyn std::error::Error>> {
2019        if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire) {
2020            return Err(
2021                "PP runtime peer byte-integrity probe previously failed; refusing native P2P \
2022                 reuse in this process (restart with MEMRA_PP_HOST_BOUNCE=1)"
2023                    .into(),
2024            );
2025        }
2026        self.init_peer_probe_geometry(e, n_embd)?;
2027        if !self.host_bounce || !self.cross_any {
2028            return Ok(());
2029        }
2030        e.ctx().bind_to_thread()?;
2031        let result = self.bounce.get_or_init(|| {
2032            HostBounceRt::new(n_embd, &self.boundaries)
2033                .map(|rt| {
2034                    let bytes = rt.capacity * std::mem::size_of::<f32>();
2035                    eprintln!(
2036                        "[pp] host-bounce staging ready: n_embd={n_embd} max_tokens={} \
2037                         slot_bytes={bytes} slots_per_cross_boundary=2",
2038                        crate::cache::PRIME_CHUNK_MAX_TOKENS,
2039                    );
2040                    rt
2041                })
2042                .map_err(|err| err.to_string())
2043        });
2044        let bounce = result
2045            .as_ref()
2046            .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2047        if bounce.n_embd != n_embd {
2048            return Err(format!(
2049                "host-bounce runtime initialized for n_embd={} but model requests n_embd={n_embd}; \
2050                 one PP runtime supports one model geometry per process",
2051                bounce.n_embd,
2052            )
2053            .into());
2054        }
2055        Ok(())
2056    }
2057
2058    /// Run a due low-frequency peer re-probe at a scheduler boundary on the CUDA owner thread.
2059    /// The server calls this between ticks, after the preceding tick's result publication. The
2060    /// probe synchronizes the stage streams it exercises; no background thread touches CUDA.
2061    fn service_runtime_peer_probe(
2062        &self,
2063        e: &Engine,
2064    ) -> Result<bool, Box<dyn std::error::Error>> {
2065        if !self.peer_probe || !self.cross_any || self.host_bounce {
2066            return Ok(false);
2067        }
2068        if PEER_RUNTIME_PROBE_FAILED.load(Ordering::Acquire) {
2069            return Err(
2070                "PP runtime peer byte-integrity probe previously failed; native P2P is latched off"
2071                    .into(),
2072            );
2073        }
2074        let row_bytes = match self.peer_probe_geometry.get() {
2075            Some(Ok(bytes)) => *bytes,
2076            _ => return Ok(false),
2077        };
2078
2079        let copies = PEER_BOUNDARY_COPIES.load(Ordering::Relaxed);
2080        loop {
2081            let last = PEER_RUNTIME_LAST_PROBE_COPY.load(Ordering::Relaxed);
2082            if !runtime_peer_probe_due(copies, last) {
2083                return Ok(false);
2084            }
2085            if PEER_RUNTIME_LAST_PROBE_COPY
2086                .compare_exchange(last, copies, Ordering::AcqRel, Ordering::Relaxed)
2087                .is_ok()
2088            {
2089                break;
2090            }
2091        }
2092
2093        let probe_index = PEER_RUNTIME_PROBES.fetch_add(1, Ordering::Relaxed);
2094        let (width_index, tokens) = runtime_peer_probe_width(probe_index);
2095        let probe_bytes = row_bytes.checked_mul(tokens);
2096        let label = format!("runtime-idle-{tokens}tok");
2097        let probe = match probe_bytes {
2098            Some(bytes) => run_peer_probe_pass(
2099                &self.stages,
2100                &self.peer_capable,
2101                false,
2102                &label,
2103                bytes,
2104            ),
2105            None => Err(format!(
2106                "PP runtime peer probe byte count overflows for row_bytes={row_bytes} \
2107                 tokens={tokens}"
2108            )
2109            .into()),
2110        };
2111        let restore = e.ctx().bind_to_thread();
2112        let verdict = match (probe, restore) {
2113            (Ok(()), Ok(())) => Ok(()),
2114            (Err(err), _) => Err(err.to_string()),
2115            (_, Err(err)) => Err(err.to_string()),
2116        };
2117        if let Err(err) = verdict {
2118            PEER_RUNTIME_PROBE_FAILURES.fetch_add(1, Ordering::Relaxed);
2119            PEER_RUNTIME_PROBE_FAILED.store(true, Ordering::Release);
2120            let message = format!(
2121                "PP runtime peer byte-integrity re-probe FAILED after \
2122                 boundary_copies={copies} rung={}/{} tokens={tokens}: {err}; refusing further \
2123                 native P2P (restart with MEMRA_PP_HOST_BOUNCE=1; MEMRA_PEER_PROBE=0 cannot \
2124                 bypass this failure)",
2125                width_index + 1,
2126                PEER_PROBE_TOKEN_WIDTHS.len(),
2127            );
2128            eprintln!("[pp] SECURITY RED: {message}");
2129            return Err(message.into());
2130        }
2131        eprintln!(
2132            "[pp] runtime peer byte-integrity re-probe PASS: \
2133             boundary_copies={copies} interval_copies={PEER_RUNTIME_PROBE_INTERVAL_COPIES} \
2134             rung={}/{} tokens={tokens} bytes={} probe_index={probe_index}",
2135            width_index + 1,
2136            PEER_PROBE_TOKEN_WIDTHS.len(),
2137            probe_bytes.unwrap(),
2138        );
2139        Ok(true)
2140    }
2141
2142    fn bounce_rt(&self) -> Result<&HostBounceRt, Box<dyn std::error::Error>> {
2143        self.bounce
2144            .get()
2145            .ok_or_else(|| -> Box<dyn std::error::Error> {
2146                "MEMRA_PP_HOST_BOUNCE=1 staging was not initialized from model geometry".into()
2147            })?
2148            .as_ref()
2149            .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })
2150    }
2151
2152    /// The engine a stage's subgraph must run through: the primary engine when the stage
2153    /// lives on the primary device, else the stage's own (remote-context) engine.
2154    pub fn engine<'a>(&'a self, s: usize, primary: &'a Engine) -> &'a Engine {
2155        self.stages[s].engine.as_ref().unwrap_or(primary)
2156    }
2157
2158    /// Bind this OS thread to stage `s`'s CUDA context before issuing work there.
2159    pub fn bind_stage(&self, s: usize) -> Result<(), Box<dyn std::error::Error>> {
2160        self.stages[s].ctx.bind_to_thread()?;
2161        Ok(())
2162    }
2163
2164    /// Enter stage `s`: until the guard drops, every engine op on this thread launches on
2165    /// the stage's stream (memra_runtime ambient-stream override).
2166    pub fn enter(&self, s: usize) -> memra_runtime::StreamOverride {
2167        memra_runtime::push_stream_override(self.stages[s].stream.clone())
2168    }
2169
2170    /// Allocate/grow BOTH slots for a boundary before pipelined issue starts. `tx()` can
2171    /// grow a slot lazily, but first-use ordering requires synchronizing the RX stream
2172    /// after that allocation. If slot 1 first grows after stage 1 of chunk N has already
2173    /// been queued, that sync drains chunk N and erases the only overlap in a two-chunk
2174    /// prime. Prewarming both slots pays the same one-time sync before either stage starts.
2175    pub fn prepare_overlap_slots(&self, b: usize, n: usize)
2176                                 -> Result<(), Box<dyn std::error::Error>> {
2177        let bd = &self.boundaries[b];
2178        let s_rx = &self.stages[b + 1].stream;
2179        let mut grew = false;
2180        for sl in &bd.slots {
2181            let mut guard = sl.buf.lock().unwrap();
2182            if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2183                *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2184                grew = true;
2185            }
2186        }
2187        if grew {
2188            s_rx.synchronize()?;
2189        }
2190        Ok(())
2191    }
2192
2193    /// Boundary TX at boundary `b` (call within the stage-`b` scope; `x` = the
2194    /// materialized [n] residual): wait for the slot's previous RX (write-after-read
2195    /// guard), copy `x` into the slot's persistent buffer via the boundary's transport on
2196    /// stage-b's stream (the owning-stream/publication law), record ev_tx. Returns the
2197    /// slot index for the paired rx().
2198    ///
2199    /// `n` is the PAYLOAD ELEMENT COUNT, not a fixed model constant: the eager arm passes
2200    /// `n_embd` (one row), the batched arm passes `b_n * n_embd` (B stacked rows, the
2201    /// [B, n_embd] boundary). The slot buffer is GROW-ONLY and the transport moves exactly
2202    /// the first `n` elements — batched serving changes B every tick (chunk fill), and a
2203    /// realloc-on-every-size-change would host-sync the RX stream per width change (see the
2204    /// SLOT FIRST-USE ORDERING note below for why each allocation needs that sync). Growing
2205    /// to the high-water mark makes the syncs O(distinct widths) instead of O(width changes).
2206    pub fn tx(&self, b: usize, x: &CudaSlice<f32>, n: usize)
2207              -> Result<usize, Box<dyn std::error::Error>> {
2208        assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2209        let bd = &self.boundaries[b];
2210        let slot_idx = if pp2_overlap() {
2211            bd.step.fetch_add(1, Ordering::Relaxed) % 2
2212        } else {
2213            0
2214        };
2215        self.tx_slot(b, x, n, slot_idx)
2216    }
2217
2218    /// Pipelined boundary TX: always alternate the shared double-buffer slots, independent
2219    /// of the decode-side `MEMRA_PP_OVERLAP` experiment flag. The boundary-local atomic
2220    /// keeps concurrent callers on one slot sequence rather than each restarting at A.
2221    pub fn tx_pipelined(&self, b: usize, x: &CudaSlice<f32>, n: usize)
2222                        -> Result<usize, Box<dyn std::error::Error>> {
2223        assert_eq!(x.len(), n, "pp tx: residual length mismatch");
2224        let slot_idx = self.boundaries[b].step.fetch_add(1, Ordering::Relaxed) % 2;
2225        self.tx_slot(b, x, n, slot_idx)
2226    }
2227
2228    fn tx_slot(&self, b: usize, x: &CudaSlice<f32>, n: usize, slot_idx: usize)
2229               -> Result<usize, Box<dyn std::error::Error>> {
2230        let bd = &self.boundaries[b];
2231        let path = BoundaryPath {
2232            boundary: b,
2233            src_stage: b,
2234            dst_stage: b + 1,
2235            transport: boundary_transport(bd.cross, self.host_bounce),
2236        };
2237        let copied_slot = self.tx_slot_path(path, bd, x, n, slot_idx)?;
2238        if path.transport == BoundaryTransport::Peer {
2239            PEER_BOUNDARY_COPIES.fetch_add(1, Ordering::Relaxed);
2240        }
2241        Ok(copied_slot)
2242    }
2243
2244    fn tx_slot_path(
2245        &self,
2246        path: BoundaryPath,
2247        bd: &BoundaryRt,
2248        x: &CudaSlice<f32>,
2249        n: usize,
2250        slot_idx: usize,
2251    ) -> Result<usize, Box<dyn std::error::Error>> {
2252        debug_assert!(slot_idx < 2);
2253        let sl = &bd.slots[slot_idx];
2254        let s_tx = &self.stages[path.src_stage].stream;
2255        s_tx.wait(&sl.ev_rx)?;
2256        let mut guard = sl.buf.lock().unwrap();
2257        if guard.as_ref().map(|bf| bf.len() < n).unwrap_or(true) {
2258            // allocated on the RX stage's stream: the buffer lives on the RX device.
2259            let s_rx = &self.stages[path.dst_stage].stream;
2260            *guard = Some(s_rx.alloc_zeros::<f32>(n)?);
2261            // SLOT FIRST-USE ORDERING (2026-08-02 pipelined-gate find): the lazy alloc's
2262            // pool-alloc + memset enqueue on the RX stream; the TX copy below issues on
2263            // the TX stream, and on a slot's FIRST use ev_rx has never been recorded —
2264            // nothing orders them. With >=2 tokens in flight the RX stream is still busy
2265            // with the previous token, the memset lands AFTER the TX copy, and the
2266            // boundary residual is zeroed (window=1 passed, window>=2 failed at the
2267            // slot-1 first-use step; -overlap arms passed because the synchronous serial
2268            // arm pre-warmed both slots). Host-sync the RX stream once per slot
2269            // allocation — at most 2*(N-1) one-time syncs per process, all during prime.
2270            s_rx.synchronize()?;
2271        }
2272        let buf = guard.as_mut().unwrap();
2273        match path.transport {
2274            BoundaryTransport::Local => s_tx.memcpy_dtod(x, buf)?,
2275            BoundaryTransport::HostBounce => {
2276                debug_assert_eq!(path.src_stage, path.boundary);
2277                debug_assert_eq!(path.dst_stage, path.boundary + 1);
2278                let bounce = self.bounce_rt()?;
2279                if n > bounce.capacity {
2280                    return Err(format!(
2281                        "pp host-bounce payload {n} exceeds geometry-sized capacity {} \
2282                         (n_embd={}, max prime tokens={})",
2283                        bounce.capacity,
2284                        bounce.n_embd,
2285                        crate::cache::PRIME_CHUNK_MAX_TOKENS,
2286                    )
2287                    .into());
2288                }
2289                let mut host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2290                // D2H is issued on the producing stage's stream. ev_tx below publishes the
2291                // completed host bytes to the receiving stream; the exact prefix avoids moving
2292                // a full 64 MiB slot for a one-row decode, and no peer pointer is formed here.
2293                s_tx.memcpy_dtoh(x, host.prefix_mut(n))?;
2294            }
2295            BoundaryTransport::Peer => {
2296                // cudaMemcpyPeerAsync (M0: 2.8x NCCL at PP activation sizes), issued on the
2297                // publishing TX stream with explicit src/dst contexts.
2298                use cudarc::driver::{DevicePtr, DevicePtrMut};
2299                let (sp, _g0) = x.device_ptr(s_tx);
2300                let (dp, _g1) = buf.device_ptr_mut(s_tx);
2301                self.stages[path.src_stage].ctx.bind_to_thread()?;
2302                unsafe {
2303                    cudarc::driver::result::memcpy_peer_async(
2304                        self.stages[path.dst_stage].ctx.cu_ctx(),
2305                        dp,
2306                        self.stages[path.src_stage].ctx.cu_ctx(),
2307                        sp,
2308                        n * std::mem::size_of::<f32>(),
2309                        s_tx.cu_stream(),
2310                    )?;
2311                }
2312            }
2313        }
2314        sl.ev_tx.record(s_tx)?;
2315        Ok(slot_idx)
2316    }
2317
2318    /// Boundary RX at boundary `b` (call within the stage-`b+1` scope): wait on the slot's
2319    /// ev_tx, copy the boundary buffer into a fresh working buffer (dtod on the RX stream —
2320    /// local on the RX device in both transports), record ev_rx. The returned buffer is
2321    /// RX-stage-owned: allocated, consumed, and eventually freed on that stage's stream.
2322    pub fn rx(&self, b: usize, slot_idx: usize, n: usize)
2323              -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2324        let bd = &self.boundaries[b];
2325        let path = BoundaryPath {
2326            boundary: b,
2327            src_stage: b,
2328            dst_stage: b + 1,
2329            transport: boundary_transport(bd.cross, self.host_bounce),
2330        };
2331        self.rx_slot_path(path, bd, slot_idx, n)
2332    }
2333
2334    fn rx_slot_path(
2335        &self,
2336        path: BoundaryPath,
2337        bd: &BoundaryRt,
2338        slot_idx: usize,
2339        n: usize,
2340    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2341        let sl = &bd.slots[slot_idx];
2342        let s_rx = &self.stages[path.dst_stage].stream;
2343        s_rx.wait(&sl.ev_tx)?;
2344        let mut guard = sl.buf.lock().unwrap();
2345        let buf = guard.as_mut().expect("pp rx before tx");
2346        assert!(buf.len() >= n, "pp rx: slot holds {} < requested {n}", buf.len());
2347        if path.transport == BoundaryTransport::HostBounce {
2348            debug_assert_eq!(path.src_stage, path.boundary);
2349            debug_assert_eq!(path.dst_stage, path.boundary + 1);
2350            let bounce = self.bounce_rt()?;
2351            let host = bounce.slot(path.boundary, slot_idx)?.lock().unwrap();
2352            let mut dst = buf.slice_mut(0..n);
2353            // The destination stream already waits ev_tx, so this H2D cannot observe the
2354            // staging slot before the source stream's D2H completes.
2355            s_rx.memcpy_htod(host.prefix(n), &mut dst)?;
2356        }
2357        // uninit working buffer (fully overwritten by the copy), allocated explicitly on
2358        // the stage stream so rx() is correct even outside an enter() scope.
2359        let mut work = unsafe { s_rx.alloc::<f32>(n)? };
2360        // Slice the slot to the payload: the buffer is grow-only (see tx), so at a narrower
2361        // width it is LONGER than `work` and cudarc's memcpy_dtod (dst.len() >= src.len())
2362        // would assert. The paired tx wrote exactly these first n elements.
2363        s_rx.memcpy_dtod(&buf.slice(0..n), &mut work)?;
2364        sl.ev_rx.record(s_rx)?;
2365        Ok(work)
2366    }
2367
2368    /// PUBLISH a DEVICE-RESIDENT result off the last stage to the caller's stream
2369    /// (lane/pp2-spec 2026-08-06).
2370    ///
2371    /// Every ppN body before this one returned HOST values — `decode_step_h_ppn` and
2372    /// `decode_step_batch_ppn` both `dtoh` inside the last-stage scope, and a dtoh on the
2373    /// producing stream is self-ordering. The verify trunk is the FIRST ppN body whose
2374    /// contract is device-resident output (`decode_step_t_h_emb_dev` exists precisely so the
2375    /// accept walk argmaxes on-device instead of moving T x n_vocab f32 per round), and
2376    /// device slices carry no stream affinity: the caller resumes on the PRIMARY stream and
2377    /// dereferences buffers whose producing kernels are still queued on the last stage's
2378    /// stream. Nothing orders them.
2379    ///
2380    /// Why this only ever failed on ONE device: with stages on separate devices the caller's
2381    /// first touch is a cross-device copy that the driver orders against the source context,
2382    /// and the readback path syncs. Two streams on the SAME device genuinely overlap, so the
2383    /// primary stream reads a buffer whose matmul has not run — nondeterministic garbage
2384    /// (measured: NaN, 3155.677, and 2.87e-5 where the reference had -2.0048926), and it
2385    /// poisons the NEXT arm in the same process because the corrupted KV persists. This is
2386    /// the same class as the SLOT FIRST-USE ORDERING find above, one level up: there the
2387    /// unordered pair was alloc-memset vs TX copy, here it is stage-N compute vs the
2388    /// caller's consumer.
2389    ///
2390    /// Fix = the boundary law applied to the exit: record an event on the producing stage
2391    /// stream, make the caller's stream wait on it. Event-wait, not a device sync, so the
2392    /// stage streams keep running for the deferred-readback arm. Call INSIDE the last-stage
2393    /// scope, after the last enqueue, with the caller's (pre-`enter`) stream.
2394    pub fn publish_to(&self, s: usize, dst: &Arc<CudaStream>)
2395                      -> Result<(), Box<dyn std::error::Error>> {
2396        let st = &self.stages[s];
2397        // Same stream (STREAMS=0 rollback, or a caller already on the stage stream): the
2398        // stream orders itself; recording+waiting would be a no-op with a stray event.
2399        if Arc::ptr_eq(&st.stream, dst) {
2400            return Ok(());
2401        }
2402        let ev = st.ctx.new_event(None)?;
2403        ev.record(&st.stream)?;
2404        dst.wait(&ev)?;
2405        Ok(())
2406    }
2407
2408    /// REVERSE PUBLICATION (#87 root cause, lane/pp2spec-crash 2026-08-07): order every
2409    /// STAGE stream behind the CALLER's stream — the mirror of `publish_to`.
2410    ///
2411    /// `publish_to` orders caller READS behind stage COMPUTE. Nothing ordered the other
2412    /// direction: buffers ALLOCATED on a stage stream (the verify's returned logits/hidden,
2413    /// the VerifyCkpt stashes) are CONSUMED by kernels the caller enqueues on the PRIMARY
2414    /// stream, and when they drop, cudarc enqueues `free_async` on the ALLOCATING (stage)
2415    /// stream. With event tracking elided (the decode-path default) the drop carries no
2416    /// read-guard, so the pool can hand the block to the NEXT stage-stream allocation and
2417    /// its writes overwrite memory the queued primary-stream consumer has not read yet.
2418    /// Measured (research/pp2spec-crash-20260807): the spec round-seed read 13/4096 NaN =
2419    /// the uninitialized-bits signature (P(NaN|random u32) ~ 1/256), clean by host re-read
2420    /// time — a read-before-write race, fatal via the argmax-sentinel -> embed_gather MMU
2421    /// fault, and gated on c>=2 because a backed-up primary stream widens the window.
2422    ///
2423    /// Fix law: before a ppN body enqueues NEW stage-stream work (allocations that may
2424    /// reuse freed blocks), every stage stream waits the caller's stream at its current
2425    /// point. All primary consumers of the previous round's stage-allocated buffers are
2426    /// enqueued by then (single host thread), so reuse-writes land strictly after them.
2427    /// Call at ppN-body ENTRY with the pre-`enter` caller stream. Door-shut configs never
2428    /// build a PpNRt, so single-card behavior is untouched.
2429    pub fn fence_stages_behind(&self, src: &Arc<CudaStream>)
2430                               -> Result<(), Box<dyn std::error::Error>> {
2431        let ev = src.context().new_event(None)?;
2432        ev.record(src)?;
2433        for st in &self.stages {
2434            if Arc::ptr_eq(&st.stream, src) {
2435                continue;
2436            }
2437            st.stream.wait(&ev)?;
2438        }
2439        Ok(())
2440    }
2441
2442    /// Deferred readback: record a fresh completion event on the LAST stage's stream
2443    /// (call after the step's logits matmul has been enqueued there).
2444    pub fn record_done(&self) -> Result<CudaEvent, Box<dyn std::error::Error>> {
2445        let last = &self.stages[self.stages.len() - 1];
2446        let ev = last.ctx.new_event(None)?;
2447        ev.record(&last.stream)?;
2448        Ok(ev)
2449    }
2450
2451    /// The dedicated readback stream (last stage's context).
2452    pub fn readback_stream(&self) -> &Arc<CudaStream> {
2453        &self.readback
2454    }
2455}
2456
2457/// Service a due runtime peer probe without constructing a PP runtime on door-shut placements.
2458/// Must be called by the CUDA owner thread at a scheduling boundary.
2459pub fn service_runtime_peer_probe(e: &Engine) -> Result<bool, Box<dyn std::error::Error>> {
2460    let Some(rt) = RTN.get() else { return Ok(false) };
2461    let rt = rt
2462        .as_ref()
2463        .map_err(|err| -> Box<dyn std::error::Error> { err.clone().into() })?;
2464    rt.service_runtime_peer_probe(e)
2465}
2466
2467/// M2 increment 3: a step's logits, still device-resident on the LAST stage. `wait()`
2468/// orders the readback stream behind the step's completion event, copies, and syncs —
2469/// tokens enqueued after this step keep running on the stage streams while the caller
2470/// drains token t. Dropping without waiting is safe (buffers free stream-ordered).
2471pub struct PendingLogits {
2472    logits: CudaSlice<f32>,
2473    ev: CudaEvent,
2474    rb: Arc<CudaStream>,
2475}
2476
2477impl PendingLogits {
2478    pub fn new(logits: CudaSlice<f32>, ev: CudaEvent, rb: Arc<CudaStream>) -> Self {
2479        PendingLogits { logits, ev, rb }
2480    }
2481
2482    /// Blocks until this step's logits are computed, returns them host-side. Only this
2483    /// step's work is waited on (event-ordered) — NOT later tokens already enqueued on
2484    /// the stage streams.
2485    pub fn wait(self) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2486        self.rb.wait(&self.ev)?;
2487        let host = self.rb.clone_dtoh(&self.logits)?;
2488        self.rb.synchronize()?;
2489        // logits drop AFTER the sync: the D2H has fully completed, so the stream-ordered
2490        // free on the compute stream cannot race the copy.
2491        Ok(host)
2492    }
2493}
2494
2495/// Bring up the PP transport while model geometry is known but before model weights upload.
2496/// Door-shut and placement-free loads remain untouched.
2497pub fn init_model_transport(
2498    e: &Engine,
2499    cfg: &memra_gguf::config::ModelConfig,
2500    n_trunk: usize,
2501) -> Result<(), Box<dyn std::error::Error>> {
2502    if pp2_streams_off() || pp2_devices_env().is_none() || pp_cuts(n_trunk).is_none() {
2503        return Ok(());
2504    }
2505    PpNRt::get(e)?.init_boundary_transport(e, cfg.n_embd as usize)
2506}
2507
2508/// Stage-owned cache allocation door: when the ppN door is open AND `MEMRA_PP_DEVICES`
2509/// is set (placement plumbing), each layer's cache is allocated by its OWNING stage's
2510/// engine — on one device this is byte-for-byte today's allocation (gated); cross-device
2511/// it puts each stage's KV on that stage's HBM. Door shut or devices unset: plain
2512/// `Cache::new` (zero behavior change). Trailing MTP/NextN layers (beyond the trunk)
2513/// map to the LAST stage.
2514pub fn new_cache(e: &Engine, cfg: &memra_gguf::config::ModelConfig, max_ctx: usize)
2515                 -> Result<crate::cache::Cache, Box<dyn std::error::Error>> {
2516    let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
2517    if let Some(fence) = pp_cuts(n_trunk) {
2518        if pp2_devices_env().is_some() && !pp2_streams_off() {
2519            let rt = PpNRt::get(e)?;
2520            rt.init_boundary_transport(e, cfg.n_embd as usize)?;
2521            let n_st = fence.len() - 1;
2522            assert_eq!(
2523                rt.n_stages(), n_st,
2524                "PpNRt stage count {} != fence stages {n_st}", rt.n_stages()
2525            );
2526            // #87 REVERSE PUBLICATION at ADMISSION (lane/pp2spec-crash): this is the one
2527            // stage-stream allocation site OUTSIDE the ppN step bodies — a NEW session's
2528            // KV alloc_zeros enqueue on the STAGE streams, and their pool blocks can be
2529            // reuse of buffers freed from ANOTHER session's in-flight verify whose
2530            // primary-stream reads are still queued (the c=2 residual: exactly one trap
2531            // per admission collision, round 0, after the step-body fences landed).
2532            // Order the stage streams behind the caller before the memsets can clobber.
2533            // Anatomy: `PpNRt::fence_stages_behind`.
2534            rt.fence_stages_behind(&e.stream())?;
2535            let devs: Vec<&dyn memra_kv::KvDev> =
2536                (0..n_st).map(|s| rt.engine(s, e) as &dyn memra_kv::KvDev).collect();
2537            let cache = crate::cache::Cache::new_ppn(&devs, &fence, cfg, max_ctx)?;
2538            sync_stages_after_load(e, n_trunk)?;
2539            return Ok(cache);
2540        }
2541        if !pp2_streams_off() {
2542            // CACHE BIRTH BARRIER (2026-08-02 pipelined-arm residual race): with the door
2543            // open but no device placement, Cache::new's alloc_zeros memsets enqueue on
2544            // the PRIMARY worker stream while the first KV appends / recurrent-state
2545            // reads run on the per-stage streams — no event orders them, and under
2546            // deferred readback the stage streams are hot immediately (a memset tail
2547            // can zero an already-appended KV row; intermittent, ~1-in-3 gate FAIL).
2548            // One context-sync per cache creation kills the class.
2549            let cache = crate::cache::Cache::new(e, cfg, max_ctx)?;
2550            sync_stages_after_load(e, n_trunk)?;
2551            return Ok(cache);
2552        }
2553    }
2554    crate::cache::Cache::new(e, cfg, max_ctx)
2555}
2556
2557/// M2 increment 2 LOAD BARRIER: weight uploads and decode-mirror builds enqueue on the
2558/// loading engines' WORKER streams; the first consumer launches on a DIFFERENT stream
2559/// with no load->decode event — the door-off reference walk on the primary worker
2560/// stream (sharded load: remote builds still in flight), or a fresh per-stage stream.
2561/// The 2026-08-02 gate finds (n2-dev01 step-0 168k-logit graze; split5 ref=0.0 head —
2562/// a half-built rp4 mirror — poisoning step-0 KV and every later step): one
2563/// context-wide synchronize per stage at load end kills the class. No-op when the door
2564/// is shut at load (single-stream load+decode is ordered by the stream itself).
2565pub fn sync_stages_after_load(e: &Engine, n_trunk: usize)
2566                              -> Result<(), Box<dyn std::error::Error>> {
2567    if pp2_streams_off() || pp_cuts(n_trunk).is_none() {
2568        return Ok(());
2569    }
2570    let rt = PpNRt::get(e)?;
2571    for s in 0..rt.n_stages() {
2572        rt.stages[s].ctx.bind_to_thread()?;
2573        unsafe {
2574            cudarc::driver::sys::cuCtxSynchronize().result()?;
2575        }
2576    }
2577    e.ctx().bind_to_thread()?;
2578    unsafe {
2579        cudarc::driver::sys::cuCtxSynchronize().result()?;
2580    }
2581    Ok(())
2582}
2583
2584/// M2 increment 2 (weight sharding): the engine that should UPLOAD layer `il`'s weights
2585/// (and build its decode mirrors) — the owning stage's engine when the door is open with
2586/// device placement and sharding not rolled back; else the primary. `il >= n_trunk`
2587/// (MTP/NextN blocks) maps to the last stage. The head (output_norm + lm head) belongs
2588/// to the last trunk layer's stage — call with `il = n_trunk - 1`.
2589pub fn layer_engine<'a>(e: &'a Engine, n_trunk: usize, il: usize)
2590                        -> Result<&'a Engine, Box<dyn std::error::Error>> {
2591    if pp_shard_off() || pp2_devices_env().is_none() || pp2_streams_off() {
2592        return Ok(e);
2593    }
2594    let Some(fence) = pp_cuts(n_trunk) else { return Ok(e) };
2595    let rt = PpNRt::get(e)?;
2596    let s = stage_of(&fence, il.min(n_trunk - 1));
2597    Ok(rt.engine(s, e))
2598}
2599
2600/// Restore a cache checkpoint through each layer's owning engine.
2601///
2602/// `source = None` is an in-place rewind: the target already owns the append-only KV bytes and
2603/// only its lengths plus recurrent state move back to the snapshot. `Some(source)` restores into
2604/// a freshly allocated larger cache: checkpoint-valid KV rows are copied from the parked cache,
2605/// while recurrent state always comes from the checkpoint's owned device copies.
2606///
2607/// This cannot use `Cache::rollback(e, ...)` under cross-device PP: a single primary engine is
2608/// not the owner of every stage's cache buffers. The rare rewind/grow boundary synchronizes open
2609/// PP contexts before publishing the restored cache to the next request.
2610pub fn restore_cache_checkpoint(
2611    e: &Engine,
2612    cfg: &memra_gguf::config::ModelConfig,
2613    source: Option<&crate::cache::Cache>,
2614    target: &mut crate::cache::Cache,
2615    snap: &crate::cache::CacheSnapshot,
2616) -> Result<(), Box<dyn std::error::Error>> {
2617    let n = target.kv.len();
2618    if target.recur.len() != n
2619        || snap.kv_len.len() != n
2620        || snap.conv.len() != n
2621        || snap.ssm.len() != n
2622        || source.is_some_and(|s| s.kv.len() != n || s.recur.len() != n)
2623    {
2624        return Err("checkpoint cache layer-count mismatch".into());
2625    }
2626    if snap.pos > target.max_ctx {
2627        return Err(format!(
2628            "checkpoint pos {} exceeds target capacity {}",
2629            snap.pos, target.max_ctx,
2630        )
2631        .into());
2632    }
2633
2634    let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
2635    for il in 0..n {
2636        let owner = layer_engine(e, n_trunk, il)?;
2637        let src_kv = source.map(|s| &s.kv[il]);
2638        match (src_kv, target.kv[il].as_mut(), snap.kv_len[il]) {
2639            (Some(Some(src)), Some(dst), Some(len)) => {
2640                if len > src.len || len > target.max_ctx {
2641                    return Err(format!(
2642                        "checkpoint layer {il} len {len} exceeds source {} or target {}",
2643                        src.len, target.max_ctx,
2644                    )
2645                    .into());
2646                }
2647                if src.kv_dim_k != dst.kv_dim_k
2648                    || src.kv_dim_v != dst.kv_dim_v
2649                    || src.k_tok_bytes != dst.k_tok_bytes
2650                    || src.v_tok_bytes != dst.v_tok_bytes
2651                {
2652                    return Err(format!("checkpoint KV layout mismatch at layer {il}").into());
2653                }
2654                let kb = len * src.k_tok_bytes;
2655                let vb = len * src.v_tok_bytes;
2656                if kb > 0 {
2657                    owner.copy_u8_into(&mut dst.k, 0, &src.k, kb)?;
2658                }
2659                if vb > 0 {
2660                    owner.copy_u8_into(&mut dst.v, 0, &src.v, vb)?;
2661                }
2662                dst.len = len;
2663                owner.set_i32_one(&mut dst.len_d, len as i32)?;
2664            }
2665            (None, Some(dst), Some(len)) => {
2666                if len > dst.len || len > target.max_ctx {
2667                    return Err(format!(
2668                        "checkpoint layer {il} len {len} exceeds live {} or target {}",
2669                        dst.len, target.max_ctx,
2670                    )
2671                    .into());
2672                }
2673                dst.len = len;
2674                owner.set_i32_one(&mut dst.len_d, len as i32)?;
2675            }
2676            (Some(None), None, None) | (None, None, None) => {}
2677            _ => return Err(format!("checkpoint KV kind mismatch at layer {il}").into()),
2678        }
2679
2680        match (
2681            target.recur[il].as_mut(),
2682            &snap.conv[il],
2683            &snap.ssm[il],
2684        ) {
2685            (Some(dst), Some(conv), Some(ssm)) => {
2686                if conv.len() != dst.conv_state.len() || ssm.len() != dst.ssm_state.len() {
2687                    return Err(
2688                        format!("checkpoint recurrent layout mismatch at layer {il}").into(),
2689                    );
2690                }
2691                owner.copy_into(&mut dst.conv_state, 0, conv, conv.len())?;
2692                owner.copy_into(&mut dst.ssm_state, 0, ssm, ssm.len())?;
2693            }
2694            (None, None, None) => {}
2695            _ => {
2696                return Err(
2697                    format!("checkpoint recurrent kind mismatch at layer {il}").into(),
2698                );
2699            }
2700        }
2701    }
2702    target.pos = snap.pos;
2703
2704    // Open PP uses per-stage streams/contexts; publish every restored plane before the caller
2705    // starts the next prime. Door-shut single-stream restores remain naturally ordered.
2706    sync_stages_after_load(e, n_trunk)?;
2707    if source.is_some() {
2708        // A grown cache replaces and drops the source immediately after this returns. Bound the
2709        // D2D copies first so an async-pool free cannot recycle a source plane prematurely.
2710        e.stream().synchronize()?;
2711    }
2712    Ok(())
2713}
2714
2715#[cfg(test)]
2716mod host_bounce_tests {
2717    use super::{
2718        boundary_transport, dual_pp_eligibility, dual_pp_timing_dropped,
2719        dual_pp_timing_snapshot, dual_pp_wave_mid, host_bounce_capacity,
2720        peer_probe_bytes_to_f32, peer_probe_decision, peer_probe_f32_to_bytes,
2721        peer_probe_mismatch_count, peer_probe_pattern, peer_probe_startup_policy,
2722        record_dual_pp_stage_result, runtime_peer_probe_due, runtime_peer_probe_width,
2723        BoundaryTransport, PeerProbeDecision, PeerProbeStartupPolicy,
2724        DUAL_PP_HOST_BOUNCE_REFUSAL, DUAL_PP_SINGLE_SLOT_REFUSAL, PEER_PROBE_FIXED_BYTES,
2725        PEER_PROBE_REQUIRED_REFUSAL, PEER_PROBE_TOKEN_WIDTHS,
2726        PEER_RUNTIME_PROBE_CYCLE_COPIES, PEER_RUNTIME_PROBE_INTERVAL_COPIES,
2727    };
2728
2729    // ---- 2026-08-11 default-flip safety regression (owner-ordered) ----------------------
2730    // All pure-resolution tests: no env mutation (parallel test threads share process env).
2731
2732    #[test]
2733    fn flip_default_is_dual_auto_with_explicit_off_and_forced_seams() {
2734        use super::{dual_pp_mode_resolve, DualPpMode};
2735        assert_eq!(dual_pp_mode_resolve(None), DualPpMode::Auto);
2736        assert_eq!(dual_pp_mode_resolve(Some("0")), DualPpMode::Off);
2737        assert_eq!(dual_pp_mode_resolve(Some("1")), DualPpMode::Forced);
2738        // Any other value is not a silent third state: treat as the default.
2739        assert_eq!(dual_pp_mode_resolve(Some("2")), DualPpMode::Auto);
2740        assert_eq!(dual_pp_mode_resolve(Some("")), DualPpMode::Auto);
2741    }
2742
2743    #[test]
2744    fn flip_overlap_follows_mode_and_one_flag_restores_preflip_serial() {
2745        use super::{pp2_overlap_resolve, DualPpMode};
2746        // Naked default = the re-gated dual arm: overlap ON.
2747        assert!(pp2_overlap_resolve(None, DualPpMode::Auto));
2748        // MEMRA_DUAL_PP=0 ALONE restores the exact pre-flip naked path (single-slot serial).
2749        assert!(!pp2_overlap_resolve(None, DualPpMode::Off));
2750        // The explicit pre-flip request keeps its binding single-slot refusal reachable.
2751        assert!(!pp2_overlap_resolve(None, DualPpMode::Forced));
2752        // Explicit values always win over the mode.
2753        for mode in [DualPpMode::Off, DualPpMode::Forced, DualPpMode::Auto] {
2754            assert!(pp2_overlap_resolve(Some("1"), mode));
2755            assert!(!pp2_overlap_resolve(Some("0"), mode));
2756        }
2757    }
2758
2759    #[test]
2760    fn flip_auto_routes_only_the_regated_regime_and_degrades_serially_elsewhere() {
2761        use super::{dual_pp_route, DualPpMode};
2762        // The exact box1 re-gate regime: PP-2, double-slot, peer transport, B>=2.
2763        assert!(dual_pp_route(DualPpMode::Auto, 2, 2, true, false));
2764        assert!(dual_pp_route(DualPpMode::Auto, 17, 2, true, false));
2765        // Outside it, Auto must DEGRADE (serial PP-N walker), never refuse:
2766        assert!(!dual_pp_route(DualPpMode::Auto, 1, 2, true, false)); // no second wave
2767        assert!(!dual_pp_route(DualPpMode::Auto, 2, 3, true, false)); // naked PP-3 keeps serving
2768        assert!(!dual_pp_route(DualPpMode::Auto, 2, 2, false, false)); // single-slot boundary
2769        assert!(!dual_pp_route(DualPpMode::Auto, 2, 2, true, true)); // host-bounce escape hatch
2770        // Forced routes every B>=2 call into the dual body so the binding refusals fire loud.
2771        assert!(dual_pp_route(DualPpMode::Forced, 2, 3, false, true));
2772        assert!(!dual_pp_route(DualPpMode::Forced, 1, 2, true, false));
2773        // Off is the rollback seam: never dual.
2774        assert!(!dual_pp_route(DualPpMode::Off, 8, 2, true, false));
2775    }
2776
2777    #[test]
2778    fn dual_pp_split_is_honest_at_one_and_ceil_first_afterward() {
2779        assert_eq!(dual_pp_wave_mid(1), None);
2780        assert_eq!(dual_pp_wave_mid(2), Some(1));
2781        assert_eq!(dual_pp_wave_mid(3), Some(2));
2782        assert_eq!(dual_pp_wave_mid(8), Some(4));
2783        assert_eq!(dual_pp_wave_mid(16), Some(8));
2784        assert_eq!(dual_pp_wave_mid(31), Some(16));
2785        assert_eq!(dual_pp_wave_mid(32), Some(16));
2786    }
2787
2788    #[test]
2789    fn dual_pp_refuses_single_slot_and_non_pp2_shapes() {
2790        assert_eq!(dual_pp_eligibility(2, false, false), Err(DUAL_PP_SINGLE_SLOT_REFUSAL));
2791        assert!(dual_pp_eligibility(2, true, false).is_ok());
2792        assert!(dual_pp_eligibility(3, true, false).is_err());
2793    }
2794
2795    #[test]
2796    fn dual_pp_refuses_unvalidated_host_bounce_transport() {
2797        assert_eq!(
2798            dual_pp_eligibility(2, true, true),
2799            Err(DUAL_PP_HOST_BOUNCE_REFUSAL),
2800        );
2801    }
2802
2803    #[test]
2804    fn dual_pp_timing_error_is_counted_without_recording_a_sample() {
2805        let dropped_before = dual_pp_timing_dropped();
2806        let (_, samples_before) = dual_pp_timing_snapshot();
2807        record_dual_pp_stage_result(0, Err::<f32, _>("CUDA_ERROR_NOT_READY"));
2808        let (_, samples_after) = dual_pp_timing_snapshot();
2809        assert_eq!(samples_after[0], samples_before[0]);
2810        assert!(dual_pp_timing_dropped() >= dropped_before + 1);
2811    }
2812
2813    #[test]
2814    fn corrupted_peer_readback_fails_closed_unless_host_bounce_is_selected() {
2815        assert_eq!(
2816            PEER_PROBE_TOKEN_WIDTHS,
2817            [1, 8, 16, crate::cache::PRIME_CHUNK_MAX_TOKENS],
2818        );
2819        let largest_payload_bytes = PEER_PROBE_TOKEN_WIDTHS[3]
2820            * 4096
2821            * std::mem::size_of::<f32>();
2822        assert_eq!(largest_payload_bytes, 64 * 1024 * 1024);
2823        assert!(largest_payload_bytes >= 1024 * 1024);
2824        let expected = peer_probe_pattern(PEER_PROBE_FIXED_BYTES, 2, 0, 1);
2825        assert_eq!(
2826            peer_probe_f32_to_bytes(&peer_probe_bytes_to_f32(&expected)),
2827            expected,
2828        );
2829        let mut corrupted = expected.clone();
2830        for offset in [0, 8_191, PEER_PROBE_FIXED_BYTES - 1] {
2831            corrupted[offset] ^= 0x5a;
2832        }
2833
2834        assert_eq!(peer_probe_mismatch_count(&expected, &corrupted), 3);
2835        assert_eq!(
2836            peer_probe_decision(&expected, &corrupted, false),
2837            Err("3 mismatched byte(s)".to_string()),
2838        );
2839        assert_eq!(
2840            peer_probe_decision(&expected, &corrupted, true),
2841            Ok(PeerProbeDecision::ProceedWithHostBounce { mismatches: 3 }),
2842        );
2843    }
2844
2845    #[test]
2846    fn probe_off_refusal_matrix_is_fail_closed_only_for_sharded_native_peer() {
2847        for probe_on in [false, true] {
2848            for sharded in [false, true] {
2849                for host_bounce in [false, true] {
2850                    let got = peer_probe_startup_policy(probe_on, sharded, host_bounce);
2851                    let expected = match (probe_on, sharded, host_bounce) {
2852                        (false, true, false) => Err(PEER_PROBE_REQUIRED_REFUSAL),
2853                        (false, true, true) => {
2854                            Ok(PeerProbeStartupPolicy::BypassedWithHostBounce)
2855                        }
2856                        _ => Ok(PeerProbeStartupPolicy::Allowed),
2857                    };
2858                    assert_eq!(
2859                        got, expected,
2860                        "probe_on={probe_on} sharded={sharded} host_bounce={host_bounce}",
2861                    );
2862                }
2863            }
2864        }
2865        assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PEER_PROBE=0"));
2866        assert!(PEER_PROBE_REQUIRED_REFUSAL.contains("MEMRA_PP_HOST_BOUNCE!=1"));
2867    }
2868
2869    #[test]
2870    fn runtime_reprobe_becomes_due_only_at_each_copy_interval() {
2871        let every = PEER_RUNTIME_PROBE_INTERVAL_COPIES;
2872        assert_eq!(PEER_RUNTIME_PROBE_CYCLE_COPIES, 4 * every);
2873        assert!(!runtime_peer_probe_due(every - 1, 0));
2874        assert!(runtime_peer_probe_due(every, 0));
2875        assert!(!runtime_peer_probe_due(2 * every - 1, every));
2876        assert!(runtime_peer_probe_due(2 * every, every));
2877        assert!(!runtime_peer_probe_due(every - 1, every));
2878    }
2879
2880    #[test]
2881    fn runtime_reprobe_rotates_every_boot_width_in_deterministic_order() {
2882        let cycle_len = PEER_PROBE_TOKEN_WIDTHS.len();
2883        let got: Vec<(usize, usize)> = (0..2 * cycle_len)
2884            .map(|probe_index| runtime_peer_probe_width(probe_index as u64))
2885            .collect();
2886        let expected: Vec<(usize, usize)> = PEER_PROBE_TOKEN_WIDTHS
2887            .into_iter()
2888            .enumerate()
2889            .chain(PEER_PROBE_TOKEN_WIDTHS.into_iter().enumerate())
2890            .collect();
2891
2892        assert_eq!(got, expected);
2893        assert_eq!(got[cycle_len - 1].1, crate::cache::PRIME_CHUNK_MAX_TOKENS);
2894        assert_eq!(got[2 * cycle_len - 1].1, crate::cache::PRIME_CHUNK_MAX_TOKENS);
2895    }
2896
2897    #[test]
2898    fn transport_selection_keeps_peer_default_and_bounces_only_cross_device() {
2899        assert_eq!(boundary_transport(false, false), BoundaryTransport::Local);
2900        assert_eq!(boundary_transport(false, true), BoundaryTransport::Local);
2901        assert_eq!(boundary_transport(true, false), BoundaryTransport::Peer);
2902        assert_eq!(
2903            boundary_transport(true, true),
2904            BoundaryTransport::HostBounce
2905        );
2906    }
2907
2908    #[test]
2909    fn step37_geometry_sizes_each_slot_from_the_prime_cap() {
2910        let (elems, bytes) = host_bounce_capacity(4096).expect("valid Step-3.7 geometry");
2911        assert_eq!(elems, 4096 * crate::cache::PRIME_CHUNK_MAX_TOKENS);
2912        assert_eq!(bytes, 64 * 1024 * 1024);
2913    }
2914
2915    #[test]
2916    fn host_bounce_capacity_rejects_invalid_or_overflowing_geometry() {
2917        assert!(host_bounce_capacity(0).is_err());
2918        assert!(host_bounce_capacity(usize::MAX).is_err());
2919    }
2920}