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